NDIS-Umfragemodus

Übersicht über den NDIS-Umfragemodus

Der NDIS-Umfragemodus ist ein vom Betriebssystem gesteuertes Abfrageausführungsmodell, das den Netzwerkschnittstellendatenpfad steuert.

Zuvor hatte NDIS keine formale Definition eines Datenpfadausführungskontexts. NDIS-Treiber basieren in der Regel auf Verzögerten Prozeduraufrufen (Deferred Procedure Calls, DPCs), um ihr Ausführungsmodell zu implementieren. Die Verwendung von DPCs kann das System jedoch überwältigen, wenn lange Indikationsketten gebildet werden, und die Vermeidung dieses Problems erfordert eine Menge Code, der schwierig korrekt zu implementieren ist. Der NDIS-Umfragemodus bietet eine Alternative zu DPCs und ähnlichen Ausführungstools.

Der NDIS-Polling-Modus verschiebt die Komplexität von Planungsentscheidungen von den NIC-Treibern zu NDIS, wobei NDIS Arbeitsgrenzwerte pro Iteration festlegt. Um diesen Umfragemodus zu erreichen, bietet Folgendes:

  1. Ein Mechanismus für das Betriebssystem, um Rückdruck auf die Netzwerkkarte (NIC) auszuüben.

  2. Ein Mechanismus, mit dem das Betriebssystem Unterbrechungen fein steuern kann.

Der NDIS-Umfragemodus ist für NDIS 6.85 und höhere Miniporttreiber verfügbar.

Probleme mit dem DPC-Modell

Das folgende Sequenzdiagramm veranschaulicht ein typisches Beispiel dafür, wie ein NDIS-Miniporttreiber eine Flut von Rx-Paketen mithilfe eines DPC bearbeitet. In diesem Beispiel ist die Hardware standard in Bezug auf PCIe-NICs. Sie verfügt über eine Empfangs-Hardware-Warteschlange und eine Unterbrechungsmaske für diese Warteschlange.

Diagramm mit dem NDIS-DPC-Modell mit Rx-Paketen und einer Empfangshardwarewarteschlange.

Wenn keine Netzwerkaktivität vorhanden ist, ist die Hardware für den Rx-Interrupt aktiviert. Wenn ein Rx-Paket eintrifft:

  1. Die Hardware generiert einen Interrupt und NDIS ruft die MiniportInterrupt-Funktion (ISR ) des Treibers auf.

  2. Der Treiber leistet im ISR sehr wenig Arbeit, da er bei einem sehr hohen IRQL ausgeführt wird. Der Treiber deaktiviert den Interrupt von der ISR und verlagert die Hardwareverarbeitung in eine MiniportInterruptDPC-Funktion (DPC).

  3. NDIS ruft schließlich den DPC des Treibers auf, und der Treiber entfernt alle abgeschlossenen Vorgänge aus der Hardwarewarteschlange und übermittelt sie zum Betriebssystem.

Zwei Problempunkte können sich auf den Netzwerkstapel auswirken, wenn der Treiber E/A-Vorgänge auf einen DPC verschiebt:

  1. Der Treiber weiß nicht, ob das System in der Lage ist, alle Daten zu verarbeiten, die angegeben sind, sodass der Treiber keine Wahl hat, sondern so viele Elemente wie möglich aus der Hardwarewarteschlange abzuarbeiten und sie in den Stapel zu übertragen.

  2. Da der Treiber einen DPC verwendet, um die Arbeit seines ISR aufzuschieben, werden alle Indikatoren auf DISPATCH_LEVEL gemacht. Dies kann das System überlasten, wenn lange Indikationsketten erstellt werden und zu Bugcheck 0x133 DPC_WATCHDOG_VIOLATION führen.

Um diese Schmerzpunkte zu vermeiden, benötigen Sie eine erhebliche Menge trickreichen Codes in Ihrem Treiber. Sie können zwar überprüfen, ob der DPC Watchdog mit der KeQueryDpcWatchdogInformation-Funktion in der Nähe des Grenzwerts liegt und aus dem DPC herausbrechen kann, sie müssen aber dennoch eine Infrastruktur dafür in Ihrem Treiber erstellen: Sie benötigen eine Gewisse Möglichkeit, um ein bisschen anzuhalten, und zeigen Sie dann weiterhin die Pakete an, und gleichzeitig müssen Sie all dies mit der Lebensdauer des Datenpfads synchronisieren.

