Compartir a través de


Modo de sondeo de NDIS

Información general sobre el modo de sondeo de NDIS

El modo de sondeo NDIS es un modelo de ejecución de sondeo controlado por el sistema operativo que controla la ruta de datos de la interfaz de red.

Anteriormente, NDIS no tenía ninguna definición formal de un contexto de ejecución de ruta de datos. Normalmente, los controladores NDIS se basan en llamadas a procedimientos diferidos (DPC) para implementar su modelo de ejecución. Sin embargo, el uso de DPC puede sobrecargar el sistema cuando se realizan cadenas de indicación largas y evitar este problema requiere una gran cantidad de código que es difícil de obtener correctamente. El modo de sondeo NDIS ofrece una alternativa a los DPC y herramientas de ejecución similares.

El modo de sondeo NDIS mueve la complejidad de las decisiones de programación fuera de los controladores NIC y a NDIS, donde NDIS establece límites de trabajo por iteración. Para lograr este modo de sondeo proporciona:

  1. Un mecanismo para que el sistema operativo ejerce presión inversa en la NIC.

  2. Un mecanismo para que el sistema operativo controle correctamente las interrupciones.

El modo de sondeo NDIS está disponible para los controladores de miniporte NDIS 6.85 y versiones posteriores.

Problemas con el modelo DPC

En el diagrama de secuencia siguiente se muestra un ejemplo típico de cómo un controlador de miniporte NDIS controla una ráfaga de paquetes Rx mediante un DPC. En este ejemplo, el hardware es estándar en términos de NIC PCIe. Tiene una cola de hardware de recepción y una máscara de interrupción para esa cola.

Diagrama que muestra el modelo DPC de NDIS con paquetes Rx y una cola de hardware de recepción.

Cuando no hay ninguna actividad de red, el hardware tiene habilitada la interrupción rx. Cuando llega un paquete Rx:

  1. El hardware genera una interrupción y NDIS llama a la función MiniportInterrupt (ISR) del controlador.

  2. El controlador hace muy poco trabajo en el ISR porque se ejecutan en un IRQL muy alto. El controlador deshabilita la interrupción del ISR y aplaza el procesamiento de hardware a una función MiniportInterruptDPC (DPC).

  3. NDIS finalmente llama al DPC del controlador y el controlador purga las finalizaciones de la cola de hardware e indica al sistema operativo.

Dos puntos débiles pueden afectar a la pila de red cuando el controlador aplaza las operaciones de E/S a un DPC:

  1. El controlador no sabe si el sistema es capaz de procesar todos los datos que se indican, por lo que el controlador no tiene opción, sino purgar tantos elementos como sea posible desde su cola de hardware e indicarles la pila.

  2. Dado que el controlador usa un DPC para aplazar el trabajo de su ISR, todas las indicaciones se realizan en DISPATCH_LEVEL. Esto puede sobrecargar el sistema cuando se realizan cadenas de indicación largas y provocar la comprobación de errores 0x133 DPC_WATCHDOG_VIOLATION.

Evitar estos puntos débiles requiere mucho código complicado en el controlador. Aunque puede comprobar si el guardián de DPC está cerca del límite con la función KeQueryDpcWatchdogInformation y salir del DPC, todavía necesita crear una infraestructura alrededor de esto en el controlador: necesita alguna manera de pausar un poco, continuar indicando los paquetes y, al mismo tiempo, debe sincronizar todo esto con la duración de la ruta de datos.

Introducción a los objetos Poll

El modo de sondeo NDIS presenta el objeto Poll para resolver los puntos débiles asociados a los DPC. Un objeto Poll es una construcción de contexto de ejecución. Los controladores de miniport pueden usar un objeto Poll en lugar de un DPC cuando se trabaja con operaciones de ruta de datos.

Un objeto Poll ofrece lo siguiente:

  • Proporciona una manera de que NDIS establezca límites de trabajo por iteración.

  • Está estrechamente vinculado a un mecanismo de notificación. Esto mantiene el sistema operativo y la NIC sincronizados con respecto a cuándo se debe procesar el trabajo.

  • Tiene un concepto de iteración e interrupciones integradas. Al usar DPC, los controladores se ven obligados a volver a habilitar la interrupción cada vez que finalizan un DPC. Al usar objetos Poll, los controladores no necesitan volver a habilitar la interrupción de cada iteración de sondeo porque el modo de sondeo informará al controlador cuando haya terminado de sondear y es el momento de volver a habilitar la interrupción.

  • Al tomar decisiones de programación, el sistema puede ser inteligente sobre si se ejecuta en DISPATCH_LEVEL o PASSIVE_LEVEL. Esto puede permitir la priorización ajustada del tráfico de diferentes NIC y dar lugar a una distribución de cargas de trabajo más justas en la máquina.

  • Tiene garantías de serialización. Una vez que ejecute código desde el contexto de ejecución de un objeto Poll, se garantiza que no se ejecutará ningún otro código relacionado con el mismo contexto de ejecución. Esto permite que un controlador NIC tenga una implementación sin bloqueos de su ruta de datos.

