Portál AbcLinuxu, 16. května 2024 20:43

Jaderné noviny - 3. 1. 2007

25. 1. 2007 | Robert Krátký
Články - Jaderné noviny - 3. 1. 2007  

Aktuální verze jádra: 2.6.20-rc3. Citát týdne: Andrew Morton. Správa zdrojů alokovaných pro zařízení. Ošklivá chyba poškozující soubory byla opravena.

Aktuální verze jádra: 2.6.20-rc3

link

Aktuální předverze řady 2.6 je 2.6.20-rc3. Linus ji vydal těsně před Novým rokem. Obsahuje opravu chyby, která způsobovala poškození souborů (vizte níže) a pár set dalších oprav.

23. prosince vyšla s další velkou dávkou oprav verze 2.6.20-rc2.

Od vydání -rc3 bylo do hlavního git repozitáře přidáno jen několik patchů. V seznamu neopravených regresí, který spravuje Adrian Bunk, je teď šest položek.

Aktuální verze -mm stromu je 2.6.20-rc2-mm1. Mezi nedávné změny patří nová verze patche pro ovladače v uživatelském prostředí, další háčky [hooks] pro paravirtualizaci, implementace obecného času pro x86_64 a obecný kód pro ovladače GPIO.

Starší jádra: verze 2.6.16.37 byla 28. prosince vydána s velkým počtem oprav.

2.4.34 vyšla také 28. prosince. Obsahuje několik bezpečnostních oprav a podporu kompilátorů gcc 4.x.

Citát týdne: Andrew Morton

link

Je mi to docela jedno, fakt. Ale na druhou stranu... já tomu rozumím. Zkus někomu vysvětlovat, jak spolu souvisí nečisté pte, stránky, radix stromy a buffer_head [pte-dirtiness, page-dirtiness, radix-tree-dirtiness and buffer_head-dirtiness].

-- Andrew Morton

Správa zdrojů alokovaných pro zařízení

link

Psaní ovladačů zařízení může být zrádné. Samotné zprovoznění zařízení - často s pomocí chybné nebo neexistující dokumentace - může být nepříjemná práce. Kromě toho však musí ovladač pro zařízení alokovat několik různých druhů zdrojů; například mapování paměti pro I/O, linky přerušení, bloky paměti, DMA buffery, registrace u více subsystémů atd. Všechny tyto alokace musí být po zmizení zařízení (nebo jeho ovladače) vráceny systému. Není neobvyklé, že autoři ovladačů něco zapomenou dealokovat, což vede k úniku zdrojů.

Když do toho započteme inicializační chyby, může být problém ještě horší. Pokud se ovladači nepodaří zařízení správně nastavit, musí vrátit všechny registrace, které až do té chvíle provedl. Pokusy o řešení chyb při inicializaci většinou nabývají podoby několika goto značek v rámci inicializační funkce nebo nějaké globální proměnné "inicializačního stavu", která popisuje, kde by mělo čištění začít. Tak jako tak nebývají tyto způsoby moc dobře testovány, takže při selhání inicializace je na nějaký únik zdrojů slušná šance.

Tejun Heo, který během uplynulého roku velmi přispěl k vylepšení linuxového SATA subsystému, už měl těchto inicializačních problémů dost. Takže dal dohromady patch pro správu zdrojů, který by mohl, pokud bude přijat, zařídit ovladačům jednodušší a stabilnější kód. Hlavní myšlenka je prostá: pokaždé, když ovladač alokuje zdroj, kód pro správu si to zapamatuje společně se všemi informacemi potřebnými pro uvolnění. Když se ovladač od zařízení odpojí, jsou všechny zapamatované alokace vráceny systému.

Takový způsob sledování alokací nelze do současného API nijak rozumně přidat. Tejunův patch místo toho vytváří nové, "spravované" verze jednotlivých alokovacích funkcí. Nové funkce vypadají jako ty staré, ale přidávají k názvu "m" (nebo "devm") a také doplňují parametr struct device - pokud už ho funkce nemá. Takže například spravované verze funkcí pro alokaci přerušení jsou:

    int devm_request_irq(struct device *dev, unsigned int irq,
		         irq_handler_t handler, unsigned long irqflags,
		    	 const char *devname, void *dev_id);
    void devm_free_irq(struct device *dev, unsigned int irq, 
                       void *dev_id);