Einführung in Abfrageobjekte

Der NDIS-Umfragemodus führt das Poll-Objekt ein, um die mit DPCs verbundenen Schmerzpunkte zu lösen. Ein Poll-Objekt ist ein Ausführungskontextkonstrukt. Miniport-Treiber können ein Poll-Objekt anstelle eines DPC verwenden, wenn es um Datenpfadvorgänge geht.

Ein Abfrage-Objekt bietet Folgendes:

  • Es bietet eine Möglichkeit für NDIS, Arbeitsgrenzwerte pro Iteration festzulegen.

  • Sie ist eng mit einem Benachrichtigungsmechanismus verknüpft. Dadurch wird das Betriebssystem und die NIC synchronisiert, wenn Arbeit verarbeitet werden muss.

  • Es hat ein Konzept der Iteration und integrierte Unterbrechungen. Wenn Sie DPCs verwenden, müssen Treiber den Interrupt jedes Mal erneut aktivieren, wenn sie einen DPC fertig stellen. Wenn Sie Poll-Objekte verwenden, müssen Sie den Interrupt während jeder Abrufiteration nicht erneut aktivieren, da der Poll Mode Ihren Treiber darüber informiert, wann die Abfrage abgeschlossen ist, und es an der Zeit ist, den Interrupt wieder zu aktivieren.

  • Beim Treffen von Planungsentscheidungen kann das System intelligent erkennen, ob es auf DISPATCH_LEVEL oder PASSIVE_LEVEL ausgeführt werden soll. Dies kann eine fein abgestimmte Priorisierung des Datenverkehrs von verschiedenen NICs ermöglichen und zu einer faireren Workloadverteilung auf dem Computer führen.

  • Es verfügt über Serialisierungsgarantien. Nachdem Sie Code aus dem Ausführungskontext eines Poll-Objekts ausgeführt haben, wird sichergestellt, dass kein anderer Code im Zusammenhang mit demselben Ausführungskontext ausgeführt wird. Dadurch kann ein NIC-Treiber über eine sperrfreie Implementierung seines Datenpfads verfügen.

Das NDIS-Umfragemodusmodell

Das folgende Sequenzdiagramm veranschaulicht, wie derselbe hypothetische PCIe-NIC-Treiber einen Burst von Rx-Paketen mithilfe eines Poll-Objekts anstatt eines DPC verarbeitet.

Diagramm, das den NDIS Poll Mode mit Rx-Paketen und einer Empfangshardwarequeue zeigt.

Wie beim DPC-Modell, wenn ein Rx-Paket eintrifft, erzeugt die Hardware einen Interrupt, NDIS ruft die ISR des Treibers auf, und der Treiber deaktiviert den Interrupt aus der ISR. An diesem Punkt unterscheidet sich das Umfragemodusmodell:

  1. Anstatt einen DPC in die Warteschlange zu stellen, stellt der Treiber ein Poll-Objekt, das er zuvor erstellt hat, vom ISR in die Warteschlange, um NDIS zu benachrichtigen, dass neue Arbeit zur Verarbeitung bereitsteht.

  2. Zukünftig wird NDIS den Abfrage-Iterationshandler des Treibers aufrufen, um die Aufgabe zu bearbeiten. Im Gegensatz zu einem DPC darf der Treiber nicht so viele Rx NBLs angeben, wie Elemente in der Hardwarewarteschlange bereit sind. Der Treiber sollte stattdessen den Abfragedatenparameter des Handlers überprüfen, um die maximale Anzahl von NBLs abzurufen, die angegeben werden können.

    Sobald der Treiber die maximale Anzahl von Rx-Paketen abruft, sollte er NBLs initialisieren, sie der vom Abrufhandler bereitgestellten NBL-Warteschlange hinzufügen und den Rückruf beenden. Der Treiber sollte den Interrupt vor dem Beenden nicht aktivieren.

  3. NDIS fragt den Netzwerktreiber weiterhin ab, bis es feststellt, dass der Treiber keine weiteren Fortschritte macht. An diesem Punkt beendet NDIS die Abfrage und fordert den Treiber auf, den Interrupt erneut zu aktivieren.

Standardisiertes INF-Schlüsselwort für den NDIS-Umfragemodus

Das folgende Schlüsselwort muss verwendet werden, um die Unterstützung für den NDIS-Umfragemodus zu aktivieren oder zu deaktivieren:

