Autor Thema: EOI, wann senden?  (Gelesen 11028 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« am: 07. October 2011, 16:59 »
Ich arbeite gerade an meinem Irq Code und da stellt sich mir mal wieder die Frage, wann sendet man einen EOI?

Wenn man alle Handler angetriggert hat oder wenn ein Handler bestätigt hat, dass es sein IRQ war? (Ich weiß das meistens nur die letzte Variante bei den meisten Systemen implementiert ist)

Interessant wäre noch zu wissen was für Vor- und Nachteile beide Varianten haben.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 07. October 2011, 17:21 »
Dann, wenn man will, dass der Interruptcontroller den nächster IRQ meldet.

In der Regel ist das entweder dann, wenn der Gerätetreiber den IRQ fertig behandelt hat (funktioniert eigentlich nur bei einem Monolithen, nehme ich an), oder aber nachdem man den jeweiligen Interrupt maskiert hat, um das eine Gerät erstmal ruhigzustellen, bis der Treiber fertig ist.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 07. October 2011, 17:31 »
Zitat von: taljeth
In der Regel ist das entweder dann, wenn der Gerätetreiber den IRQ fertig behandelt hat (funktioniert eigentlich nur bei einem Monolithen, nehme ich an)
Wieso denkst du dass das nur in einem Monolithen funktioniert, weil die Zeit für das Fertigstellen bei einem MikroKernel länger dauert?

Zitat von: taljeth
oder aber nachdem man den jeweiligen Interrupt maskiert hat, um das eine Gerät erstmal ruhigzustellen, bis der Treiber fertig ist.
Auch ne Möglichkeit, damit würde ich mir sogar ein wenig Code im IO-Apic Bereich ersparen (weil ich da ja davon ausgehen könnte, das kurz nacheinander auf verschiedenen CPUs der gleiche IRQ getriggert wurde).

Aber wo sind da die Vor- und Nachteile?

Bei beiden Varianten gibt es das Problem (ist es überhaupt eins oder geht es sogar gar nicht anders?), dass man immer warten muss bis ein Gerät fertig ist (und somit nur hoffen kann, das der IRQ schnell behandelt wird) und der nächste IRQ (was ja auch ein anderes Gerät sein könnte) behandelt werden kann.

Edit::

Ich glaube ich habe da nen Denkfehler drin. Wird der EOI nicht gesendet, lässt das dann effektiv auch andere IRQs auf der CPU nicht mehr zu? Weil wenn ja, dann muss ich es eh so machen, dass ich den Interrupt maskiere und dann den EOI sende.
« Letzte Änderung: 07. October 2011, 17:33 von FlashBurn »

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 07. October 2011, 20:24 »
Bei Mikrokerneln wandelt man einen IRQ eher in ein Event und schickt dieses Event den möglicherweise betroffenen Treibern. Die Feststellung, wann ein Interrupt erfolgreich behandelt ist, kommt dann also asynchron. Im Monolithen ist der Zeitpunkt klar, an dem man das EOI schicken kann.

Kann man ein EOI eigentlich auch gefahrlos für längere Zeit verzögern? Dann erspart man sich eigentlich doch das maskieren?

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 07. October 2011, 20:33 »
Zitat von: svenska
Kann man ein EOI eigentlich auch gefahrlos für längere Zeit verzögern? Dann erspart man sich eigentlich doch das maskieren?
Das geht so weit ich weiß nur bei level-triggered IRQs (also PCI ohne MSI), edge-triggered IRQs interessiert das wohl eher weniger ;)

Das ist auch gerade mein Problem, wo ich nicht so ganz durchsteige.

Nehmen wir an, ich bekomme einen IRQ (auf nem PIC-System) und maskiere den IRQ, sende halt die Events, sende dann den EOI. Kann es bis zu dem Zeitpunkt des Maskierens passieren das der selbe IRQ nochmal auftritt und dann nach dem IRET ausgeführt wird?

Andere Szenario, ich bekomme einen IRQ (auf nem IO-APIC SMP System) und maskiere den IRQ, sende halt die Events, sende dann den EOI. Kann es bis zu dem Zeitpunkt des Maskierens theoretisch auf einer anderen CPU zu dem selben IRQ kommen?

Allerdings war mir so, als wenn edge-triggered IRQs sowieso nicht shared waren oder? Damit hätte sich das Problem dann ja eh erledigt.

Ich würde den IRQ dann solange maskiert lassen, bis ein Treiber IRQ_HANDLED sendet (bzw. den Syscall macht) und dann wird der entsprechende IRQ wieder demaskiert.
Werden die IRQs dann irgendwie gequeuet oder gehen die verloren (während der IRQ maskiert ist)?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 07. October 2011, 21:39 »
Hallo,


mal wieder ein interessantes Thema, sehr schön. ;)


Edge-Trigered-IRQs können nicht geshared werden, das ist ja gerade der Vorteil von MSI(-X), dafür können die theoretisch wenn gerade maskiert ist auch tatsächlich verloren gehen, falls der IRQ-Controller da nicht was gegen vorsieht (was man üblicherweise schon macht).

Beim normalen PIC sinds ja nur Level-Triggered-IRQs, welche sich simpel sharen lassen. Wenn der PIC an einem seiner Eingänge einen aktiven Pegel feststellt und der betreffende IRQ nicht maskiert ist dann wird ein IRQ-Signal an die CPU gesendet, das kann bei x86 immer nur eine CPU treffen muss aber nicht zwangsläufig der AP sein. Ob dieses Routing für jeden der IRQs individuell konfigurierbar ist weiß ich jetzt nicht aber das sollte doch die APIC-Dokumentation verraten. Der Grund warum man beim PIC und APIC möglichst zügig ein EOI senden sollte ist der weil man so lange überhaupt keine IRQs mehr gemeldet bekommt. In einem Micro-Kernel ist es IMHO am effizientesten bei einem IRQ diesen zu maskieren, an alle registrierten Treiber einen asynchronen Event senden um dann so schnell wie möglich den EOI abzusetzen damit der IRQ-Handler im Kernel möglichst schnell sein IRET ausführen kann. Demaskieren kann man den betreffenden IRQ erst wieder wenn alle angetriggerten Treiber "IRQ-Erledigt" signalisieren (am besten per dediziertem Syscall). Ein Level-Triggered-IRQ bleibt vom Device üblicherweise so lange auf aktiven Pegel (und würde vom PIC/APIC auch so lange wieder an die CPU gemeldet werden wie er nicht maskiert ist und kein EOI-pending anliegt) bis die Ursache im Device behandelt wurde, deswegen muss der IRQ auch so lange maskiert bleiben bis alle betreffenden Treiber "IRQ-Erledigt" signalisieren. Wenn einer der Treiber feststellt das sein Device momentan keinen IRQ signalisiert und demzufolge keine Behandlung benötigt kann es trotzdem sein das nachdem der betreffende Event-Handler "IRQ-Erledigt" meldet und sich beendet hat in der Zeit wo andere Treiber noch Arbeiten erledigen und der IRQ daher noch maskiert bleibt dieses Device dann doch einen IRQ signalisiert, dieser IRQ würde dann vorerst unbemerkt bleiben da im PIC/APIC ja noch maskiert ist. Erst wenn der IRQ wieder demaskiert wird können diese später signalisierenden Devices behandelt werden, das verursacht u.U. recht lange Latenzen, weswegen üblicherweise empfohlen wird auch den First-Level-IRQ-Handler möglichst kurz zu halten (damit alle Treiber möglichst schnell ein "IRQ-Erledigt" signalisieren) und langwierige Arbeiten in einen Second-Level-IRQ-Handler (unter Windows die DPCs, unter Linux heißt das anders ist aber im wesentlichen das selbe Konzept) zu verlagern. All diese Aspekte von Level-Triggered- und/oder Shared-IRQs sind recht unabhängig von Micro-Kernel oder Monolith, nur die Umsetzungsdetails unterscheiden sich eben etwas. Bei einem Monolithen sind die First-Level-IRQ-Handler üblicherweise Funktionen die direkt vom generischen IRQ-Handler des Kernels aufgerufen werden (also noch im echten IRQ-Kontext laufen) und die Second-Level-IRQ-Handler sind dann Kernel-Threads die in einem eigenen Kontext laufen. Bei einem Micro-Kernel sind die First-Level-IRQ-Handler bereits User-Mode-Event-Handler und die Second-Level-IRQ-Handler ganz normale User-Mode-Threads (welche beide im Kontext des Treiber-Prozesses laufen). In den meisten kleinen OSen verzichtet man aber der Einfachheit halber auf eine Trennung in First-Level und Second-Level was zwar Komplexität/Code spart aber auch die Effizienz erheblich reduziert (aufgrund längerer Latenzen usw.).

