งานพอร์ต Tailscale ไปยัง Plan 9
(tailscale.com)- การรองรับ Plan 9 ที่เริ่มจากประกาศวันเมษาหน้าโง่ ได้ต่อยอดเป็น PR จริงและการแก้ไขเคอร์เนล·Go จนถึงสถานะที่ Tailscale ทำงานบน Plan 9 ได้ ณ วันที่ 2 เมษายน 2025
- นี่ไม่ใช่แค่ปัญหาการบิลด์ด้วย
GOOS=plan9 GOARCH=386เท่านั้น แต่ใน Plan 9 port ของ Go ยังพบทั้งปัญหา runtime crash และการจัดการกรณีพิเศษในคอมไพเลอร์ก่อน - การแก้ไขเคอร์เนล Plan 9 และ runtime·compiler ของ Go โดย Russ Cox ช่วยสะสางเรื่อง SSE, floating-point context, monotonic time, DNS และสภาพแวดล้อมสำหรับพัฒนาไปพร้อมกัน
- Tailscale ใช้อินเทอร์เฟซไฟล์
/netของ Plan 9 เพื่อต่อ การทำงานคล้าย TUN, routing, Tailscale SSH, MagicDNS และ service discovery เข้าด้วยกัน แต่บางส่วนยังเป็นการทำแบบชั่วคราวหรือยังไม่สมบูรณ์ - ขอบเขตการทดสอบตอนนี้ยังอยู่ที่ 9legacy และ
GOARCH=386เป็นหลัก ส่วน 9front·amd64·exit node·การรองรับ Gonet/netnsยังต้องมีการทดสอบเพิ่มหรือออกแบบใหม่
มุกวันเมษาหน้าโง่ที่กลายเป็นงานพอร์ตจริง
- Tailscale ประกาศรองรับ Plan 9 เมื่อวันที่ 1 เมษายน 2025 และวันถัดมาก็เปิดเผยเบื้องหลังว่าประกาศนี้อ้างอิงจากงานพอร์ตที่ใช้งานได้จริง
- งานนี้ต่อยอดไปเป็น Tailscale PR และการแก้ไขต่าง ๆ ใน Plan 9·Go
- แนวทางแรกเริ่มมาจากความคาดหวังว่า หากบิลด์ Go binary ของ Tailscale สองตัวด้วย
GOOS=plan9 GOARCH=386 go install ./cmd/tailscale{,d}ก็น่าจะเพียงพอ - ในความพยายามครั้งแรกเมื่อเดือนสิงหาคม 2023 แม้บางส่วนจะบิลด์ได้ แต่ระหว่างรันกลับเกิดการ crash แบบผิดปกติ
- เพราะ Plan 9 port ของ Go ไม่ได้เป็น first-class port จึงมี regression ที่ถูกปล่อยค้างไว้
- อีกความเป็นไปได้คือ Tailscale ไปใช้งาน Go บน Plan 9 หนักกว่าที่เคยมีมาก่อน
- งานพอร์ตหยุดชะงักตลอดปี 2024 ก่อนจะกลับมาเดินต่ออีกครั้งพร้อมไอเดียวันเมษาหน้าโง่ในเดือนมีนาคม 2025
การสะสาง SSE และการรองรับ Plan 9 ใน Go
- คำสั่ง SSE ที่ถูกนำมาใช้ครั้งแรกใน Intel Pentium III เมื่อปี 1999 เป็นหนึ่งในจุดตั้งต้นสำคัญของงานนี้
- คอมไพเลอร์ Go พยายามหลีกเลี่ยงการใช้ SSE เมื่อ target เป็น Plan 9
- เพราะเคอร์เนล Plan 9 ไม่ได้บันทึก·กู้คืนรีจิสเตอร์ SSE ใน note handler
- คอมไพเลอร์ Go ไม่สามารถรู้ได้ว่าโค้ดใดจะถูกรันใน note handler จึงพยายามปิด SSE แบบทั้งระบบ
- การจัดการกรณีพิเศษนี้พังบ่อย และทำให้มีข้อยกเว้น
plan9กระจายอยู่ทั่วคอมไพเลอร์
- Russ Cox แก้เคอร์เนล Plan 9 ให้จัดการ floating-point·SIMD context ใน note handler ได้
- ฝั่ง 386 มีการแก้ไข sys/src/9: allow floating point in note handlers
- ในเคอร์เนล amd64 9k ยังพบปัญหาเพิ่มเติม เช่น FP state aliasing หลัง fork, SIMD ใน note handler และการสูญหายของรีจิสเตอร์ใน
noted(NCONT)
- ฝั่ง Go มีการดำเนินการ ลบการจัดการกรณีพิเศษในการสร้างโค้ดสำหรับ Plan 9 ทำให้
tailscaledรันได้นานขึ้น
IPC และสภาพแวดล้อมสำหรับพัฒนา
- หลังจากนั้น
tailscaledเริ่ม crash ด้วย หน่วยความจำไม่พอ แทนที่จะเป็น stack corruption - ในความพยายามพอร์ต Plan 9 ก่อนหน้านี้ พบว่ามีบั๊กในแพ็กเกจ IPC
safesocketของ Tailscale ที่สร้าง goroutine แบบไม่สิ้นสุด - จึงแก้เฉพาะหน้าด้วยการเปลี่ยนไปใช้ localhost TCP และปัญหาก็หายไป
- แม้จะไม่ค่อยเข้ากับแนวคิด “ทุกอย่างคือไฟล์” ของ Plan 9 แต่ก็ยืนยันได้ว่าบริการอื่นบน Plan 9 ก็ใช้ localhost TCP เช่นกัน
- ในอนาคต วิธีที่ดีกว่าอาจเป็นการวาง LocalAPI บน srv9p package ที่ Russ พอร์ตมาเป็น Go
- ตอนนี้ implementation ยังใส่การยืนยันตัวตนบน localhost แบบแพลตฟอร์มอื่นไม่ได้ จึงระบุชัดว่าไม่ควรใช้บนเครื่อง Plan 9 ที่ใช้ร่วมกัน
- งานพัฒนาช่วงแรกทำบน VM ที่ใช้ 9legacy CD image และการดาวน์โหลด binary ผ่าน HTTP มารันซ้ำ ๆ ค่อนข้างช้า
- rsc/plan9 ที่ Russ Cox ทำไว้ มีทั้ง source ของ Plan 9, binary ที่คอมไพล์ไว้ล่วงหน้า และสคริปต์
./boot/qemu- qemu VM บูตแบบไม่มีดิสก์ และใช้ Git repository ที่ 9P server บน localhost ให้บริการเป็น root filesystem
- การแชร์ filesystem ระหว่างเครื่องพัฒนากับ Plan 9 ช่วยลดเวลาวนรอบจากระดับนาทีเหลือระดับวินาที
- qemu ยังใช้ virtio ด้วย จึงทำงานได้เร็วขึ้นอีก
การผนวกเครือข่าย: TUN, routing, MagicDNS
- Tailscale เวอร์ชันแรกที่ทำงานได้ ใช้โหมด user-space networking ซึ่งไม่พึ่ง kernel network stack
- TCP, UDP, ICMP ฯลฯ ถูกจัดการผ่าน netstack ของ gVisor
- หากต้องการเข้าถึง tailnet จากเครื่อง Plan 9 ต้องใช้ HTTP/SOCKS5 proxy ของ
tailscaled - แต่วิธีนี้ไม่เหมาะนัก เพราะโปรแกรมบน Plan 9 แทบไม่มีตัวไหนรู้จักตัวแปรสภาพแวดล้อม
HTTP_PROXYหรือALL_PROXY
- การทำงานคล้าย TUN ของ Plan 9 นั้นเรียบง่ายมาก
- เปิด
/net/ipifc/cloneแล้วอ่านหมายเลขอินเทอร์เฟซใหม่ - เขียน
"bind pkt\n"ไปยัง control fd แล้วจะมีอินเทอร์เฟซใหม่อย่าง/net/ipifc/2/*ปรากฏขึ้น - เปิด
/net/ipifc/2/dataเพื่ออ่านและเขียนแพ็กเก็ต IP ได้โดยตรง - ไม่ต้องมี ioctl แยกหรือการ framing ความยาวข้อมูล
- เปิด
- การจัดการ routing table ก็ทำผ่านไฟล์
/net/iproute- เขียน
"tag tail\n"เพื่อใส่แท็กtailให้ route ที่เพิ่มหลังจากนั้น - เพิ่ม route ด้วยข้อความอย่าง
"add 100.64.0.0 /106 100.102.103.104" - เพราะภายใน Plan 9 เน้น IPv6 และจัดการ IPv4 เป็น IPv4-mapped IPv6 address ทำให้ CGNAT
100.64.0.0/10ถูกแสดงเป็น/106
- เขียน
- งานของ MagicDNS บน Plan 9 คือทำให้เข้าถึง peer ได้ด้วยชื่ออย่าง
fooหรือfoo.tailnet-name.ts.net- เคยมีการพูดถึงแนวทางดัก query ที่
/net/dnsหรือ/net/cs - สุดท้าย Russ แก้ Plan 9 ให้กำหนด DNS server ทางเลือกสำหรับ DNS suffix บางตัวได้
- ปัญหาที่ DNS query ถูก negative cache ผิดพลาดก็ถูกแก้ไขเช่นกัน
- เคยมีการพูดถึงแนวทางดัก query ที่
Tailscale SSH และ service discovery
- Tailscale SSH คือ SSH server ที่ฝังอยู่ใน
tailscaledและยืนยันตัวตนด้วย Tailscale identity ที่รู้จักจาก WireGuard key ที่ผูกกับแพ็กเก็ต - ตอนแรกใช้
os/exec.Commandเรียกเชลล์ Plan 9 คือ/bin/rcแล้วต่อ stdin/stdout เข้าด้วยกัน- แม้เชลล์จะรันได้ แต่การ echo, การเลื่อนตำแหน่ง, และการ interrupt process ทำงานได้ไม่ถูกต้อง
- Russ เพิ่ม netshell example เข้าไปใน 9fans/go
- ตัวอย่างนี้ใกล้เคียงกับ telnet server ที่ไม่ปลอดภัยมาก แต่ก็เพียงพอเมื่ออยู่หลัง Tailscale SSH
- หลังจากนั้นก็ทำสิ่งอย่างดึงข้อมูลจาก
/dev/snarfของ Plan 9 ผ่าน SSH หรือ cross-compile Go test จากโน้ตบุ๊กแล้วสั่งรันผ่าน SSH ได้ง่ายขึ้น
- ฟีเจอร์เสริมของ Tailscale อย่าง service discovery ก็ถูกพิจารณาให้เหมาะกับ Plan 9 ด้วย
- วนดู
/proc/NNN/fdเพื่อหา process ที่เปิด/net/tcp/clone - จับคู่ QID ของ fd กับ
/net/tcp/NNN/{status,local}เพื่อตรวจว่ากำลัง listen อยู่หรือไม่และใช้พอร์ตใด - วิธีคำนวณหมายเลข TCP จาก QID ยังน่ากังวล เพราะเปราะบางต่อการเปลี่ยน implementation ของเคอร์เนล
- วนดู
เวลา, เว็บเดโม, v86
- เคยมีกรณีที่
tailscaledcrash เพราะ gVisor netstack รายงานว่า monotonic time ถอยหลัง- implementation เวลาใน Go สำหรับ Plan 9 ใช้ wall time เป็น monotonic time
- เมื่อ ntpd ปรับนาฬิกาย้อนกลับ สมมติฐานของ netstack เกี่ยวกับ monotonic time ก็พัง
- Russ เพิ่ม monotonic time เข้าไปใน
/dev/bintimeของ Plan 9 และแก้ให้ Go ใช้มัน - การรัน Plan 9 บนเว็บใช้ v86
- v86 รันระบบปฏิบัติการ 32 บิตด้วย WASM และมีวิธีเครือข่ายหลายแบบให้ใช้
- นี่ก็เป็นอีกเหตุผลที่เน้นไปที่
GOARCH=386
- ตอนแรกมีการเพิ่ม wsproxy protocol support เข้าไปในสภาพแวดล้อมจำลองเครือข่ายของ Tailscale เพื่อส่ง Ethernet frame ผ่าน websocket relay
- มันทำงานอยู่ในสภาพแวดล้อม integration test ที่จำลอง ARP, DHCP, DNS, NAT, control plane, DERP ฯลฯ ด้วย gVisor netstack
- แต่เพราะมีรอบรับส่ง DHCP ถ้า relay อยู่ไกล การเปิด
rioซึ่งเป็น GUI ของ Plan 9 จะช้า
- ต่อมามีการทำ WISP server ด้วยเช่นกัน แต่เวลาไม่พอจะทำให้พร้อมใช้งานจริงก่อนเปิดตัว จึงปล่อยด้วยการตั้งค่า network relay เริ่มต้นของ copy.sh/v86
- disk image ที่รวม Tailscale และ Plan 9 มีขนาด 16MB ส่วน binary ของ Tailscale มีขนาด 23MB หลังแตกไฟล์
- นี่คือเหตุผลที่เห็นขั้นตอน “gunzip…” ตอนบูต
- image ตัวอย่างถูกใส่ไว้ในโปรไฟล์ 9legacy ของ
copy.sh/v86
งานที่ยังเหลือและผลลัพธ์ที่เกิดขึ้นจริง
- ตอนนี้งานพอร์ต Tailscale ไปยัง Plan 9 ถูกทดสอบบน 9legacy เท่านั้น
- fork หลักของ Plan 9 มีทั้ง 9legacy ที่ใกล้เคียงต้นฉบับ และ 9front ที่ถูกปรับแก้มากกว่า
- แพตช์บางส่วนที่ Russ เขียนให้ 9legacy อาจต้องพอร์ตไปยัง 9front ด้วย
- การรองรับ 64 บิตด้วย
GOARCH=amd64ก็ยังต้องตรวจสอบเพิ่มเติม - ยังไม่มีการทำ exit node และการรองรับแพ็กเกจ Go
net/netns- เพื่อทำสิ่งนี้ อาจต้องกลับมาทบทวนแนวทาง เช่น ให้ Tailscale แสดงตัวเองเป็น
/netแยกต่างหากบน Plan 9
- เพื่อทำสิ่งนี้ อาจต้องกลับมาทบทวนแนวทาง เช่น ให้ Tailscale แสดงตัวเองเป็น
- งานนี้ยังช่วยยกระดับการรองรับ Plan 9 ใน Go ด้วย
- cmd/compile: use FMA on plan9, and drop UseFMA
- runtime: remove nextSampleNoFP from plan9
- cmd/compile, runtime: remove plan9 special case avoiding SSE
- net: fix parsing of interfaces on plan9 without associated devices
- os: guarantee min buffer size for ReadFile reads on /proc-like files
- net: unblock UDP Reads upon Close on plan9, add test
- runtime: fix plan9 monotonic time, crypto randomness
- โดยเฉพาะการลบการจัดการกรณีพิเศษของ Plan 9 ออกจากคอมไพเลอร์ Go ทำให้คอมไพเลอร์เรียบง่ายขึ้นและแก้ไขได้ง่ายขึ้น
- ตอนเผยแพร่เดโม v86 ยังมีมุกวันเมษาหน้าโง่จากผู้สร้าง v86 ที่ทำให้แม้แต่ข้อความ VGA ก็แสดงเป็นภาษาดัตช์ปลอม ๆ แต่หลีกเลี่ยงได้ด้วย query argument
&nojoke
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ถ้ามีอะไรสงสัย ผมตอบได้
ตอนนี้มีคนไม่กี่คนกำลังคุยเรื่องนี้กันที่ https://meet.google.com/qre-gydb-mkv
แก้ไข: ผ่านไปหนึ่งชั่วโมง ทุกคนออกไปหมดแล้ว
บล็อกโพสต์ วันที่ 1 เมษายน ก่อนหน้านี้คือ https://tailscale.com/blog/tailscale-enterprise-plan-9-suppo...
การที่ Russ Cox ผลักมุกนี้ไปจนสุดทางนี่ ระดับตำนาน จริง ๆ
ในลิสต์ 9fans มีเรื่องแบบนี้โพสต์ไว้สำหรับวัน April Fools’
เนื้อหาคือค่าใช้จ่ายในการดูแลสถาปัตยกรรมคอมพิวเตอร์ที่ยังไม่สุกงอมอย่าง mips, 386, arm, arm64, amd64 นั้นสูงเกินไป จึงตัดสินใจไปโฟกัสกับสถาปัตยกรรมที่เติบโตและเสถียรกว่า
เป้าหมายนั้นคือ power64 และ itanium ดังนั้นสถาปัตยกรรมทั้งหมดนอกจาก power64 และ itanium จะถูกแช่แข็ง·เก็บรักษา และเลื่อนสถานะเป็นสิ้นสุดอายุการใช้งาน
ไม่ได้ล้อเล่นนะ ผมอยากให้มี Plan 9 เวอร์ชัน enterprise จริง ๆ
ช่วงนี้สคริปต์ส่วนใหญ่ผมเขียนด้วย
rcและเพราะเราใช้ nix กับดึงเข้ามาอัตโนมัติด้วย dirnev ได้ เพื่อนร่วมงานเลยยอม ๆ ให้ผ่านไป ซึ่งก็ดีทีเดียวrcได้ไหม ไม่ใช่ว่าจะรันมันได้ไหมrcคือสิ่งนี้[1]:“หลักการสำคัญที่สุดในการออกแบบ rc คือมันไม่ใช่ macro processor อินพุตจะไม่ถูกสแกนเกินหนึ่งครั้งโดยโค้ดวิเคราะห์ศัพท์และไวยากรณ์เด็ดขาด”
สมัยก่อนตอนทำงานที่บริษัทยูนิกซ์แห่งหนึ่ง มีสคริปต์เชลล์ที่กำลังรันอยู่ถูกแก้ไข จนลบดิสก์งานส่วนใหญ่ไป โชคดีที่เรามีแบ็กอัปรายวันเก็บไว้ในเทป และเรื่องนั้นก็ประมาณ 17 ปีก่อนแล้ว
[1] https://www.scs.stanford.edu/nyu/04fa/sched/readings/rc.pdf
ถ้าพลาดโพสต์แรกไปและแค่อยากลองเอง มันทำงานได้ใน อิมเมจ v86 นี้:
https://copy.sh/v86/?profile=custom&m=768&vram=16&hda.url=ht...
ใน VM สามารถเริ่ม
tailscaledและtailscaleได้ ความพร้อมใช้งานของพร็อกซีมีจำกัด เลยอาจใช้เวลาสักหน่อยกว่าจะออนไลน์แก้ไข: alt ทำหน้าที่เป็นปุ่มที่สาม ถ้าจะเปิดเทอร์มินัล ให้กด alt ค้างไว้แล้วคลิกขวา เลือก new จากนั้นปล่อย alt แล้วลากด้วยคลิกขวาเพื่อปรับขนาดหน้าต่างเทอร์มินัล
กำลังมี webinar อยู่ (Google Meet) https://ftp.plan9.ts.net/webinar
ผมชอบ premise ของมุกนี้นะ แต่ยิ่งอธิบายยาวขึ้นก็ยิ่งรู้สึกหดหู่ขึ้นมา
มีของที่พังเยอะเกินไป และความซับซ้อนก็มากเกินไป สุดท้ายเพื่ออะไร แค่ทำ network tunnel อันเดียวเหรอ? ถ้างานเพิ่มเติมพวกนี้เองคือมุก ก็คงตลกดี
ผมคงคุยกับ rsc, rob pike, bradfitz ได้เป็นชั่วโมง ๆ โดยเฉพาะเรื่อง Plan 9 แน่นอนว่าคงเป็นการเสียเวลาของพวกเขาไปโดยสิ้นเชิง
ระบบปฏิบัติการนั้นชวนหลงใหลจริง ๆ
ผมนึกถึงช่วงต้นอาชีพที่มีผู้เชี่ยวชาญซึ่งทำงานด้วยกันมานั่งข้าง ๆ ค่อย ๆ แสดงวิธีทำและตอบคำถามอย่างอดทนจนกว่าผมจะเข้าใจพอ เป็นเหมือนการทำให้ว่ายน้ำได้แม้ถูกโยนลงน้ำลึก และในสามชั่วโมงก็เหมือนได้ปริญญาตรีหนึ่งใบในความรู้เฉพาะทางนั้น เป็นหนึ่งในช่วงที่เติบโตเร็วที่สุดในอาชีพ
ผมไม่รู้ C และก็ไม่รู้ Plan 9 มากพอจะใช้ได้อย่างมีประสิทธิภาพ แต่ก็มี ฟีเจอร์ที่เจ๋งและมีประโยชน์ ที่ผมอยากรู้และเรียนรู้เพิ่ม อย่างน้อยก็เพื่อจะได้เสียดายสิ่งที่ไม่มีในสามระบบปฏิบัติการหลักยุคนี้
ถ้ามีเงิน ผมอยากซื้อเวลาคุยกับทั้งสามคนเพื่อขยายความรู้เรื่อง Go และซื้อเวลาของ rsc กับ rob pike เพื่อทำความเข้าใจ Plan 9 ในแบบที่ผมอยากมีมาตลอดแต่ไม่เคยได้มาด้วยตัวเอง
ผมชอบ Plan 9 มาก การเอาหลักการจำนวนมากของมันมาสร้างระบบปฏิบัติการของตัวเองคือ โปรเจกต์วัยเกษียณ และเป้าหมายชีวิตของผม
แก้ไข: ผมจองชื่อโปรเจกต์นี้ไว้ว่า “chaos10” เพราะคงไม่มีแผนอะไรเหมือน SerenityOS
นึกไม่ถึงเลยว่าจะถึงขั้นแพตช์ เคอร์เนล Plan 9 เพื่อให้สิ่งนี้ทำงานได้