Transmitir y recibir colas

Información general

Las colas de paquetes o las colas de rutas de acceso de datos son objetos introducidos en NetAdapterCx para permitir que los controladores de cliente modelen sus características de hardware, como la transmisión de hardware y las colas de recepción, más explícitamente en los controladores de software. En este tema se explica cómo trabajar con colas de transmisión y recepción en NetAdapterCx.

Cuando el controlador cliente llama a NET_ADAPTER_DATAPATH_CALLBACKS_INIT, normalmente desde su función de devolución de llamada de evento EVT_WDF_DRIVER_DEVICE_ADD , proporciona dos devoluciones de llamada de creación de colas: EVT_NET_ADAPTER_CREATE_TXQUEUE y EVT_NET_ADAPTER_CREATE_RXQUEUE. El cliente crea colas de transmisión y recepción en estas devoluciones de llamada, respectivamente.

El marco vacía las colas antes de pasar a un estado de bajo consumo y las elimina antes de eliminar el adaptador.

Creación de colas de paquetes

Al crear una cola de paquetes, una cola de transmisión o una cola de recepción, el cliente debe proporcionar punteros a las tres funciones de devolución de llamada siguientes:

Además, el cliente puede proporcionar estas funciones de devolución de llamada opcionales después de inicializar la estructura de configuración de la cola:

Creación de una cola de transmisión

NetAdapterCx llama a EVT_NET_ADAPTER_CREATE_TXQUEUE al final de la secuencia de encendido. Durante esta devolución de llamada, los controladores de cliente suelen hacer lo siguiente:

  • Opcionalmente, registre las devoluciones de llamada de inicio y detención de la cola.
  • Llame a NetTxQueueInitGetQueueId para recuperar el identificador de la cola de transmisión que se va a configurar.
  • Llame a NetTxQueueCreate para asignar una cola.
    • Si se produce un error en NetTxQueueCreate , la función de devolución de llamada EvtNetAdapterCreateTxQueue debe devolver un código de error.
  • Consulta de desplazamientos de extensión de paquetes.

En el ejemplo siguiente se muestra cómo se pueden ver estos pasos en el código. El código de control de errores se ha dejado fuera de este ejemplo para mayor claridad.

NTSTATUS
EvtAdapterCreateTxQueue(
    _In_    NETADAPTER          Adapter,
    _Inout_ NETTXQUEUE_INIT *   TxQueueInit
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Prepare the configuration structure
    NET_PACKET_QUEUE_CONFIG txConfig;
    NET_PACKET_QUEUE_CONFIG_INIT(
        &txConfig,
        EvtTxQueueAdvance,
        EvtTxQueueSetNotificationEnabled,
        EvtTxQueueCancel);

    // Optional: register the queue's start and stop callbacks
    txConfig.EvtStart = EvtTxQueueStart;
    txConfig.EvtStop = EvtTxQueueStop;

    // Get the queue ID
    const ULONG queueId = NetTxQueueInitGetQueueId(TxQueueInit);

    // Create the transmit queue
    NETPACKETQUEUE txQueue;
    status = NetTxQueueCreate(
        TxQueueInit,
        &txAttributes,
        &txConfig,
        &txQueue);

    // Get the queue context for storing the queue ID and packet extension offset info
    PMY_TX_QUEUE_CONTEXT queueContext = GetMyTxQueueContext(txQueue);

    // Store the queue ID in the context
    queueContext->QueueId = queueId;

    // Query checksum packet extension offset and store it in the context
    NET_EXTENSION_QUERY extension;
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_CHECKSUM_NAME,
        NET_PACKET_EXTENSION_CHECKSUM_VERSION_1);

    NetTxQueueGetExtension(txQueue, &extension, &queueContext->ChecksumExtension);

    // Query Large Send Offload packet extension offset and store it in the context
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_LSO_NAME,
        NET_PACKET_EXTENSION_LSO_VERSION_1);
    
    NetTxQueueGetExtension(txQueue, &extension, &queueContext->LsoExtension);

    return status;
}

Creación de una cola de recepción

Para crear una cola de recepción de EVT_NET_ADAPTER_CREATE_RXQUEUE, use el mismo patrón que una cola de transmisión y llame a NetRxQueueCreate.

En el ejemplo siguiente se muestra cómo crear una cola de recepción podría tener un aspecto en el código. El código de control de errores se ha dejado fuera de este ejemplo para mayor claridad.

