Udostępnij przez


Wysyłanie danych sieciowych za pomocą pierścieni sieciowych

Sterowniki klientów NetAdapterCx wysyłają dane sieciowe, gdy framework wywołuje ich funkcję wywołania zwrotnego EvtPacketQueueAdvance dla kolejki transmisji. Podczas tego wywołania zwrotnego sterowniki klienta przesyłają bufory z pierścienia fragmentów kolejki do sprzętu, a następnie przekazują ukończone pakiety i fragmenty z powrotem do systemu operacyjnego.

Omówienie operacji nadajnika (Tx) i drenażu

Poniższa animacja ilustruje, w jaki sposób sterownik kliencki dla prostej karty sieciowej PCI wykonuje operacje umieszczania i opróżniania dla kolejki transmisji (Tx).

Animacja przedstawiająca operacje związane ze słupkiem pierścienia sieciowego i odpływem dla transmisji (Tx) karty sieciowej PCI.

W tej animacji pakiety należące do sterownika klienta są wyróżnione w jasnoniebieskim i ciemnoniebieskim, a fragmenty należące do sterownika klienta są wyróżnione kolorem żółtym i pomarańczowym. Jaśniejsze kolory reprezentują podsekcję odpływu elementów, których właścicielem jest kierowca, podczas gdy ciemniejsze kolory reprezentują podsekcję słupka elementów, których właścicielem jest kierowca.

Wysyłanie danych w kolejności

Oto typowa sekwencja przesyłu i opróżniania pamięci dla kierowcy, którego urządzenie przesyła dane w kolejności, na przykład prosta karta PCI.

  1. Wywołaj NetTxQueueGetRingCollection, aby pobrać strukturę kolekcji pierścieni kolejki transmisji. Można to przechowywać w przestrzeni kontekstowej kolejki, co pozwala zmniejszyć liczbę wywołań ze sterownika. Użyj zbioru pierścieni, aby pobrać pierścień pakietowy kolejki transmisji.
  2. Przesyłanie danych do sprzętu
    1. Przydziel zmienną UINT32 dla indeksu pakietów i przypisz do niej wartość NextIndex z pierścienia pakietów, który jest początkiem sekcji post ring.
    2. Wykonaj następujące czynności w pętli:
      1. Pobierz pakiet, wywołując NetRingGetPacketAtIndex z indeksem pakietów.
      2. Sprawdź, czy ten pakiet powinien być ignorowany. Jeśli powinna zostać zignorowana, przejdź do kroku 6 tej pętli. Jeśli nie, kontynuuj.
      3. Pobierz fragmenty tego pakietu. Pobierz pierścień fragmentu kolejki transmisji z kolekcji pierścieni, pobierz początek fragmentów pakietu z członka pakietu FragmentIndex, a następnie pobierz koniec fragmentów pakietu, wywołując NetRingIncrementIndex za pomocą członka pakietu FragmentCount.
      4. Wykonuj następujące czynności w pętli:
        1. Wywołaj NetRingGetFragmentAtIndex, aby uzyskać fragment.
        2. Przetłumacz deskryptor NET_FRAGMENT na skojarzony deskryptor fragmentu sprzętowego.
        3. Zwiększ indeks fragmentu, wywołując NetRingIncrementIndex.
      5. Zaktualizuj indeks Next w pierścieniu fragmentu, aby odpowiadał bieżącemu indeksowi iteratora fragmentu, co wskazuje, że przesyłanie na sprzęt zostało zakończone.
      6. Przesuń indeks pakietów, wywołując NetRingIncrementIndex.
    3. Zaktualizuj NextIndex pierścienia pakietów do indeksu pakietów, aby zakończyć przesyłanie pakietów do sprzętu.
  3. Opróżnianie ukończonych pakietów przesyłanych do systemu operacyjnego:
    1. Ustaw indeks pakietów na BeginIndexpierścienia pakietów, co oznacza początek sekcji opróżniania pierścienia.
    2. Wykonaj następujące czynności w pętli:
      1. Pobierz pakiet, wywołując NetRingGetPacketAtIndex z indeksem pakietów.
      2. Sprawdź, czy pakiet zakończył przesyłanie. Jeśli tak nie jest, wyjdź z pętli.
      3. Przesuń indeks pakietów, wywołując NetRingIncrementIndex.
    3. Zaktualizuj BeginIndex pierścienia pakietów do indeksu pakietów, aby sfinalizować wysyłanie pakietów do sprzętu.

Te kroki mogą wyglądać następująco w kodzie. Należy pamiętać, że szczegóły specyficzne dla sprzętu, takie jak umieszczanie deskryptorów na sprzęcie czy kończenie udanej transakcji, są pominięte w celu zachowania przejrzystości.

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

Wysyłanie danych w niewłaściwej kolejności

W przypadku sterowników, których urządzenia mogą zakończyć transmisje w niewłaściwej kolejności, podstawowa różnica w porównaniu do urządzeń działających w odpowiedniej kolejności polega na tym, kto przydziela bufory transmisji i jak sterownik obsługuje test ukończenia transmisji. W przypadku transmisji w kolejności z kartą sieciową PCI, która obsługuje DMA, system operacyjny zazwyczaj przydziela, dołącza i ostatecznie jest właścicielem buforów fragmentów. Następnie sterownik klienta, w kolejności, może przetestować flagę własności sprzętowej odpowiadającą każdemu fragmentowi podczas zdarzenia EvtPacketQueueAdvance.

W przeciwieństwie do tego modelu należy wziąć pod uwagę typową kartę sieciową opartą na usb. W takiej sytuacji stos USB jest właścicielem buforów pamięci do transmisji, a te mogą być umieszczone w innym miejscu w pamięci systemowej. Stos USB wskazuje ukończenie pakietów do sterownika klienta w niewłaściwej kolejności, więc sterownik klienta musi oddzielnie zarejestrować stan ukończenia pakietu podczas procedury wywołania zwrotnego. W tym celu sterownik klienta może użyć pola Scratch pakietu lub zastosować inną metodę, na przykład przechowywanie informacji w przestrzeni kontekstowej kolejki. Następnie w wywołaniu EvtPacketQueueAdvancesterownik klienta sprawdza te informacje na potrzeby testowania uzupełniania pakietów.