Sincronización del código de interrupción

Los siguientes factores complican el código de controlador que controla las interrupciones de hardware en sistemas multiprocesador:

  • Cada vez que un dispositivo interrumpe, proporciona información específica de la interrupción que es volátil porque se puede sobrescribir la próxima vez que se interrumpa el dispositivo.

  • Los dispositivos interrumpen en irQL relativamente altos y sus rutinas de servicio de interrupción (ISR) pueden interrumpir la ejecución de otro código de controlador.

  • Para las interrupciones de DIRQL, el ISR debe ejecutarse en DIRQL mientras mantiene un bloqueo de giro proporcionado por el controlador, de modo que el ISR pueda evitar interrupciones adicionales mientras guarda información volátil. El DIRQL impide la interrupción del procesador actual y el bloqueo de número impide la interrupción por otro procesador.

  • El ISR debe ejecutarse rápidamente porque el dispositivo no puede interrumpir mientras se ejecuta el ISR. Los tiempos de ejecución de ISR largos pueden ralentizar el sistema o, posiblemente, provocar la pérdida de datos.

  • Tanto el ISR como la rutina de llamada a procedimiento diferido (DPC) normalmente deben tener acceso a un área de almacenamiento en la que el ISR almacena los datos volátiles del dispositivo. Estas rutinas deben sincronizarse entre sí para que no tengan acceso al área de almacenamiento al mismo tiempo.

Debido a todos estos factores, debe usar las reglas siguientes al escribir código de controlador que controle las interrupciones:

  • Solo la función de devolución de llamada EvtInterruptIsr accede a los datos de interrupción volátiles, como los registros de dispositivos que contienen información de interrupción.

    La función de devolución de llamada EvtInterruptIsr debe mover los datos volátiles a un búfer de datos de interrupción definido por el controlador al que pueden tener acceso las funciones de devolución de llamada EvtInterruptDpcDpc,EvtInterruptWorkItem o varias funciones de devolución de llamada EvtDpcFunc .

    Si el controlador proporciona funciones de devolución de llamada EvtInterruptDpc o EvtInterruptWorkItem para sus objetos de interrupción, el mejor lugar para almacenar los datos de interrupción es el espacio de contexto del objeto de interrupción. Las funciones de devolución de llamada del objeto de interrupción pueden tener acceso al espacio de contexto del objeto mediante el identificador de objeto que reciben.

    Si el controlador proporciona varias funciones de devolución de llamada EvtDpcFunc para cada función de devolución de llamada EvtInterruptIsr , puede almacenar datos de interrupción en cada espacio de contexto del objeto DPC.

  • Todo el código de controlador que accede al búfer de datos de interrupción debe sincronizarse para que solo una rutina tenga acceso a los datos a la vez.

    Para los objetos de interrupción DIRQL, la función de devolución de llamada EvtInterruptIsr accede a este búfer de datos en IRQL = DIRQL mientras mantiene el bloqueo de número proporcionado por el controlador del objeto de interrupción. Por lo tanto, todas las rutinas que acceden al búfer también deben ejecutarse en DIRQL mientras mantiene el bloqueo de número. (Normalmente, la función de devolución de llamada EvtInterruptDpc o EvtDpcFunc de la interrupción es la única rutina que debe tener acceso al búfer).

    Todas las rutinas que tienen acceso a un búfer de datos de interrupción, excepto para la función de devolución de llamada EvtInterruptIsr , deben realizar una de las siguientes acciones:

    Ambas técnicas permiten que la función EvtInterruptDpc o EvtDpcFunc acceda a los datos de interrupción en DIRQL mientras mantiene el bloqueo de giro de la interrupción. El DIRQL impide la interrupción del procesador actual y el bloqueo de número impide la interrupción por otro procesador.

    Si el dispositivo admite varios vectores de interrupción o mensajes, y si desea sincronizar el control del controlador de estas interrupciones, puede asignar un único bloqueo de número a varios objetos de interrupción DIRQL. El marco determina el DIRQL más alto del conjunto de interrupciones y siempre adquiere el bloqueo de número en ese DIRQL para que el código sincronizado no pueda interrumpirse por ningún vector de interrupción o mensajes del conjunto.

    En el caso de los objetos de interrupción de nivel pasivo, el marco adquiere el bloqueo de interrupción de nivel pasivo antes de llamar a la función de devolución de llamada EvtInterruptIsr del controlador en IRQL = PASSIVE_LEVEL. Como resultado, todas las rutinas que acceden al búfer deben adquirir el bloqueo de interrupción o sincronizar internamente el acceso al búfer. Normalmente, la función de devolución de llamada EvtInterruptWorkItem de la interrupción es la única rutina que accede al búfer. Para obtener información sobre cómo adquirir el bloqueo de interrupción de una función de devolución de llamada EvtInterruptWorkItem , vea la sección Comentarios de esa página.

    También puede sincronizar el control del controlador de varios vectores de interrupción asignando un único bloqueo de espera a varios objetos de interrupción de nivel pasivo.

  • Si parte del código que controla las interrupciones de DIRQL debe ejecutarse en IRQL = PASSIVE_LEVEL, la función de devolución de llamada EvtInterruptDpc o EvtDpcFunc puede crear uno o varios elementos de trabajo para que el código se ejecute como funciones de devolución de llamada EvtWorkItem .

    Como alternativa, en las versiones 1.11 y posteriores de KMDF, el controlador puede solicitar un elemento de trabajo de interrupción llamando a WdfInterruptQueueWorkItemForIsr. (Recuerde que la función de devolución de llamada EvtInterruptIsr de un controlador puede llamar a WdfInterruptQueueWorkItemForIsr o WdfInterruptQueueDpcForIsr, pero no ambas).

  • Si es importante sincronizar las funciones de devolución de llamada EvtInterruptDpc y EvtDpcFunc de un controlador entre sí y con otras funciones de devolución de llamada asociadas a un dispositivo, el controlador puede establecer el miembro AutomaticSerialization en TRUE en la estructura de WDF_INTERRUPT_CONFIG de interrupción y la estructura de WDF_DPC_CONFIG del objeto DPC. Como alternativa, el controlador puede usar bloqueos de número de marco. (Si se establece el miembro AutomaticSerialization en TRUE , no se sincroniza una función de devolución de llamada EvtInterruptIsr con otras funciones de devolución de llamada. Use WdfInterruptSynchronize o WdfInterruptAcquireLock para sincronizar una función de devolución de llamada EvtInterruptIsr , como se ha descrito anteriormente en este tema).

Para obtener más información sobre la sincronización de rutinas de controladores, consulte Técnicas de sincronización para controladores de Framework-Based.