Portál AbcLinuxu, 25. prosince 2025 10:23
Aktuální verze jádra je stále 2.6.25-rc8. Citáty týdne: Avi Kivity, Rusty Russel. Hlášení o stavu Linux Driver Project. Selhání alokace paměti a děsivá varování. Zlepšení syncookies. vringfd() - kruhový zásobník pro komunikaci jádro-uživatel.
Aktuální vývojové jádro je stále 2.6.25-rc8; v minulém týdnu nebyla vydána žádná verze. Posílání patchů do repozitáře hlavní řady se výrazně zpomalilo, ale současný seznam regresí naznačuje, že vydání 2.6.25 není nijak blízko.
-- Avi Kivity konečně přináší virtualizaci na s390
-- Rusty Russel vynalezl vylepšené chybové kódy
Greg Kroah-Hartman zaslal dlouhou zprávu o stavu Projektu ovladač pro Linux [Linux Driver Project]. Hlavním problémem je nedostatek projektů. Ukazuje se, že opravdu není moc hardwaru, který Linux již nepodporuje. Téměř většina nového hardwaru přichází s již napsaným ovladačem, který vytvořil výrobce sám nebo komunita s jeho podporou.
Lidé, kteří své linuxové systémy vystaví určitému nedostatku paměti - a kteří se podívají do logů - si mohou všimnout občasné zprávy, že došlo k "selhání alokace stránky", následované děsivým backtrace. Tito lidé si také mohou všimnout toho, že přes apokalyptický zjev této zprávy svět neskončí. Ve skutečnosti systém většinou funguje dobře. Z tohoto důvodu Dave Jones, který pravděpodobně dostane kvůli každému backtrace vygenerovaném na Fedoře deset e-mailů, prohlásil, že tyto zprávy jsou prostě šum, který by měl být odstraněn. Jestli by se tak mělo skutečně stát, není nicméně zcela jasné; pochopení proč vyžaduje znát pozadí celé věci.
Obecně se jadernému alokátoru paměti nelíbí, když selže. Takže když jaderný kód požaduje paměť, kód správy paměti těžce pracuje, aby požadavek uspokojil. Jestliže tato práce zahrnuje odstranění dalších stránek do swapu nebo odstranění dat z cache stránek, budiž. Velká výjimka nicméně nastává, když je požadována atomická alokace (používající GFP_ATOMIC příznak). Kód požadující atomickou alokaci většinou není v situaci, kdy by si mohl dovolit čekat, než se v paměti douklízí; obzvlášť protože nemůže spát. Takže jestliže správce paměti není schopen uspokojit atomickou alokaci s pamětí, kterou má k dispozici, nemá jinou možnost, než selhat.
Taková selhání jsou vcelku neobvyklá, obzvláště, když jsou požadovány samostatné stránky. Jádro se snaží mít trvalou rezervu rezervních stránek, takže nároky na paměť musí být opravdu velké, aby alokace jedné stránky [single-page allocation] selhala. Vícestránkové alokace jsou obtížnější; jaderný kód správy paměti má tendence fragmentovat stránky, což ztěžuje vyhledání fyzicky souvislých stránek. V případě systému pod tlakem, kde není k dispozici téměř žádná volná paměť, šance na alokaci dvou (nebo více) souvislých stránek výrazně klesají.
Vícestránkové alokace nejsou v jádře používány často; tam, kde je to možné, se jim vývojáři vyhýbají. Nicméně jsou situace, kdy jsou nezbytné. Jedním příkladem jsou síťové ovladače, které (1) podporují vysílání a příjem paketů příliš velkých na to, aby se vešly do jedné stránky, a (2) jejichž hardware není schopen provádět rozsyp/sesbírej [scatter/gather] I/O u jednoho paketu. V takové situaci DMA buffery použité pro pakety musí být větší než jedna stránka a musí být fyzicky spojité. Tato situace se bude časem zlepšovat; schopnost rozsyp/sesbírej v hardwaru se neustále rozšiřuje a ovladače jsou přepisovány tak, aby tuto schopnost mohly využít. S dostatečně chytrým hardware potřeba vícestránkových alokací výrazně klesá.
Tímhle vším ale obcházíme hlavní bod, což je, že od jaderného kódu se očekává, že bude selhání alokací správně zvládat. Nikdy není záruka, že paměť bude k dispozici, takže jaderný kód musí být psán defenzivně. Selhání alokací musí být řešeno bez ztráty více schopností, než je nutné. Pokud předpokládáme, že jaderný kód je napsán správně, neměla by být potřeba vypisovat varování o selhání alokace. Jádro by mělo pokračovat v práci možná dokonce i bez toho, aby si uživatel něčeho všiml.
A ve skutečnosti věci opravdu prostě fungují. Nicméně diskuze vzniklá po Davově návrhu jasně ukázala, že málo vývojářů si je jisto, že jaderný kód správně reaguje v situaci, kdy jsou problémy s alokací paměti. V případech, kde selhání alokace není vyřešeno správně, systém může spadnout na náhodném místě a nechat jenom málo vodítek, co se skutečně stalo. V takové situaci může být varování o selhání alokace jediná užitečná informace, která přežije pád. Z tohoto důvodu někteří lidé chtějí, aby varování zůstala tak, jak jsou.
Shodou okolností podporuje paměťový alokátor speciální bit (__GFP_NOWARN), který způsobí, že se varování nevyšle, jestliže daná alokace selže. Proto bylo navrženo, aby alokace volané z kódu, o kterém se ví, že selhání ošetřuje správně, měly __GFP_NOWARN nastavené. To by odstranilo varování pro kód, který funguje dobře, ale ponechalo by ho pro všechny ostatní, čímž by se varování měla omezit na místa, kde by to skutečně mohl být problém. Jeff Garzik nicméně tomuto nápadu silně oponoval a prohlásil, že to udělá zmatek v kódu a "potrestá správné chování."
Dalším důvodem, proč nechat varování na místě, je jasně ukázat, že systém běží pod silným tlakem, co se paměti týče. Takové systémy nemohou běžet optimálně; často je možné udělat změny, které tlak uvolní a daný systém může běžet plynuleji. Proto bylo navrženo, že varování by mohlo být vysíláno méně často a být méně strašidelné. Nick Piggin navrhuje:
Alternativním návrhem bylo držet někde nějaký čítač, do kterého by se mohli zvídaví správci systému dívat.
Samozřejmě skutečné řešení je zajistit, aby veškerý jaderný kód byl robustní v případě selhání alokace. To může být obtížné zrealizovat, protože cesty obnovení po chybě nejsou v žádném kódu testovány často. V této situaci může naštěstí pomoci framework pro vložení selhání [fault injection framework]. Jaderní vývojáři mohou tento framework použít, aby simulovali selhání alokace ve specifické oblasti kódu a potom mohli zjistit, co se stane. Autor tohoto článku má však dojem, že relativně málo vývojářů tento nástroj využívá. Takže přesvědčení, že jádro je schopné zvládnout problémy při alokaci, asi zůstane nízké a touha ponechat varování na místě asi zůstane vysoká.
V roce 1997 byly mezi script kiddies oblíbené TCP SYN flood útoky. SYN flood je útok typu odepření služby [denial of service], který spotřebovává zdroje serveru iniciací, ale nedokončením spojení. Útoky tímto způsobem zůstávají problémem do dneška, nicméně častěji je spustí sofistikovaná síť botů [botnet] než jedinec. První linií obrany proti SYN floodu je syncookie. Ta nebyla vyvinuta specificky pro Linux, ale našla si cestu do jádra 2.1.44 v patchi od Andi Kleena.
Tato letitá vlastnost nedávno vyvolala diskuzi, když byl zaslán patch přidávající podporu syncookie pro IPv6. Patch je nyní ve frontě pro zařazení, ale během diskuze komunita začala řešit dlouhodobá omezení syncookies a znovu potvrdila, jak relevantní tato vlastnost stále je.
Aby bylo možné syncookies plně popsat, jsou potřeba nějaké vědomosti o tom, jak TCP používá k navázání spojení třícestné potřesení rukou [handshake]. První paket TCP sezení [session] přijatý serverem je známý jako SYN paket, protože obsahuje příznak synchronize control. SYN příznak indikuje, že odesílatel si přeje vytvořit nové spojení, používá se pouze během otevírací sekvence. Server také odpovídá paketem, který obsahuje SYN příznak, protože spojení je potřeba otevřít z obou stran. Druhý paket zároveň nese příznak ACK a je známý jako SYN-ACK. Slouží jak k tomu, aby server otevřel spojení ke klientovi, tak aby potvrdil přijetí otevíracího paketu z druhé strany. Nakonec klient posílá serveru čistý ACK paket, aby potvrdil příjem SYN-ACK paketu od serveru, čímž je spojení dokončeno.
Během SYN flood útoku server přijme první paket trojcestného TCP handshake a odpoví SYN-ACK, ale od iniciujícího klienta žádná další data nedostane. Když je vygenerován SYK-ACK, většina serverů také vytvoří záznam v SYN frontě. Tato fronta je čekárnou pro napůl otevřená spojení, která čekají na dokončení handshake. Útočník naschvál tato spojení opustí a místo toho generuje víc SYN paketů, kvůli kterým se vygeneruje víc záznamů ve frontě. Než může čekání vzdát a uvolnit spotřebovávané zdroje, potřebuje server počkat na vypršení dlouhého časového limitu. Během tohoto času ho útočník zaplaví mnoha dalšími napůl otevřenými spojeními, takže serveru nakonec dojdou zdroje a nemůže přijmout žádné nové spojení bez toho, aby z fronty vyhodil jiná, která mohou být legitimní. Jednoduchá řešení, jako je kvóta na počet částečně otevřených spojení pro každý protějšek [peer] nebo dynamicky nastavovaný paketový filtr, nefungují, protože SYN pakety je možné snadno podvrhnout s falešnou zdrojovou adresou.
Syncookie umožní serveru odložit spotřebu jakýchkoliv zdrojů do doby, než je přijat třetí paket v trojcestném handshake. V té době je adresa protějšku trochu autentizována, protože závěrečný paket obsahuje referenci na sekvenční číslo [sequence number], které bylo posláno serverem v druhém paketu. S tímto jištěním paketové filtry a kvóty zdrojů mohou opět sloužit jako obrana proti útokům na zdroje serveru.
Základní mechanismus, na základě kterého syncookie funguje, je pečlivý výpočet počátečního sekvenčního čísla spojení místo náhodného výběru. Po obdržení SYN server opatrně zakóduje důležité informace, které by byly uloženy jako stav do SYN fronty. Tato zakódovaná informace je kryptograficky hashována tajným klíčem, čímž se vytvoří sekvenční číslo SYN-ACK paketu, který se pošle klientovi. Třetí paket legitimního handshake obsahuje toto číslo (plus jedna) ve svém poli acknowledgment number. Tímto způsobem je informace potřebná k plnému otevření spojení opět předána serveru, aniž by bylo potřeba ukládat někam stav během handshaku.
Hlavní stinnou stránkou syncookies je to, že je v nich místo na zakódování jenom nejzákladnějších TCP handshake voleb. V době zavedení syncookies to nebyl velký problém, protože jediná volba, která se v té době výrazněji používala, byla Maximální velikost segmentu [Maximum Segment Size, MSS]. Tato volba je poskytována, aby se protějšku umožnilo vyhnout se zbytečné fragmentaci tak, že nebude posílat pakety, o kterých z dřívějška ví, že jsou příliš velké a sítí neprojdou. To je přesně ten druh informace, který se normálně ukládá jako stav v SYN frontě. Tvůrci syncookies věděli, že tato volba je důležitá pro výkon a v zakódované syncookie pro ni našli 3 bity, které aproximují skutečnou hodnotu této volby k jedné z osmi běžných hodnot.
Během následujících let nabyly na důležitosti i další volby, které nejsou se syncookies kompatibilní. Nejdůležitějšími z nich jsou volby škálování okna [window scaling] a SACK - selektivní potvrzení [selective acknowledgment]. Tyto vlastnosti v uvedeném pořadí umožní růst oknu pro kontrolu TCP zahlcení [TCP congestion control window] nad 64kB a zajistí efektivnější počínání v případě malé ztrátovosti paketů z těchto velkých oken. Bez použití těchto vlastností je nemožné získat dobré přenosové rychlosti na sítích s velkou šířkou pásma nebo velkou latencí. Mnoho domácích širokopásmových připojení potřebuje alespoň škálování okna, aby se připojení k síti plně využilo. Kvůli tomuto omezení a výpočetnímu času spotřebovanému na výpočet kryptografického hashe se linuxový síťový stack ke spojením založeným na syncookies uchýlí jenom v případě, že počet napůl otevřených spojení překročí čáru přílivu definovanou v net.ipv4.tcp_max_syn_backlog sysctl. Tato spojení mají méně vlastností než obyčejná, ale používají se jenom v případě, že by fronta musela být aktivně vyčištěna.
Ukazuje se, že mechanismus syncookies je implementován pouze pro IPv4. Dave Griffin nedávno poslal patche, které přidávají podporu i do IPv6. Andi Kleen, autor původního syncookie patche položil otázku, jestli by se tento mechanismus měl vůbec dál používat, natožpak přidávat do IPv6.
Andiho argument má tři části. Jeho první námitkou bylo omezení schopností spojení, která byla iniciována pomocí cookies - tato omezení byla v tomto článku již popsána. Postupem času hodnota zahozených voleb vzrostla, a proto vzrostla i cena za používání syncookies. Druhou námitkou bylo to, že Linux již nepoužívá všechnu paměť potřebnou pro otevřené spojení, dokud není úplně otevřeno. Místo toho používá v daném období "minisock". Minisock je 96 B velká struktura struct tcp_request_sock, která udržuje minimální stavové informace potřebné pro plné otevření spojení. struct tcp_sock pro plně otevřené spojení má 1616 B (obě měření velikosti struktur platí pro 64bitová jádra). Třetí námitkou je to, že rutiny pro správu fronty jsou mnohem sofistikovanější než ten blbý zahoď-první algoritmus, který se používal, když byly syncookies poprvé zavedeny. Andi tedy namítal, že by syncookies mohly být úplně odstraněny, protože díky spojení těchto pokroků by Linux mohl být dostatečně robustní i bez nich.
Místo zapojení se do teoretické diskuze provedli někteří čtenáři své vlastní experimenty. Jednou z nejlepších vlastností linuxové komunity je tendence podpořit své argumenty reálnými daty. I když se často objeví nesouhlas s realističností měřených situací, data nám vždycky pomohou lépe pochopit dynamiku jaderného kódu.
Tato data přesvědčivě podporují pokračující hodnotu syncookies a tento postoj, zdá se, vyhrál. Syncookie patche pro IPv6 jsou nyní ve frontě vývojového síťového stromu 2.6.26.
Největší novinkou v tomto ohledu je však pravděpodobně to, že tato diskuze obnovila zájem o problém ztracených voleb při handshake. Florian Westphal a Glen Griffin nedávno ukázali řešení tohoto nejvíce poškozujícího aspektu.
Jejich řešení je použití opakované [echoed] volby TCP časové razítko [TCP timestamp] - podobně jako klasické syncookies používají opakování sekvenčního čísla SYN-ACK v následujícím ACK. Volba timestamp byla zavedena v RFC 1323 a je široce používána na moderních systémech - Linux, Windows, FreeBSD (zahrnuje OS X). Jejím hlavním účelem je umožnit zvýšení frekvence měření doby odezvy [round trip time] v přítomnosti velkých congestion control oken.
Aby se v časovém razítku udělalo pro tyto nové informace místo, 9 nejméně významných bitů je odříznuto. Zakódované reprezentace voleb škálování okna a SACK jsou potom poslány tam a zpět za malou cenu redukované granularity časových razítek během výměny handshake - s tímto přístupem razítka ztrácí 512 nejméně významných jiffies.
Níže jsou uvedeny dva různé TCP handshaky dokončené se syncookies a timestamp patchem. Všimněte si, že nejnižší bity SYN-ACK časového razítka jsou pro každý handshake stejné i v různých časech, protože každý handshake používá stejné volby SACK a škálování okna. Výsledkem je, že hodnoty časového razítka jsou různé, ale spodních devět bitů sdílí stejnou hodnotu 0x166.
13:51:04.582464 IP 127.0.0.1.57985 > 127.0.0.1.4050: S 1061746051:1061746051(0)
win 32792 <mss 16396,sackOK,timestamp 0xfffea013 0,nop,wscale 6>
13:51:04.582478 IP 127.0.0.1.4050 > 127.0.0.1.57985: S 2800702917:2800702917(0)
ack 1061746052 win 32768 <mss 16396,sackOK,timestamp 0xfffe9f66 0xfffea013,nop,wscale 6>
13:51:04.582480 IP 127.0.0.1.57985 > 127.0.0.1.4050: .
ack 1 win 513 <nop,nop,timestamp 0xfffea013 0xfffe9466>
13:59:19.047306 IP 127.0.0.1.45979 > 127.0.0.1.4050: S 218483035:218483035(0)
win 32792 <mss 16396,sackOK,timestamp 0x0001bed4 0,nop,wscale 6>
13:59:19.047320 IP 127.0.0.1.4050 > 127.0.0.1.45979: S 1141094138:1141094138(0)
ack 218483036 win 32768 <mss 16396,sackOK,timestamp 0x0001bd66 0x0001bed4,nop,wscale 6>
13:59:19.047322 IP 127.0.0.1.45979 > 127.0.0.1.4050: .
ack 1 win 513 <nop,nop,timestamp 0x0001bed4 0x0001bd66>
I když není žádná záruka, že volba timestamp bude podporována každým TCP protějškem, časová razítka jsou široce rozšířená na nejběžnějších operačních systémech. Navíc protože časová razítka, škálování oken a selektivní potvrzování jsou všechno vlastnosti spojené se sítěmi s velkou latencí a propustností, není pravděpodobné, že se najde implementace, která by podporovala jenom podmnožinu těchto voleb.
Jediným nedostatkem tohoto schématu je, že není dostatečně obecné na to, aby bylo budoucnostivzdorné vzhledem k dalším možnostem, které se mohou u handshaku objevit. V současnosti se běžně vyskytují jenom volby MSS, SACK, škálování okna a časová razítka společně s volbou NOP, která slouží k zarovnání paketu. Nicméně jediným důvodem pro používání schématu s rozšiřitelnými volbami je nechat místo pro budoucí vylepšení. Registr IANA, který zaznamenává hodnoty voleb, byl naposledy aktualizován v únoru 2007, kdy se rezervoval kód 27 pro použití s experimentálním RFC 4782 "Rychlý start pro TCP a IP". Jenom čas ukáže, jestli tato konkrétní volba bude pro syncookies další výzvou nebo jestli se dříve objeví něco jiného.
Timestamp patch byl zaslán teprve nedávno a kromě vývojářů, kteří na něm pracovali, se k němu nevedla téměř žádná diskuze. Není jasné, jestli bude do hlavní řady akceptován ihned, ale určitě se zdá, že je řešením velmi známého základního problému se syncookies za velmi malou cenu.
S aktualizacemi pro IPv6 a moderními schématy TCP voleb se syncookies zdají být připravené dále poskytovat sladké oddychnutí ve svém poněkud esoterickém koutku síťové bezpečnosti. Možná budou spokojeně blafat dalších deset let, aniž by bylo potřeba je nějak upravovat.
Jednou z centrálních vlastností (nyní zastaveného) kevent subsystému byl kruhový zásobník zaměřený na efektivní přenášení dat mezi jádrem a uživatelským prostorem. Keventům možná došla pára, ale idea kruhového zásobníku je tu zpátky jinou cestou. Rusty Russell nyní navrhuje nové systémové volání (nazývané vringfd()), které mění část práce na virtio na nové rozhraní jádro-uživatel realizované kruhovým zásobníkem. Zaslaný patch poněkud bere dech kvůli nedostatku dokumentace tohoto nového systémového volání - obzvlášť když se vezme v úvahu, že Rusty je v tomto stylu psaní docela dobrý. Autor článku toto opomenutí vzal jako osobní výzvu, a proto se pokusil o reverzní engineering tohoto (poněkud komplexního) vringfd() rozhraní.
Uživatelský proces, který chce nastavit vkruh [vring] pro komunikaci s jádrem, musí neprve nastavit poněkud komplikovanou datovou strukturu. Jedna část začíná rozhodnutím, kolik záznamů má kruh mít; toto číslo musí být mocnina dvou, která se vejde do bezznaménkové 16bitové hodnoty. S tímto číslem (budeme ho nazývat RING_SIZE) bude struktura vypadat nějak takto:
struct divna_vring_vec {
struct vring_desc descriptors[RING_SIZE];
struct vring_avail available;
char padding[do-hranice-dalsi-stranky];
struct vring_used used[RING_SIZE];
};
Zarovnání ke stránce pro pole used je důležité - toto pole může být do jaderného prostoru mapováno odděleně. Pole se musí vejít do jedné stránky, což prakticky určuje limit 256 záznamů pro RING_SIZE na systémech s 4096 B velkými stránkami. Pokud toto API bude pokračovat, je vysoká šance, že bude nalezen způsob, jak tento limit zvýšit.
Individuální popisovače v kruhu jsou popsány touto strukturou:
struct vring_desc
{
__u64 addr; /* adresa zásobníku */
__u32 len; /* délka zásobníku */
__u16 flags; /* příznaky */
__u16 next; /* další zásobník v řadě */
};
Pro jednoduchý zásobník aplikace jednoduše namíří addr na začátek a nastaví len na příslušnou hodnotu. Jestliže do zásobníku má jádro zapisovat, aplikace by také měla nastavit VRING_DESC_F_WRITE v poli flags.
Věci se ale mohou trochu zkomplikovat, protože vringfd() rozhraní podporuje scatter/gather zásobníky rozdělené na více částí. K nastavení takového zásobníku se z uživatelského prostoru použije jeden záznam vring_desc pro každý zásobník. Pro všechny až na poslední segment by měl být nastaven příznak VRING_DESC_F_NEXT (který říká "použij také následující popisovač") a next by měl být index následujícího popisovače. Když jádro zásobník použije, bude se v této řadě pohybovat tak dlouho, dokud nenarazí na poslední (který nemá příznak VRING_DESC_F_NEXT).
Než může jádro použít zásobníky nastavené aplikací, z uživatelského prostoru musí být řečeno, že je zásobník připraven. To se provádí strukturou vring_avail:
struct vring_avail
{
__u16 flags;
__u16 idx;
__u16 ring[RING_SIZE];
};
Pole ring udržuje indexy do pole descriptors. Hodnota idx by měla vždy být indexem poslední platné položky v kruhu (ring). Když je k přenosu do nebo z jádra připraven nový zásobník, aplikace uloží index prvního popisovače do ring[idx+1] a pak inkrementuje idx. Když je kruhový zásobník založen poprvé, jádro si zapamatuje pozici idx, takže první zásobník by sem měl být přidán až poté, co je provedeno systémové volání vringfd().
Jádro bude spotřebovávat zásobníky z kruhu available podle potřeby. Jakmile je požadovaná operace se zásobníkem dokončena a jádro je s ním hotové, objeví se zásobník v oblasti used, která je strukturována takto:
struct vring_used_elem
{
__u32 id;
__u32 len;
};
struct vring_used
{
__u16 flags;
__u16 idx;
struct vring_used_elem ring[RING_SIZE];
};
Ve struktuře vring_used je idx index dalšího vstupního bodu do kruhu (ring), kam může jádro zapisovat; je inkrementován, jakmile je kruh aktualizován. Když je zásobník umístěn do kruhu used, pole id je index popisovače a len skutečná délka přenesených dat.
Vypadá to, že pole flags (příznaky) ve strukturách vring_avail a vring_used se zdají být nepoužitá.
Jakmile má aplikace celou tuto datovou strukturu nastavenou, může v jádře založit kruhový zásobník novým systémovým voláním:
long vringfd(void *addr, unsigned int ring_size, u16 *last_used);
addr je základní adresa datové struktury popsané výše, ring_size je počet popisovačů v kruhovém zásobníku a last_used je 16bitové bezznaménkové celé číslo, které indikuje, která položka v kruhu used byla aplikací naposledy použita. Pokud se nepodaří udržet hodnotu last_used aktuální, věci se sice nezpomalí, ale poll() nebude fungovat správně.
Návratová hodnota je popisovač souboru [file descriptor] spojený s kruhem.
Vytvoření vkruhu [vring] je nicméně jenom část práce. Dalším krokem je propojení s jaderným subsystémem, ze kterého se mají přenášet data. Rustyho patch zahrnuje podporu pro vkruh do ovladače virtuálního síťového rozhraní tun; aby tuto podporu mohla využít, provede aplikace zvláštní ioctl() volání, aby poskytla popisovač vkruhu tun ovladači. Jiné subsystémy budou pro podporu vkruhu potřebovat podobný mechanismus.
Jestliže aplikace používá kruhový zásobník pro přenos dat jádru, musí (1) nastavit jeden nebo více popisovačů pro zaplněné datové zásobníky v kruhu available, potom (2) provést write() volání na popisovač souboru spojený s vkruhem. Zásobník a délka předané write() jsou ignorovány; záleží jenom na předaném popisovači. Když se write() vrátí, operace je zahájena, ale nemůže se považovat za dokončenou do doby, než se popisovače zásobníků objeví v kruhu used.
Pro přenosy z jádra do uživatelského prostoru aplikace jednoduše přidá zásobníky do kruhu available a pak počká, až se objeví v kruhu used. Pokud je zavolán poll() s popisovačem vkruhu, bude blokovat, dokud nejsou k dispozici přijaté zásobníky. Jádro zjišťuje, jestli v used existují nějaké nepoužité zásobníky, porovnáním indexu vring_used->idx oproti hodnotě last_used předané aplikací. Je vhodné poznamenat, že v závislosti na tom, jak funguje relevantní jaderný subsystém, se zásobníky nemusí do kruhu used dostat, dokud není zavolán poll().
Na straně jádra začne vývojář, který chce přidat podporu vkruhu do subsystému, vytvořením množiny vring_ops:
struct vring_ops
{
void (*destroy)(void *);
int (*pull)(void *);
int (*push)(void *);
};
Všechny tyto hodnoty jsou ukazatele na funkce zadané, když se subsystém připojuje k vkruhu (bude popsáno níže). Callback pull() je vyvolán, když aplikace volá poll(); jestliže existuje nějaké zpracování popisovače, které vyžaduje přístup k uživatelskému prostoru, tohle je místo, které takové podmínky poskytuje. Jestliže pull() přidá nějaké zásobníky do kruhu used, mělo by vrátit počet zásobníků; také může vrátit záporný chybový kód. push() je volán při volání write() a indikuje, že jsou připraveny zásobníky k předání jádru; vrací nulu nebo záporný chybový kód. Callback destroy() je zavolán, když je zavřen popisovač vkruhu. Všechny tyto callbacky jsou volitelné.
Připojení k vkruhu se provede touto funkcí:
struct vring_info *vring_attach(int fd, const struct vring_ops *ops,
void *data, bool atomic_use);
Pro toto volání je fd popisovač souboru odpovídající vkruhu, ops je struktura ukládající operace popsaná výše, data je ukazatel na privátní data, který je předáván callbackům vring_ops, a atomic_use je nenulové, pokud jádro potřebuje být schopné přidávat zásobníky do kruhu used v atomickém kontextu. Návratová hodnota je ukazatel na interní datovou strukturu vkruhu nebo ERR_PTR(), pokud se něco pokazí.
K získání zásobníku z kruhu available slouží toto volání:
int vring_get_buffer(struct vring_info *vr,
struct iovec *in_iov,
unsigned int *num_in, unsigned long *in_len,
struct iovec *out_iov,
unsigned int *num_out, unsigned long *out_len);
Tato funkce vyplní pole struktur iovec odpovídajících dalšímu dostupnému zásobníku. Jestliže jádro očekává, že bude do zásobníku zapisovat, mělo by nastavit ukazatel in_iov na pole iovec, num_in, aby ukazoval na délku in_iov, a in_len, aby ukazoval na pozici, kam se uloží celková délka zásobníku (nebo NULL, pokud je tato informace k ničemu). Pro přenosy do jádra by měly být podobně nastaveny out_iov, num_out a out_len. Všimněte si, že adresy uložené v polích iovec jsou adresy v uživatelském prostoru; vring_get_buffer() je neověřuje, takže to musí udělat volající.
Je možné nastavit jak in_iov, tak out_iov; v takovém případě bude nastaven jeden z nich podle toho, jestli následující zásobník v kruhu available má nastavený příznak VRING_DESC_F_WRITE. Ve většině případů však bude mít ne-NULLové hodnoty pouze jedna sada těchto parametrů. Zjevným záměrem tohoto API je, že pokud jsou potřeba obousměrné přenosy mezi jádrem a uživatelským prostorem, měly by být použity dva oddělené vkruhy.
Návratová hodnota vring_get_buffer bude jedna z: (1) kladný index popisovače, (2) nula, která indikuje, že nejsou k dispozici žádné zásobníky, (3) záporný chybový kód.
Index popisovače by měl být uložen v posledním kroku, který indikuje, že jádro je s tím konkrétním zásobníkem hotové:
void vring_used_buffer(struct vring_info *vr, int id, u32 len); void vring_used_buffer_atomic(struct vring_info *vr, int id, u32 len);
Obě tyto funkce indikují, že zásobník id by měl být vložen do kruhu used; len je množství dat, která byla skutečně přenesena. Jestliže není možné spaní, mělo by být použito vring_used_buffer_atomic() - vkruh ale musel být připojen s nastaveným příznakem atomic_use.
Nezdá se, že by existoval způsob, jakým by se subsystém mohl od vkruhu odpojit; místo toho musí počkat, až aplikace zavře s ním spojený popisovač souboru.
Rozhraní je v raném stádiu a kód má mnoho omezení a FIXME komentářů. Zdá se tedy pravděpodobné, že se věci ještě trochu vyvinou, než bude o vringfd() vážně uvažováno jako o kandidátovi pro zařazení do hlavní řady jádra. Návrh kruhového zásobníku pro tento druh komunikace se ale objevuje vcelku pravidelně, takže se zdá, že po takovém druhu API je poptávka.
Diky za vysvetleni aktualniho stavu syncookies.
Tenhle clanek vyjde az zitra?To byl drobný přehmat... Správně měl vyjít v pátek, ale bohužel se na chvilku objevil na hlavní stránce už ve čtvrtek (a ačkoliv jsem ho zase stáhl, už byl přístupný pro ty, kdo znali jeho adresu... třeba Google).
Zoufalá potřeba po devfs se stává opět jasnější.A o pár let později řešili, jak se toho zbavit
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.