NTSTATUS
EvtAdapterCreateRxQueue(
    _In_ NETADAPTER NetAdapter,
    _Inout_ PNETRXQUEUE_INIT RxQueueInit
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Prepare the configuration structure
    NET_PACKET_QUEUE_CONFIG rxConfig;
    NET_PACKET_QUEUE_CONFIG_INIT(
        &rxConfig,
        EvtRxQueueAdvance,
        EvtRxQueueSetNotificationEnabled,
        EvtRxQueueCancel);

    // Optional: register the queue's start and stop callbacks
    rxConfig.EvtStart = EvtRxQueueStart;
    rxConfig.EvtStop = EvtRxQueueStop;

    // Get the queue ID
    const ULONG queueId = NetRxQueueInitGetQueueId(RxQueueInit);

    // Create the receive queue
    NETPACKETQUEUE rxQueue;
    status = NetRxQueueCreate(
        RxQueueInit,
        &rxAttributes,
        &rxConfig,
        &rxQueue);

    // Get the queue context for storing the queue ID and packet extension offset info
    PMY_RX_QUEUE_CONTEXT queueContext = GetMyRxQueueContext(rxQueue);

    // Store the queue ID in the context
    queueContext->QueueId = queueId;

    // Query the checksum packet extension offset and store it in the context
    NET_EXTENSION_QUERY extension;
    NET_EXTENSION_QUERY_INIT(
        &extension,
        NET_PACKET_EXTENSION_CHECKSUM_NAME,
        NET_PACKET_EXTENSION_CHECKSUM_VERSION_1); 
          
    NetRxQueueGetExtension(rxQueue, &extension, &queueContext->ChecksumExtension);

    return status;
}

Modelo de sondeo

La ruta de acceso de datos de NetAdapter es un modelo de sondeo y la operación de sondeo en una cola de paquetes es completamente independiente de otras colas. El modelo de sondeo se implementa mediante una llamada a las devoluciones de llamada anticipadas de la cola del controlador cliente, como se muestra en la ilustración siguiente:

Diagrama que muestra el flujo de sondeo en NetAdapterCx.

Avance de las colas de paquetes

La secuencia de una operación de sondeo en una cola de paquetes es la siguiente:

  1. El sistema operativo proporciona búferes al controlador cliente para transmitir o recibir.
  2. El controlador cliente programa los paquetes al hardware.
  3. El controlador cliente devuelve los paquetes completados al sistema operativo.

Las operaciones de sondeo se producen dentro de la función de devolución de llamada EvtPacketQueueAdvance del controlador cliente. Cada cola de paquetes de un controlador cliente está respaldada por estructuras de datos subyacentes denominadas anillos netos, que contienen o vinculan a los búferes de datos de red reales en la memoria del sistema. Durante EvtPacketQueueAdvance, los controladores de cliente realizan operaciones de envío y recepción en los anillos netos controlando índices dentro de los anillos, transfiriendo la propiedad del búfer entre el hardware y el sistema operativo a medida que se transmiten o reciben datos.

Para obtener más información sobre los anillos netos, consulte Introducción a los anillos netos.

Para obtener un ejemplo de implementación de EvtPacketQueueAdvance para una cola de transmisión, consulte Envío de datos de red con anillos netos. Para obtener un ejemplo de implementación de EvtPacketQueueAdvance para una cola de recepción, consulte Recepción de datos de red con anillos netos.

Habilitación y deshabilitación de la notificación de cola de paquetes

Cuando un controlador cliente recibe nuevos paquetes en los anillos netos de una cola de paquetes, NetAdapterCx invoca la función de devolución de llamada EvtPacketQueueSetNotificationEnabled del controlador cliente. Esta devolución de llamada indica a un controlador de cliente que el sondeo (de EvtPacketQueueAdvance o EvtPacketQueueCancel) se detendrá y no continuará hasta que el controlador cliente llame a NetTxQueueNotifyMoreCompletedPacketsAvailable o NetRxQueueNotifyMoreReceivedPacketsAvailable. Normalmente, un dispositivo PCI usa esta devolución de llamada para habilitar interrupciones Tx o Rx. Una vez recibida una interrupción, las interrupciones se pueden deshabilitar de nuevo y el controlador cliente llama a NetTxQueueNotifyMoreCompletedPacketsAvailable o NetRxQueueNotifyMoreReceivedPacketsAvailable para desencadenar el marco de trabajo para volver a sondear.

Habilitación y deshabilitación de la notificación para una cola de transmisión

En el caso de una NIC PCI, habilitar la notificación de cola de transmisión normalmente significa habilitar la interrupción de hardware de la cola de transmisión. Cuando se desencadena la interrupción del hardware, el cliente llama a NetTxQueueNotifyMoreCompletedPacketsAvailable desde su DPC.

Del mismo modo, para una NIC pci, deshabilitar la notificación de cola significa deshabilitar la interrupción asociada a la cola.

Para un dispositivo que tiene un modelo de E/S asincrónico, el cliente suele usar una marca interna para realizar un seguimiento del estado habilitado. Cuando se completa una operación asincrónica, el controlador de finalización comprueba esta marca y llama a NetTxQueueNotifyMoreCompletedPacketsAvailable si se establece.

