Según lo que he leído hasta ahora, "cuando el núcleo recibe una interrupción, se invocan todos los controladores registrados".
Entiendo que los controladores registrados para cada IRQ se pueden ver a través de /proc/interrupts
, y también entiendo que los controladores registrados provienen de los controladores que han invocado request_irq
pasar una devolución de llamada aproximadamente del formulario:
irqreturn_t (*handler)(int, void *)
En base a lo que sé, cada una de estas devoluciones de llamada del controlador de interrupciones asociadas con la IRQ particular debe invocarse, y depende del controlador determinar si la interrupción debe ser manejada por ella. Si el controlador no debe manejar la interrupción particular, debe devolver la macro del núcleo IRQ_NONE
.
Lo que tengo problemas para entender es cómo se espera que cada controlador determine si debe manejar la interrupción o no. Supongo que pueden realizar un seguimiento interno si se supone que esperan una interrupción. Si es así, no sé cómo podrían lidiar con la situación en la que varios conductores detrás del mismo IRQ esperan una interrupción.
La razón por la que estoy tratando de entender estos detalles es porque estoy jugando con el kexec
mecanismo para volver a ejecutar el kernel en medio de la operación del sistema mientras juego con los pines de reinicio y varios registros en un puente PCIe, así como un PCI descendente dispositivo. Y al hacerlo, después de un reinicio, recibo pánico en el kernel u otros controladores quejándose de que están recibiendo interrupciones a pesar de que no se realizó ninguna operación.
Cómo el manejador decidió que la interrupción debería ser manejada por él es el misterio.
Editar: en caso de que sea relevante, la arquitectura de la CPU en cuestión es x86
.
Respuestas:
Esto está cubierto en el capítulo 10 de Controladores de dispositivos Linux , 3a edición, por Corbet et al. Está disponible de forma gratuita en línea , o puede arrojar algunos shekels O'Reilly para árboles muertos o formularios de libros electrónicos. La parte relevante a su pregunta comienza en la página 278 en el primer enlace.
Para lo que vale, aquí está mi intento de parafrasear esas tres páginas, más otros bits que he buscado en Google:
Cuando registra un controlador IRQ compartido, el núcleo verifica que:
a. no existe otro controlador para esa interrupción, o
si. todos los previamente registrados también solicitaron compartir interrupciones
Si se aplica cualquiera de los casos, verifica que su
dev_id
parámetro sea único, de modo que el núcleo pueda diferenciar los múltiples controladores, por ejemplo, durante la eliminación del controlador.Cuando un dispositivo de hardware PCI¹ eleva la línea IRQ, se llama al manejador de interrupciones de bajo nivel del núcleo y, a su vez, llama a todos los manejadores de interrupciones registrados, pasando cada uno de los
dev_id
que utilizó para registrar el manejadorrequest_irq()
.El
dev_id
valor debe ser exclusivo de la máquina. La forma común de hacerlo es pasar un puntero al dispositivostruct
que usa su controlador para administrar ese dispositivo. Dado que este puntero debe estar dentro del espacio de memoria de su controlador para que sea útil para el controlador, es ipso facto exclusivo de ese controlador.²Si hay varios controladores registrados para una interrupción determinada, todos se llamarán cuando cualquiera de los dispositivos eleve esa línea de interrupción compartida. Si no fue el dispositivo de su conductor el que hizo esto, el controlador de interrupción de su controlador recibirá un
dev_id
valor que no le pertenece. El controlador de interrupciones de su conductor debe regresar inmediatamente cuando esto suceda.Otro caso es que su controlador está administrando múltiples dispositivos. El controlador de interrupciones del controlador obtendrá uno de los
dev_id
valores conocidos por el controlador. Se supone que su código sondea cada dispositivo para averiguar cuál provocó la interrupción.El ejemplo Corbet et al. dar es el de un puerto paralelo de PC. Cuando afirma la línea de interrupción, también establece el bit superior en su primer registro de dispositivo. (Es decir,
inb(0x378) & 0x80 == true
suponiendo una numeración de puerto de E / S estándar). Cuando su controlador detecta esto, se supone que debe hacer su trabajo, luego borre la IRQ escribiendo el valor leído desde el puerto de E / S de nuevo al puerto con la parte superior poco despejadoNo veo ninguna razón para que ese mecanismo en particular sea especial. Un dispositivo de hardware diferente podría elegir un mecanismo diferente. Lo único importante es que para que un dispositivo permita interrupciones compartidas, tiene que tener alguna forma para que el controlador lea el estado de la interrupción del dispositivo y alguna forma de borrar la interrupción. Tendrá que leer la hoja de datos de su dispositivo o el manual de programación para averiguar qué mecanismo utiliza su dispositivo en particular.
Cuando su controlador de interrupciones le dice al núcleo que manejó la interrupción, eso no impide que el núcleo continúe llamando a otros controladores registrados para esa misma interrupción. Esto es inevitable si va a compartir una línea de interrupción cuando usa interrupciones activadas por nivel.
Imagine que dos dispositivos afirman la misma línea de interrupción al mismo tiempo. (O al menos, tan cerca en el tiempo que el núcleo no tiene tiempo para llamar a un controlador de interrupciones para borrar la línea y, por lo tanto, ver la segunda afirmación como separada). El núcleo debe llamar a todos los controladores para esa línea de interrupción, para dar a cada uno una oportunidad de consultar su hardware asociado para ver si necesita atención. Es muy posible que dos controladores diferentes manejen con éxito una interrupción dentro del mismo paso a través de la lista de controladores para una interrupción dada.
Debido a esto, es imperativo que su controlador le informe al dispositivo que está logrando borrar su afirmación de interrupción en algún momento antes de que regrese el controlador de interrupción. No me queda claro qué sucede de otra manera. La línea de interrupción afirmada continuamente dará como resultado que el kernel llame continuamente a los manejadores de interrupción compartidos, o enmascarará la capacidad del kernel para ver nuevas interrupciones para que los manejadores nunca sean llamados. De cualquier manera, desastre.
Notas al pie:
Especifiqué PCI arriba porque todo lo anterior supone interrupciones disparadas por nivel , como se usa en la especificación PCI original. ISA usó interrupciones activadas por el borde , lo que hizo que el intercambio fuera complicado en el mejor de los casos, y posible incluso cuando solo es compatible con el hardware. PCIe utiliza interrupciones señalizadas por mensaje ; el mensaje de interrupción contiene un valor único que el núcleo puede usar para evitar el juego de adivinanzas round-robin requerido con el uso compartido de interrupciones PCI. PCIe puede eliminar la necesidad de compartir interrupciones. (No sé si realmente lo hace, solo que tiene el potencial de hacerlo).
Todos los controladores del kernel de Linux comparten el mismo espacio de memoria, pero se supone que un controlador no relacionado debe estar en el espacio de memoria de otro. A menos que pase ese puntero, puede estar bastante seguro de que otro controlador no obtendrá ese mismo valor accidentalmente por sí solo.
fuente
dev_id
que no es de su propiedad. Para mí, parece que hay una posibilidad distinta de cero de que un controlador que no posee ladev_id
estructura aún pueda confundirla como propia en función de cómo interpreta el contenido. Si este no es el caso, ¿qué mecanismo evitaría esto?dev_id
un puntero a algo dentro del espacio de memoria de su controlador. Otro controlador podría inventar undev_id
valor que resultó ser confuso con un puntero a la memoria que posee su controlador, pero eso no sucederá porque todos cumplen las reglas. Esto es espacio de kernel, recuerde: la autodisciplina se asume como algo natural, a diferencia del código de espacio de usuario, que puede asumir alegremente que todo lo que no está prohibido está permitido.dev_id
dispositivo que no es de su propiedad.dev_id
no te ayuda a determinar si esto ha sucedido. Tienes que preguntarle al hardware: "¿Llamaste?"kexec
.Cuando un controlador solicita una IRQ compartida, pasa un puntero al núcleo para hacer referencia a una estructura específica del dispositivo dentro del espacio de memoria del controlador.
De acuerdo con LDD3:
Al verificar los controladores IRQ de varios controladores, parece que prueban el hardware en sí para determinar si debe manejar o no la interrupción o el retorno
IRQ_NONE
.Ejemplos
Controlador UHCI-HCDEn el código anterior, el controlador está leyendo el
Controlador SDHCIUSBSTS
registro para determinar si hay una interrupción al servicio.Al igual que en el ejemplo anterior, el controlador está verificando un registro de estado,
Ath5k DriverSDHCI_INT_STATUS
para determinar si necesita dar servicio a una interrupción.Solo un ejemplo más.
fuente
Por favor visite ver este enlace :
fuente