helenos-xhci team mailing list archive
-
helenos-xhci team
-
Mailing list archive
-
Message #00015
Odložené zpracování událostí
Zdravím,
rád bych otevřel otázku, kterou jsme řešili osobně i na slacku,
aprobral ji i s někým kvalifikovanějším.
V současnosti řešíme problém, že zpracování událostí (např. port status
change event při připojení zařízení) má vyvolat sérii příkazů, kterým se
připojené zařízení inicializuje z pohledu HC. Na tyto příkazy je potřeba
čekat, protože v potvrzující zprávě od HC jsou data, která jsou nadále
potřeba pro další příkazy. Bohužel, tyto zprávy přichází do stejného
event ringu, jako samotný port status change event. Nelze tedy obsluhu
této události provádět z vlákna, ve kterém dochází k obsluze událostí
o dokončení příkazu.
Nabízí se tedy obsluhu události odložit, a zpracovat v jiném
threadu/fibrilu. Již je implementováno čekání na dokončení příkazu
uspáním fibrilu.
Možnosti jsou následující:
1) Vytvořit thread/fibril pool konstantní velikosti, který bude
obsluhovat top-half události. Implementace jednoduchá, stačí k tomu
nějaká fronta, do které zkopírujeme TRB o výsledku eventu. Nevýhodou
je, že pokud hloubka čekání na jiný výsledek v součtu přesáhne
velikost poolu, máme tu deadlock. Pokud využijeme thready, pak
vyžaduje synchronizaci a tím úpravu existujících struktur, aby byly
thread-safe. Odměnou však bude paralelismus ve zpracování událostí.
2) Kvůli každému typu eventu vytvořit vlákno, které obsluhuje tento
kontrétní event. Náladu trochu kazí opět port status change, ve
kterém je potřeba zrestartovat port, a tedy čekat na další port
status change. Nicméně tuto situaci lze vyřešit rozdělením
a zapamatováním stavu u portu. Nevýhoda je jasná, událostí je poměrně
hodně. Lze se však omezit na "těžkotonážní" události, a tím počet
vláken zredukovat. Vyžaduje synchronizaci.
3) Vytvořit fibril pro každou událost tohoto typu, tedy místo okamžitého
řešení události vytvořit fibril, který ji obslouží. Události jsou
označeny za vyřešené okamžitě, fibrily se pak budou postupně
probouzet a blokovat na čekání. Tím se uvolní prostor pro obsluhu
dalších událostí v původním fibrilu. Jednoduchá implementace. Můžeme
si však dovolit vytvářet fibril při každé významnější události?
Výhodou je, že toto řešení nevyžaduje synchronizaci, dokud se fibrily
přeplánovávají pouze explicitně.
4) Inspirovat se tím, co dělá například Fuchsia, a v podstatě
reimplementovat lightweight fibrily - mít frontu handlerů, které se
postupně spouští, ověří podmínky pro běh, a pokud nejsou splněny,
naplánují se na konec fronty. Fronta se spouští, když dojde k eventu.
Nevýhodou je, že kód pro obsluhu událostí se musí rozdělit do fází,
které se plánují do front. V zásadě se pak musí místo
synchronizačních primitiv používat odplánování, a tím si primitiva
implementovat pokaždé "ručně". Ale je to dobré řešení z hlediska výkonu.
5) Řešit konkrétně port status change event tím, že vytvoříme fibril pro
každý obsluhovaný port. To významně usnadní psaní kódu pro tuto část
roothubu - celý stavový automat pak lze napsat jako jednu funkci,
která v nečinnosti blokuje, a stav si udržuje implicitně stavem
fibrilu. Nevýhodou je však menší robustnost - možná nekonzistence
stavu automatu a skutečného stavu portu. Pokud si stav zjišťujeme
z HW při začátku obsluhy, máme větší šanci, že se tomuto vyhneme.
Přes snahu se mi nepodařilo vyluštit, jak tento problém řeší v Linuxu.
Ostatní kernely jsem zatím nestudoval.
Jaké jsou vaše názory, jak se se situací vypořádat?
Ondra
Follow ups