2 คะแนน โดย GN⁺ 2024-03-27 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เฟิร์มแวร์สวิตช์เครือข่ายของ Oxide เปิดไม่ติดหลังการทดสอบเปลี่ยนแปลง ลำดับการจ่ายไฟ และสาเหตุมาจากบั๊กที่การตรวจสอบ การยืมหน่วยความจำของ IPC ในเคอร์เนล Hubris ชนกับวิธีจัดวางหน่วยความจำแบบใหม่
  • Hubris เป็นระบบปฏิบัติการฝังตัวที่แยก task ด้วย MPU และเมื่อมีการยืมหน่วยความจำให้ task อื่นผ่าน IPC เคอร์เนลจะตรวจสอบว่าหน่วยความจำนั้นอยู่ในขอบเขตที่เข้าถึงได้จริงหรือไม่
  • ฟีเจอร์ task packing ที่เพิ่งนำมาใช้ช่วยกู้ RAM คืนได้ 30% ในบางอิมเมจเฟิร์มแวร์ แต่การตรวจสอบแบบเดิมล้มเหลวเพราะสมมติว่าหน่วยความจำที่ให้ยืมต้องอยู่ใน MPU region เดียว
  • task sequencer ตายด้วย synthetic memory fault ระหว่างพยายามยืมหน่วยความจำที่มีแอดเดรส 0x801bffd ให้กับไดรเวอร์ I2C และใน humility tasks มีสถานะ mem fault... in syscall พร้อมการรีสตาร์ต 115 ครั้ง
  • การแก้ไขคือเปลี่ยน อัลกอริทึมการตรวจสอบ ให้ยอมรับการยืมที่คร่อมหลาย MPU region ที่ติดกันได้ และใช้เวลาราว 3 ชั่วโมงตั้งแต่พบปัญหาจนแก้บั๊กในเคอร์เนลเสร็จ

สวิตช์เครือข่ายที่เปิดไม่ติด

  • Arjen Roodselaar แห่ง Oxide กำลังทดสอบการเปลี่ยนแปลง ลำดับการจ่ายไฟ และการตั้งค่านาฬิกาในเฟิร์มแวร์สวิตช์เครือข่าย ก่อนจะพบว่าหลังการเปลี่ยนแปลงที่ดูเล็กน้อย สวิตช์กลับเปิดไม่ติด
  • บางส่วนของเฟิร์มแวร์ยังตอบสนองต่อคำสั่งได้ แต่ส่วนสำคัญที่รับผิดชอบ power sequencer ดูเหมือนหยุดทำงาน
  • ข้อผิดพลาดในลำดับการจ่ายไฟอาจทำให้ฮาร์ดแวร์เสียหายจริงได้ จึงต้องตรวจสอบก่อนว่าสวิตช์พังไปแล้วหรือแค่ไม่ตอบสนอง

Hubris และหน่วยความจำที่มีจำกัด

  • Hubris เป็นระบบปฏิบัติการสำหรับ ระบบฝังตัวระดับลึก เช่น internal controller ของคีย์บอร์ด และถูกสร้างขึ้นเพื่อจัดการงานที่จำเป็นต่อการเริ่มต้นโปรเซสเซอร์ขนาดใหญ่ใน Oxide Rack
  • เฟิร์มแวร์ที่ใช้ Hubris ประกอบด้วย task หลายตัวซึ่งเป็นโปรแกรมที่คอมไพล์แยกกัน
    • แต่ละ task มีโค้ดมาตรฐานไลบรารีและส่วนที่จำเป็นของตัวเอง
    • task ถูกแยกออกจากกันด้วยฮาร์ดแวร์ MPU เพื่อไม่ให้ทำให้กันเองล่มหรือทำหน่วยความจำเสียหาย
  • บน ARM Cortex-M ตระกูล ARMv7-M ที่ใช้เป็นหลัก พื้นที่หน่วยความจำที่ป้องกันได้ต้องมีขนาดเป็น กำลังของ 2 และต้องจัดแนวตามขนาดนั้น
    • ตัวอย่างเช่น หากพื้นที่ 1024 ไบต์ต้องการเพิ่มอีก 1 ไบต์ ก็จะไม่กลายเป็น 1025 ไบต์ แต่ต้องขยายเป็น 2048 ไบต์

