3 Punkte von GN⁺ 2025-08-08 | 1 Kommentare | Auf WhatsApp teilen
  • Dieser Beitrag beleuchtet den Entwicklungsprozess von Tyr, dem modernen, in Rust entwickelten GPU-Treiber für den Linux-Kernel, und die Funktionsweise von GPU-Treibern.
  • In der GPU-Treiberentwicklung werden anhand des Beispiels VkCube die Rollenverteilung und Interaktion von UMD (User-Mode-Treiber) und KMD (Kernel-Mode-Treiber) erläutert.
  • Der UMD wandelt High-Level-APIs in Low-Level-Kommandos um, die von der GPU verstanden werden können; der KMD übernimmt Kernaufgaben wie Speicherzuweisung, Job-Scheduling und Geräteinitialisierung.
  • Die von Tyr bereitgestellte API ist identisch mit Panthor und besteht aus Geräteabfragen, Speichermanagement, Gruppenverwaltung, Job-Submit und Tiler-Heap-Management.
  • Im nächsten Beitrag werden die Arm CSF-Hardwarearchitektur und zentrale Komponenten (z. B. die MCU) sowie der Boot-Vorgang behandelt.

Einführung: Entwicklung eines modernen GPU-Kernel-Treibers auf Rust-Basis

  • Dieser Beitrag ist der zweite Artikel der Reihe über Tyr, den hochmodernen GPU-Kernel-Treiber in Rust für Arm Mali CSF unter Linux.
  • Als praktisches Beispiel wurde VkCube, ein einfaches 3D-Programm, das mit der Vulkan API einen rotierenden Würfel rendert, gewählt, um den internen Ablauf eines GPU-Treibers zu erklären.
  • Die schlichte Struktur von VkCube eignet sich besonders gut, um die Funktionsweise von GPU-Treibern zu studieren.

Grundlagen von GPU-Treibern: Rollen und Struktur von UMD und KMD

  • Besteht aus User Mode Driver (UMD) und Kernel Mode Driver (KMD)
    • UMD: implementiert APIs für normale Anwendungen wie Vulkan und OpenGL, etwa panvk (Mesa Vulkan-Treiber)
    • KMD: wie Tyr als privilegierter Kernel-Level-Treiber, der als Teil des Linux-Kernels läuft
  • Der Kernel-Mode-GPU-Treiber verbindet einen weitreichenden UMD mit der eigentlichen GPU; der UMD interpretiert API-Befehle in Befehlssätze, die von der GPU ausgeführt werden können.
  • Der UMD bereitet für die Szenen­zusammensetzung benötigte Daten wie Geometrie, Texturen und Shader vor und fordert den Speicher auf der GPU beim KMD an, bevor die Ausführung startet.
  • Shader sind eigenständige Programme, die auf der GPU laufen; in VkCube übernehmen sie Funktionen wie Würfelplatzierung, Color Mapping und Rotation. Für die Shader-Ausführung werden externe Daten benötigt (z. B. Geometrie, Farbe oder Rotationsmatrix).
  • Der UMD übergibt vorbereitete Befehle (z. B. VkCommandBuffers) an den KMD zur Ausführung und kann bei Fertigstellung benachrichtigt werden, um Ergebnisse im Speicher abzulegen.

KMD (Kernel-Mode-Treiber): Hauptverantwortung

  • Zuordnung und Mapping des GPU-Speichers (mit isolation pro Anwendung)
  • Job-Submit auf Hardware-Queues und Benachrichtigung des Benutzers bei Abschluss
  • In asynchronen, parallelen Hardware-Umgebungen ist das Management von Abhängigkeiten essenziell; um korrekte Ergebnisse sicherzustellen, übernimmt der KMD die Aufgaben Scheduling und Dependency-Validierung.
  • Dazu gehören auch Geräteinitialisierung, Betrieb der Takt-/Spannungsregler, Ausführung des Startcodes und Verwaltung der Zugriffsgerechtigkeit, damit mehrere Clients die Hardware fair nutzen.

Wo die Komplexität liegt: Arbeitsteilung zwischen UMD und KMD

  • Die Hauptkomplexität von GPU-Treibern liegt beim UMD
    • UMD: wandelt High-Level-API-Kommandos in Hardware-Kommandos um
    • KMD: stellt grundlegende Funktionen bereit wie Speichervisolierung, Sharing und faire Zugriffssteuerung, damit der UMD korrekt arbeiten kann.

Struktur der Treiber-Schnittstelle (API), die Tyr bereitstellt

  • Die Tyr-Treiber-API (= entspricht Panthor) lässt sich in fünf Hauptgruppen einteilen
    1. Geräteabfrage: DEV_QUERY (IOCTL zur Abfrage von GPU-Hardwareinformationen, Nutzung des ROM-Bereichs)
    2. Speicherzuweisung und Isolation: VM_CREATE, VM_BIND, VM_DESTROY, VM_GET_STATE, BO_CREATE, BO_MMAP_OFFSET usw.
    3. Management von Scheduling-Gruppen: GROUP_CREATE, GROUP_DESTROY, GROUP_GET_STATE (ausführliche Erklärung folgt in einem Folgebeitrag)
    4. Job-Submit: GROUP_SUBMIT (Ausführungsanfrage an die GPU über Geräte-Befehlspuffer)
    5. Tiler-Heap-Verwaltung: TILER_HEAP_CREATE, TILER_HEAP_DESTROY (Deckung des Speicherbedarfs von Tiled-Rendering-GPUs)
  • Diese APIs sind im direkten Sinne weit entfernt von der eigentlichen Pixel-Ausgabe; der UMD übernimmt die tatsächliche Befehlsausführung, während der KMD nur diese Schnittstelle für den Hardwarezugriff bereitstellt.

