Portál AbcLinuxu, 5. května 2025 23:26
Aktuální verze jádra: 2.6.25-rc6. Citáty týdne: Evgenij Poljakov, Greg Kroah-Hartman. Obnova smazaných souborů z ext3. Obecné semafory. Návrat autoritativních háčků [authoritative hooks]. Nová infrastruktura uspávání a hibernace.
Od vydání 2.6.25-rc6 se do hlavního git repozitáře dostalo jen pár změn.
V době psaní tohoto textu byl vger.kernel.org offline, důsledkem bylo mírné zpomalení vývoje. Nebo možná zpomalení řečí a zrychlení vývoje. Nicméně porucha (disk v RAID poli vgeru) je už řešena, s cílem zprovoznit vger tak rychle, jak jen to půjde.
Jeden člověk, 12 nocí (13 dní), jedna láhev kubánského rumu a trocha skotské whisky, 82 dílů Dr. House... to mám rád.
-- Jak se Jevgenij Poljakov vypořádává s prací
Takže budeme muset konvertovat všechny ovladače, že? Fajn, vždycky rád povyskočím v žebříčku "počet zaslaných patchů" :)
Carlo Wood zřejmě omylem smazal svůj domovský adresář a místo toho, aby sáhnul po zálohách, začal podrobně studovat strukturu souborového systému ext3. Výsledkem je důkladný pohled na ext3, včetně toho, jak obnovit smazané soubory. Z jeho úsilí vznikl nástroj ext3grep, který vypadá docela použitelně. Tak jak tak, tohle je naprostý nesmysl. Všechny informace tam stále jsou, stejně jako ukazatele na bloky. Je jen trochu méně pravděpodobné (než na ext2), že tam stále budou, jelikož musí být obnoveny z žurnálu. K tomu všemu nejsou metadata tak souvisle spojena s reálnými daty, takže na zpětné vyhledání souborů jsou potřeba heuristické algoritmy. (z blogu Val Henson)
Většina jaderných patchů smaže nějaký kód a nahradí jej novějším a (pravděpodobně) lepším. Ve většině případů je nový kód objemnější než ten předchozí. Čas od času přesto přijde patch, který smaže přes 7600 řádků kódu a nahradí jej pouhými 314 řádky při zachování stejné funkčnosti. Patch s obecnými semafory od Matthew Wilcoxe je jedním z nich.
Semafor je v podstatě počítadlo s připojenou čekací frontou. Když chce jaderný kód přistupovat k prostředku chráněnému semaforem, zavolá:
void down(struct semaphore *sem);
Toto volání zkontroluje počítadlo asociované se sem; pokud je hodnota vyšší než nula, je snížena a kontrola se vrátí volajícímu kódu. Jinak je volající kód uspán, dokud se někdy v budoucnu hodnota počítadla zase nezvýší. Navyšování hodnoty (když už není chráněný zdroj více potřeba) se děje voláním up(). Semafory mohou být použity všude tam, kde je vždy potřeba horního limitu na počet procesů v určité kritické sekci. V praxi je horní limit téměř vždy nastaven na jedna, takže jsou semafory používané jako přímočará primitiva pro oboustranná vyloučení [mutual exclusion primitive].
V současných jádrech jsou semafory implementovány pomocí silně optimalizovaného kódu vázaného na architekturu - v jaderném kódu je více než dvacet na sobě nezávislých implementací semaforů. Matthewův patch je všechny vyhazuje a nahrazuje je jedinou obecnou implementací, fungující na všech architekturách. Po aplikaci patche vypadá semafor takto:
struct semaphore { spinlock_t lock; int count; struct list_head wait_list; };
Implementace celkem jasně vyplývá z této definice: spinlock je použit na ochranu manipulací s count a wait_list pro uspávání procesů, zatímco čekají na zvýšení hodnoty count. Samotný kód je samozřejmě trochu komplikován ohledy na výkon a bezpečné chování s přerušeními, ale i přesto je poměrně krátký a jednoduchý.
Můžeme se zeptat: proč nebyly semafory takto řešeny už od začátku? Odpověď je, že za časů před 2.6.16 byly semafory jedním z hlavních mechanismů oboustranného vyloučení v jádře. Cyklus 2.6.16 pak přinesl mutexy z realtime stromu, na které přešla většina uživatelů semaforů, takže jejich kdysi tak velký význam z hlediska výkonu ztratil na své důležitosti. Důsledkem toho už není potřeba ručně vyladěný kód specifický pro jednotlivé architektury.
Další otázkou je, proč se vlastně semafory vůbec ještě používají? Počet jejich použití se od 2.6.16 výrazně zmenšil, ale pár jich v jádře ještě je. Některé z nich by určitě šly převést na mutexy, ale to vyžaduje opatrnou kontrolu toho, zda v kódu není používána počítací funkce semaforů. Po provedení kontroly se navíc může ukázat, že na některých místech byl semafor opravdu tou pravou datovou strukturou. A tak tu s námi semafory pravděpodobně zůstanou - ale budou vyžadovat o dost méně kódu než dříve.
Vývojáři kontejnerů mají problém, který se může zdát poměrně nekomplikovaný: rádi by z jednotlivých kontejnerů přistupovali k zařízením samostatně. S tímto přístupem by kontejnery mohly bezpečně získat přístup k danému zařízení bez ohrožení celkové bezpečnosti systému, a to i v případě, že je v kontejneru proces s právy roota, který může vytvářet nové soubory zařízení. K implementaci této vlastnosti ale vedla mnohem delší cesta, než si vývojáři představovali, protože seznam povolených zařízení [whitelist] si různé jaderné subsystémy přehazovaly jako horký brambor. Možná pro něj ale bylo nalezeno to pravé místo, což může předznamenávat změnu v budoucím rozhodování o bezpečnostních otázkách v jádře.
Originální verze patche od Pavla Emeljanova zakládala kontrolní skupinu, která spravovala přístup k zařízením v rámci kontejnerů. Vlastní pravidla a jejich vynucování bylo uloženo hluboko v subsystému modelu zařízení [device model subsystem]. Greg Kroah-Hartman namítal, že taková kontrola přístupu by měla být prováděna buď pomocí udev, nebo linuxového bezpečnostního modulu [Linux security module (LSM)]. Jelikož udev neposkytuje požadovaný stupeň kontroly a zřejmě může být problematický pro ty, kdo chtějí v kontejnerech provozovat starší distribuce, nebyla tato možnost ani vážně brána v potaz. Návrh s LSM si ale vývojáři přes malý odpor vzali k srdci.
Výsledkem byl patch pro LSM poskytující whitelist zařízení, který zaslal Serge Hallyn. Byl to zásobníkový [stacking] bezpečnostní modul, který měnil dost háčků. V ten moment přišel James Morris a navrhl, že místo tohoto řešení by whitelist měl být prostě přidán do již existujícího bezpečnostního modulu kvalifikací [capabilities] - odpadla by potřeba odděleného modulu a vše by se zjednodušilo.
A tak Serge poslušně vydal třetí verzi patche, která whitelist přesouvala do kvalifikačního modulu. Ale i tento krok vyvolal odpor - znovu citujeme Jamese Morrise:
Přesunutí tohoto mechanismu [logic] do LSM znamená, že místo toho, aby byl bezpečnostní mechanismus cgroups volán z jednoho místa v jádře (kde je také umístěn), musí být stejným způsobem volán z každého LSM (z nichž žádný nemá o cgroups ani tušení), což je podle mě zjevně špatné řešení.
Ani Caseyimu Schauflerovi se tato myšlenka moc nelíbila:
Až se naskytne další nová funkce, šoupneme ji taky do kvalifikací? Nebo ji můžeme nacpat třeba do auditu nebo CIPSO, ale jak dlouho to může pokračovat? Nakonec budeme potřebovat mechanismus umožňující více méně obecné míchání, možná s pár pravidly typu "nemíchej kostkované s pruhovaným", aby to dávalo smysl, jinak nemají tyhle druhotné funkce šanci. Vypadá to, že ještě stále nechceme povolit, aby se systém LSM snadno používal.
Tentokrát se stížnost zjevně netýkala jen whitelistu zařízení, ale také samotného modulu kvalifikací. Vypadá to, že kvalifikace trochu špatně zapadají do celé myšlenky LSM. Samotná skutečnost, že existují, je tak trochu historickým artefaktem - někteří vývojáři je takto chtěli implementovat, aby ukázali flexibitu rozhraní LSM a možnost vynechat je z embedded konfigurací. Ale nakonec to dopadlo tak, že stejně není možné je odstranit a znamenají režii pro všechny ostatní bezpečnostní moduly.
Jádro pudla je v tom, že LSM je ve své podstatě restriktivním mechanismem. LSM háček může zakázat nějakou akci, ale nikdy nemůže dovolit procesu, aby mohl udělat něco, co by při absenci bezpečnostního modulu neměl povoleno. Rozhodnutí zamítnout "autoritativní háčky" bylo jasně uděláno v roce 2001 za účelem omezit pole působnosti LSM a zajistit, aby se samotné moduly nestaly bezpečnostními problémy.
Kvalifikace jsou ale autoritativní mechanismus - kontrola kvalifikací ověřuje existenci speciálního povolení, které by jinak neexistovalo. Whitelist zařízení je něco podobného - schvaluje přístup, který by jinak byl odepřen, což do modelu LSM moc nesedí.
Serge se pak vrátil s ještě dalším patchem, který odstraňuje kód whitelistu z frameworku LSM a místo toho vkládá oddělenou sadu háčků na relevantní místa v kódu. Tyto háčky jsou hned vedle LSM háčků, ale fungují povolovacím způsobem. Zatím se zdá. že tento přístup projde - žádní vývojáři (zatím) nemluví o jeho vykopnutí do dalšího subsystému.
Přesto se věci mohou ještě změnit - Casey Schaufler nyní mluví o vytvoření "linuxového modulu práv" ["Linux privilege module"], který by spravoval veškerá ověřování oprávnění. Mohla by tam být přesunuta jak běžná kontrola přístupu, tak kvalifikace a ověřování typu "je to root?". A samozřejmě i kód whitelistu zařízení. Nikdo proti této myšlence sice nevystoupil, na druhou stranu ale ještě nikdo neviděl žádný kód. Ale v případě, že budou věci pokračovat tímto směrem, autoritativní háčky možná po několika letech od odmítnutí z LSM konečně našly domov.
Při účastech na konferencích se Jonathan Corbet už několik let hostů ptá, jestli jim na laptopech nějakým způsobem funguje uspávání a probouzení. Možnost přijít do přednáškové haly, otevřít víko a bez čekání na proběhnutí celé bootovací sekvence začít přes IRC okamžitě vyrušovat přednášejícího má přeci jen něco do sebe. Ale bez ohledu na to, zda mluvíme o uspání do paměti ("uspání") nebo uspání na disk ("hibernace"), používá tyto možnosti překvapivě málo lidí. Navzdory úsilí vyvíjenému vývojáři a distributory spoustě lidem uspání a hibernace stále zkrátka nefunguje spolehlivě.
Jonathanu Corbetovi funguje uspání vždy, ale míra úspěšnosti probuzení je asi 95 % - právě dost na to, aby jej stále používal, i když občas jej inspiruje k pořádnému množství nadávek na nevhodných místech.
Byly představeny různé přístupy, jak uspávání a hibernaci opravit, například projekty TuxOnIce a kexec jump. Další možností je zkrátka opravit kód, který je v jádře nyní. Než se tento cíl stane realitou, čeká nás ještě spousta práce, včetně stabilizace celého procesu a oddělení uspání od hibernace, což jsou, jak už Linus několikrát poměrně silně zdůrazňoval, dva rozdílné problémy. Za tímto účelem zaslal Rafael Wysocki novou infrastrukturu uspávání a hibernace pro zařízení, která má potenciál situaci zlepšit, ale za cenu vytvoření ne méně než dvaceti samostatných zpětných volání zařízení.
Pro (relativně) jednoduché uspávání do paměti existují čtyři základní zpětná volání, která by v nové struktuře pm_ops měla být poskytována každou sběrnicí a později i každým zařízením:
int (*prepare)(struct device *dev); int (*suspend)(struct device *dev); int (*resume)(struct device *dev); void (*complete)(struct device *dev);
Při uspávání uvidí každé zařízení nejdříve volání svého zpětného volání prepare(). To je jakési varování, že přijde uspání a že by měly být provedeny všechny potřebné přípravy. To zahrnuje zabránění přidání jakýchkoliv nových podzařízení [child devices] nebo čehokoliv, co by mohlo vyžadovat účast uživatelského prostoru. V tuto chvíli by také měly být provedeny všechny podstatné alokace paměti - systém je stále funkční a pokud je to potřeba, mohou být vykonány I/O operace pro uvolnění paměti. Co by se ve stavu prepare() stát nemělo, je přepnutí zařízení do stavu s nízkou spotřebou [low-power state] - zařízení musí zůstat funkční a dostupné.
Návratová hodnota nula jako obvykle signalizuje to, že byla příprava úspěšná, záporná hodnota značí neúspěch. V případě dočasných chyb (například souběh s přidáním nového podzařízení) by zpětné volání mělo vrátit -EAGAIN, což způsobí pozdější opakování pokusu.
Později bude zavoláno suspend(), což způsobí samotné vypnutí zařízení. V současném patchi dostanou zařízení volání prepare() těsně následované voláním suspend(). To se v budoucích verzích pravděpodobně změní tak, aby všechna zařízení dostala prepare() dříve, než je kterékoliv z nich uspáno. Díky tomu bude moci i poslední volání prepare() počítat s dostupností plně funkčního systému.
Proces probouzení volá resume(), což probudí dané zařízení, obnoví jej do původního stavu a obecně jej připraví na provoz. Jakmile proběhne, je pomocí complete() uklizeno vše, co zbylo po prepare(). V případě selhání procesu uspání může být complete() zavoláno bezprostředně po prepare() (uspání neproběhne).
Proces hibernace je komplikovanější v tom, že obsahuje více přechodných stavů. Stejně jako uspání do paměti začíná zavoláním prepare(), poté jsou zavolány:
int (*freeze)(struct device *dev); int (*poweroff)(struct device *dev);
Zpětné volání freeze() je provedeno před vytvořením hibernačního obrazu [hibernation image] (obraz systému zapisovaný na stálé úložiště) - mělo by přepnout zařízení do nečinného, ale provozuschopného stavu. Po uložení hibernačního obrazu a dalším zavolání prepare() je zavoláno poweroff(), což zajistí vypnutí.
Když je systém znovu zapnut, postup se opakuje v opačném pořadí voláním:
int (*quiesce)(struct device *dev); int (*restore)(struct device *dev);
Volání quiesce() [uklidnit se] je použito téměř na začátku procesu probouzení - po načtení hibernačního obrazu, ale před jeho použitím pro obnovení paměti hibernovaného systému. Toto zpětné volání by mělo zařízení uklidnit za účelem poskládání paměti bez poškození způsobeného činností zařízení. Následuje zavolání complete() a restore(), což by mělo navrátit zařízení do plně funkčního stavu. Poslední volání complete() celý proces zakončuje.
Existují ještě další dvě zpětná volání související s hibernací:
int (*thaw)(struct device *dev); int (*recover)(struct device *dev);
Tyto funkce jsou zavolány v případě, že věci nejdou podle plánu. Opět je každé z těchto volání je následováno zavoláním complete(). Cílem thaw() je vrátit zpět věci, které způsobila volání freeze() nebo quiesce() - měla by zařízení dostat zpět do funkčního stavu. Volání recover() je použito v případě, že selže vytváření nebo obnova hibernačního obrazu. Má na starosti navrácení hardwaru zpět do funkčního stavu.
Pro větší legraci existují dokonce dvě sady zpětných volání pm_ops. Jedna z nich slouží pro normální systémový provoz, ale existuje i druhá, používaná při vypnutých přerušeních a pouze jednom funkčním procesoru, tedy těsně před vypnutím nebo těsně po zapnutí systému. Interakce se zařízeními je v tomto prostředí samozřejmě odlišná, takže dávají smysl i odlišná zpětná volání. Výsledkem však je 20 nových zpětných volání kvůli plné funkčnosti uspávání a hibernace. Tato volání byla přidána do struktury bus_type:
struct pm_ops *pm; struct pm_ops *pm_noirq;
Pole o stejných jménech byla přidána také do struktury pci_driver, což dává ovladačům možnost přidat svou vlastní verzi těchto zpětných volání. Prozatím se v případě, že nebudou poskytnuty struktury pm_ops a nebudou ještě k dispozici překonvertované ovladače, použijí zpětná volání suspend() a resume() poskytovaná starým PCI ovladačem.
V době psaní tohoto článku jsou diskuze o tomto patchi omezeny výpadkem vger.kernel.org, přesto už se objevilo pár obav a je dost pravděpodobné, že v budoucích revizích se věci ještě změní - může být například zredukován počet "no IRQ" zpětných volání. S trochou štěstí nám závěrečné řešení přinese funkční a spolehlivé uspávání a hibernaci.
1) neznám frázi "tak jak tak". Znám například "buď jak buď"Co třeba "tak jako tak"?
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.