Udostępnij przez


Odbieranie danych sieciowych za pomocą pierścieni sieciowych

Sterowniki klienta NetAdapterCx odbierają dane sieciowe, gdy platforma wywołuje ich EvtPacketQueueAdvance funkcji wywołania zwrotnego dla kolejki odbierania. Podczas tego wywołania zwrotnego sterowniki klienta potwierdzają odbiór, przekazując odebrane fragmenty i pakiety do systemu operacyjnego, a następnie przesyłają nowe bufory do sprzętu.

Omówienie operacji odbierania (Rx) i opróżniania

Poniższa animacja ilustruje, jak sterownik klienta dla prostej karty sieciowej PCI wykonuje operacje umieszczania i opróżniania dla kolejki odbiorczej (Rx). Bufory fragmentów w tym przykładowym scenariuszu są przydzielane i dołączane do pierścienia fragmentów przez system operacyjny. W tym przykładzie przyjęto założenie, że relacja "jeden do jednego" istnieje między kolejką odbioru sprzętu a kolejką odbioru systemu operacyjnego.

Animacja ilustrująca operacje pierścienia bufora sieciowego i odpływu dla odbierania przez kartę sieciową 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 przedstawiają podsekcję drenażu elementów, których właścicielem jest kierowca, podczas gdy ciemniejsze kolory przedstawiają podsekcję słupkową elementów, których właścicielem jest kierowca.

Odbieranie danych w kolejności

Oto typowa sekwencja sterownika, który odbiera dane w kolejności, gdzie każdy pakiet zawiera jeden fragment.

  1. Wywołaj NetRxQueueGetRingCollection, aby uzyskać strukturę kolekcji pierścieni kolejki odbiorczej. Można to przechowywać w przestrzeni kontekstowej kolejki, aby zmniejszyć liczbę wywołań z poziomu sterownika. Użyj kolekcji pierścieni, aby pobrać iterator opróżniania dla pierścienia fragmentu kolejki odbierania i pierścienia pakietów.
  2. Wskaż odebrane dane do systemu operacyjnego, opróżniając pierścienie sieci:
    1. Przydziel zmienne UINT32 do śledzenia bieżącego indeksu fragmentu pierścienia i bieżącego indeksu pierścienia pakietów. Ustaw te zmienne na BeginIndex odpowiednich pierścieni sieci, co oznacza początek sekcji odpływu pierścienia. Przydziel zmienną UINT32 na końcu sekcji opróżniania pierścienia fragmentu, ustawiając ją na NextIndex pierścienia fragmentu.
    2. Wykonaj następujące czynności w pętli:
      1. Sprawdź, czy fragment został odebrany przez sprzęt. Jeśli nie, przerwij pętlę.
      2. Wywołaj NetRingGetFragmentAtIndex, aby uzyskać fragment.
      3. Uzupełnij informacje o fragmencie, takie jak jego ValidLength, w oparciu o pasujący deskryptor sprzętowy.
      4. Aby pobrać pakiet dla tego fragmentu, wywołaj NetRingGetPacketAtIndex.
      5. Powiąż fragment z pakietem, ustawiając FragmentIndex na aktualny indeks fragmentu w pierścieniu fragmentów i ustawiając odpowiednią liczbę fragmentów (w tym przykładzie ustawiono na 1).
      6. Opcjonalnie uzupełnij inne informacje o pakiecie, takie jak informacja o sumie kontrolnej.
      7. Zwiększ indeks fragmentu, wywołując NetRingIncrementIndex.
      8. Przesuń indeks pakietu, wywołując NetRingIncrementIndex.
    3. Zaktualizuj BeginIndex pierścienia fragmentu na bieżący indeks fragmentu i zaktualizuj BeginIndex pierścienia pakietów na bieżący indeks pakietów, aby wskazać odebrane pakiety i ich fragmenty do systemu operacyjnego.
  3. Przekaż bufory do sprzętu dla następnych odbiorów.
    1. Ustaw bieżący indeks fragmentu na NextIndexfragmentu pierścienia, co stanowi początek podsekcji poście w pierścieniu. Ustaw indeks końca fragmentu na EndIndex pierścienia fragmentu.
    2. Wykonaj następujące czynności w pętli:
      1. Prześlij informacje fragmentu do odpowiedniego deskryptora sprzętowego.
      2. Zwiększ indeks fragmentu, wywołując NetRingIncrementIndex.
    3. Zaktualizuj NextIndex pierścienia fragmentu do bieżącej zmiennej indeksu fragmentu, aby sfinalizować publikowanie fragmentów na sprzęcie.

Te kroki mogą wyglądać następująco w kodzie:

void
MyEvtRxQueueAdvance(
    NETPACKETQUEUE RxQueue
)
{
    //
    // Retrieve the receive queue's ring collection and net rings. 
    // This example stores the Rx queue's ring collection in its queue context space.
    //
    PMY_RX_QUEUE_CONTEXT rxQueueContext = MyGetRxQueueContext(RxQueue);
    NET_RING_COLLECTION const * ringCollection = rxQueueContext->RingCollection;
    NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
    NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
    UINT32 currentPacketIndex = 0;
    UINT32 currentFragmentIndex = 0;
    UINT32 fragmentEndIndex = 0;

    //
    // Indicate receives by draining the rings
    //
    currentPacketIndex = packetRing->BeginIndex;
    currentFragmentIndex = fragmentRing->BeginIndex;
    fragmentEndIndex = fragmentRing->NextIndex;
    while(currentFragmentIndex != fragmentEndIndex)
    {
        // Test for fragment reception. Break if fragment has not been received.
        ...
        //

        NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);
        fragment->ValidLength = ... ;
        NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
        packet->FragmentIndex = currentFragmentIndex;
        packet->FragmentCount = 1;

        if(rxQueueContext->IsChecksumExtensionEnabled)
        {
            // Fill in checksum info
            ...
            //
        }        

        currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
        currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
    }
    fragmentRing->BeginIndex = currentFragmentIndex;
    packetRing->BeginIndex = currentPacketIndex;

    //
    // Post fragment buffers to hardware
    //
    currentFragmentIndex = fragmentRing->NextIndex;
    fragmentEndIndex = fragmentRing->EndIndex;
    while(currentFragmentIndex != fragmentEndIndex)
    {
        // Post fragment information to hardware descriptor
        ...
        //

        currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
    }
    fragmentRing->NextIndex = currentFragmentIndex;
}

Odbieranie danych poza kolejnością

W przeciwieństwie do kolejki Tx, sterowniki klienta zazwyczaj nie otrzymują danych w niewłaściwej kolejności, jeśli mają jedną kolejkę odbiorczą systemu operacyjnego na każdą kolejkę odbiorczą sprzętu. Jest to niezależnie od typu karty sieciowej sterownika klienta. Niezależnie od tego, czy urządzenie jest oparte na standardzie PCI, a system operacyjny przydziela i posiada bufory odbiorcze, czy też urządzenie jest oparte na USB, a stos USB posiada bufory odbiorcze, sterownik klienta inicjuje pakiet dla każdego odebranego fragmentu i przekazuje go do systemu operacyjnego. Kolejność nie jest ważna w tym przypadku.

Jeśli sprzęt obsługuje więcej niż jedną kolejkę odbioru systemu operacyjnego na każdą sprzętową kolejkę odbioru, należy zsynchronizować dostęp do buforów odbierania. Zakres tego działania znajduje się poza tym tematem i zależy od projektu sprzętu.