El modelo del modo de sondeo NDIS

En el diagrama de secuencia siguiente se muestra cómo el mismo controlador PCIe NIC hipotético controla una ráfaga de paquetes Rx mediante un objeto Poll en lugar de un DPC.

Diagrama que muestra el modo de sondeo NDIS con paquetes Rx y una cola de hardware de recepción.

Al igual que el modelo DPC, cuando un paquete Rx llega al hardware genera una interrupción, NDIS llama al ISR del controlador y el controlador deshabilita la interrupción del ISR. En este momento, el modelo de modo de sondeo difiere:

  1. En lugar de poner en cola un DPC, el controlador pone en cola un objeto Poll (que creó anteriormente) desde el ISR para notificar a NDIS que el nuevo trabajo está listo para procesarse.

  2. En algún momento del futuro NDIS llama al controlador de iteración de sondeo del controlador para procesar el trabajo. A diferencia de un DPC, el controlador no puede indicar tantas NBL rx como hay elementos listos en su cola de hardware. En su lugar, el controlador debe comprobar el parámetro de datos de sondeo del controlador para obtener el número máximo de NCL que puede indicar.

    Una vez que el controlador captura hasta el número máximo de paquetes Rx, debe inicializar las NCL, agregarlas a la cola NBL proporcionada por el controlador de sondeo y salir de la devolución de llamada. El controlador no debe habilitar la interrupción antes de salir.

  3. NDIS continúa sondeando al controlador hasta que evalúa que el controlador ya no está avanzando. En este momento, NDIS dejará de sondear y pedirá al controlador que vuelva a habilitar la interrupción.

Palabra clave INF estandarizada para el modo de sondeo NDIS

La siguiente palabra clave debe usarse para habilitar o deshabilitar la compatibilidad con el modo de sondeo NDIS:

*Las palabras clave INF estandarizadas de enumeración NdisPoll tienen los siguientes atributos:

SubkeyName
Nombre de la palabra clave que debe especificar en el archivo INF y que aparece en el Registro.

ParamDesc
Texto para mostrar asociado a SubkeyName.

Valor
Valor entero de enumeración asociado a cada opción de la lista. Este valor se almacena en NDI\params\ SubkeyName\Value.

EnumDesc
Texto para mostrar asociado a cada valor que aparece en el menú.

Valor predeterminado
Valor predeterminado del menú.

SubkeyName ParamDesc Valor EnumDesc
*NdisPoll Modo de sondeo de Ndis 0 Deshabilitado
1 (valor predeterminado) habilitado

Para obtener más información sobre el uso de palabras clave de enumeración, consulte Palabras clave de enumeración.

Creación de un objeto Poll

Para crear un objeto Poll, el controlador de minipuerto realiza lo siguiente en su función de devolución de llamada MiniportInitializeEx:

  1. Asigna un contexto de minipuerto privado.
  2. Asigna una estructura NDIS_POLL_CHARACTERISTICS para especificar puntos de entrada para las funciones de devolución de llamada NdisPoll y NdisSetPollNotification.
  3. Llama a NdisRegisterPoll para crear el objeto Poll y almacenarlo en el contexto de miniport.

En el ejemplo siguiente se muestra cómo un controlador de miniporte podría crear un objeto Poll para un flujo de cola de recepción. Se omite el control de errores por motivos de simplicidad.

NDIS_SET_POLL_NOTIFICATION NdisSetPollNotification; 
NDIS_POLL NdisPoll; 

NDIS_STATUS 
MiniportInitialize( 
    _In_ NDIS_HANDLE NdisAdapterHandle, 
    _In_ NDIS_HANDLE MiniportDriverContext, 
    _In_ NDIS_MINIPORT_INIT_PARAMETERS * MiniportInitParameters 
) 
{ 
    // Allocate a private miniport context 
    MINIPORT_CONTEXT * miniportContext = ...;
 
    NDIS_POLL_CHARACTERISTICS pollCharacteristics; 
    pollCharacteristics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; 
    pollCharacteristics.Header.Revision = NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.Header.Size = NDIS_SIZEOF_NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.SetPollNotificationHandler = NdisSetPollNotification; 
    pollCharacteristics.PollHandler = NdisPoll; 

    // Create a Poll object and store it in the miniport context 
    NdisRegisterPoll( 
        NdisAdapterHandle, 
        miniportContext, 
        &pollCharacteristics, 
        &miniportContext->RxPoll); 
 
    return NDIS_STATUS_SUCCESS; 
} 

Puesta en cola de un objeto Poll para su ejecución

