Jak jądro Linuksa obsługuje współdzielone przerwania IRQ?


14

Zgodnie z tym, co przeczytałem do tej pory, „gdy jądro odbiera przerwanie, wywoływane są wszystkie zarejestrowane programy obsługi”.

Rozumiem, że zarejestrowane moduły obsługi dla każdego IRQ można przeglądać za pośrednictwem /proc/interrupts, a także rozumiem, że zarejestrowane moduły obsługi pochodzą od sterowników, którzy wywołali request_irqprzekazanie wywołania zwrotnego w przybliżeniu w postaci:

irqreturn_t (*handler)(int, void *)

Na podstawie tego, co wiem, każde z tych wywołań obsługi przerwań związanych z danym przerwaniem IRQ powinno zostać wywołane i to od procedury obsługi zależy, czy przerwanie powinno być przez nią obsługiwane. Jeśli moduł obsługi nie obsługuje określonego przerwania, musi zwrócić makro jądra IRQ_NONE.

Trudno mi zrozumieć, w jaki sposób każdy kierowca powinien ustalić, czy powinien obsłużyć przerwanie, czy nie. Przypuszczam, że mogą śledzić wewnętrznie, jeśli mają spodziewać się przerwy. Jeśli tak, nie wiem, jak poradziliby sobie z sytuacją, w której wielu kierowców stojących za tym samym IRQ spodziewa się przerwania.

Powodem, dla którego próbuję zrozumieć te szczegóły, jest to, że zadzieram z kexecmechanizmem ponownego uruchamiania jądra w trakcie działania systemu podczas gry z pinami resetującymi i różnymi rejestrami na moście PCIe, a także z dalszym PCI urządzenie. Robiąc to, po restarcie albo wpadam w panikę jądra, albo inni kierowcy narzekają, że dostają przerwania, mimo że żadna operacja nie miała miejsca.

Tajemnica polega na tym, jak przewodnik zdecydował, że przerwanie powinno zostać obsłużone.

Edycja: W przypadku, gdy jest to istotne, rozważana jest architektura procesora x86.


1
stackoverflow.com/questions/14371513/for-a-shared-interrupt-line-how-do-i-find-which-interrupt-handler-to-usec
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Odpowiedzi:


14

Jest to opisane w rozdziale 10 w Linux sterowniki urządzeń , 3. wydanie, przez Corbet et al. Jest dostępny za darmo online lub możesz rzucić szeklami sposób O'Reilly na martwe drzewa lub formularze ebook. Część odnosząca się do twojego pytania zaczyna się na stronie 278 w pierwszym łączu.