Patch také obsahuje spravované funkce pro zacházení s DMA buffery, I/O oblastmi paměti, prostými alokacemi paměti a nastavováním PCI zařízení. Umožní autorovi ovladače nahradit celou skupinu dealokačních volání jediným zavoláním devres_release_all(), což kód výrazně zjednoduší. I toto volání je vlastně zbytečné; ovladačové jádro ho zavolá, když se ovladač od zařízení odpojí.

Pro komplikovanější situace existuje "skupinový" koncept. Skupiny si lze představit jako značky v sérii alokací prováděných daným zařízením. Alokace v rámci jedné skupiny je možné vrátit bez ovlivnění ostatních. Skupinové API vypadá takto:

    void *devres_open_group(struct device *dev, void *id, gfp_t gfp);
    void devres_close_group(struct device *dev, void *id);
    void devres_remove_group(struct device *dev, void *id);
    int devres_release_group(struct device *dev, void *id);

Volání devres_open_group() vytvoří pro dané zařízení novou skupinu identifikovanou hodnotou id. Všechny potom provedené alokace budou považovány za součást této skupiny, dokud není zavoláno devres_close_group(). Pokud však inicializace funguje podle plánu, lze použít devres_remove_group(), což odstraní režii skupiny, ale alokace (včetně informací o nich) ponechá netknuté. Dojde-li k problému, devres_release_group() vrátí všehny alokace, které do skupiny patří.

O této sadě patchů se zatím moc nediskutovalo. Autoři ovladačů se možná ještě vzpamatovávají z vánočních oslav. Není těžké si představit, že by všechna ta extra režie spojená se sledováním alokací vyvolala nelibost - zvláště uvážíme-li, že ve většině případů věci normálně fungují. Nakonec by však mohl příslib správné funkce v širším spektru situací začlenění nového rozhraní motivovat.

Ošklivá chyba poškozující soubory byla opravena

link

V minulém čísle se psalo o chybě způsobující poškození souborů, která se vyskytovala převážně (ale ne pouze) u souborových systémů ext3. Některé aplikace s nezvyklým způsobem přístupu k souborům mapovaným v paměti mohly občas vidět mezery tam, kde se data nedostala až na disk. Jednou z těchto aplikací je rtorrent; jak se honba za odhalením příčiny chyby stupňovala, byly nalezeny (a vyvinuty) další případy pro testování. Problém je teď vyřešen, ale poučil nás o tom, jak se může taková nenápadná chybka projevit - a jak ji opravit.

dirty page bug - Cheezy diagram

V zájmu snahy o vysvětlení jsem [Jonathan Corbet] opět zapřáhl své spíše pochybné umělecké vlohy. Proto čtenáře laskavě prosím, aby popatřili na diagram zobrazený vpravo a potlačili své pochybnosti natolik, že si v něm představí stránku paměti - stránku, která obsahuje zajímavá data, a která reprezentuje ekvivalentní sadu bloků nacházejících se na disku v rámci souboru. Rozlišení mezi stránkou a jejími bloky je důležité, a proto stránku rozdělují tečkované čáry. Stránka v paměti o 4096 bajtech je nejspíše reprezentována osmi diskovými bloky o 512 bajtech (které jsou pravděpodobně zase spojeny diskovou jednotkou, ale budeme předstírat, že se to neděje).

Existuje pár jaderných datových struktur, které o této stránce obsahují informace, což diagram trochu komplikuje:

dirty page bug - Second diagram

Stránka může být mapována do jednoho nebo více adresních prostorů procesů. Pro každé takové mapování bude záznam v tabulce stránek [PTE - page table entry], který provádí překlad mezi virtuální adresou v uživatelském prostoru a fyzickou adresou, kde se stránka doopravdy nachází. V PTE jsou ještě další informace, včetně "nečistého" bitu ["dirty" bit]. Když aplikace stránku upraví, procesor nastaví nečistý bit, což operačnímu systému umožní zareagovat například zapsáním stránky zpět na úložné zařízení. Ukazuje-li na jednu stránku více PTE, nemusí se shodovat v tom, jestli je stránka čistá nebo ne. Jediným způsobem, jak to zjistit, je projít všechny existující PTE a podívat se, jestli není některý z nich označen jako nečistý.

Jádro si udržuje samostatnou datovou strukturu známou jako mapa paměti systému; obsahuje jednu struct page pro každou existující fyzickou stránku. Tato struktura obsahuje několik zajímavých informací, včetně ukazatele na zálohu [backing store] stránky (pokud nějaká existuje), datové struktury umožňující relativně snadno nalézt příslušné PTE a sadu příznaků. Jeden z těchto příznaků je nečistý bit - další příznak, který značí, že by stránku bylo potřeba zálohovat.

