Axboe schlägt Identitätstausch von Threads für io_uring vor
io_uring gibt es, damit eine Anwendung Arbeit einreichen kann, ohne zu blockieren — und darunter liegt seit jeher ein unbequemes Problem: Viele Pfade im Kernel wurden nie für asynchrone Ausführung geschrieben. Operationen dieser Art — die io_uring-Entsprechungen von fdatasync(), statx(), einige openat()-Fälle — gehen heute an einen separaten Worker-Thread, damit der einreichende Thread aus io_uring_enter() zurückkehren kann, ohne zu warten.
Bezahlt wird diese Vorsichtsmaßnahme im Voraus, ob sie gebraucht wird oder nicht. Einen Worker aufzuwecken und in ihn umzuschalten ist nicht umsonst, und meistens wäre die Operation ohnehin sofort fertig geworden. Blockiert sie tatsächlich, hat sich die Übergabe gelohnt; tut sie es nicht — der häufige Fall —, macht der Zusatzaufwand einen erheblichen Teil der Gesamtkosten aus. Genau davor wollten die Leute fliehen, die zu io_uring greifen.
Der RFC von Jens Axboe, über den LWN diese Woche berichtet, dreht die Reihenfolge um: Die Operation läuft auf dem einreichenden Thread, und nur der seltene Fall, dass sie wirklich blockiert, wird gesondert behandelt.
Dafür muss der Scheduler mithelfen. Ein neues Task-Flag, PF_IO_HANDOFF, markiert einen Thread in diesem Modus; will ein so markierter Thread schlafen gehen, ruft der Scheduler in io_uring hinein und sagt Bescheid. Was dann passiert, trägt im Artikel das Attribut „potenziell furchteinflößend": die beiden Threads tauschen ihre Identität. Der Worker wird dem Einreicher nachgebildet — gleiche Thread-ID, gleiche Signalbehandlung und so weiter — und arbeitet den Submission-Ring weiter ab, bis er in den User-Space zurückkehrt. Der ursprüngliche Thread übernimmt die Identität des Workers und blockiert, wie er es ohnehin getan hätte.
Aus Sicht des User-Space kommt aus io_uring_enter() derselbe Thread zurück, der hineingegangen ist. Es ist ein anderes task_struct.

Was das bedeutet
Hier wird eine Art von Komplexität gegen eine andere getauscht, und der RFC ist ehrlich darin, welche. Die saubere Alternative — jeden blockierenden Pfad im Kernel asynchron zu machen — wird seit Jahren stückweise verfolgt und ist nicht fertig, weil sie alles berührt. Der Identitätstausch bleibt auf io_uring plus einen Scheduler-Haken beschränkt, also auf eine viel kleinere Fläche — zum Preis eines Gedankens, der schwer zu halten ist: „Der Thread, mit dem du sprichst, ist nicht der, mit dem du angefangen hast."
Hinschauen muss man überall dort, wo Thread-Identität von außen beobachtet wird. Debugger, Tracing und Profiling, alles, was über Thread-IDs korreliert, Ressourcenabrechnung pro Thread, und die Annahmen von Sicherheitswerkzeugen, für die ein task_struct stabil ist. Das sind genau die Werkzeuge, zu denen man greift, wenn in einer io_uring-Anwendung etwas schiefgeht — die Kosten dieser Änderung zeigen sich also eher im Störungsfall als im Benchmark.
Und es lohnt zu benennen, was hier optimiert wird: eine Abgabe auf den Normalfall. Dieses Muster steckt überall in der Systemtechnik — ein Sicherheitsschritt bei jeder Anfrage, weil er gelegentlich nötig ist. Wo immer man so etwas hat, ist die Frage dieses Patch-Sets die richtige: Lässt sich der schlechte Fall im Moment seines Eintretens erkennen, statt ihn vorab zu bezahlen? Oft nicht ohne einen Haken in etwas, das einem nicht gehört — deshalb brauchte Axboe den Scheduler.
Es ist ein RFC, keine eingebaute Funktion. Corbets Text bei LWN ist der Ort, an dem sich verfolgen lässt, was die Kernel-Entwickler davon halten.