Portál AbcLinuxu, 5. května 2025 21:59
Aktuální verze jádra: 2.6.18-rc4. Citát týdne: Andrew Morton. Návrat ochrany před zatuhnutím blokového zařízení na síti. Kód (stále) nejistého původu. Rozhraní cdev.
Aktuální předverze je i nadále 2.6.18-rc4; Linus bude ještě nějakou dobu na dovolené. Mezitím vydal Greg Kroah-Hartman 2.6.18-rc4-gkh1 - obsahuje 64 patchů určených k začlenění do hlavního jádra po Linusově návratu.
Aktuální verze -mm stromu je 2.6.18-rc4-mm1. Mezi nedávné změny patří přepracování konfiguračních voleb SATA (Pokud spustíte naslepo 'make oldconfig', přijdete o disky), nová sada USB funkcí, velká aktualizace x86-64, přepracování kódu protokolu síťového času, podpora --bind připojení pouze pro čtení a nový ovladač embedded řadiče notebooků Thinkpad (přes pochyby o původu - viz níže).
Aktuální 2.4 jádro je 2.4.33, vydané 11. srpna. To byla poslední verze od Marcela; správu řady 2.4 převzal Willy Tarreau.
Ext3 tu bude ještě mnoho let. Nemůžeme ho nechat jen tak shnít kvůli jakési falešné víře, že ho provádění rutinní údržby z nějakého podivného důvodu poškodí.
Právě před rokem se mluvilo o sadě patchů určených k ochraně před zatuhnutím síťového subsystému. Dotyčný problém může nastat ve chvíli, kdy systém používá blokové (diskové) zařízení umístěné na druhé straně síťového spojení. Má-li systém málo paměti, je jednou z věcí, které musí provést, zápis nečistých stránek na disk, což paměť uvolní k využití pro jiné účely. Ale zápis na síťový disk může sám o sobě vyžadovat alokace paměti - potřeba, která se projeví v nejnevhodnější moment. Tento konkrétní problém, který se objevuje i u lokálně připojených disků, je už nějakou dobu vyřešen udržováním kousku paměti rezervovaného speciálně pro blokové I/O operace.
Disky připojené přes síť však mají další problém - žádný zápis nemůže být považován za dokončený, dokud není ze vzdáleného zařízení přijato potvrzení. Přijetí takového potvrzení vyžaduje, aby byl systém schopen přijímat (a zpracovávat) síťové pakety - a to si může vyžádat neomezené množství paměti. Může se objevit spousta příchozích síťových dat, která nebudou mít nic společného s čekajícími I/O požadavky, a tato data mohou znemožnit přijetí paketů, které by systém lapající po paměti tolik potřeboval. Patch pro předcházení zatuhnutí provádí určité změny zaměřené na to, aby systém mohl příchozí bloková I/O data přijímat a zpracovávat vždy.
O rok později se ta sada patchů opět vynořila. Původní autor (Daniel Phillips) přenechal hlavní slovo Peteru Zijlstrovi. Nová verze patche v mnoha směrech připomíná své předchůdce, ale přesto je tam dost změn na to, aby stály za bližší pohled.
Hlavní funkci plní patch i nadále pomocí zvětšení pohotovostní rezervované oblasti, kterou spravuje hlavní alokátor stránek. K dispozici je GFP parametr (__GFP_MEMALLOC), který v případě potřeby umožňuje uspokojení konkrétního alokačního volání z rezervy. Jde především o to, aby tato rezerva mohla být využívána k přijímání důležitých paketů, aniž by ji zahltily zbytečnosti.
Kvůli tomu také kód, který provádí blokové I/O přes síťové spojení, nastavuje na svých soketech parametr SOCK_MEMALLOC. Předchozí verze nastavily parametr na všech připojených síťových rozhraních, aby daly najevo, že daným rozhraním prochází blokové I/O. Současná verze tento krok přeskakuje. Místo toho si každý pokus o alokaci (paketové) struktury sk_buff z ovladače síťového zařízení vezme z paměťové rezervy, je-li to nutné. Takže vydrží-li to rezerva, může systém pro příchozí pakety alokovat buffery.
Klíčem je přijmout důležité pakety, aniž by se rezerva vyčerpala nepotřebnými daty. Proto je síťovací kód opatchován, aby kontroloval parametr SOCK_MEMALLOC, jakmile je identifikován soket pro každý příchozí paket. Není-li parametr nastaven a paket využívá paměť z rezervy, bude okamžitě zahozen, aby paměť uvolnil pro jiné využití. Takže pakety týkající se blokového I/O jsou přijaty a zpracovány jako obyčejně, zatímco téměř všechno ostatní je při první příležitosti likvidováno.
Nejčerstvější verze patche obsahuje nový alokátor paměti nazvaný SROG, který je využit pro správu rezervní paměti. Má být rychlý a jednoduchý, aby mohl pro systém co nejrychleji paměť uvolňovat. Kvůli tomu se snaží seskupovat související alokace a pak každou skupinu (většinou strukturu sk_buff a s ní spojenou datovou oblast) izolovat na vlastní stránky. Kdykoliv je uvolněn paket, paměť s ním spojená je celá systému hned dostupná.
Vypadá to však, že bude těžké prosadit začlenění patche. Zatuhávání se nezdá příliš pravděpodobné - nekonala se žádná záplava chybových hlášení ohledně této otázky. A ve většině případů lze potížím předejít swapováním na lokální disk. Počet systémů, jejichž majitelé si mohou dovolit fajnová síťová úložná pole, ale zároveň nemohou investovat do lokálního disku pro swapování, není pravděpodobně velký. Rozšíření síťovací vrstvy kvůli řešení tohoto problému se mnoha lidem nelíbí.
Správce síťování David Miller by rád viděl jiný přístup k alokacím paměti:
Myslím, že bychom více vytěžili z řešení, které se "síťovou pamětí" skutečně něco udělá, místo aby říkalo "tahle zařízení jsou odlišná" nebo "tyhle sokety jsou odlišné". Zvláštní případy většinou stojí za houby.
Už teď v systému globálně limitujeme a kontrolujeme paměť TCP soketů. Kdybychom to dělali pro všechny alokace, což je vlastně výchozí způsob v návrhu alokátoru od Evgenije, mohli bychom ten problém řešit rozumněji.
Komentář odkazuje na patch s alokátorem síťové paměti od Evgenije Poljakova. Jeho práce prochází prudkými změnami, takže je obtížné kód číst. Jádrem je však toto: jde o (další) samostatný alokátor paměti zaměřený na potřeby síťovacího systému. Je navržen tak, aby alokace zůstávaly na jednom lokálním procesoru, takže má každý procesor svou sadu stránek na rozdávání. Alokované objekty jsou uloženy co nejhustěji, aby se minimalizovala interní fragmentace. V současném návrhu není možnost se obrátit na systémový alokátor paměti, takže když některý z procesorů vyčerpá zásobu, alokace selže. Vyčerpání paměti ve zbytku systému však alokátor neovlivní. Autor tvrdí, že to vede ke zvýšení výkonu:
Výkonnostní testy na běžném web serveru založeném na epoll ukázaly znatelné (více než 40%) zlepšení v počtu požadavků (1600 - 1800 požadavků za vteřinu vs. více než 2300). Lze to přičíst uvolňovacímu algoritmu, který méně namáhá keš, těsnějšímu řazením objektů a tím pádem menší době odezvy z keše, menšímu počtu hledání v keších vyšších vrstev atd.
Kód pamatuje i na mapování síťových bufferů přímo do uživatelského prostoru, které by mohlo fungovat například s budoucí implementací síťových kanálů.
Patch (síťový alokátor) rozhodně správce síťování zaujal. Je však dost daleko od případného začlenění a ne všichni jsou přesvědčeni, že vyřeší všechny problémy. Takže jde o diskuzi, která může ještě nějakou dobu pokračovat.
Minulý týden jsme se dívali na situaci kolem nového ovladače embedded řadiče u notebooků Thinkpad a jeho autora, který se představil jako "Shem Multinymous". Vývoj událostí se zastavil, když Pavel Machek navrhl, že pod kód připojí své jméno, což diskuzi trochu utišilo. Ne však na dlouho.
Robert Love, autor ovladače akcelerometru, který je (mimo jiné) tímto kódem nahrazován, kód zkontroloval: Jsem rád, že má někdo zjevně lepší přístup k hardwarovým specifikacím než jsem měl já. Na to reagoval Andrew Morton:
Situace je stále ošemetná. Kde se vzaly ty informace o dalších registrech? [...]
Tímto stanovíme precedens a k tomu potřebujeme Linuse. Možná bychom mohli požádat "Shema", aby svou pravou totožnost soukromě prozradil Linusovi (a možná mně), a na tom dál stavět. Pravidlo by mohlo znít: "každý, kdo se podepíše po kód (Signed-off-by:), by měl znát totožnost ostatních".
To však nestačí Gregu Kroah-Hartmanovi:
Znamená-li to něco, tak já se těchto patchů ani nedotknu (obyčejně jdou hwmon patche Linusovi přes Jeana a mě). Pokud nechce původní vývojář pracovat otevřeně tak jako my, respektuji to, ale nemohu přijmout riziko vyplývající z akceptování takového kódu.
Jean Delvare také odmítl kód kontrolovat, protože právní pochybnosti jsou příliš velké. Shem Multinymous však vypadá, že by byl ochoten prozradit své jméno Linusovi a Andrewovi, pokud by to dostalo kód do jádra. Takže je možné, že by to tak mohlo dopadnout - kód by přeskočil správce, kteří by jej normálně měli na starosti. Všechna nejistota by však asi nezmizela, což by mohlo vést i ktomu, že by distributoři vynechali daný kód z jader, která dodávají.
"Shem" také poslal dvě samostatné zprávy ohledně původu informací použitých v ovladači. Jak se zdá, tak příběh začíná reverse-engineeringem ovladače pro Windows. Pak byla objevena pravá specifikace čipu embedded řadiče. A pak už šlo hlavně o poskládání kousků dohromady. Tak je to alespoň podáváno.
Pokud příběh obstojí, mohl by být pravděpodobně nový kód začleněn do jádra bez obav; měl by být přinejmenším tak legální jako ovladač, který by nahradil. Ale pokud se do jádra dostane, stanoví tak určitý precedens: anonymní příspěvky (nebo aspoň ty, které jsou podepsány zjevným pseudonymem) to budou mít těžké. Nikdo nechce být tím, kdo do jádra uvedl nežádoucí kód.
Od nepaměti bylo základní registrační rozhraní pro znaková zařízení v jádře následující:
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops); int unregister_chrdev(unsigned int major, const char *name);
V dávných dobách alokovalo register_chrdev() všech 256 minor čísel spojených s daným major a zadané name a file_operations byly přiřazeny ke každému z nich. Je-li jako major číslo zadána nula, bude alokováno za běhu. Odpovídající volání unregister_chrdev() všechna minor čísla uvolní. Toto volání požadovalo name z bezpečnostních důvodů; pokud neodpovídalo hodnotě udané při registraci major čísla, volání selhalo.
Během rušného období před vydáním jádra 2.6.0 se Al Viro rozhodl přijít na způsob, jak zvětšit rozsah čísel pro zařízení. Jedním z problémů vyžadujících řešení bylo obrovské množství ovladačů, které "věděly", že minor čísla nikdy nejsou vyšší než 255. Jednou možností bylo prověřit každý ovladač v jádře, jestli se k minor číslům chová správně. Času však nebylo nazbyt a dobrovolníků pro takovou práci ještě méně. Takže na to šel Al jinak: vytvořil pro registraci znakových zařízení nové rozhraní a pak reimplementoval staré rozhraní jako vrstvu zajišťující kompatibilitu, která bude alokovat pro dané major číslo minor čísla od 0 do 255. Tak mohl neupravený kód fungovat jako dosud, protože jádro zaručovalo, že nikdy nespatří minor čísla, která neviděl dříve. Postupem času měly být ovladače převedeny na nové rozhraní, které má množství výhod.
Dopadlo to však tak, že se převedení nikdy nekonalo. Protože staré rozhraní i nadále fungovalo, lidi ho znali a bylo trochu jednodušší jej používat, vývojáři u něj zůstali. Ještě podstatnější možná bylo, že nikdy nedošlo na obávaný nedostatek čísel zařízení. Větší využití dynamických čísel, obecnější rozhraní pro zařízení a mechanismus hotplug dohromady zajistily, že se (většina) linuxových systémů do staršího číselného prostoru snadno vešla, takže rozšířená čísla byla využívána zřídka. Pohled na můj systém odhalí přesně tři minor čísla větší než 255, všechna v /dev/bus/usb. Takže nikdy nebyl důvod přecházet na nové rozhraní.
Alexey Dobriyan si nedávno všiml, že unregister_chrdev() už nekontroluje parametr name, takže poslal patch, který ten argument odstranil a při té příležitosti opravil všechny "volající" (vše, co funkci volalo). Jonathan Corbet poznamenal, že by to mohla být ta správná chvíle pro převod na nové rozhraní - místo přepracovávání staršího rozhraní pro kompatibilitu. Jiný vývojář reagoval s tím, že by bylo fajn mít pro nové rozhraní lepší dokumentaci. Takže tady je rychlý přehled toho, jak by měla v 2.6 vypadat registrace znakových zařízení.
Novější rozhraní rozděluje registraci znakových zařízení na dva kroky: alokace rozsahu čísel zařízení a přiřazení specifických zařízení k těmto číslům. Alokační fázi zajišťuje jedna z následujících funkcí:
int register_chrdev_region(dev_t first, unsigned int count, const char *name); int alloc_chrdev_region(dev_t *first, unsigned int firstminor, unsigned int count, char *name);
První způsob alokuje count minor čísel začínajících u páru major/minor určeném ve first a zapamatuje u každého name. Druhý způsob je určen pro použití v případech, kdy major číslo neznáme dopředu; alokuje major číslo, pak alokuje count minor čísel začínajících na firstminor. Začátek alokovaného rozsahu čísel bude vrácen ve first. Návratová hodnota bude nula při úspěchu a záporný chybový kód při selhání.
Několik věcí stojí za zmínku. U obou možností může být použité major číslo sdíleno s jinými, úplně nesouvisejícími zařízeními. Pouze specifikovaný rozsah alokovaných minor čísel patří daným volajícím. Tato minor čísla mohou být větší než 255. Je možné, že alokovaný rozsah čísel zařízení přeteče rozsah minor čísel do dalšího major čísla. To je záměr a vše by mělo správně fungovat - ačkoliv nevím o žádném produkčním jádře, kde by alokace fungovaly tímto způsobem.
Bez ohledu na to, která alokační funkce byla použita, mohou být čísla zařízení systému vracena takto:
void unregister_chrdev_region(dev_t first, unsigned int count);
Přiřazení čísel zařízení ke konkrétním zařízením se děje prostřednictvím struktury cdev, která se nachází v <linux/cdev.h>. Strukturu cdev je možné inicializovat následující sekvencí:
struct cdev *my_dev = cdev_alloc(); if (my_dev != NULL) my_dev->ops = &my_fops; /* The file_operations structure */ my_dev->owner = THIS_MODULE; else /* No memory, we lose */
Při běžném použití však bude struktura cdev začleněna v nějaké větší struktuře specifické pro dané zařízení a alokace proběhne v rámci této struktury. V takovém případě bude funkce pro inicializaci cdev vypadat takto:
void cdev_init(struct cdev *cdev, const struct file_operations *fops); /* Need to set ->owner separately */
Oběma způsoby je struktura uvedena do řádného provozního stavu a bude vybavena file_operations, které budou volány z přiřazeného zařízení. Pole owner by mělo být inicializováno jako THIS_MODULE, aby se zabránilo neuváženému odstranění modulu v případě, že by zařízení bylo ještě aktivní.
Posledním krokem je přidání cdev do systému a přiřazení k příslušným číslům zařízení. Nástrojem pro tuto práci je:
int cdev_add(struct cdev *cdev, dev_t first, unsigned int count);
Funkce přidá cdev do systému. Bude se starat o operace pro count čísel zařízení začínajících na first; cdev bude často sloužit jedinému číslu zařízení, ale není to podmínkou. cdev_add() může selhat; je-li návratový kód nula, zařízení nebylo do systému přidáno.
A stejně důležité je to, že jakmile cdev_add() uspěje, zařízení je zpřístupněno a jeho operace se soubory mohou být volány jádrem. Takže ovladač by cdev_add() neměl volat, dokud není inicializace přiřazeného zařízení dokončena. Jinak dojde k nepříjemnostem.
Odstranění znakového zařízení ze systému se provede pomocí:
void cdev_del(struct cdev *cdev);
Po tomto volání by se na cdev nemělo odkazovat. Zvláště pokud byla cdev získána pomocí cdev_alloc(), je pravděpodobné, že bude uvolněna v cdev_del().
Jeden poslední trik, který se vyplatí znát: když jsou volány souborové operace na znakovém zařízení, bude přiřazený inode ukazatel předán jako obvykle. Pole inode->i_cdev obsahuje ukazatel na strukturu cdev pro dané zařízení. Ovladače ten ukazatel mohou používat k získání vlastní struktury specifické pro zařízení (např. s container_of()). Není už tedy nutné se pokoušet mapovat minor číslo na interní zařízení - operace, která se mnoha ovladačům nedařila.
Rozhraní cdev se během raného vývoje 2.6 trochu změnilo, ale teď už se s ním nějakou dobu nic neděje.
může systé pro příchozínemá tam být systém?
nebude to falosny pocit bezpecia?Bude. Viz poslední odstavec minulých JN.
prostě ten člověk jen sám sepíše na jednu-dvě stránky co kterej bit v kterým registru ovládá, na tom není nic složitýho ani tajnýho..Obávám se, že tohle je přesně to, co žádný zaměstnanec udělat nesmí.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.