task packing สร้างขอบเขตใหม่

  • Hubris รุ่นแรกใช้วิธีง่าย ๆ คือให้ task ใช้หนึ่ง region สำหรับ RAM และอีกหนึ่ง region สำหรับ flash แต่ทำให้เกิดช่องว่างที่ใช้งานไม่ได้ระหว่าง task และสิ้นเปลืองหน่วยความจำ
  • Matt Keeter ปรับปรุงระบบ build ให้สามารถจัดวาง task โดยผสมหลาย region แบบ power-of-two เข้าด้วยกันเมื่อเป็นไปได้
    • ฮาร์ดแวร์อนุญาตได้สูงสุด 8 region ต่อ task
    • ในบางอิมเมจเฟิร์มแวร์สามารถกู้ RAM คืนได้ 30%
    • อุปกรณ์ที่เล็กที่สุดซึ่งเดิมตึงมือจนต้อง optimize ทุกครั้ง เริ่มมีพื้นที่เผื่อใช้งาน
  • การเปลี่ยนแปลงนี้ทำให้เกิด ขอบเขต MPU region ที่คาดเดาได้ยากตรงกลางระหว่าง flash และ RAM ของ task

เบาะแสที่ humility tasks ทิ้งไว้

  • Arjen ใช้ Humility ซึ่งเป็นดีบักเกอร์ของ Hubris ตรวจสอบสวิตช์ที่ล้มเหลว และพบว่า service processor ที่รับผิดชอบ power sequencing ยังมีชีวิตและทำงานอยู่ จึงดูไม่น่าใช่ปัญหาฮาร์ดแวร์
  • ในผลลัพธ์ของ humility tasks task sequencer แสดงสถานะดังนี้
mem fault (precise: 0x801bffd) in syscall (was: wait: reply from i2c_driver/gen0)
  • task เดียวกันนี้ถูก รีสตาร์ต 115 ครั้ง และใน Hubris การรีสตาร์ต task มักแทบจะหมายถึงการตอบสนองต่อการ crash
  • สตริงสถานะนี้มีความหมายดังนี้
    • mem fault: ละเมิดกฎการจัดการหน่วยความจำ
    • precise: 0x801bffd: ระบุแอดเดรสที่มีปัญหาได้อย่างชัดเจน
    • in syscall: task ไม่ได้กำลังรันโค้ดปกติ แต่กำลังอยู่ใน system call
    • was: wait: reply from i2c_driver/gen0: กำลังรอคำตอบจากข้อความที่ส่งไปยังไดรเวอร์ I2C
  • gen0 หมายความว่า i2c_driver ยังไม่เคย crash เลย ขณะที่ sequencer อยู่ที่ generation 115

Hubris IPC และการยืมหน่วยความจำ

  • task ใน Hubris สื่อสารกันผ่านข้อความ IPC ซึ่งทำงานคล้ายการเรียกฟังก์ชัน
    • task ที่ส่งข้อความจะหยุดรอ
    • task ที่รับข้อความจะได้สิทธิ์ควบคุม CPU
    • เมื่อผลลัพธ์กลับมา task ที่ส่งจึงจะถูกปลุกอีกครั้ง
  • IPC ถูกออกแบบให้เข้ากับ ownership model ของ Rust ได้ดี โดย task สามารถยืมบางส่วนของหน่วยความจำตัวเองให้ task อื่นพร้อมกับข้อความ IPC ได้
  • task ที่โต้ตอบกับอุปกรณ์ I2C จะยืมช่วงหน่วยความจำของตัวเองให้ไดรเวอร์ I2C bus และไดรเวอร์จะอ่านหรือเขียนช่วงนั้นโดยตรง
    • ช่วยลดความจำเป็นที่ bus driver ต้องมี buffer pool แยก
    • ลดจำนวนครั้งของการคัดลอกข้อมูล
  • หากทำผิดอาจกลายเป็นช่องโหว่ด้านความปลอดภัยได้ ดังนั้นเคอร์เนล Hubris จึงห้าม task ยืมหน่วยความจำที่ตัวเองไม่ได้เป็นเจ้าของจริงหรือเข้าถึงไม่ได้
    • server จะได้รับรหัสข้อผิดพลาด
    • client จะได้รับ fault และถูกยุติเสมอ
    • สิ่งนี้ถูกมองเป็น access violation ที่บ่งชี้ถึงบั๊ก ความเสียหาย หรือความเป็นไปได้ของการโจมตี

