De acordo com o que li até agora, "quando o kernel recebe uma interrupção, todos os manipuladores registrados são chamados".
Entendo que os manipuladores registrados para cada IRQ podem ser visualizados via /proc/interrupts
, e também entendo que os manipuladores registrados são provenientes dos drivers que invocaram a request_irq
passagem em um retorno de chamada aproximadamente do formulário:
irqreturn_t (*handler)(int, void *)
Com base no que eu sei, cada um desses retornos de chamada do manipulador de interrupção associados ao IRQ específico deve ser chamado, e cabe ao manipulador determinar se a interrupção deve realmente ser tratada por ele. Se o manipulador não deve lidar com a interrupção específica, ele deve retornar a macro do kernel IRQ_NONE
.
O que estou tendo problemas para entender é como é esperado que cada driver determine se deve lidar com a interrupção ou não. Suponho que eles possam acompanhar internamente se deveriam esperar uma interrupção. Nesse caso, não sei como eles seriam capazes de lidar com a situação em que vários drivers por trás do mesmo IRQ estão esperando uma interrupção.
A razão pela qual estou tentando entender esses detalhes é porque estou mexendo com o kexec
mecanismo para reexecutar o kernel no meio da operação do sistema enquanto brinco com os pinos de redefinição e vários registradores em uma ponte PCIe, bem como uma PCI a jusante. dispositivo. E, ao fazer isso, após uma reinicialização, eu estou tendo pânico no kernel ou outros drivers reclamando que estão recebendo interrupções, mesmo que nenhuma operação esteja ocorrendo.
Como o manipulador decidiu que a interrupção deveria ser tratada por ele é o mistério.
Edit: Caso seja relevante, a arquitetura da CPU em questão é x86
.
Respostas:
Isso é abordado no capítulo 10 dos Linux Device Drivers , 3ª edição, de Corbet et al. Está disponível gratuitamente on - line , ou você pode usar o shekels O'Reilly para encontrar árvores mortas ou formulários de e-books. A parte relevante para sua pergunta começa na página 278 no primeiro link.
Pelo que vale, aqui está minha tentativa de parafrasear essas três páginas, além de outros bits que pesquisei no Google:
Quando você registra um manipulador de IRQ compartilhado, o kernel verifica se:
uma. não existe outro manipulador para essa interrupção ou
b. todos os registrados anteriormente também solicitaram o compartilhamento de interrupções
Se um dos casos se aplicar, ele verifica se seu
dev_id
parâmetro é único, para que o kernel possa diferenciar os vários manipuladores, por exemplo, durante a remoção do manipulador.Quando um dispositivo de hardware PCI¹ aumenta a linha de IRQ, o manipulador de interrupção de baixo nível do kernel é chamado e, por sua vez, chama todos os manipuladores de interrupção registrados, passando cada um de volta o que
dev_id
você usou para registrar o manipuladorrequest_irq()
.O
dev_id
valor precisa ser exclusivo da máquina. A maneira mais comum de fazer isso é passar um ponteiro para o dispositivo questruct
o driver usa para gerenciar esse dispositivo. Como esse ponteiro deve estar no espaço de memória do driver para ser útil ao motorista, é ipso facto exclusivo desse driver.²Se houver vários drivers registrados para uma determinada interrupção, todos eles serão chamados quando qualquer um dos dispositivos aumentar a linha de interrupção compartilhada. Se não foi o dispositivo do seu motorista que fez isso, o manipulador de interrupções do seu motorista receberá um
dev_id
valor que não pertence a ele. O manipulador de interrupções do seu motorista deve retornar imediatamente quando isso acontecer.Outro caso é que seu driver está gerenciando vários dispositivos. O manipulador de interrupções do driver obterá um dos
dev_id
valores conhecidos pelo driver. Seu código deve pesquisar cada dispositivo para descobrir qual deles causou a interrupção.O exemplo Corbet et al. give é o de uma porta paralela do PC. Quando afirma a linha de interrupção, também define o bit superior em seu primeiro registro de dispositivo. (Ou seja,
inb(0x378) & 0x80 == true
assumindo a numeração da porta de E / S padrão.) Quando o manipulador detectar isso, ele deve fazer seu trabalho e, em seguida, limpe o IRQ escrevendo o valor lido da porta de E / S na porta com a parte superior. pouco limpo.Não vejo nenhuma razão para que esse mecanismo específico seja especial. Um dispositivo de hardware diferente pode escolher um mecanismo diferente. A única coisa importante é que, para um dispositivo permitir interrupções compartilhadas, é necessário que haja alguma maneira de o driver ler o status de interrupção do dispositivo e alguma maneira de limpar a interrupção. Você precisará ler a folha de dados ou o manual de programação do seu dispositivo para descobrir qual mecanismo o seu dispositivo específico usa.
Quando o manipulador de interrupção diz ao kernel que manipulou a interrupção, isso não impede que o kernel continue chamando outros manipuladores registrados para a mesma interrupção. Isso é inevitável se você deseja compartilhar uma linha de interrupção ao usar interrupções acionadas por nível.
Imagine dois dispositivos afirmarem a mesma linha de interrupção ao mesmo tempo. (Ou pelo menos, tão perto no tempo que o kernel não tem tempo para chamar um manipulador de interrupções para limpar a linha e, assim, ver a segunda asserção como separada.) O kernel deve chamar todos os manipuladores para essa linha de interrupção, para fornecer a cada uma chance de consultar seu hardware associado para ver se ele precisa de atenção. É bem possível para dois drivers diferentes manipularem com êxito uma interrupção na mesma passagem pela lista de manipuladores para uma determinada interrupção.
Por esse motivo, é imperativo que seu driver informe ao dispositivo que está conseguindo limpar sua declaração de interrupção algum tempo antes do retorno do manipulador de interrupções. Não está claro para mim o que acontece de outra maneira. A linha de interrupção continuamente declarada resultará no kernel chamando continuamente os manipuladores de interrupção compartilhados ou mascarará a capacidade do kernel de ver novas interrupções para que os manipuladores nunca sejam chamados. De qualquer maneira, desastre.
Notas de rodapé:
Especifiquei o PCI acima porque todos os itens acima pressupõem interrupções acionadas por nível , conforme usado na especificação original do PCI. O ISA usava interrupções acionadas por borda , o que tornava o compartilhamento complicado, na melhor das hipóteses, e possível mesmo assim somente quando suportado pelo hardware. O PCIe usa interrupções sinalizadas por mensagem ; a mensagem de interrupção contém um valor exclusivo que o kernel pode usar para evitar o jogo de adivinhação exigido com o compartilhamento de interrupção do PCI. O PCIe pode eliminar a grande necessidade de compartilhamento de interrupção. (Não sei se realmente existe, apenas que tem potencial.)
Todos os drivers do kernel do Linux compartilham o mesmo espaço de memória, mas um driver não relacionado não deve estar mexendo no espaço de memória de outro. A menos que você passe esse ponteiro, você pode ter certeza de que outro motorista não terá o mesmo valor acidentalmente por si próprio.
fonte
dev_id
que não possui. Para mim, parece que existe uma chance diferente de zero de que um driver que não possui adev_id
estrutura ainda possa confundi-la como sua com base na maneira como interpreta o conteúdo. Se não for esse o caso, que mecanismo impediria isso?dev_id
um ponteiro para algo dentro do espaço de memória do driver. Outro piloto poderia tornar-se umdev_id
valor que aconteceu para ser confusable com um ponteiro para a memória o driver possui, mas isso não vai acontecer, porque todo mundo está jogando pelas regras. Este é o espaço do kernel, lembre-se: a autodisciplina é assumida como uma questão de disciplina, diferente do código do espaço do usuário, que pode alegremente assumir que qualquer coisa não proibida é permitida.dev_id
que não possui.dev_id
não ajuda a determinar se isso aconteceu. Você precisa perguntar ao hardware: "Você tocou?"kexec
.Quando um driver solicita um IRQ compartilhado, ele passa um ponteiro para o kernel para uma referência a uma estrutura específica do dispositivo no espaço de memória do driver.
De acordo com o LDD3:
Ao verificar os manipuladores de IRQ de vários drivers, parece que eles analisam o próprio hardware para determinar se deve lidar com a interrupção ou o retorno
IRQ_NONE
.Exemplos
Driver UHCI-HCDNo código acima, o driver está lendo o
Driver SDHCIUSBSTS
registro para determinar se há uma interrupção no serviço.Assim como no exemplo anterior, o driver está verificando um registro de status
Ath5k DriverSDHCI_INT_STATUS
para determinar se precisa reparar uma interrupção.Apenas mais um exemplo.
fonte
Por favor, visite este link :
fonte