multithreading linux-kernel linux-device-driver

multithreading - Aclaración sobre el comportamiento de request_threaded_irq



linux-kernel linux-device-driver (4)

  1. Anteriormente, la mitad inferior no era una task y aún no podía bloquear . La única diferencia era que las interrupciones estaban deshabilitadas. El tasklet o softirq permiten diferentes interbloqueo entre el hilo ISR del controlador y la API del usuario ( ioctl() , read() y write() ).
  2. Creo que la work queue es casi equivalente. Sin embargo, el tasklet / ksoftirq tiene una alta prioridad y es utilizado por todas las funcionalidades basadas en ISR en ese procesador. Esto puede brindar mejores oportunidades de programación. Además, hay menos para el controlador para administrar; todo ya está incorporado en el código del controlador ISR del kernel.
  3. Debes manejar esto. Por lo general , se pueden usar almacenamientos intermedios ping-pong o un kfifo como usted sugiere. El handler debe ser codicioso y obtener todos los datos del UART antes de devolver IRQ_WAKE_THREAD .

He recorrido la web, pero no he encontrado una respuesta convincente a un par de preguntas relacionadas que tengo, con respecto a la función "request_threaded_irq".

Pregunta 1: Primero, estaba leyendo este artículo, con respecto a las IRQ enhebradas:

http://lwn.net/Articles/302043/

y hay una línea que no me queda clara:

"La conversión de una interrupción a enhebrada solo tiene sentido cuando el código del manejador se aprovecha de ella integrando la funcionalidad tasklet / softirq y simplificando el bloqueo".

Entiendo que si hubiésemos avanzado con un enfoque "tradicional" de mitad superior / inferior, hubiésemos necesitado tanto bloqueos de giro como inhabilitación de IRQ local para interferir con los datos compartidos. Pero, lo que no entiendo es cómo las interrupciones de subprocesos simplificarían la necesidad de bloqueo integrando la funcionalidad de tasklet / softirq.

Pregunta 2: En segundo lugar, ¿qué ventaja (si hay alguna) tiene un enfoque request_threaded_handler sobre un enfoque de la mitad inferior basado en work_queue? En ambos casos parece, como si el "trabajo" se deferiera a un hilo dedicado. Entonces cuál es la diferencia ?

Pregunta 3: Por último, en el siguiente prototipo:

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)

¿Es posible que la parte del "controlador" de la IRQ se active continuamente por la IRQ relevante (digamos un UART que recibe caracteres a una velocidad alta), incluso mientras el "hilo_fn" (escribiendo rx''d bytes en una memoria intermedia circular) parte de el controlador de interrupción está ocupado procesando IRQ de las activaciones previas? Entonces, ¿el manejador no intentaría "despertar" un "hilo_fn" ya en ejecución? ¿Cómo se comportaría el irq thread_fn en ese caso?

Realmente apreciaría si alguien puede ayudarme a entender esto.

Gracias, vj


Para la pregunta n. ° 3, cuando se activa un threadedirq, la línea de interrupción correspondiente se enmascara / deshabilita. cuando el threadedirq se ejecuta y completa, lo habilita hacia el final del mismo. por lo tanto, no habrá ningún disparo de interrupción mientras se está ejecutando el threadedirq respectivo.


Para la Pregunta 2, un subproceso de IRQ en la creación se configura con una prioridad más alta, a diferencia de las secuencias de trabajo. En kernel/irq/manage.c , verá algún código como el siguiente para la creación de subprocesos del kernel para IRQ con subprocesos:

static const struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name); if (IS_ERR(t)) { ret = PTR_ERR(t); goto out_mput; } sched_setscheduler_nocheck(t, SCHED_FIFO, &param);

Aquí puede ver que la política de programación del hilo del kernel está configurada como RT ( SCHED_FIFO ) y la prioridad del hilo está establecida en MAX_USER_RT_PRIO/2 que es más alta que los procesos normales.

Para la Pregunta 3, la situación que describió también puede ocurrir con interrupciones normales. Normalmente en el núcleo, las interrupciones se desactivan mientras se ejecuta un ISR. Durante la ejecución del ISR, los caracteres pueden seguir llenando el búfer del dispositivo y el dispositivo puede y debe continuar para afirmar una interrupción incluso cuando las interrupciones están deshabilitadas.

El trabajo del dispositivo es asegurarse de que la línea IRQ se mantenga activa hasta que se lean todos los caracteres y el ISR complete cualquier procesamiento. También es importante que la interrupción se desencadene a nivel, o dependiendo del diseño esté bloqueada por el controlador de interrupción.

Por último, el dispositivo / periférico debe tener un FIFO de tamaño adecuado para que los caracteres entregados a una velocidad alta no se pierdan por un ISR lento. El ISR también debe diseñarse para leer tantos caracteres como sea posible cuando se ejecuta.

En términos generales, lo que he visto es que un controlador tendría un FIFO de cierto tamaño X , y cuando el FIFO se llena X/2 , activaría una interrupción que haría que el ISR obtuviera la mayor cantidad de datos posible. El ISR lee tanto como sea posible y luego borra la interrupción. Mientras tanto, si el FIFO sigue siendo X/2 , el dispositivo mantendría la línea de interrupción afirmada, haciendo que el ISR se ejecute nuevamente.


El trabajo original de convertir manipuladores "duros" / "blandos" a manejadores roscados fue hecho por Thomas Gleixner y su equipo al construir el proyecto PREEMPT_RT Linux (también conocido como Linux-as-un-RTOS) (no es parte de la línea principal). Para que realmente Linux se ejecute como un RTOS, no podemos tolerar una situación en la que un controlador de interrupciones interrumpa el hilo rt (app) más crítico; pero ¿cómo podemos asegurarnos de que el hilo de la aplicación incluso anule una interrupción? Haciéndolo (la interrupción) enhebrado, programable (SCHED_FIFO) y tiene una prioridad más baja que el hilo de la aplicación (hilos de interrupción rtprio tiene por defecto 50). Entonces, un subproceso de la aplicación "rt" FEDO con un rtprio de 60 podría "adelantarse" (lo suficientemente cerca como para que funcione) incluso un hilo de interrupción. Eso debería responder a tus preguntas y respuestas. 2.

Wrt a Qs 3: Como han dicho otros, su código debe manejar esta situación. Una vez dicho esto, observe que un punto clave para usar un manejador de subprocesos es que pueda hacer un trabajo que (posiblemente) bloquee (duerma). Si su trabajo de "la mitad inferior" está garantizado como no bloqueante y debe ser rápido, utilice los manejadores de estilo tradicional "superior / medio / bh". ¿Cómo podemos hacer eso? Simple: no use request_threaded_irq () simplemente llame request_irq () - el comentario en el código dice claramente (wrt 3er parámetro):

* @thread_fn: Function called from the irq handler thread * If NULL, no irq thread is created"

Alternativamente, puede pasar el indicador IRQF_NO_THREAD a request_irq.

(Por cierto, una comprobación rápida con cscope en el árbol de fuentes del kernel 3.14.23 muestra que request_irq () se llama 1502 veces [dándonos el manejo de interrupciones sin hilos ], y request_threaded_irq () [ threaded interrupts] se llama explícitamente 204 veces).