synthetic fault และสาเหตุที่แท้จริง

  • Hubris แยก fault ออกเป็น real fault และ synthetic fault
    • real fault คือการละเมิดกฎของฮาร์ดแวร์ เช่น dereference null pointer หรือเขียนลงใน code region
    • synthetic fault คือการละเมิดกฎซอฟต์แวร์ที่ Hubris เพิ่มเข้ามา เช่น IPC หรือการยืมหน่วยความจำ
  • fault ของ sequencer เป็น synthetic fault ที่เกิดระหว่างการยืมหน่วยความจำให้ไดรเวอร์ I2C ผ่าน IPC
  • แอดเดรส 0x801bffd ที่เป็นปัญหาเป็นแอดเดรส flash ที่ถูกต้อง แต่เพราะมันอยู่ต่ำกว่าขอบเขตกำลังของ 2 อยู่ 3 ไบต์ จึงดูมีแพตเทิร์นแปลกตา
  • ผลลัพธ์จาก humility mem แสดงให้เห็นว่า task sequencer มี flash สอง region ที่ชนติดกันพอดีที่ 0x801c000
LOW         HIGH           SIZE ATTR   ID TASK
0x08018000 - 0x0801bfff   16kiB r-x--- 17 sequencer
0x0801c000 - 0x0801dfff    8kiB r-x--- 17 sequencer
  • แม้ทั้งสอง region จะเป็นของ task เดียวกันและในการรันโปรแกรมปกติฮาร์ดแวร์ MPU ก็อนุญาตให้เข้าถึงได้ไม่มีปัญหา แต่การตรวจสอบการยืมหน่วยความจำใน IPC ของเคอร์เนลกลับตั้งอยู่บนสมมติฐานอีกแบบหนึ่ง

จุดที่การทำให้ง่ายในอดีตกลายเป็นบั๊ก

  • การตรวจสอบแบบเดิมของเคอร์เนลเพียงเช็กว่า memory slice ที่จะให้ยืมนั้นอยู่ครบถ้วนภายใน region เดียว ของ task หรือไม่
self.region_table().iter().any(|region| {
    region.covers(slice)
        && region.attributes.contains(desired)
        && !region.attributes.intersects(forbidden)
})
  • โค้ดนี้สอดคล้องกับสมมติฐานในการออกแบบตอนนั้น คือ task หนึ่งมี RAM region เดียวและ flash region เดียว
  • เมื่อมี task packing หน่วยความจำของ task เดียวกันจึงสามารถถูกแบ่งออกเป็นหลาย MPU region ที่ติดกันได้ และสมมติฐานเดิมก็ใช้ไม่ได้อีกต่อไป
  • การเข้าถึงหน่วยความจำทั่วไปไม่โดนผลกระทบ เพราะฮาร์ดแวร์ MPU เป็นผู้ตรวจเองโดยตรง และปัญหาจะปรากฏเฉพาะตอนพยายาม ยืมหน่วยความจำนั้นผ่าน IPC เท่านั้น

