Mengirimkan dan menerima antrean

Gambaran Umum

Antrean paket, atau antrean jalur data adalah objek yang diperkenalkan di NetAdapterCx untuk memungkinkan driver klien memodelkan fitur perangkat keras mereka, seperti transmisi perangkat keras dan menerima antrean, lebih eksplisit dalam driver perangkat lunak. Topik ini menjelaskan cara bekerja dengan transmisi dan menerima antrean di NetAdapterCx.

Ketika driver klien Anda memanggil NET_ADAPTER_DATAPATH_CALLBACKS_INIT, biasanya dari fungsi panggilan balik peristiwa EVT_WDF_DRIVER_DEVICE_ADD , ia menyediakan dua panggilan balik pembuatan antrean: EVT_NET_ADAPTER_CREATE_TXQUEUE dan EVT_NET_ADAPTER_CREATE_RXQUEUE. Klien membuat transmisi dan menerima antrean dalam panggilan balik ini masing-masing.

Kerangka kerja menginisiasi antrean sebelum beralih ke status daya rendah dan menghapusnya sebelum menghapus adaptor.

Membuat antrean paket

Saat membuat antrean paket, baik antrean transmisi atau antrean terima, klien harus memberikan penunjuk ke tiga fungsi panggilan balik berikut:

Selain itu, klien dapat menyediakan fungsi panggilan balik opsional ini setelah menginisialisasi struktur konfigurasi antrean:

Membuat antrean transmisi

NetAdapterCx memanggil EVT_NET_ADAPTER_CREATE_TXQUEUE di akhir urutan power-up. Selama panggilan balik ini, driver klien biasanya melakukan hal berikut:

  • Secara opsional daftarkan mulai dan hentikan panggilan balik untuk antrean.
  • Panggil NetTxQueueInitGetQueueId untuk mengambil pengidentifikasi antrean transmisi untuk disiapkan.
  • Panggil NetTxQueueCreate untuk mengalokasikan antrean.
    • Jika NetTxQueueCreate gagal, fungsi panggilan balik EvtNetAdapterCreateTxQueue harus mengembalikan kode kesalahan.
  • Kueri untuk offset ekstensi paket.

Contoh berikut menunjukkan bagaimana langkah-langkah ini mungkin terlihat dalam kode. Kode penanganan kesalahan telah dibiarkan dari contoh ini untuk kejelasan.

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

Membuat antrean terima

Untuk membuat antrean terima dari EVT_NET_ADAPTER_CREATE_RXQUEUE, gunakan pola yang sama dengan antrean transmisi dan panggil NetRxQueueCreate.

Contoh berikut menunjukkan bagaimana membuat antrean penerima mungkin terlihat dalam kode. Kode penanganan kesalahan telah dibiarkan dari contoh ini untuk kejelasan.

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

Model polling

Jalur data NetAdapter adalah model polling, dan operasi polling pada satu antrean paket benar-benar independen dari antrean lain. Model polling diimplementasikan dengan memanggil panggilan balik lanjutan antrean driver klien, seperti yang ditunjukkan pada gambar berikut:

Diagram yang menunjukkan aliran polling di NetAdapterCx.

Memajukan antrean paket

Urutan operasi polling pada antrean paket adalah sebagai berikut:

  1. OS memberikan buffer kepada driver klien untuk mengirimkan atau menerima.
  2. Driver klien memprogram paket ke perangkat keras.
  3. Driver klien mengembalikan paket yang telah selesai ke OS.

Operasi polling terjadi dalam fungsi panggilan balik EvtPacketQueueAdvance driver klien. Setiap antrean paket dalam driver klien didukung oleh struktur data yang mendasar yang disebut cincin bersih, yang berisi atau menautkan ke buffer data jaringan aktual dalam memori sistem. Selama EvtPacketQueueAdvance, driver klien melakukan operasi kirim dan terima pada cincin bersih dengan mengontrol indeks dalam cincin, mentransfer kepemilikan buffer antara perangkat keras dan OS saat data dikirimkan atau diterima.

Untuk informasi selengkapnya tentang cincin bersih, lihat Pengantar cincin bersih.

Untuk contoh penerapan EvtPacketQueueAdvance untuk antrean transmisi, lihat Mengirim data jaringan dengan cincin bersih. Untuk contoh penerapan EvtPacketQueueAdvance untuk antrean terima, lihat Menerima data jaringan dengan cincin bersih.