Edge-Triggered-IRQs sind da völlig anders, da ein Device für jedes Ereignis normalerweise nur einmal triggert lohnt es sich unter bestimmten Voraussetzungen nicht ein mal zu maskieren. Während man beim PIC/APIC bei jedem IRQ mindestens 3 mal (teuer) auf den PIC/APIC zugreifen muss (Maskieren, EOI und Demaskieren) kann man bei Edge-Triggered-IRQs bei passendem Design durchaus ganz ohne Zugriffe auf den IRQ-Controller auskommen (auf meiner Plattform möchte ich das so umsetzen, bei x86 ist das leider nicht so einfach möglich da ja selbst MSI(-X) über einen APIC läuft). Auch ist eine Trennung in First-Level und Second-Level völlig überflüssig da ja nichts geshared wird und sich die IRQs damit auch nicht gegenseitig blockieren können, der einzigste Engpass ist das Ausliefern der IRQs vom IRQ-Controller an die CPUs aber das ist in Relation noch ein recht schneller Vorgang. Wie man richtig effizientes IRQ-Handling am tollsten umsetzt brauch ich Euch aber wohl nicht weiter vorschwärmen da ihr eh alle bei(m) x86(-Dinosaurier) bleiben wollt.


Unabhängig von Level-Triggered und Edge-Trigered sollte man aber noch Mechanismen vorsehen die die IRQ-Frequenz auf ein erträgliches Level reduzieren damit das System nicht von einem einzelnen Device bis zum Stillstand beansprucht werden kann. Ein Gigabit-Ethernet-Controller kann bis zu gut 80'000 Ethernet-Frames mit je 1500 Bytes pro Sekunde empfangen (bei Ethernet-Frames mit der Minimalgröße von 64 Bytes sind es fast 2 Millionen Stück pro Sekunde), wenn der für jedes empfangenen Ethernet-Frame einen IRQ generieren würde könnte man damit selbst eine moderne Multi-GHz-CPU völlig auslasten, das selbe trifft auch aufs Senden zu. Leider bietet die x86-Plattform hierfür keine generischen Mechanismen so das man auf die Tricks der einzelnen Devices angewiesen ist die der jeweilige Treiber natürlich auch benutzen muss damit die auch was wirken. Hier ist bei x86 also ganz klar die Software gefragt, bei gesharten IRQs kann man aber auch nicht einfach irgendetwas im generischen Kernel vorsehen da sonst IRQs an denen mehr Devices hängen schneller in eine Begrenzung laufen als IRQs an denen nur wenige Devices hängen.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 07. October 2011, 22:01 »
Boah, kannst du das nochmal irgendwie zusammenfassen ;) Ich habe den Beitrag jetzt 3x gelesen und habe das Gefühl das wesentliche immer noch nicht erfasst zu haben.

Um es kurz zu machen, edge-triggered IRQs können nicht geshared werden, aber ist es möglich das 2 davon auf unterschiedlichen CPUs ankommen bevor auf der ersten CPU der IRQ maskiert wurde? Weil dann müsste ich mich um "Multi-Threading" im IRQ Handler kümmern, ansonsten nicht.
Level-triggered IRQs können geshared werden, aber es wird kein neuer IRQ ausgelöst, bevor der alte nicht per EOI beendet wurde, somit kann man den IRQ wunderbar maskieren und bekommt keinen neuen.

Man würde also nen IRQ bekommen, am besten sofort maskieren, die Events versenden und den EOI senden!?

Ich habe im Moment noch das Problem, dass das Senden der Events ganz schön lange dauern könnte bei level-triggered IRQs. Denn das könnten ja mehrere Geräte sein und dann müsste, bei meinem Design, pro Event ein Thread erstellt werden (was ja nicht gerade billig ist). Ich würde da dann versuchen das ganze zu optimieren, in dem ich entweder bei Treibern, den Thread für die Events nicht zerstöre oder ich sagen würde, das alle Threads die gekillt werden, erstmal als "Zombie"-Threads im Task bleiben, damit bei neuen Threads weniger Arbeit nötig ist. Das wiederrum funktioniert nur gut, fürs Messaging, wo immer die gleiche Stack-Größe benötigt wird. Denn das ist das eigentlich zeitaufwändige, einen Bereich im Adress-Space für den Stack zu finden, der groß genug ist.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 08. October 2011, 00:12 »
Hallo,

edge-triggered ist Mist. Ein EOI kannst du dir damit Interrupts auch nicht sparen, weil sonst ein Gerät die IRQs schneller feuern kann als die CPU mit der Bearbeitung hinterherkommt; bei dynamischem IRQ-Routing (pro IRQ) gibt das Ärger, wenn dein Kernel die IRQs dann nicht synchronisiert.

Pegelgesteuert ist grundsätzlich besser und wenn man da dedizierte Leitungen nutzt, kann man die auch wie flankengesteuert betrachten.

Für jeden einzelnen Interrupt einen Thread zu erzeugen ist für meine Begriffe etwas unhandlich, weil ein kurzzeitiger IRQ-Sturm direkt zu einem OOM führen kann. Daher würde ich pro Gerätetreiber maximal einen ausstehenden IRQ-Handler zulassen. Queuen ist auch nicht besonders elegant, weil (je nach HW) der erste IRQ-Handler die Ursache für den zweiten IRQ beheben kann und der zweite Handler dann ins Leere greift. Einen IRQ-Handler-Thread pro Treiber und IRQ zu haben, finde ich besser.

Treiber können IRQ-Stürme auch erkennen. Wenn z.B. in 5 Sekunden 1000 IRQs gekommen sind, wird auf Polling umgestellt, bis die IRQs wieder seltener werden. Das ist dann aber zwingend HW-spezifisch.

Man würde also nen IRQ bekommen, am besten sofort maskieren, die Events versenden und den EOI senden!?
Zwischen "IRQ bekommen" und "EOI senden" ist der Interrupt immer maskiert. Also brauchst du dir keine Sorgen machen, dass der gleiche IRQ auf einer anderen CPU auftaucht. :-)

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 08. October 2011, 09:43 »
Zitat von: svenska
Für jeden einzelnen Interrupt einen Thread zu erzeugen ist für meine Begriffe etwas unhandlich, weil ein kurzzeitiger IRQ-Sturm direkt zu einem OOM führen kann. Daher würde ich pro Gerätetreiber maximal einen ausstehenden IRQ-Handler zulassen. Queuen ist auch nicht besonders elegant, weil (je nach HW) der erste IRQ-Handler die Ursache für den zweiten IRQ beheben kann und der zweite Handler dann ins Leere greift. Einen IRQ-Handler-Thread pro Treiber und IRQ zu haben, finde ich besser.
Da habe ich mich dann missverständlich ausgedrückt. Ich will auch nur einen Thread pro IRQ und die Events werden dann gequeuet und dieser eine Thread arbeitet die dann ab. Nur dadurch das ich die Events übers IPC System schicke, wird jedes Mal (wenn nicht schon ein Thread vorhanden ist) ein neuer Thread erstellt.

Zitat von: svenska
Zwischen "IRQ bekommen" und "EOI senden" ist der Interrupt immer maskiert. Also brauchst du dir keine Sorgen machen, dass der gleiche IRQ auf einer anderen CPU auftaucht.
Ist das wirklich so? Da ich es so verstanden hatte, das edge-triggered IRQs nicht auf ein EOI warten.

Bsp. wären MSIs die ja edge-triggered sind und die werden bei mir dann per lowest-priority versendet und so kann es passieren, das auf CPU A IRQ 1 ankommt und während dessen das Event versendet wird, kommt IRQ 2 auf CPU B an (weil dessen Priorität jetzt kleiner ist) und der selbe Handler wird nochmal ausgeführt. Der IRQ ist ja nur im lokalen APIC maskiert und nicht direkt im Gerät oder?

Bisher hatte ich das abgefangen, indem ich einen per IRQ Counter hatte der den Startwert -1 hat. Bei jedem IRQ Eintritt wird er um 1 erhöht und wenn er ungleich 0 ist, wird direkt wieder aus dem IRQ Handler gesprungen (da bereits ein IRQ Handler das Event versendet). Der IRQ Handler der bereits läuft, dekrementiert nach dem senden des Events und des EOIs diesen per IRQ Counter und wenn das Ergebnis nicht negativ ist, wird nochmal ein Event versendet.
Dadurch serialisiere ich den ganzen Spaß und emuliere das "Focus-Processor" Feature älterer CPUs.

Die Frage ist aber, ob das wirklich nötig ist und soweit ich es halt verstanden habe, ist es das, aber die ganze Sache ist nicht so einfach.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 08. October 2011, 11:32 »
Hallo,


