3 คะแนน โดย GN⁺ 2025-08-08 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • บทความนี้มุ่งเน้นที่กระบวนการพัฒนาของ Tyr ไดรเวอร์ GPU สมัยใหม่สำหรับเคอร์เนล Linux ที่พัฒนาด้วย Rust และหลักการทำงานของไดรเวอร์ GPU
  • ในการพัฒนาไดรเวอร์ GPU อธิบายการแยกความรับผิดชอบและการโต้ตอบระหว่าง UMD (User Mode Driver) และ KMD (Kernel Mode Driver) ผ่านตัวอย่าง VkCube
  • UMD จะแปลง API ระดับสูงเป็นคำสั่งระดับต่ำที่ GPU เข้าใจได้ ในขณะที่ KMD รับผิดชอบงานหลัก เช่น การจัดสรรหน่วยความจำ การกำหนดตารางงาน และการเริ่มต้นอุปกรณ์
  • API ที่ Tyr ไดรเวอร์ให้มามีโครงสร้างเช่นเดียวกับ Panthor ประกอบด้วยการสืบค้นอุปกรณ์ การจัดการหน่วยความจำ การจัดการกลุ่ม การส่งงาน และการจัดการ Tiler heap เป็นต้น
  • ในบทความต่อไปจะอธิบาย สถาปัตยกรรมฮาร์ดแวร์ Arm CSF และส่วนประกอบสำคัญ (เช่น MCU) รวมถึงกระบวนการบูต

บทนำ: การพัฒนาไดรเวอร์เคอร์เนล GPU สมัยใหม่ด้วย Rust

  • บทความนี้เป็นบทความที่สองของซีรีส์การพัฒนาของ Tyr ไดรเวอร์ GPU Rust สมัยใหม่ที่รองรับ GPU อิงสถาปัตยกรรม Arm Mali CSF ในเคอร์เนล Linux
  • เพื่อเป็นตัวอย่างจริง จึงเลือกโปรแกรม 3D แบบง่ายอย่าง VkCube ซึ่งเรนเดอร์ลูกบาศก์หมุนโดยใช้ Vulkan API เพื่ออธิบายการทำงานภายในของไดรเวอร์ GPU
  • โครงสร้างที่เรียบง่ายของ VkCube เหมาะเป็นตัวอย่างกรณีศึกษาในการเรียนรู้หลักการทำงานของไดรเวอร์ GPU

พื้นฐานไดรเวอร์ GPU: บทบาทและโครงสร้างของ UMD และ KMD

  • ประกอบด้วย User Mode Driver (UMD) และ Kernel Mode Driver (KMD)
    • UMD: ทำหน้าที่นำ API ของโปรแกรมทั่วไปมาทำงาน เช่น Vulkan, OpenGL ผ่านการใช้งานจริงอย่าง panvk (ไดรเวอร์ Vulkan ของ Mesa เป็นต้น)
    • KMD: เช่น Tyr เป็นต้น เป็นไดรเวอร์ระดับเคอร์เนลที่มีสิทธิ์เข้าถึงฮาร์ดแวร์ และทำงานเป็นส่วนหนึ่งของเคอร์เนล Linux
  • ไดรเวอร์ GPU โหมดเคอร์เนลเชื่อมต่อ UMD กับ GPU จริง โดยแปลงคำสั่ง API ที่ได้รับให้เป็นชุดคำสั่งที่ GPU สามารถเข้าใจได้
  • UMD เตรียมข้อมูลที่จำเป็นสำหรับการประกอบฉาก เช่น geometry, texture, shader เป็นต้น แล้วก่อนการทำงานจะขอให้ KMD จัดสรรหน่วยความจำ GPU
  • Shader คือโปรแกรมอิสระที่ทำงานบน GPU และใน VkCube มีบทบาทในการจัดตำแหน่งลูกบาศก์ การลงสี และการหมุน การรัน shader ต้องอาศัยข้อมูลภายนอก เช่น geometry, color, แมทริกซ์การหมุน เป็นต้น
  • UMD ส่งคำสั่งที่เตรียมไว้ (เช่น VkCommandBuffers) ให้ KMD เพื่อดำเนินการ และเมื่อเสร็จสิ้นงานจะรับการแจ้งเตือนเพื่อบันทึกผลลัพธ์ลงในหน่วยความจำ

ความรับผิดชอบหลักของ KMD (Kernel Mode Driver)

  • การ จัดสรรและแมปหน่วยความจำ GPU (จัดสรรแบบแยกสำหรับแต่ละแอปพลิเคชัน)
  • การส่งงานไปยังคิวฮาร์ดแวร์ และให้การแจ้งเตือนแก่ผู้ใช้เมื่อทำงานเสร็จ
  • ในสภาพแวดล้อมฮาร์ดแวร์แบบไม่ตรงเวลาและแบบขนาน การ จัดการความขึ้นต่อกันของงาน เป็นสิ่งจำเป็น และเพื่อให้ได้ผลลัพธ์ที่ถูกต้อง KMD จะรับหน้าที่กำหนดตารางและตรวจสอบ dependency
  • รวมถึงการเริ่มต้นอุปกรณ์ การขับตัวควบคุมคลอค/แรงดันไฟ การรันโค้ด startup และการจัดการรอบการเข้าถึงเพื่อให้หลายไคลเอนต์แบ่งปันฮาร์ดแวร์ได้อย่างเป็นธรรม