Desde un ISR, los controladores de minipuerto llaman a NdisRequestPoll para poner en cola un objeto Poll para su ejecución. En el ejemplo siguiente se muestra el control de recepción, pero se omite el uso compartido de líneas de interrupción por motivos de simplicidad.

BOOLEAN 
MiniportIsr( 
  KINTERRUPT * Interrupt, 
  void * Context 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Check if this interrupt is due to a received packet 
    if (hardwareContext->ISR & RX_OK) 
    { 
        // Disable the receive interrupt and queue the Poll 
        hardwareContext->IMR &= ~RX_OK; 
        NdisRequestPoll(miniportContext->RxPoll, nullptr); 
    }

    return TRUE; 
} 

Implementación del controlador de iteración de sondeo

NDIS invoca la devolución de llamada NdisPoll del controlador de miniporte para sondear las indicaciones de recepción y enviar finalizaciones. NDIS invoca primero NdisPoll cuando el controlador llama a NdisRequestPoll para poner en cola un objeto Poll. NDIS seguirá invocando a NdisPoll mientras el controlador está avanzando hacia adelante sobre las indicaciones de recepción o las finalizaciones de transmisión.

Para las indicaciones de recepción, el controlador debe hacer lo siguiente en NdisPoll:

  1. Compruebe el parámetro receive de la estructura NDIS_POLL_DATA para obtener el número máximo de NBL que puede indicar.
  2. Capturar hasta el número máximo de paquetes Rx.
  3. Inicialice las NCL.
  4. Agréguelos a la cola NBL proporcionada por la estructura NDIS_POLL_RECEIVE_DATA (ubicada en la estructura NDIS_POLL_DATA del parámetro PollData de NdisPoll).
  5. Salga de la devolución de llamada.

Para las finalizaciones de transmisión, el controlador debe hacer lo siguiente en NdisPoll:

  1. Compruebe el parámetro de transmisión de la estructura de NDIS_POLL_DATA para obtener el número máximo de NBL que puede completar.
  2. Capturar hasta el número máximo de paquetes Tx.
  3. Complete las NBL.
  4. Agréguelos a la cola NBL proporcionada por la estructura NDIS_POLL_TRANSMIT_DATA (ubicada en la estructura NDIS_POLL_DATA del parámetro PollData de NdisPoll).
  5. Salga de la devolución de llamada.

El controlador no debe habilitar la interrupción del objeto Poll antes de salir de la función NdisPoll . NDIS seguirá sondeando al controlador hasta que evalúe que no se está realizando ningún progreso hacia delante. En este momento, NDIS dejará de sondear y pedirá al controlador que vuelva a habilitar la interrupción.

Este es el modo en que un controlador puede implementar NdisPoll para un flujo de cola de recepción.

_Use_decl_annotations_ 
void 
NdisPoll( 
    void * Context, 
    NDIS_POLL_DATA * PollData 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Drain received frames 
    auto & receive = PollData->Receive; 
    receive.NumberOfRemainingNbls = NDIS_ANY_NUMBER_OF_NBLS; 
    receive.Flags = NDIS_RECEIVE_FLAGS_SHARED_MEMORY_VALID; 

    while (receive.NumberOfIndicatedNbls < receive.MaxNblsToIndicate) 
    { 
        auto rxDescriptor = HardwareQueueGetNextDescriptorToCheck(hardwareContext->RxQueue); 

        // If this descriptor is still owned by hardware stop draining packets 
        if ((rxDescriptor->Status & HW_OWN) != 0) 
            break; 

        auto nbl = MakeNblFromRxDescriptor(miniportContext->NblPool, rxDescriptor); 

        AppendNbl(&receive.IndicatedNblChain, nbl); 
        receive.NumberOfIndicatedNbls++; 

        // Move to next descriptor 
        HardwareQueueAdvanceNextDescriptorToCheck(hardwareContext->RxQueue); 
    } 
} 

Administración de interrupciones

Los controladores de miniporte implementan la devolución de llamada NdisSetPollNotification para habilitar o deshabilitar la interrupción asociada a un objeto Poll. NDIS suele invocar la devolución de llamada NdisSetPollNotification cuando detecta que el controlador de miniporte no está avanzando en NdisPoll. NDIS usa NdisSetPollNotification para indicar al controlador que dejará de invocar NdisPoll. El controlador debe invocar a NdisRequestPoll cuando el nuevo trabajo esté listo para procesarse.

Este es el modo en que un controlador podría implementar NdisSetPollNotification para un flujo de cola de recepción.

_Use_decl_annotations_ 
void 
NdisSetPollNotification( 
    void * Context, 
    NDIS_POLL_NOTIFICATION * Notification 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    if (Notification->Enabled) 
    { 
        hardwareContext->IMR |= RX_OK; 
    } 
    else 
    { 
        hardwareContext->IMR &= ~RX_OK; 
    } 
}