Si NetAdapterCx llama a EvtPacketQueueSetNotificationEnabled con NotificationEnabled establecido en FALSE, el cliente no debe llamar a NetTxQueueNotifyMoreCompletedPacketsAvailable hasta que NetAdapterCx llame a esta función de devolución de llamada con NotificationEnabled establecido en TRUE.

Por ejemplo:

VOID
MyEvtTxQueueSetNotificationEnabled(
    _In_ NETPACKETQUEUE TxQueue,
    _In_ BOOLEAN NotificationEnabled
)
{
    // Optional: retrieve queue's WDF context
    MY_TX_QUEUE_CONTEXT *txContext = GetTxQueueContext(TxQueue);

    // If NotificationEnabled is TRUE, enable transmit queue's hardware interrupt
    ...
}

VOID
MyEvtTxInterruptDpc(
    _In_ WDFINTERRUPT Interrupt,
    _In_ WDFOBJECT AssociatedObject
    )
{
    MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);

    NetTxQueueNotifyMoreCompletedPacketsAvailable(interruptContext->TxQueue);
}

Habilitación y deshabilitación de la notificación para una cola de recepción

En el caso de una NIC PCI, habilitar la notificación de cola de recepción es muy similar a una cola Tx. Normalmente, esto significa habilitar la interrupción de hardware de la cola de recepción. Cuando se desencadena la interrupción de hardware, el cliente llama a NetRxQueueNotifyMoreReceivedPacketsAvailable desde su DPC.

Por ejemplo:

VOID
MyEvtRxQueueSetNotificationEnabled(
    _In_ NETPACKETQUEUE RxQueue,
    _In_ BOOLEAN NotificationEnabled
)
{
    // optional: retrieve queue's WDF Context
    MY_RX_QUEUE_CONTEXT *rxContext = GetRxQueueContext(RxQueue);

    // If NotificationEnabled is TRUE, enable receive queue's hardware interrupt
    ...
}

VOID
MyEvtRxInterruptDpc(
    _In_ WDFINTERRUPT Interrupt,
    _In_ WDFOBJECT AssociatedObject
    )
{
    MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);

    NetRxQueueNotifyMoreReceivedPacketsAvailable(interruptContext->RxQueue);
}

Para un dispositivo USB, o cualquier otra cola con un mecanismo de finalización de recepción de software, el controlador cliente debe realizar un seguimiento en su propio contexto si la notificación de la cola está habilitada. Desde la rutina de finalización (desencadenada por ejemplo cuando un mensaje está disponible en el lector continuo USB), llame a NetRxQueueNotifyMoreReceivedPacketsAvailable si la notificación está habilitada. En el ejemplo siguiente se muestra cómo hacerlo.

VOID
UsbEvtReaderCompletionRoutine(
    _In_ WDFUSBPIPE Pipe,
    _In_ WDFMEMORY Buffer,
    _In_ size_t NumBytesTransferred,
    _In_ WDFCONTEXT Context
)
{
    UNREFERENCED_PARAMETER(Pipe);

    PUSB_RCB_POOL pRcbPool = *((PUSB_RCB_POOL*) Context);
    PUSB_RCB pRcb = (PUSB_RCB) WdfMemoryGetBuffer(Buffer, NULL);

    pRcb->DataOffsetCurrent = 0;
    pRcb->DataWdfMemory = Buffer;
    pRcb->DataValidSize = NumBytesTransferred;

    WdfObjectReference(pRcb->DataWdfMemory);

    ExInterlockedInsertTailList(&pRcbPool->ListHead,
                                &pRcb->Link,
                                &pRcbPool->ListSpinLock);

    if (InterlockedExchange(&pRcbPool->NotificationEnabled, FALSE) == TRUE)
    {
        NetRxQueueNotifyMoreReceivedPacketsAvailable(pRcbPool->RxQueue);
    }
}

Cancelación de colas de paquetes

Cuando el sistema operativo detiene la ruta de acceso de datos, comienza invocando la función de devolución de llamada EvtPacketQueueCancel del controlador cliente. Esta devolución de llamada es donde los controladores de cliente realizan cualquier procesamiento necesario antes de que el marco elimine las colas de paquetes. La cancelación de una cola de transmisión es opcional y depende de si el hardware admite la cancelación de transmisión en curso, pero se requiere la cancelación de una cola de recepción.

Durante EvtPacketQueueCancel, los controladores devuelven paquetes al sistema operativo según sea necesario. Para obtener ejemplos de código de la cola de transmisión y la cancelación de la cola de recepción, consulte Cancelación de datos de red con anillos netos.

Después de llamar a la devolución de llamada EvtPacketQueueCancel del controlador, el marco continúa sondeando la devolución de llamada EvtPacketQueueAdvance del controlador hasta que se devuelvan todos los paquetes y búferes al sistema operativo.