Mengirim data jaringan dengan cincin bersih

Driver klien NetAdapterCx mengirim data jaringan ketika kerangka kerja memanggil fungsi panggilan balik EvtPacketQueueAdvance mereka untuk antrean transmisi. Selama panggilan balik ini, driver klien memposting buffer dari cincin fragmen antrean ke perangkat keras, lalu mengosongkan paket dan fragmen yang selesai kembali ke OS.

Gambaran umum operasi kirim (Tx) dan pengurasan

Animasi berikut menggambarkan bagaimana driver klien untuk kartu antarmuka jaringan (NIC) PCI sederhana melakukan operasi pasca dan pengosongan untuk antrean transmisi (Tx).

Animasi memperlihatkan pos cincin bersih dan operasi pengurasan untuk transmisi (Tx) untuk kartu antarmuka jaringan PCI.

Dalam animasi ini, paket yang dimiliki oleh driver klien disorot dengan warna biru muda dan biru tua, dan fragmen yang dimiliki oleh driver klien disorot dengan warna kuning dan oranye. Warna yang lebih terang mewakili subbagian pengurasan elemen yang dimiliki driver, sementara warna yang lebih gelap mewakili subbagian pos elemen yang dimiliki driver.

Mengirim data secara berurutan

Berikut adalah urutan posting dan pengurasan umum untuk driver yang perangkatnya mengirimkan data secara berurutan, seperti PCI NIC sederhana.

  1. Panggil NetTxQueueGetRingCollection untuk mengambil struktur pengumpulan cincin antrean transmisi. Anda dapat menyimpan ini di ruang konteks antrean untuk mengurangi panggilan keluar dari driver. Gunakan koleksi cincin untuk mengambil cincin paket antrean transmisi.
  2. Posting data ke perangkat keras:
    1. Alokasikan variabel UINT32 untuk indeks paket dan atur ke NextIndex cincin paket, yang merupakan awal dari sub-bagian pasca cincin.
    2. Lakukan hal berikut dalam perulangan:
      1. Dapatkan paket dengan memanggil NetRingGetPacketAtIndex dengan indeks paket.
      2. Periksa apakah paket ini harus diabaikan. Jika harus diabaikan, lewati ke langkah 6 dari perulangan ini. Jika tidak, lanjutkan.
      3. Dapatkan fragmen paket ini. Ambil cincin fragmen antrean transmisi dari koleksi cincin, ambil awal fragmen paket dari anggota FragmentIndex paket, lalu ambil akhir fragmen paket dengan memanggil NetRingIncrementIndex dengan FragmentCount paket.
      4. Lakukan hal berikut dalam perulangan:
        1. Panggil NetRingGetFragmentAtIndex untuk mendapatkan fragmen.
        2. Terjemahkan deskriptor NET_FRAGMENT ke dalam deskriptor fragmen perangkat keras terkait.
        3. Tingkatkan indeks fragmen dengan memanggil NetRingIncrementIndex.
      5. Perbarui indeks Berikutnya cincin fragmen agar sesuai dengan Indeks iterator fragmen saat ini, yang menunjukkan bahwa postingan ke perangkat keras selesai.
      6. Tingkatkan indeks paket dengan memanggil NetRingIncrementIndex.
    3. Perbarui NextIndex cincin paket ke indeks paket untuk menyelesaikan posting paket ke perangkat keras.
  3. Pengosongan selesai mengirimkan paket ke OS:
    1. Atur indeks paket ke BeginIndex cincin paket, yang merupakan awal dari sub bagian pengurasan cincin.
    2. Lakukan hal berikut dalam perulangan:
      1. Dapatkan paket dengan memanggil NetRingGetPacketAtIndex dengan indeks paket.
      2. Periksa apakah paket telah selesai dikirimkan. Jika belum, keluar dari perulangan.
      3. Tingkatkan indeks paket dengan memanggil NetRingIncrementIndex.
    3. Perbarui BeginIndex cincin paket ke indeks paket untuk menyelesaikan posting paket ke perangkat keras.

Langkah-langkah ini mungkin terlihat seperti ini dalam kode. Perhatikan bahwa detail khusus perangkat keras seperti cara memposting deskriptor ke perangkat keras atau membersihkan transaksi pasca yang berhasil dibiarkan untuk kejelasan.

void
MyEvtTxQueueAdvance(
    NETPACKETQUEUE TxQueue
)
{
    //
    // Retrieve the transmit queue's ring collection and packet ring. 
    // This example stores the Tx queue's ring collection in its queue context space.
    //
    PMY_TX_QUEUE_CONTEXT txQueueContext = MyGetTxQueueContext(TxQueue);
    NET_RING_COLLECTION const * ringCollection = txQueueContext->RingCollection;
    NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
    UINT32 currentPacketIndex = 0;

    //
    // Post data to hardware
    //      
    currentPacketIndex = packetRing->NextIndex;
    while(currentPacketIndex != packetRing->EndIndex)
    {
        NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);        
        if(!packet->Ignore)
        {
            NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
            UINT32 currentFragmentIndex = packet->FragmentIndex;
            UINT32 fragmentEndIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex + packet->FragmentCount - 1);
            
            for(txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors = 0; 
                currentFragmentIndex != fragmentEndIndex; 
                txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors++)
            {
                NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);

                // Post fragment descriptor to hardware
                ...
                //

                currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
            }

            //
            // Update the fragment ring's Next index to indicate that posting is complete and prepare for draining
            //
            fragmentRing->NextIndex = currentFragmentIndex;
        }
        currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
    }
    packetRing->NextIndex = currentPacketIndex;

    //
    // Drain packets if completed
    //
    currentPacketIndex = packetRing->BeginIndex;
    while(currentPacketIndex != packetRing->NextIndex)
    {        
        NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex); 
        
        // Test packet for transmit completion by checking hardware ownership flags in the packet's last fragment
        // Break if transmit is not complete
        ...
        //
        
        currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
    }
    packetRing->BeginIndex = currentPacketIndex;
}

Mengirim data tidak berurutan

Untuk driver yang perangkatnya mungkin menyelesaikan transmisi secara tidak berurutan, perbedaan utama dari perangkat dalam urutan terletak pada siapa yang mengalokasikan buffer transmisi dan bagaimana driver menangani pengujian untuk penyelesaian transmisi. Untuk transmisi dalam urutan dengan PCI NIC yang berkemampuan DMA, OS biasanya mengalokasikan, memasang, dan pada akhirnya memiliki buffer fragmen. Kemudian, secara berurutan, driver klien dapat menguji bendera kepemilikan perangkat keras yang sesuai dari setiap fragmen selama EvtPacketQueueAdvance.

Berbeda dengan model ini, pertimbangkan NIC berbasis USB yang khas. Dalam situasi ini, tumpukan USB memiliki buffer memori untuk transmisi dan buffer tersebut mungkin terletak di tempat lain dalam memori sistem. Tumpukan USB menunjukkan penyelesaian ke driver klien tidak berurutan, sehingga driver klien perlu merekam status penyelesaian paket secara terpisah selama rutinitas panggilan balik penyelesaiannya. Untuk melakukannya, driver klien dapat menggunakan bidang Scratch paket, atau dapat menggunakan beberapa metode lain seperti menyimpan informasi di ruang konteks antreannya. Kemudian, dalam panggilan ke EvtPacketQueueAdvance, driver klien memeriksa informasi ini untuk pengujian penyelesaian paket.