Portál AbcLinuxu, 19. dubna 2024 09:56

Jaderné noviny – 23. 2. 2012: Co dělat se sleep(0)

5. 3. 2012 | Luboš Doležel
Články - Jaderné noviny – 23. 2. 2012: Co dělat se sleep(0)  

Aktuální verze jádra: 3.3-rc4. Citát týdne: David Miller. Náprava ABI sys_poll(). dma-buf a proprietární moduly. Oracle nabízí DTrace pro Linux. Co dělat se sleep(0).

Obsah

Aktuální verze jádra: 3.3-rc4

link

Aktuální vývojová verze jádra je 3.3-rc4 vydaná 18. února, což je o pár dnů později, než se čekalo. Tentokrát je důvodem pro zpoždění to, že nám trvalo několik dnů, než jsme vystopovali narušování stavu plovoucích čísel, ke kterému docházelo na 32bitovém x86 – ale jen pokud jste měli moderní CPU (proč pak používáte 32bitová jádra?) s podporou instrukcí AES-NI. A pak jste museli zapnout podporu těchto instrukcí *a* použít bezdrátový ovladač, který jich využívá. Nejpravděpodobnějším důvodem je používání infrastruktury mac80211 s WPA se šifrováním AES (tedy typicky WPA2). Dále se dostalo i na řadu dalších oprav; krátký seznam změn najdete v oznámení.

Stabilní aktualizace: verze 3.0.22 a 3.2.7 byly vydány 20. února; obsahují obvyklý výčet důležitých oprav.

Citát týdne: David Miller

link

Když napíšete

if (ret) {
        one_line_statement();
}
tak někde umře štěňátko. A lidi od DRM právě odrovnali celý útulek.

-- David Miller

Náprava ABI sys_poll()

link

Systémové volání poll() má tři parametry, jedním z nich je hodnota časového limitu (timeout), která určuje horní mez (v milisekundách) toho, jak dlouho bude proces čekat. Manuálová stránka říká, že typem této hodnoty je int. Z důvodů, které už dávno zapadly v historii, interní jaderná implementace poll() vždy očekávala hodnotu časového limitu v podobě long. A to bylo zdrojem občasných bugů.

Většinou to funguje, jak má. Typy int a long jsou na většině architektur shodné a pokud už se liší, tak se glibc postará o příslušnou změnu se zachováním znaménka. Pokazí se to ale právě tehdy, když 32bitový proces běží na systému x86-64. V tomto případě 32bitová funkce sys_poll() předá hodnotu nativnímu jadernému protějšku jen tak, aniž by docházelo ke znaménkové úpravě. Takže pokud je hodnota záporná (značící, že poll() může klidně čekat donekonečna), jádro místo ní uvidí velkou kladnou hodnotu.

Problém se dá opravit několika způsoby. Linus se rozhodl pro změnu parametru časového limitu na int v jádře. Díky tomu, že teď je timeout 32bitovou hodnotou na všech systémech, je hned o jednu příčinu zmatků méně. Tento přístup má ale jedno drobné riziko: nelze vyloučit, že existuje nějaká aplikace, která skutečně používala 64bitové timeouty. Aby taková aplikace mohla fungovat, znamenalo by to obcházení glibc (protože její operace se znaménky používání 64bitových hodnot znemožňuje), tudíž je nepravděpodobné, že se s tím někdo někdy obtěžoval, ale kdo ví. Pokud by tato změna nějakou skutečnou aplikaci porouchala, musel by se patch zase odstranit a muselo by se najít nějaké komplikovanější řešení.

Linusův patch byl začleněn do verze 3.3-rc5, takže máte několik týdnů na vyjádření svých obav.

dma-buf a proprietární moduly

link