Mengaktifkan dan menonaktifkan pemberitahuan antrean paket

Ketika driver klien menerima paket baru dalam cincin bersih antrean paket, NetAdapterCx memanggil fungsi panggilan balik EvtPacketQueueSetNotificationEnabled driver klien. Panggilan balik ini menunjukkan kepada driver klien bahwa polling (dari EvtPacketQueueAdvance atau EvtPacketQueueCancel) akan berhenti dan tidak akan berlanjut sampai driver klien memanggil NetTxQueueNotifyMoreCompletedPacketsAvailable atau NetRxQueueNotifyMoreReceivedPacketsAvailable. Biasanya, perangkat PCI menggunakan panggilan balik ini untuk mengaktifkan gangguan Tx atau Rx. Setelah interupsi diterima, gangguan dapat dinonaktifkan lagi dan driver klien memanggil NetTxQueueNotifyMoreCompletedPacketsAvailable atau NetRxQueueNotifyMoreReceivedPacketsAvailable untuk memicu kerangka kerja memulai polling lagi.

Mengaktifkan dan menonaktifkan pemberitahuan untuk antrean transmisi

Untuk PCI NIC, mengaktifkan pemberitahuan antrean transmisi biasanya berarti mengaktifkan interupsi perangkat keras antrean transmisi. Ketika gangguan perangkat keras diaktifkan, klien memanggil NetTxQueueNotifyMoreCompletedPacketsAvailable dari DPC-nya.

Demikian pula, untuk PCI NIC, menonaktifkan pemberitahuan antrean berarti menonaktifkan gangguan yang terkait dengan antrean.

Untuk perangkat yang memiliki model I/O asinkron, klien biasanya menggunakan bendera internal untuk melacak status yang diaktifkan. Ketika operasi asinkron selesai, handler penyelesaian memeriksa bendera ini dan memanggil NetTxQueueNotifyMoreCompletedPacketsAvailable jika diatur.

Jika NetAdapterCx memanggil EvtPacketQueueSetNotificationEnabled dengan NotificationEnabled diatur ke FALSE, klien tidak boleh memanggil NetTxQueueNotifyMoreCompletedPacketsAvailable sampai NetAdapterCx selanjutnya memanggil fungsi panggilan balik ini dengan NotificationEnabled diatur ke TRUE.

Contohnya:

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

Mengaktifkan dan menonaktifkan pemberitahuan untuk antrean terima

Untuk NIC PCI, mengaktifkan pemberitahuan antrean terima terlihat sangat mirip dengan antrean Tx. Ini biasanya berarti mengaktifkan interupsi perangkat keras antrean penerima. Ketika gangguan perangkat keras diaktifkan, klien memanggil NetRxQueueNotifyMoreReceivedPacketsAvailable dari DPC-nya.

Contohnya:

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

Untuk perangkat USB, atau antrean lain dengan perangkat lunak yang menerima mekanisme penyelesaian, driver klien harus melacak dalam Konteksnya sendiri apakah pemberitahuan antrean diaktifkan. Dari rutinitas penyelesaian (dipicu misalnya ketika pesan tersedia di pembaca berkelanjutan USB), panggil NetRxQueueNotifyMoreReceivedPacketsAvailable jika pemberitahuan diaktifkan. Contoh berikut menunjukkan bagaimana Anda dapat melakukan ini.

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

Membatalkan antrean paket

Ketika OS menghentikan jalur data, os dimulai dengan memanggil fungsi panggilan balik EvtPacketQueueCancel driver klien. Panggilan balik ini adalah tempat driver klien melakukan pemrosesan apa pun yang diperlukan sebelum kerangka kerja menghapus antrean paket. Pembatalan untuk antrean transmisi bersifat opsional dan tergantung pada apakah perangkat keras mendukung pembatalan pengiriman dalam penerbangan, tetapi pembatalan untuk antrean terima diperlukan.

Selama EvtPacketQueueCancel, driver mengembalikan paket ke OS sesuai kebutuhan. Untuk contoh kode pengiriman antrean dan menerima pembatalan antrean, lihat Membatalkan data jaringan dengan cincin bersih.

Setelah memanggil panggilan balik EvtPacketQueueCancel driver, kerangka kerja terus melakukan polling panggilan balik EvtPacketQueueAdvance driver sampai semua paket dan buffer telah dikembalikan ke OS.