Transmettre et recevoir des files d’attente

Vue d’ensemble

Les files d’attente de paquets, ou files d’attente de chemin de données, sont des objets introduits dans NetAdapterCx pour permettre aux pilotes clients de modéliser leurs fonctionnalités matérielles, telles que les files d’attente de transmission et de réception matérielles, plus explicitement dans les pilotes logiciels. Cette rubrique explique comment utiliser les files d’attente de transmission et de réception dans NetAdapterCx.

Lorsque votre pilote client appelle NET_ADAPTER_DATAPATH_CALLBACKS_INIT, généralement à partir de sa fonction de rappel d’événement EVT_WDF_DRIVER_DEVICE_ADD , il fournit deux rappels de création de file d’attente : EVT_NET_ADAPTER_CREATE_TXQUEUE et EVT_NET_ADAPTER_CREATE_RXQUEUE. Le client crée respectivement des files d’attente de transmission et de réception dans ces rappels.

L’infrastructure vide les files d’attente avant de passer à un état d’alimentation faible et les supprime avant de supprimer l’adaptateur.

Création de files d’attente de paquets

Lors de la création d’une file d’attente de paquets, une file d’attente de transmission ou une file d’attente de réception, le client doit fournir des pointeurs vers les trois fonctions de rappel suivantes :

En outre, le client peut fournir ces fonctions de rappel facultatives après l’initialisation de la structure de configuration de la file d’attente :

Création d’une file d’attente de transmission

NetAdapterCx appelle EVT_NET_ADAPTER_CREATE_TXQUEUE à la toute fin de la séquence de mise sous tension. Pendant ce rappel, les pilotes clients effectuent généralement les opérations suivantes :

  • Inscrivez éventuellement les rappels de début et d’arrêt pour la file d’attente.
  • Appelez NetTxQueueInitGetQueueId pour récupérer l’identificateur de la file d’attente de transmission à configurer.
  • Appelez NetTxQueueCreate pour allouer une file d’attente.
    • Si NetTxQueueCreate échoue, la fonction de rappel EvtNetAdapterCreateTxQueue doit retourner un code d’erreur.
  • Requête pour les décalages d’extension de paquets.

L’exemple suivant montre à quoi ces étapes peuvent ressembler dans le code. Le code de gestion des erreurs a été exclu de cet exemple pour plus de clarté.

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;
}

Création d’une file d’attente de réception

Pour créer une file d’attente de réception à partir de EVT_NET_ADAPTER_CREATE_RXQUEUE, utilisez le même modèle qu’une file d’attente de transmission et appelez NetRxQueueCreate.

L’exemple suivant montre à quoi peut ressembler la création d’une file d’attente de réception dans le code. Le code de gestion des erreurs a été exclu de cet exemple pour plus de clarté.

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;
}

Modèle d’interrogation

Le chemin des données NetAdapter est un modèle d’interrogation, et l’opération d’interrogation sur une file d’attente de paquets est complètement indépendante des autres files d’attente. Le modèle d’interrogation est implémenté en appelant les rappels anticipés de file d’attente du pilote client, comme illustré dans la figure suivante :

Diagramme montrant le flux d’interrogation dans NetAdapterCx.

Progression des files d’attente de paquets

La séquence d’une opération d’interrogation sur une file d’attente de paquets est la suivante :

  1. Le système d’exploitation fournit des mémoires tampons au pilote client pour la transmission ou la réception.
  2. Le pilote client programme les paquets sur le matériel.
  3. Le pilote client retourne les paquets terminés au système d’exploitation.

Les opérations d’interrogation se produisent dans la fonction de rappel EvtPacketQueueAdvance du pilote client. Chaque file d’attente de paquets dans un pilote client est soutenue par des structures de données sous-jacentes appelées anneaux net, qui contiennent ou sont liées aux mémoires tampons de données réseau réelles dans la mémoire système. Pendant EvtPacketQueueAdvance, les pilotes clients effectuent des opérations d’envoi et de réception sur les anneaux net en contrôlant les index au sein des anneaux, en transférant la propriété de la mémoire tampon entre le matériel et le système d’exploitation à mesure que les données sont transmises ou reçues.

Pour plus d’informations sur les anneaux nets, consultez Présentation des anneaux nets.