Mechanismus pro sdílení bufferů DMA byl zařazen do jádra verze 3.3; jde o možnost, jak pod taktovkou uživatelského prostoru umožnit sdílení bufferů DMA mezi nezávislými ovladači zařízení. Patche dma-buf v podobě, v jaké byly začleněny do verze 3.3, obsahují řadu funkcí, které ovladače používají pro přístup k takovým bufferům; tyto funkce jsou exportovány jako „pouze pro GPL“. Kvůli tomu se ozval Robert Morell z NVIDIA, kterému se pochopitelně nelíbilo, že by toto rozhraní nebylo přístupné proprietárnímu ovladači z dílny jeho firmy.

Moc čtenářů asi nepřekvapí, že reakce na Robertovu stížnost nebyly zrovna plné pochopení. Po chvíli diskuze utichla bez jakéhokoliv řešení. Nedávno ale Rob Clark zpravil svět o debatě, která proběhla na Embedded Linux Conference:

V reakci na proběhlou debatu musím souhlasit, že infrastruktura dma-buf je zamýšlena jako rozhraní mezi subsystémy ovladačů. A protože (prozatím) veškerá podpora pro ARM SoC GL bohužel znamená používání uzavřeného kódu v uživatelském prostoru a protože považuji uživatelský prostor a jádro v oblasti grafických ovladačů za úzce provázané, nemyslím si, že se zde můžeme odvolávat na nějaký zavedený morální standard. Tudíž nemohu namítat proti tomu, aby se místo EXPORT_SYMBOL_GPL() objevilo EXPORT_SYMBOL().

Od té doby se o tom už nemluvilo; stejně tak se ani v jádře nic ohledně těchto funkcí nezměnilo. Změna tónu ve výše citovaných větách může znamenat, že se snad postoje lidí obměkčují a že API pro sdílení bufferů bude nakonec dostupné i pro proprietární moduly.

Oracle nabízí DTrace pro Linux

link

Oracle oznámil dostupnost betaverze trasovacího frameworku DTrace, který byl přeportován na jejich „Unbreakable Enterprise Kernel“. Ohledně toho, jak port funguje nebo jak se používá, se tohoto teď moc neví; linuxové fórum DTrace zatím zeje dost prázdnotou. Ukázka použití je ale k mání v blogovém zápisku od Wima Coekaertse.

Co dělat se sleep(0)

link

Obecně platným pravidlem vývojářů jádra je vyhýbat se rozbíjení kódu v uživatelském prostoru, i když je takový kód často vnímán jako principiálně špatný. Ale jsou tu i výjimky; nedávná debata týkající se chování časovače může být ukázkou, jak k takovým výjimkám může dojít.

Standardní céčková funkce sleep() má tu definici, že uspí volající proces na alespoň tolik sekund, kolik bylo určeno. Člověk by si mohl myslet, že volání sleep() s argumentem nula asi moc nedává smysl; proč uspávat procesor na 0 sekund? Jenže se ukázalo, že někteří vývojáři dělají taková volání, aby se CPU na chvilku vzdali. Smyslem je chovat se slušně a před pokračováním v práci nechat krátce běžet i jiné procesy. Aplikace, které dělají polling nebo jsou z jiného důvodu náchylné k přílišnému vytěžování CPU, jsou často „napravovány“ voláním nulových sleepů.

Bylo nebylo za horami v zemi Linuxu, sleep(0) vždy uspával volající proces na alespoň jeden ti(c)k hodin. S příchodem časovačů o vysokém rozlišení do jádra se toto chování změnilo; pokud proces požádal o uspání na vypršeném časovači (což je situace sleepu na nula sekund), volání se jednoduše vrátilo zpět do volajícího procesu. Pak bylo přidáno polevování časovačů [timer slack], které může protáhnout čekání s cílem probouzet najednou hned několik procesorů. Toto chování způsobuje to, že časovače běží o trochu déle, než se žádalo, ale výsledkem je méně probouzení procesorů a tudíž úspora energie. V případě sleepu na nula sekund přidání polevování časovačů mění vypršený časovač na nevypršený časovač, takže volající proces je opět uspáván.

