Nach dem, was ich bisher gelesen habe: "Wenn der Kernel einen Interrupt empfängt, werden alle registrierten Handler aufgerufen."
Ich verstehe, dass die registrierten Handler für jeden IRQ über angezeigt werden können /proc/interrupts
, und ich verstehe auch, dass die registrierten Handler von den Treibern stammen, die die request_irq
Übergabe eines Rückrufs in etwa in der Form aufgerufen haben :
irqreturn_t (*handler)(int, void *)
Basierend auf dem, was ich weiß, sollte jeder dieser Interrupt-Handler-Rückrufe, die dem bestimmten IRQ zugeordnet sind, aufgerufen werden, und es ist Sache des Handlers, zu bestimmen, ob der Interrupt tatsächlich von ihm behandelt werden soll. Wenn der Handler den bestimmten Interrupt nicht verarbeiten soll, muss er das Kernelmakro zurückgeben IRQ_NONE
.
Ich habe Probleme zu verstehen, wie jeder Treiber bestimmen soll, ob er den Interrupt behandeln soll oder nicht. Ich nehme an, sie können intern den Überblick behalten, wenn sie einen Interrupt erwarten sollen. Wenn ja, weiß ich nicht, wie sie mit der Situation umgehen können, in der mehrere Treiber hinter demselben IRQ einen Interrupt erwarten.
Der Grund, warum ich versuche, diese Details zu verstehen, ist, dass ich mit dem kexec
Mechanismus herumspiele, um den Kernel mitten im Systembetrieb erneut auszuführen, während ich mit den Reset-Pins und verschiedenen Registern auf einer PCIe-Brücke sowie einer nachgeschalteten PCI spiele Gerät. Und dabei bekomme ich nach einem Neustart entweder Kernel-Panics oder andere Treiber, die sich beschweren, dass sie Interrupts empfangen, obwohl keine Operation stattgefunden hat.
Wie der Handler entschieden hat, dass der Interrupt von ihm behandelt werden soll, ist das Rätsel.
Bearbeiten: Falls relevant, handelt es sich um die betreffende CPU-Architektur x86
.
Antworten:
Dies wird in Kapitel 10 von Linux Device Drivers , 3. Auflage, von Corbet et al. Es ist kostenlos online verfügbar , oder Sie werfen einige Schekel O'Reillys Weg für tote Baum- oder E-Book-Formulare. Der für Ihre Frage relevante Teil beginnt auf Seite 278 im ersten Link.
Für das, was es wert ist, ist hier mein Versuch, diese drei Seiten und andere Teile, die ich gegoogelt habe, zu paraphrasieren:
Wenn Sie einen gemeinsam genutzten IRQ-Handler registrieren, überprüft der Kernel Folgendes:
ein. Für diesen Interrupt existiert kein anderer Handler
b. Alle zuvor registrierten Benutzer forderten auch die gemeinsame Nutzung von Interrupts an
In beiden Fällen wird überprüft, ob Ihr
dev_id
Parameter eindeutig ist, damit der Kernel die mehreren Handler unterscheiden kann, z. B. beim Entfernen von Handlern.Wenn ein PCI¹-Hardwaregerät die IRQ-Leitung auslöst, wird der Low-Level-Interrupt-Handler des Kernels aufgerufen, der wiederum alle registrierten Interrupt-Handler aufruft und die zurückgibt, über die
dev_id
Sie den Handler registriert habenrequest_irq()
.Der
dev_id
Wert muss für die Maschine eindeutig sein. Die übliche Vorgehensweise besteht darin, einen Zeiger an das Gerät zu übergeben, mitstruct
dem der Treiber dieses Gerät verwaltet. Da sich dieser Zeiger im Speicher Ihres Fahrers befinden muss, damit er für den Fahrer nützlich ist, ist er ipso facto für diesen Fahrer einzigartigWenn mehrere Treiber registriert für einen bestimmten Interrupt sind, werden sie alle aufgerufen werden , wenn jede der Geräte Raises , die Interrupt - Leitung geteilt. Wenn dies nicht das Gerät Ihres Fahrers war, wird dem Interrupt-Handler Ihres Fahrers ein
dev_id
Wert übergeben, der nicht dazu gehört. In diesem Fall muss der Interrupt-Handler Ihres Fahrers unverzüglich zurückkehren.Ein weiterer Fall ist, dass Ihr Treiber mehrere Geräte verwaltet. Der Interrupt-Handler
dev_id
des Fahrers erhält einen dem Fahrer bekannten Wert. Ihr Code soll jedes Gerät abfragen, um herauszufinden, welches den Interrupt ausgelöst hat.Das Beispiel Corbet et al. gib das von einem PC parallel port. Wenn es die Interruptleitung aktiviert, setzt es auch das oberste Bit in seinem ersten Geräteregister. (Unter der
inb(0x378) & 0x80 == true
Annahme einer Standard-E / A-Port-Nummerierung.) Wenn Ihr Handler dies erkennt, sollte er seine Arbeit erledigen. Löschen Sie dann den IRQ, indem Sie den vom E / A-Port gelesenen Wert an den Port mit der Spitze zurückschreiben ein bisschen gelöscht.Ich sehe keinen Grund dafür, dass ein bestimmter Mechanismus etwas Besonderes ist. Ein anderes Hardwaregerät könnte einen anderen Mechanismus wählen. Das einzig Wichtige ist, dass ein Gerät, das gemeinsame Interrupts zulässt, eine Möglichkeit haben muss, den Interrupt-Status des Geräts auszulesen und den Interrupt auf irgendeine Weise zu löschen . Sie müssen das Datenblatt oder Programmierhandbuch Ihres Geräts lesen, um herauszufinden, welchen Mechanismus Ihr bestimmtes Gerät verwendet.
Wenn Ihr Interrupt-Handler dem Kernel mitteilt, dass er den Interrupt verarbeitet hat, wird der Kernel nicht daran gehindert, weitere für denselben Interrupt registrierte Handler aufzurufen. Dies ist unvermeidlich, wenn Sie eine Interrupt-Leitung gemeinsam nutzen, wenn Sie pegelgetriggerte Interrupts verwenden.
Stellen Sie sich zwei Geräte vor, die zur gleichen Zeit dieselbe Interrupt-Leitung aktivieren. (Oder zumindest so zeitnah, dass der Kernel keine Zeit hat, einen Interrupt-Handler aufzurufen, um die Leitung zu löschen, und dadurch die zweite Behauptung als getrennt ansieht.) Der Kernel muss alle Handler für diese Interrupt-Leitung aufrufen, um jede zu geben eine Möglichkeit, die zugehörige Hardware abzufragen, um festzustellen, ob Aufmerksamkeit erforderlich ist. Es ist durchaus möglich, dass zwei verschiedene Treiber einen Interrupt innerhalb desselben Durchlaufs durch die Handlerliste für einen bestimmten Interrupt erfolgreich verarbeiten.
Aus diesem Grund muss Ihr Treiber dem Gerät, das es verwaltet, unbedingt mitteilen, dass die Interrupt-Bestätigung gelöscht werden soll, bevor der Interrupt-Handler zurückkehrt. Mir ist nicht klar, was sonst passiert. Die ständig aktivierte Interrupt-Zeile führt entweder dazu, dass der Kernel die gemeinsam genutzten Interrupt-Handler ständig aufruft, oder sie maskiert die Fähigkeit des Kernels, neue Interrupts zu sehen, so dass die Handler niemals aufgerufen werden. So oder so, Katastrophe.
Fußnoten:
Ich habe PCI oben angegeben, da alle oben genannten Punkte pegelgetriggerte Interrupts voraussetzen , wie sie in der ursprünglichen PCI-Spezifikation verwendet wurden. ISA verwendete flankengetriggerte Interrupts, die das Teilen im besten Fall schwierig machten und selbst dann möglich waren, wenn sie von der Hardware unterstützt wurden. PCIe verwendet nachrichtengesteuerte Interrupts. Die Interrupt-Nachricht enthält einen eindeutigen Wert, den der Kernel verwenden kann, um das Round-Robin-Ratespiel zu vermeiden, das für die gemeinsame Nutzung von PCI-Interrupts erforderlich ist. Durch PCIe kann die Notwendigkeit einer Unterbrechungsfreigabe entfallen. (Ich weiß nicht, ob es tatsächlich funktioniert, nur dass es das Potenzial dazu hat.)
Linux-Kerneltreiber teilen sich alle den gleichen Speicherplatz, aber ein nicht verwandter Treiber sollte nicht im Speicherplatz eines anderen herumspielen. Wenn Sie diesen Zeiger nicht herumreichen, können Sie ziemlich sicher sein, dass ein anderer Treiber diesen Wert nicht versehentlich von sich aus findet.
quelle
dev_id
, als er besitzt. Meiner Meinung nach besteht die Möglichkeit, dass ein Treiber, der diedev_id
Struktur nicht besitzt , sie immer noch als seine eigene verwechselt, je nachdem, wie sie den Inhalt interpretiert. Wenn dies nicht der Fall ist, welcher Mechanismus würde dies verhindern?dev_id
einen Zeiger auf etwas im Speicher Ihres Fahrers setzen. Ein anderer Treiber könnte einendev_id
Wert ausmachen , der zufällig mit einem Zeiger auf den Speicher Ihres Treibers verwechselt werden kann, aber das wird nicht passieren, weil jeder nach den Regeln spielt. Dies ist Kernel-Space, denken Sie daran: Selbstdisziplin wird selbstverständlich vorausgesetzt, im Gegensatz zu User-Space-Code, der leichtfertig davon ausgehen kann, dass alles erlaubt ist, was nicht verboten ist.dev_id
, der ihm nicht gehört.dev_id
hilft Ihnen nicht festzustellen, ob dies geschehen ist. Sie müssen die Hardware fragen: "Sie haben angerufen?"kexec
.Wenn ein Treiber einen gemeinsam genutzten IRQ anfordert, übergibt er einen Zeiger an den Kernel, der auf eine gerätespezifische Struktur im Speicherbereich des Treibers verweist.
Nach LDD3:
Bei der Überprüfung der IRQ-Handler mehrerer Treiber scheint es, dass sie die Hardware selbst prüfen, um zu bestimmen, ob sie den Interrupt oder die Rückgabe behandeln soll oder nicht
IRQ_NONE
.Beispiele
UHCI-HCD-TreiberIm obigen Code liest der Fahrer das
SDHCI-TreiberUSBSTS
Register, um festzustellen, ob eine Unterbrechung für die Wartung vorliegt .Wie im vorherigen Beispiel überprüft der Treiber ein Statusregister,
Ath5k-TreiberSDHCI_INT_STATUS
um festzustellen, ob ein Interrupt bedient werden muss.Nur noch ein Beispiel.
quelle
Bitte besuchen Sie diesen Link :
quelle