Dla tego, co warto, oto moja próba sparafrazowania tych trzech stron oraz innych fragmentów, które przejrzałem w Google:

  • Po zarejestrowaniu współużytkowanej procedury obsługi IRQ jądro sprawdza, czy:

    za. dla tego przerwania nie istnieje żaden inny moduł obsługi, lub

    b. wszyscy poprzednio zarejestrowani zażądali również udostępniania przerwania

    Jeśli którykolwiek z przypadków ma zastosowanie, sprawdza, czy dev_idparametr jest unikalny, aby jądro mogło rozróżnić wiele programów obsługi, np. Podczas usuwania programu obsługi.

  • Kiedy urządzenie sprzętowe PCI¹ podnosi linię IRQ, wywoływany jest niskopoziomowy moduł obsługi przerwań jądra, który z kolei wywołuje wszystkie zarejestrowane moduły obsługi przerwań, przekazując każde z powrotem za pośrednictwem, dev_idktórego użyto do zarejestrowania modułu obsługi request_irq().

    dev_idWartość musi być unikalna maszynowego. Typowym sposobem na to jest przekazanie wskaźnika do urządzenia structużywanego przez sterownik do zarządzania tym urządzeniem. Ponieważ ten wskaźnik musi znajdować się w obszarze pamięci sterownika, aby był użyteczny dla sterownika, jest więc ipso faktyczny dla tego sterownika .²

    Jeśli dla danego przerwania zarejestrowanych jest wiele sterowników, wszystkie zostaną wywołane, gdy którekolwiek z urządzeń podniesie tę wspólną linię przerwania. Jeśli nie zrobiło to urządzenie twojego kierowcy, moduł obsługi przerwań twojego sterownika otrzyma dev_idwartość, która do niego nie należy. Osoba obsługująca przerwania kierowcy musi natychmiast powrócić, gdy to nastąpi.

    Innym przypadkiem jest to, że sterownik zarządza wieloma urządzeniami. Program obsługi przerwań sterownika otrzyma jedną z dev_idwartości znanych sterownikowi. Twój kod powinien sondować każde urządzenie, aby dowiedzieć się, które z nich spowodowało przerwanie.

    Przykład Corbet i in. daje to port równoległy komputera. Gdy potwierdza linię przerwania, ustawia także górny bit w swoim pierwszym rejestrze urządzenia. (To znaczy inb(0x378) & 0x80 == trueprzy założeniu standardowej numeracji portów we / wy.) Gdy moduł obsługi wykryje to, powinien wykonać swoją pracę, a następnie wyczyścić przerwanie IRQ, zapisując wartość odczytaną z portu we / wy z powrotem do portu z górną krawędzią nieco wyczyszczone.

    Nie widzę powodu, dla którego ten szczególny mechanizm jest wyjątkowy. Inne urządzenie sprzętowe może wybrać inny mechanizm. Jedyną ważną rzeczą jest to, że aby urządzenie zezwoliło na wspólne przerwania, musi mieć jakiś sposób, aby sterownik mógł odczytać status przerwania urządzenia, i jakiś sposób na usunięcie przerwania. Musisz przeczytać arkusz danych urządzenia lub instrukcję programowania, aby dowiedzieć się, z jakiego mechanizmu korzysta twoje urządzenie.

  • Kiedy twój program obsługi przerwań informuje jądro, że obsłużył przerwanie, nie powstrzymuje to jądra od dalszego wywoływania innych programów obsługi zarejestrowanych dla tego samego przerwania. Jest to nieuniknione, jeśli użytkownik ma współdzielić linię przerwania podczas korzystania z przerwań wyzwalanych poziomem.

    Wyobraź sobie, że dwa urządzenia jednocześnie zapewniają tę samą linię przerwania. (Lub przynajmniej tak blisko, że jądro nie ma czasu na wywołanie procedury obsługi przerwań w celu wyczyszczenia linii, a tym samym postrzegania drugiego potwierdzenia jako osobnego.) Jądro musi wywoływać wszystkie procedury obsługi tej linii przerwań, aby dać każdemu szansę na sprawdzenie powiązanego sprzętu, aby sprawdzić, czy wymaga uwagi. Jest całkiem możliwe, że dwa różne sterowniki z powodzeniem obsługują przerwanie w ramach tego samego przejścia przez listę modułów obsługi dla danego przerwania.

    Z tego powodu konieczne jest, aby sterownik poinformował urządzenie, że zarządza, aby wyczyścić swoje potwierdzenie przerwania na jakiś czas przed powrotem procedury obsługi przerwania. Nie jest dla mnie jasne, co stanie się inaczej. Ciągle potwierdzana linia przerwań albo spowoduje, że jądro będzie ciągle wywoływać wspólne procedury obsługi przerwań, albo zamaskuje zdolność jądra do zobaczenia nowych przerwań, tak że programy obsługi nigdy nie zostaną wywołane. Tak czy inaczej, katastrofa.


Przypisy:

  1. Podałem powyżej PCI, ponieważ wszystkie powyższe zakładają przerwania wyzwalane poziomem , tak jak w oryginalnej specyfikacji PCI. ISA zastosowało przerwania wyzwalane zboczem , co w najlepszym wypadku utrudniało dzielenie się i było możliwe nawet wtedy, gdy było obsługiwane przez sprzęt. PCIe wykorzystuje przerwania sygnalizowane komunikatem ; komunikat przerwania zawiera unikalną wartość, której jądro może użyć, aby uniknąć gry polegającej na zgadywaniu w trybie round-robin wymaganej przy udostępnianiu przerwań PCI. PCIe może wyeliminować potrzebę dzielenia przerwań. (Nie wiem, czy tak się dzieje, tylko że ma potencjał).

  2. Wszystkie sterowniki jądra Linuksa współużytkują to samo miejsce w pamięci, ale niezwiązany sterownik nie powinien chować się w przestrzeni pamięci innej osoby. Jeśli nie ominiesz tego wskaźnika, możesz być całkiem pewien, że inny kierowca sam nie wymyśli tej samej wartości.


