Nowości Jądro

Opublikowane:

01.02.2018

Kronikarz Zack Brown informuje o poglądach, wydarzeniach, dylematach i innych nowościach w społeczności programistów jądra

Obsługa I3C

Boris Brezilon opublikował łatki implementujące część głównej infrastruktury I3C. Jest to kompleksowe uaktualnienie protokołu I2C, służącego komunikacji przez porty szeregowe, z którego korzysta duża grupa czujników, ponieważ jest to prosty, dwużyłowy interfejs. Jednak, podczas gdy prostota sprawia, że ilość urządzeń sensorycznych rośnie, zarządzanie rosnącym wolumenem danych przesyłanych tą drogą i ilością przerwań potrzebnych do kolejnych urządzeń zaczyna mieć duże znaczenie. Dlatego właśnie powstał I3C.

Rozwiązanie Borisa jest wstecznie kompatybilne z I2C, dzięki czemu nie sprawia problemów użytkownikom. Jednak w niektórych kwestiach autor musiał pójść na kompromis i dlatego korzystanie z jego API może sprawiać problemy. Dodatkowo pozostawił niezaimplementowaną część API I3C, chociaż planuje uzupełnić tę lukę w przyszłości.

Jednym ze wspomnianych kompromisów jest wymaganie, aby operacje związane z oprogramowaniem użytkownika były nieatomowe (czyli dany proces może zostać przerwany przez coś innego). Jest to pewnie niedogodność, gdyż kod użytkownika musi znać swój aktualny stan w czasie wywołania API I3C. Boris zaznaczył jednak, że może to zmienić. Po prostu takie rozwiązanie było szybsze.

Jednym z brakujących fragmentów API jest obsługa urządzeń podłączanych podczas pracy komputera (hot plugging), co dla wielu może skreślać nowe rozwiązanie. Oczywiście, z czasem kod I3C będzie uzupełniany i brakujące API zostaną dodane.

Wolfram Sang przyjrzał się kodowi źródłowemu, ale nie miał obiekcji i zatwierdził łatki.

Arnd Bergmann zapytał, dlaczego Boris stworzył cały nowy podsystem dla I3C, zamiast po prostu rozwinąć istniejący I2C, tak aby wspierał też I3C. Boris odpowiedział:

„I3C i I2C są zupełnie inne. Nie mówię o warstwie fizycznej, ale o tym jak magistrala ma być obsługiwana przez warstwę programową. Uważam, że I3C bliżej jest do magistrali wykrywających urządzenia automatycznie, takich jak USB czy SPI.

Wszystkie urządzenia I3C mogą być wykryte i nie muszą być identyfikowane na poziomie płyty (z wykorzystaniem drzewa urządzeń (DT), ACPI czy czegokolwiek innego). Ponadto, niektóre z nich można podłączyć do działającego sprzętu i, co najważniejsze, wszystkie identyfikują się w czasie procedury wykrycia (nazywanej DAA w I3C).

Istnieje coś analogicznego do «klasy urządzenia». W świecie I3C nazywa się to DCR (Device Characteristic Register, czyli Rejestr Cech Urządzenia), ale odgrywa tę samą rolę: jest to zbiór standardowych interfejsów, z którymi urządzenia muszą być zgodne, jeśli chcą być kompatybilne z DCR ID (jak przyspieszeniomierz, żyroskop itp.) […]

Urządzenia udostępniają również 48-bitowe, składające się z mniejszych pól, tymczasowe ID. Dwa z tych pól są szczególnie interesujące: ID producenta i ID części, które można porównać do dostawcy i ID produktu w świecie USB.

Podane informacje (DCR, ID producenta i ID części) mogą zostać wykorzystane przy dopasowaniu sterowników, zamiast nazw, jak to odbywa się w urządzeniach I2C.

Jak więc widać, współpraca z szyną I3C jest zupełnie inna niż z I2C”

Boris dodał: „Oczywiście mogę przenieść cały kod do drivers/i2c/, ale nie zmieni to faktu, że magistrale I3C i I2C są całkowicie różne i niewiele je łączy”.