ตำแหน่งของความซับซ้อน: การแบ่งงานระหว่าง UMD และ KMD

  • ความซับซ้อนของไดรเวอร์ GPU ส่วนใหญ่อยู่ที่ UMD
    • UMD: แปลงคำสั่ง API ระดับสูงเป็นคำสั่งฮาร์ดแวร์
    • KMD: จัดฟังก์ชันหลักเพื่อให้ UMD ทำงานได้อย่างถูกต้อง เช่น การแยกความเป็นส่วนตัวของหน่วยความจำ การแบ่งปัน และการเข้าถึงที่เป็นธรรม

โครงสร้างอินเทอร์เฟซไดรเวอร์ (API) ของ Tyr

  • API ของไดรเวอร์ Tyr (=เหมือนกับ Panthor) แบ่งได้เป็น 5 กลุ่มหลัก
    1. การสืบค้นข้อมูลอุปกรณ์: DEV_QUERY (ตรวจสอบข้อมูลฮาร์ดแวร์ GPU ผ่าน IOCTL และใช้พื้นที่ ROM)
    2. การจัดสรรและแยกหน่วยความจำ: VM_CREATE, VM_BIND, VM_DESTROY, VM_GET_STATE, BO_CREATE, BO_MMAP_OFFSET เป็นต้น
    3. การจัดการกลุ่ม scheduling: GROUP_CREATE, GROUP_DESTROY, GROUP_GET_STATE (รายละเอียดจะอธิบายในบทความต่อไป)
    4. การส่งงาน: GROUP_SUBMIT (ส่งคำสั่งรันผ่าน device command buffer ไปยัง GPU)
    5. การจัดการ Tiler Heap: TILER_HEAP_CREATE, TILER_HEAP_DESTROY (รองรับความต้องการหน่วยความจำของ GPU แบบ tiled rendering)
  • API เหล่านี้ยังห่างไกลจากงานการวาดภาพจริง แต่ UMD รับผิดชอบการรันคำสั่งจริง ในขณะที่ KMD มีเพียงให้บริการอินเทอร์เฟซเหล่านี้เพื่อการเข้าถึงฮาร์ดแวร์

