Portál AbcLinuxu, 5. května 2025 10:38
Aktuální verze jádra: 2.6.34-rc5. Citáty týdne: Toshiharu Harada, Matthew Garrett, Ted Ts'o, Andrew Morton. CPU*PID = bordel. Blokování uspání. Mohlo by 2.6.35 být bez BKL? Subsystém cpuidle – nicnedělání je složitá práce.
Současné vývojové jádro je stále 2.6.34-rc5; během uplynulého týdne nebyly vydány žádné předverze. Proud změn neslábne; obsahuje spoustu oprav, ale také je zde balónový ovladač VMware (krátce zmíněný zde začátkem dubna) a ovladač ipeth, který zajišťuje řetězení USB na iPhone. Vydání 2.6.34-rc6 lze očekávat brzy – pravděpodobně několik milisekund po zveřejnění tohoto čísla.
Stabilní aktualizace: Aktualizace stabilních jader 2.6.32.12 a 2.6.33.3 byly vydány 26. dubna. Obě aktualizace jsou obrovské, přes 100 patchů v každé z nich.
-- Ted Ts'o
napsal Jonathan Corbet, 27. dubna
Mike Travis nedávno narazil na problém: Když máte systém s pouhými 2048 procesory, na každém procesoru je místo jenom pro 16 procesů, pak se narazí na výchozí limit 32k ID procesu. Na systémech s mnoha procesory většinou tolik procesů na každém CPU neběží, ale 16 se stejně zdá být poněkud málo – obzvláště když se zváží to, kolik jaderných vláken na každém CPU běží. S 2k procesory mohou jaderná vlákna sama o sobě způsobit, že systému dojdou ID procesu; s 4k procesory systém dokonce ani nenabootuje.
Navržené řešení byl nový parametr pro boot, který by umožnil specifikovat větší maximální číslo pro ID procesu. Tento nápad nicméně nepochodil; není zájem přidávat další volby jenom kvůli tomu, aby systém nabootoval. Fakt, že souběžností řízení pracovní fronty by tento problém měly nakonec vyřešit (tím, že se zbaví velkého počtu vláken pracovních front) také nepomohl; z jaderné volby se tím stává jenom provizorní oprava. Změny pracovních front ale příliš nepomohou lidem, kteří tento problém mají nyní; daná práce nakonec pravděpodobně bude začleněna, ale podle všeho nijak rychle.
Pravděpodobně tedy bude začleněna krátkodobější oprava. Místo jaderného parametru však nejspíše půjde o nějakou heuristiku, která se podívá na počet procesů a zpřístupní dostatečný počet ID. Pokud bude výchozí limit příliš nízký, automaticky se zvýší.
Zde zbývá jediná obava: Co prastaré aplikace, které ukládají čísla procesu jako znaménkové 16bitové celé číslo? Podle všeho takové aplikace existují. Není nicméně tak jisté, že takové aplikace existují na systémech s 4096 procesory – tato obava tedy pravděpodobně tuto změnu nezpomalí. Do doby, než se my ostatní dostaneme k novým a nablýskaným 4096jádrovým systémům, snad budou všechny zbývající chybné aplikace opraveny.
napsal Jonathan Corbet, 28. dubna
Co se týče začlenění jaderného kódu Androidu do jádra, jedním z klíčových bodů neshody bylo API probouzecích zámků [wakelocks]. O probouzecích zámcích jsou někteří přesvědčeni, že jdou proti tomu, jak se má řešit správa napájení, takže je bylo těžké začlenit. Ovladače Androidu ale probouzecí zámky používají, takže bez tohoto API je také nelze začlenit. Bylo by je možné přepracovat tak, aby probouzecí zámky nepoužívaly, ale pak by hlavní řada obsahovala ovladače, které nikdo nepoužívá – rozhodně ne nejlepší možný výsledek. Řešení probouzecích zámků tedy bylo nějakou dobu jedna z priorit.
Výsledkem práce v této oblasti jsou patche blokující uspání [suspend block patches], které nedávno zaslal Arve Hjønnevåg. Jméno této vlastnosti se změnilo a stejně tak se změnilo API, ale základ je stejný: Umožnit systému se automaticky uspat, když se nic neděje, a zároveň kódu umožnit říct „něco se děje“ jak na úrovni jádra, tak na úrovni programů v uživatelském prostoru.
Patche blokování uspání přidávají nový soubor /sys/power/policy do sysfs; výchozí hodnota je „forced“ (vynucené). Když je politika „vynucená“, přechody mezi stavy systému se odehrají v reakci na explicitní zápisy do /sys/power/state, jak je obvyklé. Pokud se nicméně politika přepne na „opportunistic“ (dle příležitosti), věci se trochu změní. Stav zapsaný do /sys/power/state se neprojeví okamžitě; místo toho jádro do tohoto stavu přejde pokaždé, když usoudí, že je systém nečinný. API pro blokování uspání lze poté použít k tomu, aby se systému zabránilo uspat se, když je to nežádoucí.
Patche byly zaslány dvakrát a dostalo se jim mnoha komentářů, takže bylo opraveno hodně věcí. V nedávné době nicméně mezi reakcemi převažovalo „ack“ v různých podobách. Dalo by se tedy usoudit, že blokování uspání má rozumnou šanci dostat se během začleňovacího okna 2.6.35 do jádra. To by následně mělo otevřít dveře začlenění velkého množství kódu ovladačů z projektu Android. Se štěstím se tolik zmiňovaný rozpor mezi Androidem a komunitou stane minulostí – tedy alespoň na úrovni jádra.
napsal Jonathan Corbet, 27. dubna
Odstranění velkého jaderného zámku [big kernel lock, BKL] je jedním z nejdéle trvajících projektů v historii vývoje jádra. BKL představuje zjevný problém pro škálovatelnost i spravovatelnost od svého přidání ve vývojovém cyklu 1.3; snaha ho odstranit začala v cyklu 2.1. Blížící se jádro 2.6.34 bude nicméně stále velký jaderný zámek obsahovat i přes veškerou snahu se ho zbavit. Dobrá zpráva je, že 2.6.35 by mohlo fungovat bez něj – přinejmenším pro některé konfigurace.
Během let bylo používání BKL vytlačováno do čím dál tím nižších úrovní jádra. Jakmile je lock_kernel() například vytlačeno do jednotlivého ovladače, je relativně jednoduché zjistit, jestli je tam opravdu zapotřebí, a nakonec může být úplně odstraněno. Ve vnitřní části jádra nicméně zbývá jedna pozice, kterou si BKL drží: Implementace ioctl(). Jádro podporuje operaci neobsahující BKL unlocked_ioctl(), ale mnoho ovladačů stále závisí na starší verzi chráněné BKL.
Oprava ioctl() je zjevně klíčová součást práce na odstranění BKL. Proto Frederic Weisbecker a Arnd Bergmann zaslali patch, který pro danou změnu připravuje půdu. Patch přidává další variantu ioctl() nazvanou locked_ioctl() do struktury file_operations. Hlavní myšlenka je zde mít k dispozici jak ioctl(), tak locked_ioctl() dost dlouho na to, aby bylo možné změnit veškerý kód, který stále BKL potřebuje, přičemž poté bude možné ioctl() odstranit. Nová funkce tedy závisí na nové konfigurační volbě CONFIG_BKL.
Patch se daleko nedostal; Linusovi se vůbec nelíbilo ani locked_ioctl(), ani CONFIG_BKL. Tak začalo hledání alternativ – nakonec se zdá, že na locked_ioctl() nikdy nedojde, ale konfigurační volba možná existovat bude.
Linus navrhl se s locked_ioctl() vůbec neobtěžovat. Místo toho by se všechny operace ioctl() jedním velkým patchem přejmenovaly na bkl_ioctl(). To by umožnilo snadno najít kód, který závisí na BKL, pomocí grep, aniž by bylo potřeba přidávat do struct file_operations další funkci, byť dočasně. Patch zajišťující toto přejmenování byl zaslán; možná bude začleněn do 2.6.35.
A nebo ne. Arnd použil tradičnější přístup a jeho patch jednoduše vytlačuje BKL do každé zbývající ioctl() funkce, která ho potřebuje. Jakmile specifické ioctl() řeší získání BKL samo, lze ho z vnitřního jaderného kódu volat jako unlocked_ioctl(). Až budou všechny takové funkce konvertovány, zamykající verze ioctl() může zmizet a BKL lze z tohoto kusu kódu odstranit. Toto vytlačení znamená víc práce než přejmenování, ale dosáhne se tím několika důležitých cílů.
Jeden z těchto cílů je jednoduché zatlačení BKL blíže ke kódu, který na něm závisí, což vede k pozdějšímu odstranění. Další je dostat se blíž k bodu, kdy bude možné BKL jednoduše konfigurací z jádra odstranit úplně – zde přichází ke slovu konfigurační volba CONFIG_BKL. Vypnutí teto volby odstraní podporu BKL, což způsobí, že kód na tomto zámku závisející se nepřeloží. Takový kód lze poté označit, což opět zjednodušuje nalezení a opravu dané závislosti na BKL.
Na první pohled odstranění BKL nemusí vypadat jako něco, po čem je potřeba tak toužit; zabírá jenom málo místa a režie vypadá poměrně zanedbatelně, když ho nikdo nepoužívá. Je zde ale možná malá – ale významná – úspora: V současnosti musí plánovač při každém přepnutí kontextu ověřovat, jestli je potřeba uvolnit BKL po odcházejícím procesu a jestli je potřeba tento zámek znovu zabrat kvůli přicházejícímu procesu. K přepínání kontextu dochází dost často na to, aby stálo za to jej zrychlit, jak nejvíc to bude možné; úplné odstranění BKL k tomuto cíli trochu přispěje.
Konfigurovatelný BKL bude také motivací pro každého, kdo zjistí, že překlad jádra bez BKL je blokován nějakým starým ovladačem. Většina zbývajících ovladačů, které závisí na BKL, je nemilovaná a nespravovaná; mnoho z nich se vůbec nepoužívá. Ty, které se stále používají, mohou být klidně opraveny, jakmile se najde nějaký dostatečně schopný vývojář, který si uvědomí, že k úplnému vypovězení BKL ze specifického systému postačuje jenom trocha práce.
Nakonec tedy 2.6.35 nebude jako celek jádro bez BKL. Ale jestli se tato práce začlení a jestli budou přijaty nějaké další patche, mělo by být možné vytvořit na mnoha konfiguracích jádro bez velkého jaderného zámku. A to je jistě úspěch hodný oslavy.
napsal Jonathan Corbet, 26. dubna
Autor článku měl nedávno důvod přehrabovat se v subsystému cpuidle. Nikdy nemá smysl takovou práci dělat jenom za jediným účelem, když ji lze zároveň použít jako zdroj materiálu pro Jaderné noviny. Následuje tedy několikaúrovňová diskuze o cpuidle, k čemu je a jak funguje. Jak se ukazuje, nic nedělat je těžší, než by si jeden myslel.
Na většině systémů je procesor nečinný po většinu času. Nemůžeme pořád provozovat práci náročnou na CPU, jako je překlad jádra, kódování videa, předvídání klimatu nebo yum. Když není co na práci, přejde procesor do klidového stavu, ve kterém čeká, až bude zase zapotřebí. Bylo nebylo, na mnoha systémech byl „klidový stav“ vlákno, které s nejnižší možnou prioritou běželo v nekonečné smyčce, dokud si systém nenašel něco lepšího na práci. Zabití tohoto nečinného procesu byl snadný způsob, jakým se dala vyvolat panika na stroji VAX/VMS, který neměl tušení, jak nic nedělat bez úlohy vyhrazené k tomuto účelu.
Aktivně čekající smyčka při svém běhu spotřebovává energii; současný názor je takový, že vynakládání velkého množství energie za účelem dosažení ničeho nebývá dobrý nápad. Vývojáři CPU tedy vymysleli způsoby, jak může procesor přejít do stavu snížené spotřeby, když nemá co na práci. Typicky, když je přepnuto do tohoto stavu, CPU zastavuje hodiny a vypíná napájení některých nebo všech obvodů, dokud nepřijde nějaké přerušení. Výsledek je, že se dostáváme k mnohem většímu nic na watt než při aktivním čekání.
Většina CPU má dokonce několik způsobů, jak efektivněji nic nedělat. Tyto režimy nečinnosti nazývané též „C stavy“ se liší v množství ušetřené energie, ale také v množství ztracených pomocných informací a době, která je potřeba pro přechod do plně funkčního stavu. Na laptopu autora článku jsou k dispozici tři režimy nečinnosti s následujícími charakteristikami:
C1 | C2 | C3 | |
---|---|---|---|
Latence opuštění stavu (µs) | 1 | 1 | 57 |
Spotřeba energie (mW) | 1000 | 500 | 100 |
Na typickém procesoru C1 pouze vypíná hodiny, zatímco C2 vypíná i další hodiny v systému a C3 vypne některé části CPU. Na takovém systému by dávalo smysl strávit co nejvíce času ve stavu C3; a opravdu, zatímco je psána tato věta, systém je přibližně 97 % času v C3. Jeden by si myslel, že emacs by mohl zaměstnávat CPU o trochu lépe, ale ani Emacs není pro současné procesory taková výzva. Stav C1 se nepoužívá vůbec a trocha času se tráví v C2.
Člověk by si mohl položit otázku, proč se systém vůbec zatěžuje s něčím jiným než s C3; proč netrvat na co nejvíce ničeho za korunu? Odpověď je, že C3 má samozřejmě svou cenu. Latence opuštění tohoto stavu je 57 µs, což znamená, že se systém musí zavázat k tomu, že nějakou dobu nebude nic dělat. Opětovné spuštění procesoru spotřebovává energii samo o sobě a další náklady – stav C3 může znamenat nutnost vyprázdnit L2 cache – se také projeví. Do C3 má tedy cenu přejít jenom v případě, že systém ví, že nebude muset na nic reagovat s latencí menší než 57µs. Jestliže tato podmínka není splněna, dává smysl použít jiný stav nečinnosti. Rozhodování v této oblasti má na starosti subsystém cpuidle.
Každý procesor má jiné charakteristiky stavů nečinnosti a je zapotřebí jiných akcí při přechodu do a z těchto stavů. Kód cpuidle toto všechno abstrahuje do oddělené vrstvy ovladačů; ovladače samy osobě jsou často k nalezení v kódu specifickém pro architekturu nebo v kódu ACPI. Na druhou stranu rozhodnutí o tom, který stav dává v dané situaci smysl, je z největší části záležitostí politiky. Rozhraní „governoru“ cpuidle umožňuje implementovat různé politiky pro různé potřeby. Na obě vrstvy se podíváme.
Na nejvyšší úrovni je rozhraní ovladače cpuidle vcelku jednoduché. Začíná registrací ovladače v subsystému:
#include <linux/cpuidle.h> struct cpuidle_driver { char name[CPUIDLE_NAME_LEN]; struct module *owner; }; int cpuidle_register_driver(struct cpuidle_driver *drv);
Tohle všechno zajistí, že bude jméno ovladače k dispozici v sysfs. Jádro cpuidle také vynutí to, aby v systému existoval jenom jeden ovladač cpuidle naráz.
Jakmile je ovladač zaveden, může registrovat cpuidle „zařízení“ pro každé CPU v systému – je možné, aby různé procesory měly naprosto rozdílné nastavení, i když autor předpokládá, že v reálném světě se taková konfigurace moc často nevyskytuje. První krok je popis toho, jaké nečinné stavy procesor nabízí:
struct cpuidle_state { char name[CPUIDLE_NAME_LEN]; char desc[CPUIDLE_DESC_LEN]; void *driver_data; unsigned int flags; unsigned int exit_latency; /* in US */ unsigned int power_usage; /* in mW */ unsigned int target_residency; /* in US */ unsigned long long usage; unsigned long long time; /* in US */ int (*enter) (struct cpuidle_device *dev, struct cpuidle_state *state); };
Pole name a desc popisují stav; nakonec se objeví v sysfs. driver_data slouží k uložení privátních dat ovladače. Další čtyři pole počínaje flags popisují charakteristiky tohoto stavu. Možné hodnoty flags jsou:
CPUIDLE_FLAG_TIME_VALID by měl být nastaven, pokud je možné přesně měřit dobu strávenou v daném stavu nečinnosti
CPUIDLE_FLAG_CHECK_BM označuje, že daný stav není kompatibilní s DMA aktivitou řídící sběrnici [bus mastering]. Hluboký spánek mezi jinými vypne hardware sledující cykly sběrnice, což znamená, že cache lokální pro procesor nemusí být v reakci na DMA aktualizovány. To může vést k problémům s poškozením dat.
CPUIDLE_FLAG_POLL říká, že tento stav nezpůsobuje žádné latence, ale také nešetří energii.
CPUIDLE_FLAG_SHALLOW označuje „lehký“ spánek s nízkou latencí a minimální úsporou energie.
CPUIDLE_FLAG_BALANCED označuje mezistav s nějakou latencí a střední úsporou energie
CPUIDLE_FLAG_DEEP je značka pro stav hlubokého spánku s vysokou latencí a velkou úsporou energie.
Hloubka spánku je také popsána ve zbývajících polích:
Funkce enter() je zavolána, když se aktuální governor rozhodne přepnout CPU do daného stavu state; lépe bude popsána níže. V usage se uchovává informace, kolikrát se do daného stavu vstoupilo, time zaznamenává čas, který byl v daném režimu stráven.
Ovladač cpuidle by měl pro každé CPU vyplnit stavy do struktury cpuidle_device:
struct cpuidle_device { unsigned int cpu; int last_residency; int state_count; struct cpuidle_state states[CPUIDLE_STATE_MAX]; struct cpuidle_state *last_state; void *governor_data; struct cpuidle_state *safe_state; /* Ostatní vypuštěno */ };
Ovladač by také měl nastavit state_count na počet platných stavů a cpu na počet CPU popsaných tímto zařízením. safe_state ukazuje na nejhlubší spánek, do kterého je bezpečné vstoupit, když je jinde v systému aktivní DMA. Zařízení by mělo být registrováno pomocí:
int cpuidle_register_device(struct cpuidle_device *dev);
Návratová hodnota je jako obvykle nulová nebo záporný chybový kód.
Jediná další věc, kterou ovladač potřebuje udělat, je implementovat přechody mezi stavy. Jak jsme viděli výše, to zajišťuje funkce enter() spojená s každým stavem:
int (*enter)(struct cpuidle_device *dev, struct cpuidle_state *state);
Volání enter() značí požadavek současného governoru, aby bylo CPU spojené s dev přepnuto do daného stavu state. Zde je vhodné poznamenat, že funkce enter() si může vybrat jakýkoliv jiný stav, pokud k tomu má dobrý důvod, ale měla by skutečný stav vložit do pole last_state daného zařízení. Jestliže má požadovaný stav nastaveno CPUIDLE_FLAG_CHECK_BM a v systému probíhá DMA ovládající sběrnici, měl by proběhnout přechod do safe_state. Návratová hodnota enter() by měla být doba, která se v daném stavu strávila, vyjádřeno v mikrosekundách.
Pokud ovladač potřebuje dočasně pozastavit aktivitu cpuidle, může zavolat:
void cpuidle_pause_and_lock(void); void cpuidle_resume_and_unlock(void);
Berte na vědomí, že cpuidle_pause_and_lock() blokuje cpuidle pro všechna CPU v systému. Také zabírá mutex, který je držen, dokud se nezavolá cpuidle_resume_and_unlock(), takže by se neměla používat dlouho.
Správu napájení lze pro specifické CPU řídit takto:
int cpuidle_enable_device(struct cpuidle_device *dev); void cpuidle_disable_device(struct cpuidle_device *dev);
Tyto funkce lze volat jenom v případě, že je cpuidle jako celek pozastavené, takže nejprve je potřeba volat cpuidle_pause_and_lock()
Governory implementují politiku cpuidle. Jádro povoluje existenci více governorů naráz, ale každé CPU může řídit jenom jeden naráz. Kód governoru začíná vyplněním struktury cpuidle_governor:
struct cpuidle_governor { char name[CPUIDLE_NAME_LEN]; unsigned int rating; int (*enable) (struct cpuidle_device *dev); void (*disable) (struct cpuidle_device *dev); int (*select) (struct cpuidle_device *dev); void (*reflect) (struct cpuidle_device *dev); struct module *owner; /* … */ };
name identifikuje governor v uživatelském prostoru, rating udává, jak si o sobě governor myslí, že je užitečný. Ve výchozím nastavení jádro používá governor s nejvyšší hodnotou, ale administrativně je možné to změnit.
Governor poskytuje čtyři zpětná volání [callback]. První dvě, enable() a disable() se volají, když se governor začíná nebo přestává používat. obě jsou volitelné; pokud governor nepotřebuje o těchto událostech vědět, nemusí dané funkce dodat.
Funkce select() je povinná; volá se, když CPU nemá nic na práci a přeje si, aby governor vybral optimální způsob, jak ničeho nedosáhnout. V této funkci governor může aplikovat vlastní heuristiku, podívat se na blížící se události časovače a obecně se snažit zjistit, jak dlouho může očekávat, že bude daný stav nečinnosti trvat. Podle toho se vybere spánek, který dává největší smysl. Návratová hodnota by měla být celočíselný index cílového stavu (v poli dev->states).
Když se rozhoduje, měl by governor brát v potaz současné požadavky na latenci vyjádřené jiným kódem v systému. Mechanismus pro registraci těchto požadavků je subsystém „pm_qos“. Tímto systémem lze registrovat několik požadavků na kvalitu služby, ale nejrelevantnější je pro governory cpuidle požadavek na latenci CPU. Tuto informaci ze získat pomocí:
#include <linux/pm_qos_params.h> int max_latency = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY);
Na některých systémech může příliš hluboký spánek napáchat zmatek v DMA operacích (zde můžete věřit zkušenostem autora), takže je důležité respektovat požadavky na latenci, které mají ovladače.
Funkce reflect() se volá, když CPU stav spánku opustí; governor může výsledné informace o čase použít k tomu, aby zhodnotil, jak dobré bylo jeho rozhodnutí.
Vývojáři ovladačů mohou použít tyto funkce pm_qos a specifikovat požadavky na latenci:
#include <linux/pm_qos_params.h> int pm_qos_add_requirement(int qos, char *name, s32 value); int pm_qos_update_requirement(int qos, char *name, s32 new_value); void pm_qos_remove_requirement(int qos, char *name);
API se v současných jádrech příliš nepoužívá; zdá se, že ve většině případů ovladače říkají systému, že přechody do stavů hlubokého spánku nejsou vítány. Není třeba říkat, že by ovladač měl blokovat hluboký spánek jenom v případě, že je to opravdu nutné; požadavek na latenci by měl být zrušen, když neprobíhá I/O.
Tím je popsán subsystém a API cpuidle ve 2.6.34. Zvědavci mohou najít základní kód a kód governorů v drivers/cpuidle, ovladače cpuidle žijí v drivers/acpi/processor_idle.c a v několika implementacích subarchitektury ARM. Je vidět, že na současných systémech je nicnedělání poměrně složitá práce.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.