Jednak Arnd chciał kontynuować dyskusję. Stwierdził, że istnieją argumenty, aby nie rozszerzać I2C, tak aby obejmowało też I3C, uważa jednak, że są też przesłanki, by jednak to zrobić, nawet jeśli powstanie lekki bałagan. Powiedział: „Odzwierciedlenie fizycznej hierarchii w oprogramowaniu ma wiele zalet i jeśli urządzenia I2C i I3C mogą być podłączone do tej samej magistrali, dobry model powinien przedstawiać je na tym samym poziomie. Odnosi się to zarówno do jądra (w sysfs i strukturach danych), jak i do drzewa urządzeń (zakładając, że urządzenia I3C należy w ogóle w ten sposób opisywać). Oba rozwiązania nie muszą korzystać z tego samego modelu, ale łatwiej by było, gdyby tak robiły”.

Następnie dodał: „W przeszłości dyskutowaliśmy już, czy I2C i SPI powinny był połączone pod jednym typem bus_type, ponieważ wiele urządzeń może być podłączanych do obu interfejsów. Jeśli okazuje się, że urządzenia I3C dość często wspierają tryb awaryjny I2C, posiadanie tej samej wartości bus_type może w znacznym stopniu uprościć sterowniki, gdyż wymagana będzie jedynie pojedyncza struktura i2c_driver. Większe skomplikowanie po stronie podsystemu, zrównoważone więc będzie przez uproszczenie sterowników”.

Boris stwierdził, że nie dostrzega korzyści płynących z takich rozwiązań i wyraził zdziwienie na temat idei połączenia I2C i SPI. Poprosił Arnda o wyjaśnienie, na co ten odpowiedział: „nigdy nie dokonaliśmy tego połączenia, więc pewnie okazało się, że koszt tej operacji był większy niż korzyści”. Jednak określił, że ogólnym celem jest uproszczenie opcji konfiguracyjnych kompilacji jądra. Powiedział:

„Największy problem z pojedynczym sterownikiem pracującym na różnych magistralach (I2C z SPI albo I2C z I3C) jest obsługa różnych kombinacji konfiguracji (na przykład I2C=m, SPI=y).

Najprostszym rozwiązaniem jest funkcja module_init, która rejestrowałaby oba sterowniki, ale wymaga do wprowadzenia zależności od Kconfig w obu podsystemach. Dodatkowo nie możemy korzystać z module_i2c_driver().

Innym rozwiązaniem jest zdefiniowanie kilku #ifdef wraz ze skomplikowanymi zależnościami sterowników w Kconfig, tak aby rejestrować tylko sterowniki obiektów na magistralach, które są dostępne. Da się to zrobić, ale praktycznie nikt bez głębszej analizy nie zrozumie tego za pierwszym razem.

Rozwiązaliśmy to w ten sposób, że podstawowy sterownik znajduje się w jednym module, a dodatkowe jego części, dla konkretnych magistrali, znajdują się w innych modułach, dla których ponownie możemy skorzystać z module_i2c_driver. W jądrze znajduje się wiele wystąpień sterowników łączących w sobie I2C i SPI i działa to dobrze, chociaż powiększa się koszt w porównaniu do sytuacji z jednym sterownikiem, który mógłby na przykład korzystać z regmap w celu określenia różnic w funkcji probe(). Takie rozwiązanie sprawiłoby, że wszystko byłoby w jednym miejscu”.

Dla Borisa miało to sens, gdyż widział zasadność posiadania jednego podsystemu. Dalej jednak uważał, że scenariusz łączenia obu rozwiązań nie prezentuje się dobrze. Zapytał, czy „nie możemy rozwiązać tego problemu z pomocą makra module_i3c_i2c_driver(), które przejęłoby całą złożoność sterowników I2C/I3C?”.