สรุปและแผนต่อไป

  • บทความนี้ได้สำรวจ โครงสร้างโดยรวมและแนวทางการไหลภายในของไดรเวอร์ GPU และ API สำคัญที่ Tyr ให้
  • โดยใช้เนื้อหานี้เป็นฐาน บทความต่อๆ ไปในซีรีส์จะครอบคลุม สถาปัตยกรรมฮาร์ดแวร์ Arm CSF, Microcontroller Unit (MCU) และส่วนประกอบหลักอื่นๆ รวมถึงขั้นตอนการเริ่มต้นไดรเวอร์

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

 
GN⁺ 2025-08-08
ความคิดเห็นบน Hacker News
  • เป็นบทความที่ดีมาก แต่เนื้อหาสั้นไปหน่อย มันเพิ่งเริ่มน่าสนใจกำลังดีแล้วก็รู้สึกว่าหมดตอนไปแล้ว รอดูตอนต่อไป
    • คาดว่าจะมีตอนต่อไปที่น่าติดตามในสัปดาห์หน้า ซึ่งจะได้เห็นคำสั่งที่รอคอยอยู่ใน GPU หลุดออกจากคิวและเริ่มทำงาน ระดับนามธรรมที่บทความนี้พูดถึงคือการส่งข้อมูลข้ามขอบเขต user/kernel เป็นหลัก จึงเน้นแต่การจัดการคิวและบัฟเฟอร์ จึงมีการคำนวณจริงไม่มากเท่าไหร่ สิ่งสำคัญจริง ๆ คือสิ่งที่จะเกิดขึ้นเมื่อคำสั่งที่ถูกใส่ในคิวถูกประมวลผล ที่ GPU ก็มีกลไกสัญญาณการทำงานเสร็จอีกทางที่ส่งกลับอีกทางหนึ่ง ซึ่งผมก็สนใจประเด็นนั้นเหมือนกัน การทำงานแบบ async ส่วนใหญ่จะอยู่ฝั่งโค้ดผู้ใช้ ไม่ใช่ในไดร์เวอร์ โดยไดร์เวอร์จะส่งสัญญาณเสร็จงานกลับไปเท่านั้น
  • ผมใช้เครื่อง RK3588 หนึ่งเครื่องเป็นเดสก์ท็อปและใช้ panfrost อยู่ แต่บางครั้งใน Firefox จอภาพก็กลายเป็นสีดำหรือต่างพื้นที่แบบโปร่งใส ซึ่งเป็นพฤติกรรมที่ประหลาด
    • RK3588 จริง ๆ แล้วใช้ไดร์เวอร์ panthor ซึ่งเป็นหัวข้อในบทความนี้ ไม่ใช่ panfrost
  • อยากรู้ว่ามีการพิจารณาใช้ uring_cmd แทน ioctls ไหม เพราะนี่เป็นโปรเจกต์ที่สร้างขึ้นใหม่ทั้งหมด จึงดูเหมือนว่าจะนำมาใช้ได้ และสงสัยว่าทำไมจึงคิดว่าข้อดีมีน้อยมาก
    • เพราะ GPU มีคิวคำสั่งแบบ async ของตัวเองอยู่แล้ว IOCTL เดิมก็หน้าที่เขียนลงคิวคำสั่งนั้นอยู่แล้ว ดังนั้นการสร้างคิว async เพิ่มเติมอีกชั้นที่ฝั่ง CPU เพื่อ schedule การเขียนแบบนี้ไม่ค่อยคุ้มค่ามากนัก แต่ถ้าเป็นข้อเสนอมาตรฐานให้สร้างคิวคำสั่งของ GPU เองแบบ uring แล้ว map เข้าสู่ userspace ต้องยอมรับว่าหากจะรองรับสเปก io_uring API ได้ครบถ้วน ต้องเปลี่ยนแปลง firmware อย่างค่อนข้างมาก และด้วยข้อจำกัดของฮาร์ดแวร์อาจทำได้ยากมากจนเป็นไปไม่ได้
    • ไดร์เวอร์ที่บทความอธิบายถึงนั้นปฏิบัติตาม API ที่ userspace Mesa library ต้องการโดยตรง
  • อ่านแล้วน่าสนใจมาก และอยากรู้ว่ามีตอนต่อหรือต่อเนื่องทางตรรกะให้สนใจต่อได้หรือไม่
    • เป็นบทความที่ปล่อยครั้งแรกวันนี้ จึงคาดว่าจะมีการอัปเดตอีกต่อไป
  • เข้าใจว่าหัวข้อ “Rust GPU driver” มีกลอุบายดึงการคลิก แต่ความจริงก็คือมันไม่ใช่ไดร์เวอร์ GPU แบบ Arm Mali CSF ใช่ไหม? ผมไม่ค่อยชอบที่ทำให้มุ่งเน้นไปที่ meta-tool เพื่อพัฒนาเครื่องมือมากไป มันฟังดูเหมือนเป้าหมายหลักคือการสร้างบางอย่างด้วย Rust เอง ในบทความระบุว่าเป็น “Arm Mali-based GPU driver kernel” แต่ก็ไม่ได้เรียกตัวเองว่า arm mali driver โดยตรง ผมมองว่าการสร้างไดร์เวอร์หมายถึงการเชื่อมต่อ API ระหว่าง API ของ OS กับ API ของผู้ผลิตฮาร์ดแวร์เท่านั้น ไม่ได้เป็นการสร้างเฟรมเวิร์กเพิ่มชั้นนามธรรมด้านบน และขออภัยหากฟังดูตรงไปตรงมามากไปหน่อย
    • สิ่งที่ทำให้ Rust เป็นคีย์เวิร์ดสำคัญในนี้คือกรณีนี้เป็นหนึ่งในตัวอย่างแรก ๆ ของไดร์เวอร์ GPU ที่ใช้ Rust ใน Linux
    • อย่าโกรธนะที่พูดค่อนข้างตรง ๆ ดูจากโทนคำพูดของคุณเหมือนคุณไม่ค่อยรู้เลยว่า GPU driver ยุคนี้เป็นยังไง ผมใช้มันมาถึง 15 ปีแล้วตั้งแต่ต้นก็รู้ว่ามันยิ่งซับซ้อนขึ้นตามเวลา และในซอร์สโค้ด Linux kernel เอง GPU driver ก็เป็นส่วนที่มีสัดส่วนบรรทัดโค้ดมากที่สุดเกือบทั้งหมด แทบทุกไดร์เวอร์รองรับการ์ดกราฟิกหลายรุ่นแล้ว คำถามคือคุณรู้ไหมว่ามันไม่สมเหตุสมผลเลยหรือที่จะแยกไดร์เวอร์ที่เป็นอิสระกันอย่างแท้จริงสำหรับการ์ด GPU แต่ละตัว งานเขียนไดร์เวอร์ GPU ไม่ได้เป็นแค่การต่อจุดเชื่อม API สองฝั่งแบบยากยิ่งกว่าเดิม หากคุณมั่นใจที่จะแย้งโต้ได้ กรุณาแสดง GPU driver ที่คุณเขียนให้ดู หนึ่งในสิ่งที่อยากยืนยันคือมันจบที่เชื่อมเส้นไม่กี่เส้นแค่นั้นจริง ๆ หรือไม่
    • Rust จึงสำคัญเพราะนี่เป็นหนึ่งในกรณีแรก (หรือหนึ่งในกรณีแรก ๆ) ที่มีการใช้โครงสร้างของ Rust ใน GPU driver