Pour obtenir un exemple d’implémentation d’EvtPacketQueueAdvance pour une file d’attente de transmission, consultez Envoi de données réseau avec des anneaux nets. Pour obtenir un exemple d’implémentation d’EvtPacketQueueAdvance pour une file d’attente de réception, consultez Réception de données réseau avec des anneaux net.

Activation et désactivation de la notification de file d’attente de paquets

Lorsqu’un pilote client reçoit de nouveaux paquets dans les anneaux nets d’une file d’attente de paquets, NetAdapterCx appelle la fonction de rappel EvtPacketQueueSetNotificationEnabled du pilote client. Ce rappel indique à un pilote client que l’interrogation ( evtPacketQueueAdvance ou EvtPacketQueueCancel) s’arrêtera et ne se poursuivra pas tant que le pilote client n’appellera pas NetTxQueueNotifyMoreCompletedPacketsAvailable ou NetRxQueueNotifyMoreReceivedPacketsAvailable. En règle générale, un appareil PCI utilise ce rappel pour activer les interruptions Tx ou Rx. Une fois qu’une interruption est reçue, les interruptions peuvent être à nouveau désactivées et le pilote client appelle NetTxQueueNotifyMoreCompletedPacketsAvailable ou NetRxQueueNotifyMoreReceivedPacketsAvailable pour déclencher l’infrastructure pour recommencer l’interrogation.

Activation et désactivation de la notification pour une file d’attente de transmission

Pour une carte réseau PCI, l’activation de la notification de file d’attente de transmission signifie généralement l’activation de l’interruption matérielle de la file d’attente de transmission. Lorsque l’interruption matérielle se déclenche, le client appelle NetTxQueueNotifyMoreCompletedPacketsAvailable à partir de son DPC.

De même, pour une carte réseau PCI, la désactivation de la notification de file d’attente signifie désactiver l’interruption associée à la file d’attente.

Pour un appareil qui a un modèle d’E/S asynchrone, le client utilise généralement un indicateur interne pour suivre l’état activé. Lorsqu’une opération asynchrone se termine, le gestionnaire d’achèvement vérifie cet indicateur et appelle NetTxQueueNotifyMoreCompletedPacketsAvailable s’il est défini.

Si NetAdapterCx appelle EvtPacketQueueSetNotificationEnabled avec NotificationEnabled défini sur FALSE, le client ne doit pas appeler NetTxQueueNotifyMoreCompletedPacketsAvailable jusqu’à ce que NetAdapterCx appelle ensuite cette fonction de rappel avec NotificationEnabled défini sur TRUE.

Par exemple :

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);
}

Activation et désactivation de la notification pour une file d’attente de réception

Pour une carte réseau PCI, l’activation de la notification de réception de file d’attente ressemble beaucoup à une file d’attente Tx. Cela signifie généralement l’activation de l’interruption matérielle de la file d’attente de réception. Lorsque l’interruption matérielle se déclenche, le client appelle NetRxQueueNotifyMoreReceivedPacketsAvailable à partir de son DPC.

Par exemple :

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);
}

Pour un périphérique USB ou toute autre file d’attente avec un mécanisme d’achèvement de réception logicielle, le pilote client doit suivre dans son propre contexte si la notification de la file d’attente est activée. À partir de la routine d’achèvement (déclenchée par exemple lorsqu’un message devient disponible dans le lecteur continu USB), appelez NetRxQueueNotifyMoreReceivedPacketsAvailable si la notification est activée. L’exemple suivant montre comment procéder.

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);
    }
}

Annulation des files d’attente de paquets

Lorsque le système d’exploitation arrête le chemin des données, il commence par appeler la fonction de rappel EvtPacketQueueCancel du pilote client. Ce rappel est l’endroit où les pilotes clients effectuent tout traitement nécessaire avant que l’infrastructure supprime les files d’attente de paquets. L’annulation d’une file d’attente de transmission est facultative et dépend si le matériel prend en charge l’annulation de transmission en cours d’exécution, mais l’annulation d’une file d’attente de réception est requise.

Pendant EvtPacketQueueCancel, les pilotes retournent les paquets au système d’exploitation en fonction des besoins. Pour obtenir des exemples de code d’annulation de file d’attente de transmission et de réception, consultez Annulation de données réseau avec des anneaux nets.

Après avoir appelé le rappel EvtPacketQueueCancel du pilote, l’infrastructure continue d’interroger le rappel EvtPacketQueueAdvance du pilote jusqu’à ce que tous les paquets et mémoires tampons aient été retournés au système d’exploitation.