Wolfram, który wcześniej zgodził się na łatki Borisa, uważał, że łączenie I2C z I3C może być czymś zupełnie innym niż łączenie I2C z SPI. W tym drugim przypadku zakłada się raczej, że system może posiadać albo jedno, albo drugie, dlatego wspieranie obu obejmowało wszystkie możliwości. Jednak wątpił, czy istnieje sprzęt działający jednocześnie na I2C i I3C. A ponieważ kod I3C jest wstecznie kompatybilny z I2C, nie istnieje potrzeba ich łączenia. Użytkownik może po prostu podłączyć urządzenie I2C i będzie działać.

Boris odpowiedział, że chociaż nie zna aktualnie urządzeń, które korzystałyby z obu protokołów, to „specyfikacja jasno określa, do czego służą statyczne adresy, i jedną z możliwości ich wykorzystania jest podłączenie urządzenia I3C do magistrali I2C, na której zachowywałoby się jak urządzenie I2C”. Istnieje więc prawdopodobieństwo, i Wolfram się z tym zgodził, że w przyszłości pojawią się urządzenia korzystające z obu protokołów.

Boris próbował połączyć I2C z I3C, ale miał trudności już w fazie projektowania rozwiązania. Powiedział też: „Trudno jest zaprojektować rozwiązanie dla urządzeń, których się nie posiada. Jak na razie mogę naśladować i zmieniać I2C w zależności od potrzeb użytkownika”.

W innym miejscu Greg Kroah-Hartman poprosił Borisa o oddzielenie dokumentacji od łatki i opublikowanie jej oddzielnie, tak aby kod był łatwiejszy do przejrzenia. Zapoznał się jednak z całością i miał kilka uwag, na które Boris obiecał odpowiedzieć.

Greg zauważył, dzięki brakującemu typowi danych, że Boris nigdy nie testował usunięcia urządzenia I3C, po tym jak już zostało zainstalowane. Boris odpowiedział: „Masz mnie, nigdy tego nie testowałem”. Pytał potem o techniczne aspekty tego typu przypadku.

W tym momencie dyskusja zeszła na bardziej specjalistyczne tematy, gdyż Greg i Boris zastanawiali się nad problemem usuwania urządzeń w momencie pracy systemu.

Ostatecznie odpowiedź na zadane początkowo pytanie nie padła, ale wydaje się oczywiste, że kod Borisa zostanie przerobiony, tak aby uwzględnić uwagi Grega i Arnda. Jest to jednak zadanie, które może być dużo bardziej skomplikowane, niż Boris przewidywał, i raczej nie ma on urządzeń potrzebnych do testów (w pewnym momencie napisał: „wszystkie testy wykonywałem na sztucznie emulowanych urządzeniach I3C”). Wygląda więc na to, że I3C zostanie prędzej czy później włączone do jądra, ale ciągle istnieją dość duże przeszkody do pokonania.

Naprawa mmap()

Dan Williams zauważył pewien błąd. Wywołanie systemowe mmap() nie sprawdzało nieznanych flag, co oznaczało, że współczesne, nowe mmap, którego zachowanie nie współgra ze starszymi systemami, nie będzie kończyć się w akceptowalny sposób. Dan chciał zaimplementować nowe wywołanie, mmap3(), które by sprawdzało wszystkie flagi.

Zrodziło to kilka problemów. Po pierwsze, Christoph Hellwig zauważył, że „dodawanie nowych wywołań systemowych to bolesny proces; przerobienie wszystkich rodzajów architektury trwać będzie wieki (szczególnie w przypadku wielu architektur 32-bitowych, dla których należy wziąć pod uwagę różne poziomy granularności), a potem trzeba jeszcze uwzględnić biblioteki C, nie wspominając o aplikacjach”.

Christoph zasugerował skorzystanie ze stosowanego już obejścia przez __MAP_VALID.

Dan odpowiedział: „Zgadzam się, że w ten sposób tworzy się bałagan i opóźnienia dla architektur oraz bibliotek libc, ale przecież skierowane to jest do nowych aplikacji i bibliotek, które wiedzą, że należy szukać nowej flagi, więc muszą wykonać dodatkową pracę, szukając nowego wywołania systemowego”.