Fazit und weitere Schritte

  • In diesem Beitrag wurde die Gesamtstruktur und der interne Ablauf eines GPU-Treibers sowie die zentralen APIs von Tyr betrachtet.
  • Auf Basis dieser Inhalte werden in den Folgebeiträgen die Arm CSF-Hardwarearchitektur, der Microcontroller Unit (MCU) und der Initialisierungsablauf des Treibers behandelt.

1 Kommentare

 
GN⁺ 2025-08-08
Hacker News-Kommentare
  • Es war wirklich ein guter Artikel, aber er war dafür etwas zu kurz; gerade als es spannend wurde, endete er, und ich bin auf die Fortsetzung gespannt.
    • Nächste Woche soll auch eine weitere spannende Folge folgen, in der man sieht, wie wartende Befehle auf der GPU aus der Queue herauskommen und ausgeführt werden. Diese Beitragsschicht der Abstraktion beschäftigt sich mit dem Datenfluss an der Userspace-/Kernel-Grenze. Deshalb geht es hauptsächlich um Queue- und Pufferverwaltung, sodass dort selbst nicht viel echte Verarbeitung stattfindet. Die wirklich wichtigen Schritte passieren, wenn die in die Queue eingereihten Befehle ausgeführt werden. Umgekehrt wird von der GPU auch noch ein weiteres Completion-Signal zurückgegeben, was ich ebenfalls spannend finde. Den Großteil dieser asynchronen Verarbeitung übernimmt jedoch nicht der Treiber, sondern der Nutzercode; der Treiber leitet nur das Abschluss-Signal weiter.
  • Ich nutze ein RK3588-Gerät als Desktop mit Panfrost, und ab und zu gibt es bei Firefox einen schwarzen Bildschirm bzw. transparente Bereiche – ein seltsamer Bug.
    • Der RK3588 verwendet in Wirklichkeit nicht Panfrost, sondern den im Artikel behandelten Panthor-Treiber.
  • Ich frage mich, ob über die Nutzung von uring_cmd statt ioctls nachgedacht wurde; dieses Projekt wird ja komplett neu gebaut, also scheint es grundsätzlich möglich zu sein. Mich interessiert, warum du kaum Vorteile darin siehst.
    • Die GPU hat bereits eine eigene asynchrone Befehlswarteschlange, und IOCTLs sind ohnehin dafür gedacht, relativ günstig genau in diese Queue zu schreiben. Daher ist es ziemlich limitiert, auf CPU-Seite noch eine zweite asynchrone Queue einzubauen, nur um diese Writes zu planen/schedulen. Wenn der Vorschlag darin bestünde, die GPU-Befehlsqueue selbst als uring in den Userspace zu mappen, müsste die Firmware ziemlich umfangreich geändert werden, um die io_uring-API vollständig zu unterstützen, und es könnte wegen der Hardware sogar unmöglich sein.
    • Der im Artikel beschriebene Treiber folgt exakt der API, die die Mesa-Userspace-Bibliothek verlangt.
  • Ich habe den Artikel wirklich mit Interesse gelesen und mich gefragt, ob es einen weiteren Teil gibt oder zumindest eine logische Fortsetzung.
    • Da es sich heute um eine Erstveröffentlichung handelt, erwarte ich weitere Updates.
  • Mir ist klar, dass der Titel „Rust GPU driver“ mehr Klicks lockt, aber im Kern ist das doch ein Arm-Mali-CSF-basierter GPU-Treiber, oder nicht? Persönlich gefällt mir die Fokussierung auf ein Meta-Tooling zur Tool-Entwicklung nicht besonders; es klingt eher so, als ginge es tatsächlich darum, in Rust etwas zu bauen. Im Beitrag wurde zwar ein „Arm-Mali-basierter GPU-Treiber-Kernel“ genannt, aber er wird trotzdem nicht bewusst als Arm-Mali-Treiber bezeichnet. Das Erstellen eines Treibers bedeutet im Grunde nicht nur, eine Verbindungsleitung zwischen OS-API und Hersteller-API zu ziehen; ich denke nicht, dass man hier ein zusätzliches Framework aufbaut, das eine weitere Abstraktionsschicht bereitstellt. Bitte nimm meinen direkten Ton bitte nicht persönlich.
    • Rust ist hier wichtig, weil dies einer der ersten (wenn nicht der allererste) Fälle ist, in dem Rust-Infrastruktur in einem GPU-Treiber unter Linux eingesetzt wurde.
    • Bitte nimm mir die Grobheit nicht übel: An deinem Ton hört es sich so an, als ob du gar nicht weißt, wie ein moderner GPU-Treiber aussieht. Ich habe sie vor 15 Jahren noch benutzt, aber ich weiß, dass sie seitdem deutlich komplexer geworden sind. Schon im Linux-Kernel-Quellcode ist der Bereich der GPU-Treiber einer der größten nach Zeilenzahl. Fast alle Treiber unterstützen mehrere Grafikkarten, und ich frage mich, ob dir klar ist, wie unvernünftig es wäre, für jede einzelne GPU-Karte einen vollständig unabhängigen Treiber zu schreiben. GPU-Treiberarbeit ist nicht damit getan, einfach nur zwei APIs mit ein paar Leitungen zu verbinden; in der Praxis ist es deutlich komplexer. Wenn du das widerlegen willst, zeig doch bitte deinen eigenen GPU-Treiber, und ich schaue gern nach, ob du wirklich nur ein paar Leitungen verbunden und dann fertig warst.
    • Rust ist vor allem deshalb relevant, weil es einer der ersten (wenn nicht der allererste) Fälle ist, in dem Rust-Infrastruktur in einem GPU-Treiber verwendet wurde.