Boah, kannst du das nochmal irgendwie zusammenfassen ;)
Äh, nö! Wenn ich das noch weiter verdichten würde fürchte ich das die Kompression dann verlustbehaftet ist und das wäre wohl auch nicht zielführend. Ließ noch mal und wenn Du etwas nicht verstanden hast dann stell eine möglichst konkrete Frage. ;)


Ansonsten habe ich den (subjektiven) Eindruck dass das Wesen eines IRQ hier noch nicht in seinem ganzen Fassettenreichtum verstanden wurde.

Vielleicht sollte ich noch mal von vorne Anfangen:

Zuerst gibt es in einem HW-Gerät ein Ereignis das die Aufmerksamkeit der Software (also des Treibers) erfordert. In eigentlich allen Geräten gibt es bereits da ein Maskenregister mit dem der Treiber auswählen kann um welche Ereignisse er sich überhaupt kümmern möchte.

Das HW-Gerät meldet dieses Ereignis per IRQ-Signal an den IRQ-Controller, nur auf diesem Weg unterschiedet man zwischen Level-Triggered und Edge-Triggered. Auch kann ein HW-Gerät mehrere (virtuelle) IRQ-Leitungen haben um z.B. verschiedene Ereignisse an unterschiedliche Handler (eventuell mit unterschiedlichen Prioritäten) im Treiber signalisieren zu können.

Im IRQ-Controller werden die ankommenden IRQs gesammelt und nach Priorität sortiert, der aktive IRQ mit der höchsten Priorität wird dann an eine CPU gemeldet, welche CPU das trifft ist erstmal egal, das kann fest konfiguriert sein oder auch dynamisch ermittelt werden, aber es trifft immer nur eine CPU. Solange der IRQ-Controller mit dem Ausliefern eines IRQs beschäftigt ist wird von diesem IRQ-Controller kein weiterer IRQ an eine CPU ausgeliefert (weder an die selbe noch an eine andere CPU), aber es können natürlich weitere IRQs von den anderen HW-Geräten gemeldet werden die der IRQ-Controller intern speichert und dann als nächstes an eine CPU ausliefert. Der IRQ-Controller kann als eigenständiger Chip auf dem Mainboard residieren (so wie beim Ur-x86) oder auch als integraler Bestandteil der CPU existieren (so wie bei den meisten SoCs) aber er bleibt logisch eine eigenständige Funktionseinheit. Es können in einem System auch mehrere unabhängige IRQ-Controller existieren (so wie die vielen APICs in modernen x86-Computern) aber jeder HW-Geräte-IRQ hat einen individuellen klar festgelegten Weg, sollte das OS da irgendetwas nicht sauber konfigurieren wird es zu sehr merkwürdigen Phänomenen kommen.

Wie der IRQ-Controller die IRQs an die CPU weiterleitet ist sehr unterschiedlich aber die oben genannten Eigenschaften treffen IMHO auf alle Implementierungen zu. Meistens signalisiert der IRQ-Controller mit einer einzelnen Leitung der CPU das ein HW-IRQ anliegt, diese unterbricht dann an der nächsten geeigneten Stelle (also zwischen zwei Befehlen) ihre Arbeit (falls IRQs nicht gerade abgeschaltet sind) und fragt den IRQ-Controller welcher IRQ anliegt, der IRQ-Controller antwortet entsprechend und die CPU startet dann den zugehörigen IRQ-Handler. Bis hier hin ist dieser Vorgang komplett in Hardware realisiert, wenn der erste Befehl des IRQ-Handlers abgearbeitet wird weiß die CPU bereits welcher IRQ das ist (bei x86 also welchen der 256 Interrupts sie anstoßen muss). Nachdem der IRQ-Controller den IRQ an die CPU ausgeliefert hat bleibt er in einem inaktiven Zustand, in welchem er zwar neue IRQs von den HW-Geräten annimmt und speichert aber keine weiteren IRQs an eine CPU (weder an die selbe noch an eine andere CPU) ausliefert, bis er das EOI bekommt. In der Zeit ist der IRQ-Controller natürlich für die CPU ansprechbar so das der IRQ-Handler seinen IRQ maskieren kann und erst danach das EOI sendet um das Ausliefern anderer IRQs wieder zu ermöglichen. Zu diesem Zeitpunkt ist das Ereignis im HW-Gerät ja noch nicht behandelt so das bei Level-Triggered-IRQs dieser noch aktiv ist, deswegen muss ja auch maskiert werden damit der IRQ-Controller nach dem EOI nicht sofort den selben IRQ noch mal ausliefert. Man könnte auch das EOI ans Ende des richtigen IRQ-Handlers im Treiber legen aber das würde bedeuten das in der gesamten Zeit keine anderen IRQs ausgeliefert werden können, bei einem Monolithen auf einem Single-CPU-System ist das eine durchaus sinnvolle Vorgehensweise aber bei Multi-Core oder Micro-Kernel eher nicht. Bei Edge-Triggered-IRQs ist das auch etwas anders aber das erkläre ich später (wenn gewünscht). Nachdem der Treiber das Ereignis im HW-Gerät abgearbeitet hat geht das Level-Triggered-IRQ-Signal zwischen HW-Gerät und IRQ-Controller wieder auf inaktiv zurück und der entsprechende IRQ kann im IRQ-Controller gefahrlos demaskiert werden. Ab hier löst ein neues Ereignis in dem selben HW-Gerät auch wieder einen neuen IRQ aus.


Ich hoffe ich konnte ein paar Unklarheiten beseitigen, wenn nicht dann einfach fragen.


Edit: hier muss unbedingt noch korrikgiert werden das ein IRQ-Controller bei Level-Triggered-IRQs diese nicht speichert. Wenn also ein IRQ gerade maskiert ist oder der IRQ-Controller gerade auf ein EOI wartet dann dürfen eigentlich sämtliche IRQ-Leitungen jeden beliebigen Pegel annehmen ohne dass das auf spätere Aktionen des IRQ-Controller irgendeinen Einfluss hat, entscheidend sind die Pegel der IRQ-Leitungen wenn der IRQ-Controller aktiv ist bzw. wenn der betreffende IRQ nicht maskiert ist. Alles andere fällt in die Kategorie "Spurious Interrupt" und sollte normalerweise nicht auftreten (ich persönlich würde das sogar als Fehler im System bezeichnen aber bei der x86-Plattform kann das wohl tatsächlich mal auftreten). Sorry das ich das oben falsch formuliert habe und jetzt noch editieren musste aber ich wollte das nicht einfach so falsch stehen lassen.


Grüße
Erik
(es wäre schön wenn das jemand noch ein wenig aufhübscht und ins Wiki überträgt)
« Letzte Änderung: 08. October 2011, 18:48 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 08. October 2011, 12:15 »
Zitat von: erik
Es können in einem System auch mehrere unabhängige IRQ-Controller existieren (so wie die vielen APICs in modernen x86-Computern)
Damit sind der (da es meistens, von Servern mal abgesehen, ja nur einen gibt) IO-APIC und die Local-APICs gemeint!?

Ich habe mir gerade noch mal die IO-APIC und Local-APIC Doku angesehen und beim IO-APIC steht da, dass edge-triggered IRQs nur "nochmal" gesendet werden, wenn der Empfänger nicht das entsprechende IRR-Bit gesetzt hat. Das wiederrum heißt, dass es durchaus passieren kann, das der Handler noch am Arbeiten ist und ein anderer Local-APIC den selben Handler wieder aufruft.
Der entsprechende IRQ (bzw. der gesamte IO-APIC) wird nur bei level-triggered IRQs bis zu einem EOI vom Lokalen-APIC geblockt, nicht aber bei edge-triggered. Dort wird nur so lange geblockt bis der Lokale-APIC den IRQ angenommen hat.

Soweit habe ich die Doku verstanden (was aber nix heißen muss ;)).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 08. October 2011, 16:51 »
Hallo,


Damit sind der (da es meistens, von Servern mal abgesehen, ja nur einen gibt) IO-APIC und die Local-APICs gemeint!?
Ja. Mir ist auch bewusst das in üblichen PCs nur ein I/O-APIC drin ist aber ich vermute mal dass das in naher Zukunft nicht mehr zwangsläufig so sein muss und bei Servern ist das schon lange nicht mehr so, woraus folgt das ein OS das die APICs voll nutzen will nicht nur mit mehreren Local-APICs sondern auch mit mehreren I/O-APICs anständig umgehen können muss.

Ich habe mir gerade noch mal die IO-APIC und Local-APIC Doku angesehen und beim IO-APIC steht da, dass edge-triggered IRQs nur "nochmal" gesendet werden, wenn der Empfänger nicht das entsprechende IRR-Bit gesetzt hat. Das wiederrum heißt, dass es durchaus passieren kann, das der Handler noch am Arbeiten ist und ein anderer Local-APIC den selben Handler wieder aufruft.
Ich habe absolut nicht verstanden von was für einer Situation Du hier schreibst, Bitte präzisiere das ganze noch mal und mach eine konkrete Frage draus. Ein IRQ sollte meiner Meinung nach überhaupt nicht "nochmal" gesendet werden, das ist IMHO totaler Quatsch, wichtig ist nur das absolut kein IRQ verloren geht. Ich vermute mal das Du diese Beschreibung aus der APIC-Doku irgendwie im falschen Zusammenhang gelesen hast.