Odnosząc się do możliwości wykorzystania __MAP_VALID, zauważył, że „nowa flaga mmap w tym wypadku musi współpracować z MAP_SHARED, podczas gdy MAP_PRIVATE jest ignorowany”.

Powiedział jednak, że jeśli nie będzie to zbyt uciążliwe, może zgodzić się na takie rozwiązanie.

Christoph zaczął sprawdzać kod mmap i stwierdził, że znalazł rozwiązanie problemu ignorowania MAP_PRIVATE. Stwierdził, że to zdecydowanie lepsza opcja niż dodawanie nowego wywołania systemowego.

Jednak Kirill A. Shutemov przyjrzał się odkryciu Christopha i zauważył, że niektóre architektury, w szczególności PA-RISC, nie będą współpracować z tym rozwiązaniem. Christoph odpowiedział: „Nie powinniśmy się przejmować pamięcią trwałą w PA-RISC. Musimy jedynie znaleźć sposób wykluczenia PA-RISC, tak aby nie skomplikować sobie życia”. Kirill jest przeciwny takiemu rozwiązaniu. Twierdzi, że interfejs wywołania systemowego powinien być uniwersalny i nie może zachowywać się inaczej w zależności od urządzenia, na którym był uruchomiony.

Dyskusja trwała, choć wydawało się, że powoli odchodzi od pierwotnego tematu. W końcu Dan wrócił do niego, mówiąc:

 „Problemem jest to, że aby wspierać nowe flagi mmap, dla danych architektur należy wyszukać flagi, które na pewno nie będą działać na starszych jądrach. Przypisanie na PA-RISC wartości 0x8 do MAP_DIRECT nie działa, ponieważ będzie ignorowane na starszych jądrach.

Jednak już teraz niektóre architektury posiadają własne punkty dostępowe sys_mmap. Architektury, które nie mogą działać standardowo (wydaje się, że tylko PA-RISC), będą zmuszone dodać nowe wywołanie mmap. Jest to dobry kompromis pozwalający każdej innej architekturze dodać taką funkcjonalność w istniejącym wywołaniu mmap”.

Helge Deller nie zgodził się z tym, mówiąc: „Nie chcę, aby inne architektury cierpiały z powodu PA-RISC. Jednak dodawanie nowego wywołania tylko na potrzeby PA-RISC też nie jest dobrym rozwiązaniem, ponieważ nikt potem tego u siebie nie wprowadzi”.

Helge zaproponował w tym wypadku ingerencję w ABI (interfejs binarny aplikacji) dla PA-RISC. W ten sposób uzyska się prawidłowe działanie bez większych kombinacji. Dodał, że i tak nie ma zbyt wielu użytkowników PA-RISC, a większość z nich regularnie aktualizuje jądro, ponieważ w ostatnim czasie dodano wiele zmian w kodzie.

Jednak Dan odpowiedział: „Problem polega na tym, że chcemy uniknąć w ABI błędów, a w szczególności błędnych wyników przekazywanych jako prawidłowe. Utkniemy, jeśli niektóre aplikacje będą oczekiwały, że wartość 0x8 jest ignorowana, lub na odwrót, aplikacja, która musi opierać się na semantyce MAP_SYNC/MAP_DIRECT, zakłada, że wynik jest błędny, podczas gdy flagi te są ignorowane”.

W tym momencie dyskusja wygasła i nie jest do końca jasne, jakie rozwiązanie zostanie przyjęte dla mmap(). Uważam, że obserwowanie, jak każda z propozycji trafia na przeszkody, jest niezwykle ciekawe. Wprowadzanie nowych wywołań systemowych jest czasochłonne. Proponowane obejście problemu działać będzie tylko w niektórych przypadkach. Jeśli jednak je poprawimy tak, że będzie działać zawsze, ciągle pozostaje jeden rodzaj architektury, na którym odnotujemy błędy. Z kolei ingerencja w interfejs binarny aplikacji nie jest rozwiązaniem, ponieważ właśnie tego chcieliśmy uniknąć na początku.