*NdisPoll Aufzählungsstandardisierte INF-Schlüsselwörter weisen die folgenden Attribute auf:

Unterschlüsselname
Der Name des Schlüsselworts, das Sie in der INF-Datei angeben müssen und die in der Registrierung angezeigt wird.

ParamDesc
Der Anzeigetext, der mit "SubkeyName" verknüpft ist.

Wert
Der ganzzahlige Enumerationswert, der jeder Option in der Liste zugeordnet ist. Dieser Wert wird in NDI\params\ SubkeyName\Value gespeichert.

EnumDesc
Der Anzeigetext, der jedem Wert zugeordnet ist, der im Menü angezeigt wird.

Standard
Der Standardwert für das Menü.

Unterschlüsselname ParamDesc Wert EnumDesc
*NdisPoll Ndis-Umfragemodus 0 Arbeitsunfähig
1 (Standard) Aktiviert

Weitere Informationen zur Verwendung von Aufzählungsschlüsselwörtern finden Sie unter Aufzählungsschlüsselwörter.

Erstellen eines Poll-Objekts

Zum Erstellen eines Poll-Objekts führt der Miniporttreiber in der MiniportInitializeEx-Rückruffunktion Folgendes aus:

  1. Weist einen privaten Miniportkontext zu.
  2. Weist eine NDIS_POLL_CHARACTERISTICS Struktur zu, um Einstiegspunkte für die Rückruffunktionen NdisPoll und NdisSetPollNotification anzugeben.
  3. Ruft NdisRegisterPoll auf, um das Poll-Objekt zu erstellen und im Miniportkontext zu speichern.

Das folgende Beispiel zeigt, wie ein Miniporttreiber ein Poll-Objekt für einen Empfangswarteschlangenfluss erstellen kann. Fehlerbehandlung wird aus Gründen der Einfachheit ausgelassen.

NDIS_SET_POLL_NOTIFICATION NdisSetPollNotification; 
NDIS_POLL NdisPoll; 

NDIS_STATUS 
MiniportInitialize( 
    _In_ NDIS_HANDLE NdisAdapterHandle, 
    _In_ NDIS_HANDLE MiniportDriverContext, 
    _In_ NDIS_MINIPORT_INIT_PARAMETERS * MiniportInitParameters 
) 
{ 
    // Allocate a private miniport context 
    MINIPORT_CONTEXT * miniportContext = ...;
 
    NDIS_POLL_CHARACTERISTICS pollCharacteristics; 
    pollCharacteristics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; 
    pollCharacteristics.Header.Revision = NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.Header.Size = NDIS_SIZEOF_NDIS_POLL_CHARACTERISTICS_REVISION_1; 
    pollCharacteristics.SetPollNotificationHandler = NdisSetPollNotification; 
    pollCharacteristics.PollHandler = NdisPoll; 

    // Create a Poll object and store it in the miniport context 
    NdisRegisterPoll( 
        NdisAdapterHandle, 
        miniportContext, 
        &pollCharacteristics, 
        &miniportContext->RxPoll); 
 
    return NDIS_STATUS_SUCCESS; 
} 

Einreihen eines Poll-Objekts zur Ausführung

Von einem ISR rufen Miniporttreiber NdisRequestPoll auf, um ein Poll-Objekt für dessen Ausführung in die Warteschlange zu stellen. Das folgende Beispiel zeigt die Empfangsverarbeitung, ignoriert jedoch die gemeinsame Nutzung von Interruptlinien aus Gründen der Einfachheit.

BOOLEAN 
MiniportIsr( 
  KINTERRUPT * Interrupt, 
  void * Context 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Check if this interrupt is due to a received packet 
    if (hardwareContext->ISR & RX_OK) 
    { 
        // Disable the receive interrupt and queue the Poll 
        hardwareContext->IMR &= ~RX_OK; 
        NdisRequestPoll(miniportContext->RxPoll, nullptr); 
    }

    return TRUE; 
} 

Implementierung des Poll-Iterations-Handlers

NDIS ruft den NdisPoll-Rückruf des Miniporttreibers auf, um auf Empfangsmeldungen zu prüfen und Sendeabschlüsse zu erfassen. NDIS ruft zuerst NdisPoll auf, wenn der Treiber NdisRequestPoll aufruft, um ein Poll-Objekt in die Warteschlange zu stellen. NDIS wird NdisPoll- aufrufen, während der Fahrer fortschritte bei der Empfangsanzeige oder beim Übertragen von Abschlussen voranschreitet.