Der entsprechende IRQ (bzw. der gesamte IO-APIC) wird nur bei level-triggered IRQs bis zu einem EOI vom Lokalen-APIC geblockt, nicht aber bei edge-triggered. Dort wird nur so lange geblockt bis der Lokale-APIC den IRQ angenommen hat.
Bei Edge-Triggered-IRQs reicht es ja auch völlig wenn der IRQ-Controller nur so lange blockiert wird bis der IRQ an die nächste Verarbeitungseinheit erfolgreich ausgeliefert wurde, schließlich steht ja kein permanent aktives Signal (wie bei einem Level-Triggered-IRQ) an das man erstmal ignorieren muss bis der Treiber das Ereignis im HW-Gerät auch tatsächlich bearbeitet hat. Bei Edge-Triggered-IRQs sendet das HW-Gerät pro Ereignis normalerweise auch nur genau einen IRQ zum IRQ-Controller, alles andere würde ich als Defekt im HW-Gerät bezeichnen. Dazu kann (aber nicht muss) im HW-Gerät noch ein internes automatisches Maskenregister existieren das nachfolgende gleiche Events (z.B. weitere empfangene Ethernet-Frames in einem Netzwerkkontroller) blockiert bis der Treiber diese Event-Art komplett abgearbeitet hat (z.B. den Empfangspuffer komplett geleert hat).


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 08. October 2011, 17:26 »
Zitat von: erik
Bei Edge-Triggered-IRQs reicht es ja auch völlig wenn der IRQ-Controller nur so lange blockiert wird bis der IRQ an die nächste Verarbeitungseinheit erfolgreich ausgeliefert wurde, schließlich steht ja kein permanent aktives Signal (wie bei einem Level-Triggered-IRQ) an das man erstmal ignorieren muss bis der Treiber das Ereignis im HW-Gerät auch tatsächlich bearbeitet hat. Bei Edge-Triggered-IRQs sendet das HW-Gerät pro Ereignis normalerweise auch nur genau einen IRQ zum IRQ-Controller, alles andere würde ich als Defekt im HW-Gerät bezeichnen. Dazu kann (aber nicht muss) im HW-Gerät noch ein internes automatisches Maskenregister existieren das nachfolgende gleiche Events (z.B. weitere empfangene Ethernet-Frames in einem Netzwerkkontroller) blockiert bis der Treiber diese Event-Art komplett abgearbeitet hat (z.B. den Empfangspuffer komplett geleert hat).
Genau das habe ich mir gedacht.

Also mit "nochmal" meinte ich einen neuen IRQ (dein Bsp. Ethernet-Karte), falls die so schnell kommen das der vorherige IRQ noch im Handler steckt, kann es sein das der neue IRQ an einen anderen APIC geschickt wird und somit der Handler nochmal aufgerufen wird.

Falls dem wirklich so ist, muss ich meinen Code von heute nochmal bearbeiten und den alten Zustand wiederherstellen (mit einer kleinen Veränderung).

Also bei level-triggered blockiert schon der IO-APIC bis ein EOI kommt, bei edge-triggered allerdings nicht, aber ich muss trotzdem ein EOI senden, damit das entsprechende Bit im Lokalen-APIC gelöscht wird.
Die Handler müssen also Multithreading-safe sein. Bei level-triggered tut es nicht weh und bei edge-triggered hat meine Variante den Vorteil dass das focus-processor Feature emuliert wird (womit man ein wenig mehr Performance rauskitzelt, aber nur ein winziges bißchen).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 08. October 2011, 20:00 »
Hallo,


kann es sein das der neue IRQ an einen anderen APIC geschickt wird und somit der Handler nochmal aufgerufen wird.
Selbst bei Edge-Triggered-IRQs sollte es nicht passieren das der selbe IRQ an einen anderen IRQ-Controller geschickt wird. Es ist natürlich möglich das ein erneuter IRQ dynamisch an eine andere CPU geroutet wird so das der selbe IRQ-Handler mehrmals aufgerufen werden könnte aber in diesem Fall muss entweder die SW das verkraften (wovon ich persönlich eher abraten würde) oder es müssen bei der Konfiguration der IRQ-Controller und der betreffenden HW-Gräte geeignete Maßnahmen ergriffen werden (was meine Empfehlung ist). Das könnte z.B. das maskieren des IRQ im ersten IRQ-Controller (der diesen IRQ entgegen nimmt) sein oder z.B. ein automatisches Maskenregister im HW-Gerät (bei GBit-Ethernet-Controller ist sowas üblicherweise vorhanden und alle anderen anständigen Geräte bei denen mit einem hohen IRQ-Aufkommen zu rechnen ist haben auch entsprechende Mechanismen schließlich wissen die (ausreichend qualifizierten) HW-Entwickler um diese Problematik).

Wenn Du wirklich Edge-Triggered-IRQs sinnvoll einsetzen möchtest (was MSI(-X) zwingend voraussetzt) dann solltest Du aber darüber nachdenken den HW-Geräten, bei denen mit hohem IRQ-Aufkommen zu rechnen ist, mehrere IRQs zuzuweisen um somit die IRQ-Last bequem auf mehrere IRQs und mehrere CPUs verteilen zu können.

focus-processor Feature
Google hat mir da nicht wirklich geholfen, Bitte erkläre mir das etwas näher oder gib ein paar bessere Suchworte oder gar einen guten Link.


edge-triggered ist Mist [.....] Pegelgesteuert ist grundsätzlich besser [.....] wird auf Polling umgestellt
Boa, das alles in einem Beitrag, ich bin platt (deswegen hab ich mir das Antworten auch aufgehoben bis ich ausreichend Zeit für ne wohl überlegte Antwort hab). Wer sowas raushaut hat IMHO das Konzept von IRQs oder wie man heutzutage mit IRQs umgeht oder beides nicht so wirklich verstanden.

edge-triggered ist Mist. Ein EOI kannst du dir damit Interrupts auch nicht sparen, weil sonst ein Gerät die IRQs schneller feuern kann als die CPU mit der Bearbeitung hinterherkommt; bei dynamischem IRQ-Routing (pro IRQ) gibt das Ärger, wenn dein Kernel die IRQs dann nicht synchronisiert.
Also erst einmal bedeutet Edge-Triggered ja nicht das der IRQ-Controller einem IRQ-Sturm hilflos gegenüber steht, es ist durchaus möglich (ob auch üblich kann ich nicht abschätzen) das man dagegen geeignete Maßnahmen im IRQ-Controller vorsieht, man kann z.B. die maximale IRQ-Frequenz pro IRQ begrenzen indem man nach jedem Ausliefern einen IRQ-spezifischen Counter mit einem individuell konfigurierten Wert loslaufen lässt der diesen IRQ so lange blockiert bis er 0 erreicht hat (das lässt sich simpel realisieren und ist recht wirksam und ist daher der Weg den ich auf meiner Plattform nutzen möchte). Auch besteht nach wie vor die Möglichkeit das der IRQ erstmal (automatisch) maskiert wird nachdem er ausgeliefert wurde und vom Treiber explizit wieder demaskiert wird (was aber mindestens einen teuren Zugriff auf den IRQ-Controller pro IRQ erfordert, was der Grund ist warum ich das in meiner Plattform nicht möchte).