Oczywiście, w wielu częściach jądra przydałyby się możliwości zmian w ABI. Jednak jest to akurat coś, co społeczność chce pozostawić nienaruszone. Jedyna rzecz, jaka może wymusić zmiany w tej części jądra, to dziury w bezpieczeństwie. Bezpieczeństwo jest najważniejsze. Czyż jednak nie byłoby pięknie, gdybyśmy kiedyś dostali specjalne wydanie jądra z możliwością modyfikacji ABI, gdzie każdy fragment jądra mógłby go zmienić chociaż jeden raz? Co za szaleństwo! Co za rozpusta! A potem... poczucie winy, wzajemne oskarżenia. Niesamowite.

Śledzenie zajętości RAM-u w przypadku braku pamięci

Yang Shi zirytował się, kiedy na jego maszynie pamięć była zajęta, a OOM (out-of-memory) killer nie mógł znaleźć żadnego procesu, który mógłby zakończyć, aby uniknąć paniki jądra. Jeśli nie ma żadnego procesu do zakończenia, to dlaczego brakuje pamięci? Okazało się, że cała pamięć w systemie alokowana jest do płyt (slab), które mają status nieodzyskiwalnych. Yang opublikował łatkę dodającą do programu slabinfo opcję -U, dzięki której otrzymamy informacje jedynie o nieodzyskiwalnych płytach. W ten sposób użytkownik będzie mógł zidentyfikować problem i znaleźć sposób na naprawę sytuacji.

Michal Hocko nie miał nic przeciwko wprowadzeniu łatki do jądra, ale zauważył, że kod Yanga może znacznie powiększyć raport wynikowy z OOM killera, który już teraz jest niezwykle rozwlekły. Z tego powodu proponuje, aby nowa opcja była domyślnie wyłączona.

Yang nie zgodził się z tą uwagą, mówiąc, że dodatkowe dane w raporcie pojawią się tylko w przypadku poważnych awarii, a nie jako element standardowej procedury. Dodał też, że bez problemu można dodać plik do systemu plików proc (procfs), dzięki czemu w każdej sytuacji możliwa będzie kontrola nad zwracanymi przez program danymi.

Jednak Michal stwierdził, że dla niego nie ma to znaczenia. Powiedział: „Większość raportów OOM, które widziałem, to była pamięć przypięta (pinned) z przestrzeni użytkownika”. Woli jednak, aby raport był bardziej obszerny niż mniej.

Kontynuowali dyskusję i w końcu doszli do porozumienia, kiedy to Yang zasugerował: „Może powinniśmy podać informację o stosunku nieodzyskiwalnych płyt do całości pamięci. Na przykład kiedy osiąga on wartość równą i większą od 50%, wtedy podajemy statystyki płyt w OOM? Dodatkowo procent ten można by było zmieniać w /proc.”.

Michal zgodził się, że ma to sens, i na tym wątek się zakończył.

Autor: Zack Brown Lista dyskusyjna poświęcona rozwojowi jądra jest głównym narzędziem komunikacyjnym programistów jądra. Ruch na niej jest ogromny – dochodzi do dziesięciu tysięcy listów tygodniowo. Pozostawanie z wszystkim na bieżąco to zadanie bardzo trudne dla zwykłych śmiertelników. Zack Brown jest jedną z niewielu osób, która uważnie śledzi wszystkie dyskusje.

Autor: Zack Brown

Lista dyskusyjna poświęcona rozwojowi jądra jest głównym narzędziem komunikacyjnym programistów jądra. Ruch na niej jest ogromny – dochodzi do dziesięciu tysięcy listów tygodniowo. Pozostawanie z wszystkim na bieżąco to zadanie bardzo trudne dla zwykłych śmiertelników. Zack Brown jest jedną z niewielu osób, która uważnie śledzi wszystkie dyskusje.

Aktualnie przeglądasz

Luty 2018 - Nr 168
Feb-2018a

Top 5 czytanych

Znajdź nas na Facebook'u

Opinie naszych czytelników

Nasze wyróżnienia i partnerzy:partnerzy linux magazine
wiper-pixel