Výchozí polevování trvá 50 µs, takže je nepravděpodobné, že by to ve většině aplikací způsobovalo viditelné změny. Ale zdá se, že na některých systémech je tato hodnota nastavena trochu moc vysoko – v řádu sekund – aby se dosáhlo největších úspor energie. To pak odpovídajícím způsobem může prodloužit sleep a tedy aplikace trochu porouchat.

Matthew Garrett, který zastává názor, že není dobré aplikace rozbíjet, zaslal patch, který sleepy na nula sekund speciálně ošetřuje. Myšlenka je prostá: pokud je požadovaný čas nulový, polevování se mu vyhne a proces nebude uspáván po neurčitou dobu. Problémem tohoto přístupu k věci je to, že proces stále nedocílí kýženého výsledku: namísto vzdání se procesoru jen dojde ke zbytečnému systémovému volání a proces bude zase pokračovat v tom, co dělal doposud. Bez polevování časovačů se požadavek na uspání nad vypršeným časovačem vrátí hned zpátky do uživatelského prostoru, aniž by jakkoliv prošel plánovačem.

Alternativou by bylo přeměnit volání sleep(0) na volání sched_yield(). Jenže tento nápad se netěší popularitě mezi vývojáři plánovače, kteří si myslí, že volání sched_yield() jsou téměř vždy špatný nápad. Říkají, že je lepší opravit aplikace tak, aby přestaly dělat polling nebo obecně to, o čem si vývojáři myslí, že je dobrým důvodem k explicitnímu vzdávání se procesoru.

Podle Matthewa se to týká nemalého množství aplikací:

Testování na Fedoře odhalilo přibližně 125 balíčků z 11000 nebo tak nějak, takže přibližně 1 % uživatelského prostoru v některých situacích používá sleep(0). Všechno v distribuci asi opravit můžeme, ale i tak to znamená, že je na světě znatelné množství kódu, které je porouchaný.

Obvyklým přístupem při vývoji jádra by bylo vyhnout se rozbíjení těchto aplikací. I když aplikace závisí na nedefinovaném nebo nedokumentovaném chování – což je rozhodně tato situace – je stále lepší, když aktualizace jádra nepromění funkční kód v rozbitý. Někteří účastníci se vyjádřili v tom smyslu, že by se tak mělo postupovat i tentokrát.

Situace kolem sleep(0) se ale od jiných trochu liší. Vývojáři aplikací se v tomto případě nemohou odvolávat na dlouhodobě funkční chování, protože během poslední desítky let se reakce jádra na sleep(0) několikrát změnila. A podle Thomase Gleixnera je těžké poznat, kdy by se k takovému volání mělo přistupovat jinak nebo co by se mělo dělat:

Krucinál, nemůžeme přece dát dohromady rozumnou definici, jak takovou věc odlišně ošetřit, protože prostě není ani možné udělat jasnou hranici, co je zvláštní případ a co ne. A neexistuje žádná rozumná definice, co dělat – hned se vrátit nebo projít přes schedule() nebo něco jiného.

Thomas má obavy, že by se pak lidé mohli dovolávat zvláštního ošetřování u podobných volání – například u nanosekundové obdoby nanosleep() – a výsledkem by pak bylo hromadění nepořádku v hlavním kódu časovače. Takže než abychom se snažili tyto případy definovat a donekonečna museli výsledek tohoto snažení udržovat, si myslí, že bychom měli v případech, kdy je polevování časovače nastaveno na velkou hodnotu, nechat postižený kód svému osudu. A v tomto bodě diskuze odezněla, což znamená, že se ohledně omezení dopadu polevování časovače asi nebude nic dělat.

Odkazy a zdroje

Kernel coverage at LWN.net: February 23, 2012

Další články z této rubriky

Jaderné noviny – přehled za březen 2024
Jaderné noviny – přehled za únor 2024
Jaderné noviny – přehled za leden 2024
Jaderné noviny – přehled za prosinec 2023
Jaderné noviny – přehled za listopad 2023

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.