Für Empfangsanzeigen sollte der Fahrer in NdisPoll folgendes tun:

  1. Überprüfen Sie den Empfangsparameter der NDIS_POLL_DATA-Struktur , um die maximale Anzahl von NBLs abzurufen, die angegeben werden können.
  2. Rufen Sie die maximale Anzahl von Rx-Paketen ab.
  3. Initialisieren Sie die NBLs.
  4. Fügen Sie sie der NBL-Warteschlange hinzu, die von der NDIS_POLL_RECEIVE_DATA Struktur bereitgestellt wird (befindet sich in der NDIS_POLL_DATA Struktur der NdisPollPollData Parameter).
  5. Beenden Sie den Rückruf.

Für Übertragungsabschlüsse sollte der Treiber in NdisPoll die folgenden Schritte ausführen:

  1. Überprüfen Sie den Übertragungsparameter der NDIS_POLL_DATA-Struktur , um die maximale Anzahl von NBLs abzurufen, die er abschließen kann.
  2. Rufen Sie bis zur maximalen Anzahl von Tx-Paketen ab.
  3. Schließen Sie die NBLs ab.
  4. Fügen Sie sie der NBL-Warteschlange hinzu, die von der NDIS_POLL_TRANSMIT_DATA Struktur bereitgestellt wird (befindet sich in der NDIS_POLL_DATA Struktur der NdisPollPollData Parameter).
  5. Beenden Sie den Rückruf.

Der Treiber sollte den Interrupt des Poll-Objekts nicht aktivieren, bevor die NdisPoll-Funktion beendet wird. NDIS fragt den Treiber weiterhin ab, bis er bewertet, dass kein Fortschritt erzielt wird. An diesem Punkt beendet NDIS die Abfrage und fordert den Treiber auf, den Interrupt erneut zu aktivieren.

Hier erfahren Sie, wie ein Treiber NdisPoll für einen Empfangswarteschlangenablauf implementiert.

_Use_decl_annotations_ 
void 
NdisPoll( 
    void * Context, 
    NDIS_POLL_DATA * PollData 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    // Drain received frames 
    auto & receive = PollData->Receive; 
    receive.NumberOfRemainingNbls = NDIS_ANY_NUMBER_OF_NBLS; 
    receive.Flags = NDIS_RECEIVE_FLAGS_SHARED_MEMORY_VALID; 

    while (receive.NumberOfIndicatedNbls < receive.MaxNblsToIndicate) 
    { 
        auto rxDescriptor = HardwareQueueGetNextDescriptorToCheck(hardwareContext->RxQueue); 

        // If this descriptor is still owned by hardware stop draining packets 
        if ((rxDescriptor->Status & HW_OWN) != 0) 
            break; 

        auto nbl = MakeNblFromRxDescriptor(miniportContext->NblPool, rxDescriptor); 

        AppendNbl(&receive.IndicatedNblChain, nbl); 
        receive.NumberOfIndicatedNbls++; 

        // Move to next descriptor 
        HardwareQueueAdvanceNextDescriptorToCheck(hardwareContext->RxQueue); 
    } 
} 

Verwalten von Unterbrechungen

Miniport-Treiber implementieren den NdisSetPollNotification-Rückruf , um den mit einem Poll-Objekt verknüpften Interrupt zu aktivieren oder zu deaktivieren. NDIS ruft in der Regel den NdisSetPollNotification- Rückruf auf, wenn erkannt wird, dass der Miniporttreiber in NdisPollkeinen Fortschritt erzielt. NDIS verwendet NdisSetPollNotification, um dem Treiber mitzuteilen, dass er nicht mehr NdisPoll-aufruft. Der Treiber sollte NdisRequestPoll- aufrufen, wenn neue Arbeiten verarbeitet werden können.

Hier erfahren Sie, wie ein Treiber NdisSetPollNotification für einen Empfangswarteschlangenfluss implementiert.

_Use_decl_annotations_ 
void 
NdisSetPollNotification( 
    void * Context, 
    NDIS_POLL_NOTIFICATION * Notification 
) 
{ 
    auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context); 
    auto hardwareContext = miniportContext->HardwareContext; 

    if (Notification->Enabled) 
    { 
        hardwareContext->IMR |= RX_OK; 
    } 
    else 
    { 
        hardwareContext->IMR &= ~RX_OK; 
    } 
}