A nakonec sada struktur, které mohou být se stránkou spojeny:

dirty page bug - Third diagram

"Hlava bufferu" ["bh" - buffer head] pochází z nejranějších dnů Linuxu. Lze si ji představit jako mapování mezi diskovým blokem a jeho kopií v paměti. Bh už není tak důležité pro linuxovou správu paměti jako dříve, ale dost souborových systémů to pořád používá pro sledování diskového I/O. Ne každý blok na stránce musí mít nutně bh; pokud si souborový systém myslí, že zapsat potřebují jen některé bloky, nemusí pro ty ostatní bh struktury vytvářet. Kromě jiného obsahuje bh struktura ještě další nečistý příznak.

Když je tolik příznaků, které značí v podstatě totéž, není tak překvapivé, že nakonec vznikly zmatky. Správa redundantních datových struktur může představovat problém všude - tím více u jádra, které přidává ještě další obtíže.

Hluboko v jádře je funkce nazývaná set_page_dirty(); používá ji kód pro správu paměti, když si všimne (díky PTE nebo přímé akci aplikace), že stránka potřebuje zapsat do zálohy. Mimo jiné zkopíruje nečistý bit ze záznamů tabulky stránek do struktury page. Je-li stránka součástí souboru, zavolá set_page_dirty() zpět do příslušného souborového systému - ale pouze pokud daný souborový systém poskytuje odpovídající metodu. Mnohé souborové systémy však zpětné volání set_page_dirty() nenabízejí; u těchto souborových systémů tedy jádro projde seznam všech souvisejících bh struktur a každou označí jako nečistou.

A v tom je ten problém. Souborový systém si mohl klidně všimnout, že blok reprezentovaný daným bh je nečistý, a začít na něm I/O před voláním set_page_dirty(). Když je I/O hotový, souborový systém odstraní z bh nečistý příznak. Pokud dojde na volání set_page_dirty() ve chvíli, kdy na bloku probíhá I/O, souborový systém si nevšimne, že se data bloku mohla po zápisu změnit. Místo toho bude blok označen jako čistý, ačkoliv to, co bylo zapsáno, neodpovídá tomu, co je právě v paměti. Výsledkem je poškození dat.

Linusova oprava je jednoduchá. Když se subsystém virtuální paměti rozhodne, že je na čase zapsat stránku, provede se nové volání set_page_dirty(). To zajistí, že v okamžiku volání metody writepage() souborového systému budou všechny bh označeny jako nečisté. Díky tomu je jisté, že budou zapsány všechny bloky stránky; testování potvrdilo, že problém s poškozováním souborů se tím odstraní. Patch byl zařazen do hlavního git repozitáře; měl by se objevit i ve stabilní aktualizaci 2.6.19.

Do budoucna je řešením pokračovat ve vytlačování bh z cest jaderných I/O. Linus k tomu řekl:

Buffer head je posledních několik let čistě "IO entita" a ne kešová entita. Když někdo používá pro zápis do zálohy bh, přeskakuje tím skutečnou keš (stránkovou), a proto s tím jsou problémy.

Myslím, že ext3 je už v posledních křečích. Pořád používá bh v místech, kde by vážně neměl. Výsledkem je, že věci jako přístup k adresářům jsou pomalejší, než by měly. Bohužel mám dojem, že ani ext4 nic z toho neopraví.

Ted Ts'o odpověděl, že oprava pro ext4 není úplně vyloučena, ale týká se to i jiných souborových systémů. Ext3 však pravděpodobně bh nepřestane používat, takže je jádro bude muset podporovat navždy.

Tento příběh ukazuje, jak těžké může být najít a opravit některé druhy chyb v jádře. Zpočátku bylo pro vývojáře obtížné problém reprodukovat, takže museli spoléhat na to, že za ně budou patche testovat ti, kdo chybu původně objevili. Tito testeři vydrželi po celou dobu, přičemž zkompilovali a otestovali hodně jader, než byl od problému pokoj. Zaslouží si velkou část uznání za vyřešení.

Související články

Jaderné noviny - 20. 12. 2006
Jaderné noviny - 13. 12. 2006
Jaderné noviny - 6. 12. 2006
Jaderné noviny - 29. 11. 2006

Odkazy a zdroje

Kernel coverage at LWN.net: January 3, 2007

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

Jaderné noviny – přehled za duben 2024
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

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