Pegelgesteuert ist grundsätzlich besser und wenn man da dedizierte Leitungen nutzt, kann man die auch wie flankengesteuert betrachten.
Nein, man muss grundsätzlich Maskieren und Demaskieren wenn man den IRQ-Controller nicht komplett Blockieren will bis der IRQ-Handler im Treiber komplett fertig ist (und erst dann EOI signalisiert), die Nachteile des Blockierens hab ich vorhin schon mal erklärt und das Maskieren/Demaskieren kostet teure und in Relation zu Edge-Triggered-IRQs unnötige Zugriffe auf den IRQ-Controller. Außerdem sind ausreichend dedizierte IRQ-Leitungen illusorisch, schon ein einzelner AHCI-Controller kann bis zu 32 IRQs haben und die besseren SAS-Controller/10GBit-Ethernet-Controller/.... noch deutlich mehr. Level-Triggered-IRQs sind ein Relikt aus vergangenen Zeiten das nur noch aus Kompatibilitätsgründen künstlich/umständlich am Leben gehalten wird (wer in der PCI-Express-Spec nachließt wie dort die alten PCI-IRQs (#A...#D) emuliert werden und sich die daraus ergebenden Probleme vergegenwärtigt wird verstehen was ich mit "künstlich/umständlich" meine).

Treiber können IRQ-Stürme auch erkennen. Wenn z.B. in 5 Sekunden 1000 IRQs gekommen sind, wird auf Polling umgestellt, bis die IRQs wieder seltener werden. Das ist dann aber zwingend HW-spezifisch.
Willst Du wirklich ernsthaft "Polling" als Methode empfehlen? Sorry, aber das ist ebenfalls ein längst ausgestorbenes Relikt aus selig vergangenen Zeiten. Außerdem sind 200 IRQs pro Sekunde noch lange keine hohe CPU-Last, was macht den der IRQ-Handler so aufwendiges wenn man damit bereits eine CPU platt machen kann? Etwa händisches Datenlutschen an lahmen I/O-Ports? Auch das ist längst ausgestorben, wer solch vorsintflutliche HW bei heutigen Datenraten einsetzt hat es auch nicht besser verdient. Das mag ja bei einer RS232-Schnittstelle mit maximal gut 900kBaud (also maximal 92 kByte/s) noch vertretbar sein (die entsprechenden UARTs, >= 16750, haben dafür auch ausreichend große FIFOs die eine angemessen niedrige IRQ-Rate ermöglichen) aber für alles andere ist das einfach nicht mehr tragbar.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 08. October 2011, 20:16 »
Zitat von: erik
Selbst bei Edge-Triggered-IRQs sollte es nicht passieren das der selbe IRQ an einen anderen IRQ-Controller geschickt wird.
Soweit ich es verstanden habe, ist der edge-triggered IRQ mit der Annahme (dem ACK) am Lokalen-APIC abgeschlossen und es kann ein neuer IRQ verarbeitet (im IO-APIC) werden. Dieser neue IRQ kann ja wieder ein edge-triggered IRQ (vom selben Gerät) sein und ein andere Prozessor hat jetzt ne niedrigere Priorität als der der den letzten IRQ immernoch bearbeitet.
Dann kommt es zur Situation das der Handler wieder aufgerufen wird, obwohl gerade eine andere CPU im gleichen Handler ist.

Zitat von: erik
Das könnte z.B. das maskieren des IRQ im ersten IRQ-Controller (der diesen IRQ entgegen nimmt) sein oder z.B. ein automatisches Maskenregister im HW-Gerät
Problem, wie oben erläutert, ist die Zeit bis zum Maskieren des IRQ und ich will mich ehrlich gesagt nicht abhängig machen von irgendeinem Treiber. Ja, man kann die CPU und damit den PC damit in die Knie zwingen, aber es sollte zu keinem Problem kommen, nur weil der Handler auf mehreren CPUs gleichzeitig läuft (was eine Form der Synchronisierung erfordert).

Zitat von: erik
Wenn Du wirklich Edge-Triggered-IRQs sinnvoll einsetzen möchtest (was MSI(-X) zwingend voraussetzt) dann solltest Du aber darüber nachdenken den HW-Geräten, bei denen mit hohem IRQ-Aufkommen zu rechnen ist, mehrere IRQs zuzuweisen um somit die IRQ-Last bequem auf mehrere IRQs und mehrere CPUs verteilen zu können.
Kann das der PCI-Bustreiber ermitteln oder muss ich mich da auf den Treiber verlassen, der halt mehrere IRQs anfordert?

Zwecks "focus processor" zitiere ich mal das Intel Manual:
Zitat von: Intel Manual, 10.7.2.6, Lowest Priority Delivery Mode
(P6 family and Pentium processors.) For these processors, if a focus processor
exists, it may accept the interrupt, regardless of its priority. A processor is said to be
the focus of an interrupt if it is currently servicing that interrupt or if it has a pending
request for that interrupt. For Intel Xeon processors, the concept of a focus processor
is not supported.
Heißt im Endeffekt, wenn eine CPU schon den gleichen IRQ bearbeitet, wird auch der gerade eingetroffene IRQ (trotz anderer Prioritäten) wieder an diese CPU geleitet.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 08. October 2011, 20:39 »
Hallo,

Also erst einmal bedeutet Edge-Triggered ja nicht das der IRQ-Controller einem IRQ-Sturm hilflos gegenüber steht, es ist durchaus möglich (ob auch üblich kann ich nicht abschätzen) das man dagegen geeignete Maßnahmen im IRQ-Controller vorsieht, man kann z.B. die maximale IRQ-Frequenz pro IRQ begrenzen
Wenn der IRQ-Controller die maximale IRQ-Frequenz relativ willkürlich begrenzt, dann ist das schlechtes Design, weil dann ja bewusst Interrupts fallengelassen werden, wenn eine Hardware sich anders verhält.

Das kannst du auf deiner Plattform gerne so realisieren, aber auf dem PC ist das nicht möglich, wenn du nicht irgendwelche Treiber sterben lassen möchtest.

Auch besteht nach wie vor die Möglichkeit das der IRQ erstmal (automatisch) maskiert wird nachdem er ausgeliefert wurde und vom Treiber explizit wieder demaskiert wird (was aber mindestens einen teuren Zugriff auf den IRQ-Controller pro IRQ erfordert, was der Grund ist warum ich das in meiner Plattform nicht möchte).
Du gehst den Weg, dass ein IRQ also nach einer gewissen Zeit automatisch demaskiert wird und begrenzt auf diese Weise die IRQ-Frequenz des Geräts. Solange du garantieren kannst, dass du den Interrupt in diesem Zeitslot auch wirklich verarbeitet hast und dass die Hardware niemals schneller feuert, dann ist das in Ordnung. Aber kannst du das immer garantieren?

Pegelgesteuert ist grundsätzlich besser und wenn man da dedizierte Leitungen nutzt, kann man die auch wie flankengesteuert betrachten.
Nein, man muss grundsätzlich Maskieren und Demaskieren wenn man den IRQ-Controller nicht komplett Blockieren will bis der IRQ-Handler im Treiber komplett fertig ist (und erst dann EOI signalisiert),
Warum? Man muss in der Hardware die IRQ-Quelle behandeln. Bei nicht gemeinsam genutzten Interrupts fällt dann der IRQ zwingend auf inaktiv ab und steigt erst später wieder auf aktiv an. Das ist dann ein neuer Interrupt und würde einen EOI ersparen.

Außerdem sind ausreichend dedizierte IRQ-Leitungen illusorisch, schon ein einzelner AHCI-Controller kann bis zu 32 IRQs haben und die besseren SAS-Controller/10GBit-Ethernet-Controller/.... noch deutlich mehr.
Wo ist das Problem? Mir geht es um die Schnittstelle zwischen Interrupt Controller und CPUs; wie die Interrupts selbst zum Interrupt Controller kommen, ist an der Stelle egal (und darf auch gerne edge triggered über einen Bus sein).

Level-Triggered-IRQs sind ein Relikt aus vergangenen Zeiten das nur noch aus Kompatibilitätsgründen künstlich/umständlich am Leben gehalten wird
Level triggered IRQs bieten die Möglichkeit der gemeinsamen Nutzung. Wenn man diese nicht nutzt, hat man die gleichen Vorteile wie bei flankengesteuerten Interrupts, ist aber flexibler.

Treiber können IRQ-Stürme auch erkennen. Wenn z.B. in 5 Sekunden 1000 IRQs gekommen sind, wird auf Polling umgestellt, bis die IRQs wieder seltener werden.
Willst Du wirklich ernsthaft "Polling" als Methode empfehlen?
Der Treiber für meine 3c589 hat das mal gemacht. Wenn ich da die maximalen 10 MBit/s durchpumpe, ist ein Core mit 80%hi durchaus dicht. Ich weiß ja nicht, wie du das siehst, aber da ist Polling durchaus akzeptabel... die zweite Variante wäre natürlich, die Hardware zu ersetzen - aber wenn du für ein vorhandenes System schreibst, dann ist das nicht immer eine Option.

vorsintflutliche HW bei heutigen Datenraten einsetzt hat es auch nicht besser verdient.
Bei deiner Plattform hast du natürlich die vollkommene Freiheit, dir Hardware auszusuchen und zusammenzustellen, die freundlich ist. Bei x86 ist das eben nicht so, da zählt billig - und damit kämpfen sowohl Linux als auch Microsoft.

Und da ist eine gut funktionierende, stabile, suboptimale Lösung mehr wert als eine high-performance hochmoderne Methode, die nur mit bestimmter Hardware funktioniert. Für deine Plattform mag das egal sein, aber ganz so herablassend muss es nicht sein. Nicht jeder baut sich die CPU nach dem Wunsch-OS und die Realität beim Hobby-OS-Dever ist nunmal kein modernes IOMMU-HPET-PCIe-Quadcore-System mit 8 GB RAM, sondern auch mal ein Pentium II mit RTL8029 und defekten ACPI-Tabellen...

Gruß,
Svenska

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 09. October 2011, 00:59 »
Hallo,

Soweit ich es verstanden habe, ist der edge-triggered IRQ mit der Annahme (dem ACK) am Lokalen-APIC abgeschlossen und es kann ein neuer IRQ verarbeitet (im IO-APIC) werden. Dieser neue IRQ kann ja wieder ein edge-triggered IRQ (vom selben Gerät) sein und ein andere Prozessor hat jetzt ne niedrigere Priorität als der der den letzten IRQ immernoch bearbeitet.
Hm, gibt es bei den APICs wirklich keinen Mechanismus mit dem man einen IRQ per Default erst mal maskieren kann? Du hattest doch was von einem "IRR-Bit" geschrieben, kann man damit nicht irgendwas drehen? Ich kenne die APICs nicht wirklich und hab auch nie die Spezifikation komplett durchgelesen, daher kann ich Dir da leider nicht wirklich helfen, sorry. Irgendwie fällt es mir aber schwer zu glauben dass das nicht funktioniert, auf der anderen Seite ist das auch nicht so schlimm wenn das nicht gehen sollte da anständige HW (und nur die würde ich auf die Benutzung von MSI(-X) konfigurieren, das muss man nämlich explizit machen) ja entsprechende Mechanismen selber hat um die IRQ-Rate auf einem angenehmen Niveau zu halten.

Kann das der PCI-Bustreiber ermitteln oder muss ich mich da auf den Treiber verlassen, der halt mehrere IRQs anfordert?
Wie viele IRQs ein PCI-Gerät maximal haben kann verraten die Capabilities für MSI bzw. MSI-X ganz genau, sämtliche Ressourcen (inkl. dem IRQ-Routing) lassen sich bei PCI komplett ohne die gerätespezifischen Treiber konfigurieren. Wen ein Treiber dann weniger IRQs haben möchte kann man das auch nachträglich noch reduzieren. Wenn ein PCI-Device MSI und MSI-X unterstützt dann solltest Du auf jeden Fall MSI-X bevorzugen und auch voll konfigurieren, wenn der Treiber das später nicht voll nutzt kannst Du bei MSI-X sehr flexibel downgraden wogegen bei MSI nur eine Zweierpotenz von 1 bis einschließlich 32 als IRQ-Anzahl möglich ist. Nebst dessen das bei MSI-X auch immer pro IRQ ein Mask-Bit verfügbar ist wogegen das bei MSI optional ist (ich kenne auch kein Device dass das bei MSI unterstützt).

Heißt im Endeffekt, wenn eine CPU schon den gleichen IRQ bearbeitet, wird auch der gerade eingetroffene IRQ (trotz anderer Prioritäten) wieder an diese CPU geleitet.
Aha, Danke. Das bedeutet also das man versucht die Cache-Effizienz möglichst hoch zu halten.


Wenn der IRQ-Controller die maximale IRQ-Frequenz relativ willkürlich begrenzt, dann ist das schlechtes Design, weil dann ja bewusst Interrupts fallengelassen werden, wenn eine Hardware sich anders verhält.
Wie kommst Du hier auf "willkürlich"? Ich hab doch extra "mit einem individuell konfigurierten Wert" geschrieben und so möchte ich das auf meiner Plattform auch realisieren. Mir schwebt vor das man die minimale IRQ-Delay-Zeit in einem Bereich von etwa 5 µs bis 1 ms (also etwa 1000 Hz bis 200 kHz) frei konfigurieren kann (bei einer Auflösung von 1 µs reicht dafür ein 10 Bit Counter aus). In dieser Zeit werden zwar weitere IRQ-Signale von dem zugehörigen HW-Gerät für den betreffenden IRQ angenommen (und auch ordnungsgemäß gezählt) aber eben nicht an eine CPU ausgeliefert. Es soll nur die maximale Auslieferfrequenz für jeden IRQ individuell konfigurierbar sein, weniger geht natürlich immer und wenn der letzte IRQ schon lange her ist dann kommt der nächste IRQ auch sofort (ohne zusätzliche Latenz) durch (so er denn nicht von einem anderen IRQ mit höherer Priorität verdrängt wird und auch eine CPU diesen IRQ akzeptiert). Der Delay-Counter wird auf jeden Fall immer nur bei einer tatsächlich erfolgreichen Auslieferung des betreffenden IRQ gestartet (genau zu dem Zeitpunkt wo auch der Zähler mit den vom HW-Gerät empfangenen IRQ-Signalen genullt wird). Das einzigste was hier willkürlich ist sind die Grenzen für den Counter-Start-Wert, wobei ich 200 kHz als absolute Obergrenze und auch 1000 Hz als tiefste Grenze für angebracht halte. Mehr als 200 kHz für einen IRQ sind wohl nicht nötig (wenn ein einzelnes HW-Gerät mehr braucht dann muss es eben zwangsläufig mehrere IRQs benutzen) und 1000 Hz als tiefste Grenze lassen dem System auch bei komplexeren IRQ-Handlern noch genug Luft zum atmen (und zum eventuellen ziehen der Notbremse per Mask-Bit).

Du gehst den Weg, dass ein IRQ also nach einer gewissen Zeit automatisch demaskiert wird und begrenzt auf diese Weise die IRQ-Frequenz des Geräts. Solange du garantieren kannst, dass du den Interrupt in diesem Zeitslot auch wirklich verarbeitet hast und dass die Hardware niemals schneller feuert, dann ist das in Ordnung. Aber kannst du das immer garantieren?
Ich garantiere keines von beidem, wie sollte ich den auch? Wann der richtige IRQ-Handler, in meinem Fall ein asynchroner Event-Handler als extra PopUp-Thread im User-Mode-Treiber, fertig ist kann ich als OS-Entwickler nicht beeinflussen. Und die IRQ-Signal-Frequenz der Hardware hab ich auch nicht immer unter meiner Kontrolle. Schließlich müssten HW-Gerät und Treiber auch von Fremden erstellt werden können (sogar als Closed-Source).

Man muss in der Hardware die IRQ-Quelle behandeln.
Hä, wie soll das den bitte gehen? Wie soll die HW einen IRQ behandeln (z.B. ein empfangenes Ethernetframe)? Die abschließende Behandlung eines IRQ macht immer die Software (also der Treiber) und erst dann geht bei Level-Triggered-IRQs auch die IRQ-Leitung vom HW-Gerät zum IRQ-Controller wieder auf inaktiv zurück. Genau deswegen muss ja maskiert werden damit der IRQ-Controller in der Zeit weitere IRQs an eine (andere) CPU melden kann (bei Multi-Core und/oder Micro-Kernel ist das eine absolute Notwendigkeit).

Mir geht es um die Schnittstelle zwischen Interrupt Controller und CPUs
Halt! Stopp! Einen Moment mal, ich dachte wir reden hier von der Verbindung zwischen HW-Gerät und IRQ-Controller, jedenfalls hab ich bis jetzt nur darüber geschrieben (Bitte interpretiere meine Antworten auch entsprechend). Wie die Verbindung zwischen IRQ-Controller und CPU aussieht ist ein uninteressantes Implementierungsdetail das hier nicht beachtet wird. Bei meiner Plattform möchte ich das Level-Triggered machen, weil es nur einen IRQ-Controller aber dafür mehrere CPUs gibt ist das IMHO die bessere Lösung für diesen Teilabschnitt.

Level triggered IRQs bieten die Möglichkeit der gemeinsamen Nutzung. Wenn man diese nicht nutzt, hat man die gleichen Vorteile wie bei flankengesteuerten Interrupts, ist aber flexibler.
Jetzt bin ich aber mal neugierig. Welche Möglichkeit bietet den IRQ-Sharing überhaupt? Von allerlei lustigen Problemen mit Treibern und Hardware mal abgesehen. Dadurch das so ein gesharter IRQ immer als ganzes maskiert wird bietet der auf jeden Fall die Möglichkeit für zusätzliche Latenzen wenn z.B. gerade der IRQ-Handler eines der beteiligten HW-Geräte noch läuft (also der IRQ noch maskiert ist) und ein anderes Gerät nun auch aktiv den Wunsch nach Aufmerksamkeit signalisiert muss dieses noch mindestens so lange warten bis der noch aktive IRQ-Handler eines anderen HW-Gerätes fertig ist (selbst wenn sich noch ein paar CPUs langweilen) und erst dann geht das Spiel mit dem Ausliefern dieses IRQ an eine CPU wieder von vorne los.

Der Treiber für meine 3c589 hat das mal gemacht. Wenn ich da die maximalen 10 MBit/s durchpumpe, ist ein Core mit 80%hi durchaus dicht.
Durch das Polling wird zwar eventuell die CPU-Last reduziert (wovon ich gar nicht mal wirklich überzeugt bin) aber auf jeden Fall auch der Durchsatz was das Problem eigentlich nur schlimmer macht oder länger hinaus zieht. Wenn eine gegebene HW-Kombination eine bestimmte Aufgabe (z.B. das Sättigen einer 10MBit/s-Verbindung) nicht anständig hinbekommt dann geht es eben nicht und da ist es auch egal wie die Software da versucht das Schlimmste zu vermeiden, die gewünschte Aufgabe wird einfach nicht erfüllt und damit ist die gegebene HW-Kombination schlicht und ergreifend nicht tauglich für diese Aufgabe.

Ich weiß ja nicht, wie du das siehst, aber da ist Polling durchaus akzeptabel...
Also ich sehe das so das Polling grundsätzlich nicht akzeptabel ist, das passt einfach nicht ins Konzept eines preemptiven Multitaskings, fertig.

Bei deiner Plattform hast du natürlich die vollkommene Freiheit, dir Hardware auszusuchen und zusammenzustellen, die freundlich ist. Bei x86 ist das eben nicht so, da zählt billig - und damit kämpfen sowohl Linux als auch Microsoft.
Ich sehe da keinen wesentlichen Unterschied zwischen dem x86-PC und meinem System, wenn z.B. eine Netzwerkkarte ein so langsames Interface zur Software hat das man selbst für eine 10MBit/s-Verbindung eine CPU nahezu komplett Auslasten muss dann ist das einfach nicht akzeptabel, egal was diese CPU für eine Befehlsarchitektur hat.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 09. October 2011, 02:19 »
Hallo,

Wenn der IRQ-Controller die maximale IRQ-Frequenz relativ willkürlich begrenzt, dann ist das schlechtes Design, weil dann ja bewusst Interrupts fallengelassen werden, wenn eine Hardware sich anders verhält.
Wie kommst Du hier auf "willkürlich"? Ich hab doch extra "mit einem individuell konfigurierten Wert" geschrieben und so möchte ich das auf meiner Plattform auch realisieren.
Naja, du denkst dir einen Bereich aus, den du zulassen willst und damit gut. Das ist ziemlich willkürlich.

Ich garantiere keines von beidem, wie sollte ich den auch?
Damit akzeptierst du fallengelassene Interrupts, wenn ein Gerät zu schnell feuert.

Man muss in der Hardware die IRQ-Quelle behandeln.
Hä, wie soll das den bitte gehen? Wie soll die HW einen IRQ behandeln (z.B. ein empfangenes Ethernetframe)?
Du musst  in der Hardware den IRQ-Grund behandeln. Wenn du das letzte Ethernet-Paket aus der Hardware gepopelt hast, dann ist der IRQ beendet, ob du dem Interrupt Controller das nun mitteilst oder nicht.

Die abschließende Behandlung eines IRQ macht immer die Software (also der Treiber) und erst dann geht bei Level-Triggered-IRQs auch die IRQ-Leitung vom HW-Gerät zum IRQ-Controller wieder auf inaktiv zurück. Genau deswegen muss ja maskiert werden damit der IRQ-Controller in der Zeit weitere IRQs an eine (andere) CPU melden kann (bei Multi-Core und/oder Micro-Kernel ist das eine absolute Notwendigkeit).
Ja, dieser eine aktive Interrupt muss maskiert werden. Die anderen nicht. :-)

Halt! Stopp! Einen Moment mal, ich dachte wir reden hier von der Verbindung zwischen HW-Gerät und IRQ-Controller, jedenfalls hab ich bis jetzt nur darüber geschrieben (Bitte interpretiere meine Antworten auch entsprechend).
Achso. Aus der Sicht ist es für die Software ziemlich egal, was da für ein Interrupt verwendet wird. Zwischen Interrupt Controller und CPU sollte es in jedem Fall level-triggered sein (oder sich zumindest so verhalten).

Jetzt bin ich aber mal neugierig. Welche Möglichkeit bietet den IRQ-Sharing überhaupt?
Es lassen sich ein paar Leitungen einsparen und der PIC braucht nur 15 Eingänge haben... :-P Was ich meinte ist, wenn du schon dedizierte Interrupts benutzt, dann kannst du auch level-triggered Interrupts so benutzen, dass du kein extra EOI an den Interrupt Controller mehr schicken brauchst: Die Hardware ist in jedem Fall kurzzeitig im Zustand "es liegt kein Interrupt an" (der wurde ja gerade bearbeitet).

Der Treiber für meine 3c589 hat das mal gemacht. Wenn ich da die maximalen 10 MBit/s durchpumpe, ist ein Core mit 80%hi durchaus dicht.
Durch das Polling wird zwar eventuell die CPU-Last reduziert (wovon ich gar nicht mal wirklich überzeugt bin) aber auf jeden Fall auch der Durchsatz was das Problem eigentlich nur schlimmer macht oder länger hinaus zieht.
Nicht wirklich (zumindest nicht in dem Fall). Der Treiber schaltet in der Hardware die Interrupts aus und erspart sich somit mehrere Registerzugriffe fürs IRQ-Handling. Außerdem gibt es weniger Kontextwechsel. Effektiv wird das System wesentlich responsiver und unter zusätzlicher Last sinkt zwar der Durchsatz etwas - aber die Latenzen bleiben gleich.

Wenn ich das jetzt versuche (wo kein Polling erkannt wird), dann sollte ich nebenher möglichst wenig tun, weil sonst nichts mehr reagiert. Die Verbindung nutze ich nur zum Video-Livestream von einer DBox2; Netzwerkkarte auf einem Core und MPlayer auf dem zweiten Core und das System tobt am Anschlag. Das war vorher wesentlich besser und lief trotzdem flüssig. Sind halt grenzwertige Lasten. Übrigens wurde auf Interrupts zurückgeschaltet, wenn der IRQ-Sturm vorbei war (also das Streaming wieder aus). Dann gibts auch keine Probleme mit der CPU-Last im Idle mehr.

wenn z.B. eine Netzwerkkarte ein so langsames Interface zur Software hat das man selbst für eine 10MBit/s-Verbindung eine CPU nahezu komplett Auslasten muss dann ist das einfach nicht akzeptabel, egal was diese CPU für eine Befehlsarchitektur hat.
In meinem Fall ist das halt eine 16-Bit-PCMCIA-Karte hinter einem CardBus-Controller. Eine Realtek verhält sich da nicht anders, egal ob sie nun 10 MBit (RTL8029), 100 MBit (RTL8139) oder 1000 MBit (RTL8169) spricht... so manche Billigware verursacht massiv IRQs unter Volllast.

Immerhin schafft die 3c589 ihre 10 MBit/s. Meine anderen PCMCIA-Karten (ne2000) steigen bei 3-5 MBit/s aus.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 09. October 2011, 09:35 »
Zitat von: erik
Hm, gibt es bei den APICs wirklich keinen Mechanismus mit dem man einen IRQ per Default erst mal maskieren kann? Du hattest doch was von einem "IRR-Bit" geschrieben, kann man damit nicht irgendwas drehen? Ich kenne die APICs nicht wirklich und hab auch nie die Spezifikation komplett durchgelesen, daher kann ich Dir da leider nicht wirklich helfen, sorry.
Bitte ganz genau sagen, welchen APIC du meinst, ob Lokalen oder IO! Denn was nutzt es dir wenn der IRQ im Lokalen-APIC maskiert ist, deswegen können die anderen CPUs den gleichen IRQ trotzdem empfangen.

Soweit ich es verstanden habe, ist der IRQ aber im IO-APIC nur bis zur Empfangsbestätigung maskiert:
Zitat von: IO-APIC Manual, 3.2.4, I/O REDIRECTION TABLE REGISTERS
The IOAPIC responds to an edge triggered interrupt as long as the interrupt is wider than one CLK cycle. The
interrupt input is asynchronous; thus, setup and hold times need to be guaranteed for at lease one rising edge of
the CLK input.  Once the interrupt is detected, a delivery status bit internal to the IOAPIC is set. A new edge on
that Interrupt input pin will not be recongnized until the IOAPIC Unit broadcasts the corresponding message over
the APIC bus and the message has been accepted by the destination(s) specified in the destination field. That
new edge only results in a new invocation of the handler if its acceptance by the destination APIC causes the
Interrupt Request Register bit to go from 0 to 1. (In other words, if the interrupt wasn't already pending at the
destination.)
Vielleicht habe ich den Abschnitt aber auch falsch verstanden.

Zitat von: erik
In dieser Zeit werden zwar weitere IRQ-Signale von dem zugehörigen HW-Gerät für den betreffenden IRQ angenommen (und auch ordnungsgemäß gezählt) aber eben nicht an eine CPU ausgeliefert.
Könnte der Counter nicht theoretisch dann irgendwann überlaufen? Allerdings glaube ich dass das Gerät vorher die Daten wegwirft, bevor sowas passiert.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 09. October 2011, 22:56 »
Hallo,


du denkst dir einen Bereich aus, den du zulassen willst und damit gut. Das ist ziemlich willkürlich.
Okay, ein Stück weit willkürlich ist das wohl schon aber ich denke die festgelegten Grenzen ergeben einen Sinn. Ich rechne nicht damit das meine CPUs mehr als 100 MHz erreichen und damit auch nicht mehr Power haben als ein 100 MHz Pentium Pro so das eine Interruptfrequenz von mehr als 200 kHz (das bedeutet das alle 500 CPU-Takte ein IRQ kommt) einfach nicht mehr realistisch verarbeitet werden kann, egal wie simpel der IRQ-Handler auch ist. Die Untergrenze liegt einfach darin begründet das der Delay-Counter ja so oft vorhanden ist wie der IRQ-Controller IRQs verwalten kann und da möchte ich diesen Counter natürlich nicht größer machen als unbedingt erforderlich ist da sonst unnötig viel FPGA-Kapazität dafür drauf geht. Im Interface zur SW wird der Wert wohl 16 Bit groß sein und eine Auflösung von 0,25 µs haben und die HW stutzt diesen Wert dann auf 10 Bit mit 1 µs zurecht bevor er intern abgespeichert und verarbeitet wird (das zurechtstutzen kostet nur ein einziges mal FPGA-Kapazität). Vom Design her kann mein IRQ-Controller bis zu 4032 IRQs verwalten (mit so vielen 10 Bit-Countern kann man selbst einen großen FPGA überfüllen) und wenn ich für AHCI mal wirklich die vollen 32 IRQs pro AHCI-Controller nutzen möchte dann werde ich meinem IRQ-Controller wohl mit mindestens 256 IRQs ausstatten müssen und wenn ich damit den FPGA nicht zum überlaufen bringen will dann muss ich eben sparsam damit umgehen.

Damit akzeptierst du fallengelassene Interrupts, wenn ein Gerät zu schnell feuert.
Nö, bei mir geht kein einziger IRQ verloren, die werden ja immer gezählt. Was genau meinst Du eigentlich mit "fallengelassene Interrupts"?

Ja, dieser eine aktive Interrupt muss maskiert werden. Die anderen nicht.
Aber das demaskieren kosten mindestens einen (teuren) Zugriff auf den IRQ-Controller und deswegen möchte ich lieber Edge-Triggered-IRQs, da ist das nicht unbedingt erforderlich. Wenn der generische IRQ-Handler im OS-Kernel zu oft für den selben IRQ aufgerufen wird muss in Software gequeued werden, das kostet Performance (die CPU wird zu oft unnötiger Weise in den Kernel-Mode geschaltet und kann dabei keine sinnvolle Arbeit verrichten) also muss der IRQ-Controller in irgendeiner Weise diesen IRQ erst einmal maskieren. Wenn das demaskieren aber in Software gemacht werden müsste würde das bei jedem IRQ einen teuren Zugriff in den Chipsatz bedeuten und das kostet auch Performance. Diese Automatik mit dem Delay-Counter ist einfach eine Art Trade-Off bei der ich zwar relativ viel Kapazität im FPGA benötige (ein einfaches Mask-Bit wäre deutlich billiger) aber dafür kann ich das Maximum an Performance für die Software bekommen.

Aus der Sicht ist es für die Software ziemlich egal, was da für ein Interrupt verwendet wird. Zwischen Interrupt Controller und CPU sollte es in jedem Fall level-triggered sein (oder sich zumindest so verhalten).
Die Verbindung zwischen IRQ-Controller und CPU ist für die Software und auch für die HW-Geräte völlig uninteressant, diese Verbindung muss nur bestimmte Eigenschaften haben und so lange diese vorhanden sind kann der Plattform-Designer da einbauen was immer er will, meinetwegen auch Rauchzeichen. In meiner Plattform-Spezifikation ist diese Verbindung auch nicht konkretisiert sondern eben nur deren Eigenschaften, konkret hab ich das erst in der Beschreibung für die tatsächliche Umsetzung meiner Plattform.

Es lassen sich ein paar Leitungen einsparen und der PIC braucht nur 15 Eingänge haben
Ist das Dein ernst? In meiner gesamten Plattform gibt es nicht eine einzige IRQ-Leitung (für die HW-Geräte, zwischen dem IRQ-Controller und den CPUs sind 2 dedizierte Leitungen vorgesehen). Edge-Triggered-IRQs kann man wunderbar als MSI einfach mit in den eh vorhandenen Kommunikationskanal (bei mir der PCI-Bus) zwischen Chipsatz und den HW-Geräten integrieren. Also das Argument zählt schon mal nix.

Was ich meinte ist, wenn du schon dedizierte Interrupts benutzt, dann kannst du auch level-triggered Interrupts so benutzen
Das ich so viele dedizierte IRQs unterstütze bedeutet nicht das jeder IRQ auch eine dedizierte Leitung hat. So viele Pins hätte ich am FPGA gar nicht übrig. Mit MSI-X werden pro PCI-Function bis zu 2048 IRQs ermöglicht, es wurde meines Wissens nach noch nie ein Chip mit einem Gehäuse das so viele I/O-Pins hat hergestellt.

dass du kein extra EOI an den Interrupt Controller mehr schicken brauchst: Die Hardware ist in jedem Fall kurzzeitig im Zustand "es liegt kein Interrupt an" (der wurde ja gerade bearbeitet).
Bei Level-Triggered-IRQs benötigt man immer mindestens entweder ein EOI oder eine Demaskierung da ja zwischen dem Moment wo der IRQ-Handler angestoßen wird (und ab dem der IRQ-Controller möglichst wieder eventuelle weitere IRQs ausliefern können soll) und dem Moment wo die Ursache im HW-Gerät behandelt wurde ja eine gewisse Zeit vergeht in der die IRQ-Leitung noch auf aktivem Pegel ansteht. In dieser Zeit soll der IRQ-Controller ja wieder in der Lage sein weitere IRQs auszuliefern also muss in irgendeiner Weise maskiert werden. Bei Egde-Triggered-IRQs ist das nicht so, da steht ja in der Zeit nichts statisch an was der IRQ-Controller absichtlich ignorieren muss. Da in dieser Zeit bei Edge-Triggered natürlich trotzdem neue IRQ-Signale vom HW-Gerät kommen können ist aus meiner Sicht eine automatische und zeitlich begrenze Maskierung angebracht, wenn auch vom Grundprinzip her nicht zwingend erforderlich.

so manche Billigware verursacht massiv IRQs unter Volllast.
Ja, das ist wahr, einer der Gründe warum ich aus Prinzip keine Realtek-HW kaufe. Aber meine Plattform soll ja selbst mit Realtek-HW zurecht kommen, zumindest theoretisch, so das ich schon der Meinung bin in meinem IRQ-Controller entsprechend vorsorgen zu müssen.

Immerhin schafft die 3c589 ihre 10 MBit/s.
Das erwarte ich eigentlich auch, wenn ich eine Netzwerkkarte in einem zeitgemäßen PC einbaue dann erwarte ich auch das man die extern verfügbare Datenrate auch tatsächlich erreichen kann. Ich hatte mal eine NE2000 kompatible als PCI-Gerät, da waren wimre auch die I/O-Port-Zugriffe relativ flott, mit der konnte man die 10 MBit/s auch in voll-duplex ausreizen (wenn auch mit deutlich spürbarer CPU-Last unter Windows 3.11).


Bitte ganz genau sagen, welchen APIC du meinst
Ich meine gar nichts, es fällt mir nur schwer zu glauben das es in den APICs (egal ob IO oder Local) keine Methode geben soll Edge-Triggered-IRQs irgendwie zu begrenzen. Zu Not machst Du Level-Triggered-IRQs draus, auch das ist mit MSI(-X) machbar, zumindest bei den APICs der x86-Plattform, Du musst dann nur immer vor dem demaskieren noch den Pegel wieder händisch auf inaktiv setzen.

Könnte der Counter nicht theoretisch dann irgendwann überlaufen?
Ja, dieser Counter könnte theoretisch überlaufen, in der Praxis wird er sättigen also beim Maximum einfach nicht mehr weiter zählen. Ich habe vor diesen Zähler 6 Bit breit zu machen so das maximal 63 IRQ-Signale gezählt werden können, ich denke das sollte für anständige Hardware reichen. Wenn ein HW-Gerät wiederholt durch einen vollgelaufenen IRQ-Zähler auffällt dann kann der Kernel diesen IRQ ja auch ganz abschalten.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen