Jens Axboe piektdien nosūtījis io_uring sarakstam 15 ielāpu sēriju, kas maina veidu, kā kodols apstrādā bloķējošus pieprasījumus. Uz tmpfs fsync operāciju skaits pie rindas dziļuma 1 kāpj no 28 200 līdz 221 000 sekundē. Tas ir 681 % pieaugums par 5 procentpunktiem lielāka CPU patēriņa cenā.
Sērija skar 33 failus, pievieno 2167 rindas un dzēš 116. Ielāpi rakstīti pret 7.3-rc2.
Kāpēc fsync līdz šim gāja apkārt
io_uring izpilda pieprasījumu uz vietas ar karodziņu IO_URING_F_NONBLOCK. Ja tā neiet, pieprasījumu nodod io-wq darbiniekam. Daļai operāciju nebloķējoša ceļa kodolā nav vispār: fsync, statx, openat un pārējā *at saime, xattr, fadvise, splice. Tās tiek nodotas io-wq bez izņēmuma. Katra nodošana maksā pavediena pamodināšanu, konteksta pārslēgu un task_work atgriezienu.
Praksē lielākā daļa šo izsaukumu nemaz nenobloķējas. fdatasync, kuram nav ko skalot. statx, kas trāpa dcache. openat ar O_TMPFILE karodziņu. Visi trīs būtu pabeigti uz vietas, taču io_uring uz to paļauties nevar, tāpēc maksā par nodošanu katru reizi.
io_uring ir SPIESTS būt piesardzīgs, lai kodolā nejauši nenobloķētos, pat ja operācijas pārsvarā nekad nebloķējas. Jens Axboe sērijas pavadvēstulē
Kas tieši pāriet citam pavedienam
Jaunā pieeja izpilda šos pieprasījumus uz vietas bloķējošā režīmā. Par nodošanu tiek maksāts tikai tad, ja pieprasījums tiešām nobloķējas. Tajā brīdī iesniedzējs jau ir dziļi kodolā ar pieprasījumu uz sava steka, tāpēc darbu pārcelt vairs nevar. Pārcelt var identitāti.
Kad iesniedzējs nobloķējas, dīkstāvē esošs io-wq darbinieks pārņem tā lietotājam redzamo identitāti: tid, signālu stāvokli, tiesības, plānotāja atribūtus, cgroup un lietotāja reģistru stāvokli. Šis darbinieks pabeidz io_uring_enter() izsaukumu un atgriežas lietotāja telpā iesniedzēja vietā. Sākotnējais uzdevums pabeidz pieprasījumu kā io-wq darbinieks un iekļaujas pūlā. Lietotāja telpa saņem atpakaļ to pašu tid. Aiz tā vienkārši stāv cits task_struct.
Bloķēšanos noķer plānotāja āķis. Uzdevums ar karodziņu PF_IO_HANDOFF liek sched_submit_work() iesaukties io_uring blakus jau esošajiem io-wq un darba rindu āķiem. Tieši tur notiek identitātes nodošana. Slēdzeni uring_lock tobrīd vēl tur bloķējošais uzdevums, kas to atbrīvo paaugstinātā darbinieka vārdā. Axboe pavadvēstulē piebilst, ka līdzīgu ideju mēģināja īstenot jau pirms aptuveni 20 gadiem.
Kur skaitļi aug un kur krīt
Mērījumi veikti virtme-ng viesī ar 8 virtuālajiem kodoliem uz x86 konfigurācijas bez atkļūdošanas. Viens un tas pats kodols, funkciju ieslēdz sysctl slēdzis. CPU kolonna rāda visa procesa patēriņu kopā ar io-wq darbiniekiem procentos no viena kodola. Katrs skaitlis ir divu palaišanu vidējais.
Pie rindas dziļuma 1 ieguvumi ir lieli:
- fsync uz tmpfs: no 28 200 uz 221 000 operācijām sekundē (+681 %)
- fadvise DONTNEED uz ext4: no 26 100 uz 150 000 (+478 %)
- statx uz ext4: no 23 600 uz 121 000 (+414 %)
- openat ar O_TMPFILE uz tmpfs: no 13 400 uz 43 000 (+222 %)
- renameat uz ext4: no 7400 uz 13 400 (+81 %)
Pie dziļuma 32 aina apgriežas. fsync uz ext4 krīt no 42 200 uz 14 600 operācijām, tas ir 65 % zaudējums. openat ar O_TMPFILE uz ext4 zaudē 49 %, statx uz ext4 zaudē 29 %. splice uz cauruli visos trijos dziļumos paliek gandrīz nekustīgs: +8 %, 0 % un -10 %.
CPU patēriņš pie tam krīt strauji. renameat uz ext4 ar dziļumu 8 iepriekš noslogoja 544 % no viena kodola, pēc izmaiņas 100 %. Uz tmpfs tas pats tests krīt no 514 % uz 98 %.
Zaudējumiem Axboe nosauc divus iemeslus. Pirmais: agrāk darbs tika izklāts pa vairākiem io-wq darbiniekiem. Pie liela rindas dziļuma šī paralelizācija palīdz. Jo dārgāka ir zemā līmeņa kodola daļa, jo mazāks rindas dziļums vajadzīgs, lai parādītos zaudējums. openat ir skaidrākais tāds gadījums. Otrais iemesls: dažas operācijas bloķējas vienmēr. Tieši tāds ir fsync uz ext4, kur nodošana io-wq uzreiz paliek ātrāka.
Mitigācijas idejas Axboe saka, ka viņam ir, taču RFC stadijā tās neizklāsta. Zemi rindas dziļumi esot tas, ko ar šādām operācijām parasti dara.
Kas paliek ārpusē
Nodošana tiek atteikta uzreiz vairākos gadījumos: izsekoti uzdevumi, per-task perf konteksti, PI futex, audita konteksti, uzvilkti per-thread CPU taimeri, core scheduling marķieri un vfork vecāki. Uzskaite par pavedienu pārceļas līdzi, tāpēc skaitītāji, ko lietotāja telpa redz vienam tid, paliek monotoni.
Divas nepilnības Axboe nosauc pats. LSM stāvoklis glabājas uzdevumā, ne credentials struktūrā, tāpēc tas nepārceļas. Tāpat nepārceļas PR_SET_IO_FLUSHER. Abos gadījumos nepārcelšana var tikai ierobežot, ne paplašināt atļauto.
Lasīšana un rakstīšana failos ar FMODE_NOWAIT paliek ārpusē, jo tiem nebloķējošais ceļš ar poll atkārtojumu jau strādā. uring_cmd arī, jo tādi draiveri kā ublk piesaista stāvokli iesniedzējam uzdevumam. x86 atsakās apkalpot AMX lietotājus, arm64 atsakās no SME. Citas arhitektūras šo vēl neatbalsta.
Statuss
Sērija ir RFC. Tā nokārto pilno liburing testu komplektu gan uz x86-64, gan arm64. Kods pieejams git.kernel.dk zarā io_uring-thread-handoff.3. Phoronix raksta, ka mērķis varētu būt 7.4 izstrādes cikls.
Pēdējais, 15. ielāps, kas tāpat apstrādātu IOSQE_ASYNC pieprasījumus, sērijā atdalīts atsevišķi. Axboe raksta, ka vēl nav pārliecināts, vai to vispār vajag darīt, jo tā ir lietotāja telpai redzama politikas maiņa.
Atbildes viņš gaida par trim lietām: plānotāja āķi un pašu identitātes pārcelšanu, x86 un arm64 reģistru stāvokļa apstrādi, kā arī par to, vai kāds paļaujas uz lietotāja identitātes palikšanu pie viena task_struct. Lielākais jaunais fails sērijā ir kernel/thread_handoff.c ar 490 rindām. Tas ir arī vienīgais, kas no io_uring nav atkarīgs.
Komentāri
Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.