Selon ce que j'ai lu jusqu'à présent, "lorsque le noyau reçoit une interruption, tous les gestionnaires enregistrés sont invoqués".
Je comprends que les gestionnaires enregistrés pour chaque IRQ peuvent être consultés via /proc/interrupts
, et je comprends également que les gestionnaires enregistrés proviennent des pilotes qui ont appelé à request_irq
passer un rappel à peu près de la forme:
irqreturn_t (*handler)(int, void *)
D'après ce que je sais, chacun de ces rappels de gestionnaire d'interruption associé à l'IRQ particulier devrait être invoqué, et c'est au gestionnaire de déterminer si l'interruption doit en effet être gérée par lui. Si le gestionnaire ne doit pas gérer l'interruption particulière, il doit renvoyer la macro du noyau IRQ_NONE
.
Ce que j'ai du mal à comprendre, c'est comment chaque pilote est censé déterminer s'il doit gérer l'interruption ou non. Je suppose qu'ils peuvent suivre en interne s'ils sont censés s'attendre à une interruption. Si c'est le cas, je ne sais pas comment ils pourraient faire face à la situation dans laquelle plusieurs pilotes derrière le même IRQ attendent une interruption.
La raison pour laquelle j'essaie de comprendre ces détails est parce que je joue avec le kexec
mécanisme pour ré-exécuter le noyau au milieu du fonctionnement du système tout en jouant avec les broches de réinitialisation et divers registres sur un pont PCIe ainsi qu'un PCI en aval dispositif. Et ce faisant, après un redémarrage, je reçois soit des paniques du noyau, soit d'autres pilotes se plaignant de recevoir des interruptions même si aucune opération n'a eu lieu.
La façon dont le gestionnaire a décidé que l'interruption devait être gérée est le mystère.
Edit: Dans le cas où c'est pertinent, l'architecture CPU en question l'est x86
.
Réponses:
Ceci est couvert dans le chapitre 10 de Linux Device Drivers , 3rd edition, par Corbet et al. Il est disponible gratuitement en ligne , ou vous pouvez lancer la manière de certains shekels O'Reilly pour les formulaires d'arbre mort ou d'ebook. La partie pertinente à votre question commence à la page 278 du premier lien.
Pour ce que ça vaut, voici ma tentative de paraphraser ces trois pages, ainsi que d'autres bits que j'ai recherchés:
Lorsque vous enregistrez un gestionnaire IRQ partagé, le noyau vérifie que:
une. aucun autre gestionnaire n'existe pour cette interruption, ou
b. toutes les personnes précédemment enregistrées ont également demandé un partage d'interruption
Si l'un ou l'autre cas s'applique, il vérifie alors que votre
dev_id
paramètre est unique, afin que le noyau puisse différencier les multiples gestionnaires, par exemple lors de la suppression du gestionnaire.Lorsqu'un périphérique matériel PCI¹ lève la ligne IRQ, le gestionnaire d'interruption de bas niveau du noyau est appelé, et à son tour appelle tous les gestionnaires d'interruption enregistrés, en renvoyant chacun le nom
dev_id
utilisé pour enregistrer le gestionnaire viarequest_irq()
.La
dev_id
valeur doit être unique à la machine. La façon la plus courante de procéder consiste à passer un pointeur sur le périphérique questruct
votre pilote utilise pour gérer ce périphérique. Étant donné que ce pointeur doit se trouver dans l'espace mémoire de votre pilote pour être utile au pilote, il est ipso facto unique à ce pilote.²Si plusieurs pilotes sont enregistrés pour une interruption donnée, ils seront tous appelés lorsque l' un des périphériques déclenche cette ligne d'interruption partagée. Si ce n'est pas le périphérique de votre pilote qui a fait cela, le gestionnaire d'interruption de votre pilote recevra une
dev_id
valeur qui ne lui appartient pas. Le gestionnaire d'interruption de votre chauffeur doit immédiatement revenir lorsque cela se produit.Un autre cas est que votre pilote gère plusieurs périphériques. Le gestionnaire d'interruption du pilote obtiendra l'une des
dev_id
valeurs connues du pilote. Votre code est censé interroger chaque appareil pour savoir lequel a déclenché l'interruption.L'exemple Corbet et al. donner est celui d'un port parallèle PC. Lorsqu'il affirme la ligne d'interruption, il définit également le bit supérieur dans son premier registre de périphérique. (Autrement dit,
inb(0x378) & 0x80 == true
en supposant une numérotation de port d'E / S standard.) Lorsque votre gestionnaire le détecte, il est censé faire son travail, puis effacer l'IRQ en écrivant la valeur lue du port d'E / S sur le port avec le haut peu effacé.Je ne vois aucune raison pour laquelle un mécanisme particulier est spécial. Un périphérique matériel différent pourrait choisir un mécanisme différent. La seule chose importante est que pour qu'un périphérique autorise les interruptions partagées, il doit avoir un moyen pour le pilote de lire l'état d'interruption du périphérique, et un moyen d' effacer l'interruption. Vous devrez lire la fiche technique ou le manuel de programmation de votre appareil pour savoir quel mécanisme utilise votre appareil particulier.
Lorsque votre gestionnaire d'interruption indique au noyau qu'il a géré l'interruption, cela n'empêche pas le noyau de continuer à appeler d'autres gestionnaires enregistrés pour cette même interruption. Cela est inévitable si vous devez partager une ligne d'interruption lorsque vous utilisez des interruptions déclenchées par le niveau.
Imaginez deux appareils affirmant la même ligne d'interruption en même temps. (Ou du moins, si proche dans le temps que le noyau n'a pas le temps d'appeler un gestionnaire d'interruption pour effacer la ligne et ainsi voir la deuxième assertion comme distincte.) Le noyau doit appeler tous les gestionnaires de cette ligne d'interruption, pour donner à chaque une chance d'interroger son matériel associé pour voir s'il a besoin d'attention. Il est tout à fait possible pour deux pilotes différents de gérer avec succès une interruption au cours du même passage dans la liste des gestionnaires pour une interruption donnée.
Pour cette raison, il est impératif que votre pilote indique au périphérique qu'il parvient à effacer son assertion d'interruption quelque temps avant le retour du gestionnaire d'interruption. Ce n'est pas clair pour moi ce qui se passe autrement. La ligne d'interruption affirmée en continu entraînera soit le noyau appelant en permanence les gestionnaires d'interruption partagés, soit elle masquera la capacité du noyau à voir de nouvelles interruptions afin que les gestionnaires ne soient jamais appelés. De toute façon, un désastre.
Notes de bas de page:
J'ai spécifié PCI ci-dessus parce que tout ce qui précède suppose des interruptions déclenchées par le niveau , telles qu'utilisées dans la spécification PCI d'origine. ISA utilisait des interruptions déclenchées par les bords , ce qui rendait le partage délicat au mieux, et possible même alors uniquement s'il était pris en charge par le matériel. PCIe utilise des interruptions signalées par message ; le message d'interruption contient une valeur unique que le noyau peut utiliser pour éviter le jeu de devinettes à tour de rôle requis avec le partage d'interruption PCI. PCIe peut éliminer le besoin même de partage d'interruption. (Je ne sais pas si c'est le cas, juste qu'il a le potentiel de le faire.)
Les pilotes du noyau Linux partagent tous le même espace mémoire, mais un pilote indépendant n'est pas censé contourner l'espace mémoire d'un autre. À moins que vous ne passiez ce pointeur, vous pouvez être sûr qu'un autre pilote ne parviendra pas par lui-même à la même valeur par accident.
la source
dev_id
qui ne lui appartient pas. Pour moi, il semble qu'il y ait une chance non nulle qu'un pilote qui ne possède pas ladev_id
structure puisse toujours la confondre avec la sienne en fonction de la façon dont il interprète le contenu. Si ce n'est pas le cas, quel mécanisme empêcherait cela?dev_id
un pointeur sur quelque chose dans l'espace mémoire de votre pilote. Un autre pilote pourrait constituer unedev_id
valeur qui s'est avérée être confondue avec un pointeur sur la mémoire que possède votre pilote, mais cela ne se produira pas parce que tout le monde respecte les règles. Il s'agit de l'espace noyau, rappelez-vous: l'autodiscipline est supposée comme une évidence, contrairement au code de l'espace utilisateur, qui peut présumer allègrement que tout ce qui n'est pas interdit est autorisé.dev_id
qu'il ne possède pas.dev_id
ne vous aide pas à déterminer si cela s'est produit. Vous devez demander au matériel, "Vous avez sonné?"kexec
.Lorsqu'un pilote demande une IRQ partagée, il transmet un pointeur au noyau vers une référence à une structure spécifique au périphérique dans l'espace mémoire du pilote.
Selon LDD3:
Lors de la vérification de plusieurs gestionnaires IRQ de pilotes, il apparaît qu'ils sondent le matériel lui-même afin de déterminer s'il doit gérer l'interruption ou le retour
IRQ_NONE
.Exemples
Pilote UHCI-HCDDans le code ci-dessus, le pilote lit le
Pilote SDHCIUSBSTS
registre pour déterminer s'il y a une interruption de service.Tout comme dans l'exemple précédent, le pilote vérifie un registre d'état
Pilote Ath5kSDHCI_INT_STATUS
pour déterminer s'il doit réparer une interruption.Encore un exemple.
la source
Veuillez consulter ce lien :
la source