ความขัดข้องที่เกิดจากสองฟีเจอร์ทำงานร่วมกัน

  • task packing ทำงานแบบฉวยโอกาส
    • มีข้อจำกัดสูงสุด 8 region ต่อ task
    • task ไดรเวอร์ฮาร์ดแวร์ใช้บาง region ไปแล้วกับ memory-mapped register
    • ระบบจะพยายามจัดวางแบบฉลาดขึ้นก็ต่อเมื่อยังมีช่อง region เหลือ
  • ผลคือจุดแบ่ง region เกิดขึ้นในตำแหน่งที่ผู้เขียน task คาดเดาได้ยาก
  • การเปลี่ยนขนาดเล็กน้อยของ task A อาจไปเปลี่ยนตำแหน่งขอบเขต MPU region ของ task B ที่ไม่เกี่ยวข้องกัน
  • แค่เพิ่มโค้ดดีบักก็อาจทำให้การตัดสินใจเรื่อง layout และขอบเขต region เปลี่ยน จน crash หายไปได้
  • Matt ปิด task packing ในระบบ build ทันทีเพื่อให้ Arjen สร้างอิมเมจเฟิร์มแวร์ที่ใช้งานได้ก่อน พร้อมกับเดินหน้าวิเคราะห์และแก้บั๊กในเคอร์เนลไปพร้อมกัน

วิธีแก้ในเคอร์เนล

  • หัวใจของการแก้คือเปลี่ยน อัลกอริทึมตรวจสอบการเข้าถึงหน่วยความจำ ให้ยอมรับกรณีที่หน่วยความจำที่จะยืมคร่อมหลาย MPU region ที่ติดกันอย่างพอดี
  • อัลกอริทึมใหม่ถูกออกแบบให้ไล่ดู region table เพียงครั้งเดียว
    • Hubris พยายามหลีกเลี่ยงการเปิดเผยการดำเนินการที่มี time complexity ซึ่ง task สามารถควบคุมได้
    • ประสิทธิภาพจึงต้องขึ้นกับ region table ขนาดคงที่ ไม่ใช่กับขนาดของหน่วยความจำที่ยืม
    • region table มีขนาดคงที่ที่ 8 รายการ
  • เพื่อให้ทำได้ ระบบ build จึงถูกเปลี่ยนให้จัดเรียง task region ตามลำดับแอดเดรสจากน้อยไปมาก
regions.sort_by_key(|i| region_table.get_index(*i).unwrap().1.base);
  • คอมมิตแก้ไข ทำให้เคอร์เนลใช้คุณสมบัติการเรียงลำดับนี้เพื่อตรวจสอบการเข้าถึงได้ในต้นทุนที่ต่ำกว่า
  • โค้ดที่ซับซ้อนขึ้นถูกแยกออกจากแกนหลักของเคอร์เนล Hubris ไปไว้ใน crate ที่พกพาได้มากกว่า และมีการเพิ่ม unit test สำหรับ corner case สำคัญ
  • ด้วยโค้ดใหม่นี้จึงสามารถเปิด task packing ได้อีกครั้ง โดยไม่ทิ้ง crash ที่คาดเดายากไว้ให้ผู้พัฒนา task

ทำไมปัญหาจึงไม่ลุกลามหนัก

  • เรื่องทั้งหมดเริ่มจากสวิตช์เครือข่ายเปิดไม่ติด และจบลงด้วยการแก้บั๊กในเคอร์เนลภายในเวลาประมาณ 3 ชั่วโมง
  • ด้วย fault isolation ในเฟิร์มแวร์สวิตช์ที่ประกอบด้วย 23 task แบบแยกกัน sequencer จึงเป็นเพียง task เดียวที่ตายซ้ำ ๆ ขณะที่องค์ประกอบอื่นจำนวนมากยังทำงานต่อได้
    • ระบบอัปเดตเฟิร์มแวร์
    • IP network stack สำหรับอินเทอร์เฟซการจัดการและควบคุม
    • บริการเครือข่ายหลายตัว ตั้งแต่ implementation ของโปรโตคอล echo ไปจนถึงอินเทอร์เฟซ control plane ของแร็ก
    • I2C, SMBus, PMBus สำหรับตรวจสอบเซนเซอร์ พัดลม และสถานะระบบอื่น ๆ
    • ไดรเวอร์ทรานซีฟเวอร์ QSFP 100G จำนวน 32 พอร์ตด้านหน้าเครื่อง
  • Hubris IPC ถูกออกแบบบนสมมติฐานว่า task อื่นอาจล้มเหลวได้ และงานที่ระบุว่า idempotent สามารถ retry ซ้ำได้อย่างโปร่งใส
  • บั๊กเดิมในการตรวจสอบการเข้าถึงหน่วยความจำเป็นการปฏิเสธการเข้าถึงของโปรแกรมที่ถูกต้อง ไม่ใช่การเปิดทางให้การเข้าถึงที่ผิดหรือมุ่งร้าย จึงไม่มีผลกระทบด้านความปลอดภัย
  • ในช่วงที่ sequencer และไดรเวอร์ I2C กำลังจะแชร์หน่วยความจำกันนั้น sequencer ตายไปก่อน แต่ไดรเวอร์ I2C ยังทำงานต่อได้โดยไม่มีความเสี่ยงจากความเสียหาย