1
Jak już wspomniałeś, moduł obsługi przerwań może być przekazywany jako dev_idtaki, którego nie jest właścicielem. Wydaje mi się, że istnieje niezerowa szansa, że ​​sterownik, który nie jest właścicielem dev_idstruktury, może nadal pomylić ją z własną, w zależności od tego, jak interpretuje zawartość. Jeśli tak nie jest, to jaki mechanizm temu zapobiegałby?
bsirang

Można temu zapobiec, tworząc dev_idwskaźnik do czegoś w przestrzeni pamięci kierowcy. Kolejny kierowca mógł uzupełnić dev_idwartość, stało się zbliżonej do wskaźnika do pamięci sterownik jest właścicielem, ale to nie zdarzy, bo każdy bawi się zgodnie z zasadami. To jest przestrzeń jądra, pamiętaj: samodyscyplina jest zakładana oczywiście, w przeciwieństwie do kodu przestrzeni użytkownika, który może bezczelnie zakładać, że wszystko, co zabronione, jest dozwolone.
Warren Young,

Zgodnie z rozdziałem dziesiątym LDD3: „Ilekroć dwa lub więcej sterowników współużytkuje linię przerwania, a sprzęt przerywa procesor na tej linii, jądro wywołuje każdą procedurę obsługi zarejestrowaną dla tego przerwania, przekazując każdemu swojemu dev_id” Wygląda na to, że poprzednie zrozumienie było niepoprawne w odniesieniu do tego, czy moduł obsługi przerwań może zostać przekazany w pliku dev_id, którego nie jest właścicielem.
bsirang

To był mój błąd. Kiedy to napisałem, połączyłem dwie koncepcje. Zredagowałem swoją odpowiedź. Warunkiem, który wymaga szybkiego powrotu programu obsługi przerwań, jest wywołanie go z powodu potwierdzenia przerwania przez urządzenie, którym nie zarządza. Wartość dev_idnie pomaga ustalić, czy tak się stało. Musisz zapytać sprzęt: „Zadzwoniłeś?”
Warren Young,

Tak, teraz muszę dowiedzieć się, w jaki sposób majstruję, co powoduje, że inni kierowcy uważają, że ich urządzenia „zadzwoniły” po ponownym uruchomieniu jądra za pośrednictwem kexec.
bsirang

4

Gdy sterownik żąda współdzielonego IRQ, przekazuje wskaźnik do jądra do odwołania do struktury specyficznej dla urządzenia w przestrzeni pamięci sterownika.

Według LDD3:

Ilekroć dwa lub więcej sterowników współużytkuje linię przerwania, a sprzęt przerywa procesor na tej linii, jądro wywołuje każdą procedurę obsługi zarejestrowaną dla tego przerwania, przekazując każdemu swojemu dev_id.

Po sprawdzeniu procedur obsługi IRQ kilku sterowników wydaje się, że sondują one sam sprzęt w celu ustalenia, czy powinien on obsłużyć przerwanie lub powrót IRQ_NONE.

Przykłady

Sterownik UHCI-HCD
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

W powyższym kodzie kierowca czyta USBSTSrejestr, aby ustalić, czy nastąpiło przerwanie usługi.

Sterownik SDHCI
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

  if (!intmask || intmask == 0xffffffff) {
    result = IRQ_NONE;
    goto out;
  }

Podobnie jak w poprzednim przykładzie, sterownik sprawdza rejestr statusu, SDHCI_INT_STATUSaby ustalić, czy musi obsłużyć przerwanie.

Sterownik Ath5k
  struct ath5k_softc *sc = dev_id;
  struct ath5k_hw *ah = sc->ah;
  enum ath5k_int status;
  unsigned int counter = 1000;

  if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
        !ath5k_hw_is_intr_pending(ah)))
    return IRQ_NONE;

Jeszcze tylko jeden przykład.


0

Proszę sprawdzić sprawdź ten link :

Zwykłą praktyką jest wyzwalanie dolnych połówek lub innej logiki w module obsługi IRQ tylko po sprawdzeniu statusu IRQ z rejestru odwzorowanego w pamięci. Dlatego problem jest domyślnie rozwiązany przez dobrego programistę.


Treść linku jest niedostępna
użytkownik3405291
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.