โครงสร้างพื้นฐานการดีบักและการทำงานของทีม

  • Humility เป็นดีบักเกอร์ที่พัฒนาควบคู่กับเคอร์เนล Hubris และ Arjen สามารถระบุตำแหน่งโค้ดที่ crash ได้ถึงระดับเลขบรรทัดภายในไม่กี่นาที พร้อมแชร์ snapshot ที่เป็นอิสระของ service processor ได้
  • Hubris จะบันทึก core dump แบบบีบอัดของ task ที่ crash ลงใน RAM และสามารถดึงกลับมาทางเครือข่ายได้
    • จึงได้ crash dump แม้ไม่มี persistent storage ที่เขียนได้
    • ฟีเจอร์ crash dump ไม่ได้อยู่ในเคอร์เนล แต่เป็น task แยกต่างหาก
  • โปรเซสเซอร์เหล่านี้ไม่ได้จัดการข้อมูลเวิร์กโหลดของลูกค้า แต่รับผิดชอบเฉพาะทราฟฟิกสำหรับการจัดการระบบ และรายงานการ crash ก็ไม่ได้อัปโหลดอัตโนมัติ
  • ส่วนที่ไม่ขึ้นกับสถาปัตยกรรมของเคอร์เนล Hubris มีขนาดโค้ด 1,789 บรรทัด และคอมเมนต์ 1,192 บรรทัด ส่วนการรองรับ ARMv6-M, ARMv7-M และ ARMv8-M เพิ่มอีก 1,075 บรรทัดของโค้ดและคอมเมนต์ 534 บรรทัด
  • แนวคิดของเคอร์เนลและ IPC ใน Hubris ค่อนข้างเรียบง่าย ดังนั้นเมื่อ fault ชี้ไปที่ IPC จึงมีจุดที่ต้องตรวจสอบไม่มากนัก

1 ความคิดเห็น

 
GN⁺ 2024-03-27
ความคิดเห็นจาก Hacker News
  • Hubris ดีจริง ๆ ผมลองอ่านโค้ดเคอร์เนลประมาณ 30 นาที มันห่างไกลจากโค้ด C ที่เคยเห็นซึ่งเต็มไปด้วยมาโคร ifdef ชอบตั้งชื่อตัวแปรสองตัวอักษร และขาดคอมเมนต์มาก ๆ แต่เขียนได้ชัดเจนและดีมาก
    เหมาะเป็นของอ่านก่อนนอนด้วย แนะนำให้ลองไล่ดูสักรอบ: https://github.com/oxidecomputer/hubris/blob/b44e677fb39cde8...

    • รู้สึกขัดใจพอสมควรที่วัฒนธรรม C ส่วนใหญ่ดูจะสรุปได้ว่า “ขี้เกียจเรียนรู้วิธีพิมพ์ด้วยความเร็วที่พอเหมาะ”
      พื้นที่ดิสก์ สำหรับซอร์สโค้ดไม่ใช่ปัญหาใหญ่มาตั้งแต่ 40 ปีก่อนแล้ว แต่ก็ยังตระหนี่กับชื่อตัวแปรกันอยู่
    • AI อาจทำให้ธรรมเนียมนี้หายไปได้ เอาโค้ด C เก่า ๆ หยาบ ๆ ใส่เข้าไปใน AI แล้วจู่ ๆ ตัวแปรทั้งหมดก็อาจถูกจัดระเบียบและตั้งชื่อในแบบที่ผู้ใช้ชอบได้
      เพราะ AI เรียนรู้รสนิยมและนิสัยของโค้ดเดอร์คนนั้นได้อย่างแม่นยำ
  • เป็นบทความที่ดี แต่ตำแหน่งคอมเมนต์ด้านล่างน่าเสียดาย
    คอมเมนต์เหนือ regions.sort_by_key(|i| region_table.get_index(*i).unwrap().1.base); ที่ว่า “ต้องจัดเรียงตามที่อยู่จากน้อยไปมาก และเคอร์เนลใช้คุณสมบัตินี้เพื่อตรวจสอบการเข้าถึงได้ถูกลง” นั้นไม่ค่อยใช่รายละเอียดของฟังก์ชันนี้เท่าไร แต่เป็น invariant ของฟิลด์ ที่ผู้เขียนทุกคนต้องรักษา และผู้อ่านทุกฝั่งสามารถนำไปใช้ได้
    ดังนั้นดูเหมาะกว่าที่จะใส่ไว้ในสตริงเอกสารของ TaskDesc::regions: https://github.com/oxidecomputer/hubris/commit/b44e677fb39cd...

    • ถึงอย่างนั้น การมีคอมเมนต์อยู่ข้างโค้ดจัดเรียงก็ดี ไม่อย่างนั้นตัวการจัดเรียงนั้นเองอาจดูค่อนข้างน่าประหลาดใจ
      วิธีที่ดีที่สุดน่าจะเป็นการสร้าง เมธอด constructor ให้ TaskDesc เพื่อจัดเรียง region และบังคับใช้ invariant นั้น เห็นได้ว่าโค้ดเริ่มซับซ้อนขึ้นเรื่อย ๆ ตอนนี้จึงน่าจะคุ้มที่จะใช้เวลาสักหน่อยห่อความซับซ้อนไว้ในเมธอด
  • เป็นหนึ่งใน ประกาศรับสมัครงาน ที่ดีที่สุดเท่าที่เคยเห็นมา ชอบโฟลว์ที่ค่อย ๆ เปลี่ยนไปคุยเรื่องวัฒนธรรมอย่างเป็นธรรมชาติ แล้วปิดท้ายด้วย “ว่าแต่เรากำลังรับคนอยู่นะ”
    เป็นบทวิเคราะห์หลังเหตุการณ์ที่ยอดเยี่ยมจริง ๆ และแม้ผมจะเป็นนักพัฒนาระดับแอปพลิเคชันก็ยังตามทัน พอดีตอนนี้กำลังอ่าน Rust in Action อยู่ เลยยิ่งพร้อมกับเนื้อหาแบบนี้มากขึ้น
    การได้เห็นคนที่ใส่คอมเมนต์ในโค้ดเยอะ ๆ ก็เป็นเรื่องน่ายินดีเสมอ literate programming ใช้ได้ผลจริง

    • น่าเสียดายที่เรื่องนี้ใช้ได้เฉพาะในสหรัฐฯ
  • ดูเหมือนภาคก่อนหน้าจะดูได้ที่นี่

    1. https://hachyderm.io/@mjk/112157472314396711
    2. https://www.mattkeeter.com/blog/2024-03-25-packing/
  • ส่วนที่ว่า “การผสานรวมของทีมอย่างแน่นแฟ้นและไม่เป็นลำดับชั้น” สะดุดตา แม้จะไม่ใช่ฟีเจอร์ของ Hubris โดยตรง แต่คำอธิบายที่ว่าแยก Hubris ออกจากทีมที่สร้างมันได้ยาก และทีมวิศวกรรมของ Oxide แทบไม่มีไซโลภายในนั้นน่าประทับใจ
    อยากฟังเพิ่มเติมว่าทำไมถึงสร้างวัฒนธรรมที่ส่งเสริม ความเปิดกว้าง ความใฝ่รู้ และการสื่อสาร พร้อมกับกดพฤติกรรมเชิงป้องกันตัว การสร้างจักรวรรดิ และการกั้นประตู และนำไปใช้จริงอย่างไรบ้าง นอกจากนี้ยังสงสัยว่าการบ่มเพาะวัฒนธรรมแบบนี้ในองค์กรมีข้อเสียหรือไม่
    บางที่ก็เลือกใช้ระบบลำดับชั้นที่เข้มงวดกว่า และผังองค์กรอาจต้องกำหนดเชิงกลยุทธ์ จึงยังไม่ค่อยเข้าใจ trade-off นัก

    • ค่านิยมที่ระบุไว้นั้นประเมินได้ยาก แต่โดยทั่วไปข้อเสียของ องค์กรที่ไม่มีโครงสร้างกำหนดไว้อย่างชัดเจน คือสุดท้ายก็จะเกิดโครงสร้างอำนาจบางรูปแบบอยู่ดี
      ถ้าโครงสร้างนั้นไม่ถูกระบุไว้อย่างชัดเจน มันก็จะเปิดเผยน้อยกว่า ไม่ได้ถูกเลือกอย่างตั้งใจ และโดยเฉพาะสำหรับคนที่ไม่ถนัดปฏิสัมพันธ์ทางสังคม ก็จะยิ่งเข้าใจได้ยากขึ้น ด้วยลักษณะที่เหมือนเงานี้ มันจึงอาจปล่อยให้เกิดพฤติกรรมที่เป็นปัญหามากขึ้น และต่อให้ไม่ได้แย่มาก ก็ทำให้การประสานงานยากขึ้นมาก
      เคยเจอเรื่องแบบนี้ในหลายบริษัท บริษัทที่ปรึกษาขนาดใหญ่แห่งหนึ่งมีโครงสร้างอำนาจทางการอยู่ก็จริง แต่ในทางปฏิบัติไม่ค่อยยึดตามนั้น และวิธีเข้าไปอยู่ในโปรเจกต์ก็ใกล้เคียงกับการสนิทกับคนฝ่ายขายหรือฝ่ายบริหารมากกว่าผ่านช่องทางทางการ ถ้าสร้างเครือข่ายทางสังคมที่จำเป็นได้ดีก็ดีไป แต่ถ้าไม่ได้ มันก็ทำงานได้ไม่ดี
      ตัวอย่างคล้ายกันคือ “The Tyranny of Structurelessness” ซึ่งเป็นปาฐกถาของนักสตรีนิยมที่เห็นสิ่งเดียวกันเกิดขึ้นในองค์กรที่มองลำดับชั้นว่าเป็นปิตาธิปไตยและปฏิเสธมัน และก็มีการถกเถียงคล้ายกันใน Valve ที่โครงสร้างภายในไม่ชัดเจน โปรเจกต์โอเพนซอร์สก็เจอปัญหาเดียวกันได้ และผมมองว่าความขัดแย้งบางส่วนในฝั่ง Rust ก็เกิดจากปัญหาลักษณะคล้ายกัน
      แต่โครงสร้างอำนาจที่ชัดเจนไม่จำเป็นต้องเป็นลำดับชั้นเสมอไป องค์กรธุรกิจแบบดั้งเดิมเป็นแบบลำดับชั้น แต่โครงสร้างของ Oxide อาจชัดเจนและไม่เป็นลำดับชั้นได้ วิธีแบบนี้มักทำงานได้ดีกว่าเมื่อขนาดเล็ก และบริษัทที่ปรึกษาที่กล่าวถึงก่อนหน้านี้เป็นตัวอย่างที่ใหญ่ที่สุดเท่าที่ผมรู้จักของบริษัทที่ดำเนินงานใกล้เคียงกับรูปแบบอิสระ แต่ก็ยังมีฐานรองรับอยู่บ้าง
      เรื่องนี้ไม่ใช่ทวิภาค แต่เป็นสเปกตรัม ต่อให้โครงสร้างอำนาจบนกระดาษแข็งตัวที่สุด ข้างใต้ก็ยังมีโครงสร้างโดยนัยที่ซับซ้อนกว่า และนั่นคือธรรมชาติของกลุ่มมนุษย์
      ผมไม่ได้เชื่อว่าโครงสร้างที่ชัดเจนดีกว่าโครงสร้างโดยนัยเสมอไป แค่พูดถึงข้อเสียที่สังเกตได้ในองค์กรที่ระบุโครงสร้างน้อยกว่าเท่านั้น และโครงสร้างอำนาจที่ชัดเจนกว่าก็มีปัญหาของตัวเองเช่นกัน เรื่องนี้ยังเกี่ยวข้องกับ “seeing like a state” หรือปัญหาเรื่อง legibility ด้วย
  • เป็นบทความที่ยอดเยี่ยมซึ่งแสดงให้เห็นกระบวนการดีบักปัญหาซับซ้อนอย่างลึกซึ้ง ข้อเท็จจริงที่ว่าระบบส่วนที่เหลือยังคงเสถียรได้ดี แสดงถึง คุณภาพทางวิศวกรรม ของทีม Oxide อย่างชัดเจน
    โดยส่วนตัวแล้วก็ได้แรงบันดาลใจมาก และตั้งใจจะลองนำเทคนิคคล้าย ๆ กันไปใช้ในงานประจำวัน

  • ถ้าปฏิบัติกับฮาร์ดแวร์นั้นเหมือน TLB ที่เติมด้วยซอฟต์แวร์ ก็จะรองรับ region ได้มากกว่า 8 รายการ

    • ผมเดาว่าพวกเขาน่าจะ (a) ต้องการสมรรถนะระดับ soft real-time และ (b) ไม่อยากใส่องค์ประกอบสำคัญที่อาจขัดขวางความสามารถในการดีบักหรือความน่าเชื่อถือ
      คงไม่ทำเด็ดขาดเว้นแต่จะเป็นทางเลือกสุดท้าย virtual paging นั้นยุ่งเหยิง และไม่อยากให้เหลือข้อสงสัย
    • ผมรู้ว่า TLB คือ translation lookaside buffer แต่สงสัยว่า “soft fill” ในที่นี้หมายถึงอะไร
  • สิ่งที่ Oxide ทำอยู่นั้นน่าทึ่งจริง ๆ

    • ต่อจาก Tailscale ตอนนี้ Oxide ก็กลายเป็นสิ่งที่ผู้คนรักในหมู่โปรเจกต์ที่คน 99% ไม่จำเป็นต้องใช้
  • ไม่ว่าเหล่าคนของ Oxide จะทำอะไร ผมก็ชอบทั้งนั้น และนี่ก็เป็นหนึ่งในนั้น

  • ตั้งชื่อระบบปฏิบัติการว่า Hubris เหรอ? อา นั่นมัน… พูดไม่ออกเลย

    • คุณน่าจะดีใจที่รู้ว่าชื่อดีบักเกอร์คือ “humility”: https://github.com/oxidecomputer/humility
    • พูดให้ถูกคือ Brian Cantrill เป็นคนตั้งชื่อระบบปฏิบัติการว่า hubris
      ใครที่สติยังดีอยู่จะเขียนระบบปฏิบัติการใหม่ในยุคนี้กัน? คำตอบคือคนที่พยายามแก้ปัญหาที่ระบบปฏิบัติการทุกตัวเมิน นั่นคือปัญหาคอนโทรลเลอร์บนเมนบอร์ดและการ์ดขยาย ซึ่งระบบปฏิบัติการไม่ได้ควบคุมและไม่สามารถควบคุมได้
    • ดูค่อนข้างเข้ากับแบรนด์ดี