NDIS-Abrufmodus
Ü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 hergestellt werden, und die Vermeidung dieses Problems erfordert eine Menge Code, der schwierig ist, richtig zu kommen. Der NDIS-Umfragemodus bietet eine Alternative zu DPCs und ähnlichen Ausführungstools.
Der NDIS-Umfragemodus verschiebt die Komplexität von Planungsentscheidungen von NIC-Treibern und in NDIS, wobei NDIS Arbeitsgrenzwerte pro Iteration festlegt. Um diesen Umfragemodus zu erreichen, bietet Folgendes:
Ein Mechanismus für das Betriebssystem, um Druck auf die NIC auszuüben.
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 für die Behandlung eines NDIS-Miniporttreibers für einen Platz von Rx-Paketen mithilfe eines DPC. In diesem Beispiel ist die Hardware standard in Bezug auf PCIe-NICs. Sie verfügt über eine Empfangshardwarewarteschlange und eine Unterbrechungsmaske für diese Warteschlange.
Wenn keine Netzwerkaktivität vorhanden ist, ist die Hardware für den Rx-Interrupt aktiviert. Wenn ein Rx-Paket eintrifft:
Die Hardware generiert einen Interrupt und NDIS ruft die MiniportInterrupt-Funktion (ISR) des Treibers auf.
Der Treiber leistet im ISR sehr wenig Arbeit, da er bei einem sehr hohen IRQL ausgeführt wird. Der Treiber deaktiviert den Interrupt vom ISR und verschärft die Hardwareverarbeitung auf eine MiniportInterruptDPC-Funktion (DPC).
NDIS ruft schließlich den DPC des Treibers auf, und der Treiber entwässert alle Fertigstellungen aus der Hardwarewarteschlange und gibt sie an das Betriebssystem an.
Zwei Schmerzpunkte können sich auf den Netzwerkstapel auswirken, wenn der Treiber E/A-Vorgänge auf einen DPC zurückstellen:
Der Treiber weiß nicht, ob das System in der Lage ist, alle daten zu verarbeiten, die angegeben werden, sodass der Treiber keine Wahl hat, sondern so viele Elemente wie möglich aus der Hardwarewarteschlange zu entwässern und sie auf den Stapel hinzuweisen.
Da der Treiber einen DPC verwendet, um die Arbeit von seinem ISR zu verzögern, werden alle Hinweise an DISPATCH_LEVEL gemacht. Dies kann das System überwältigen, wenn lange Indikationsketten erstellt werden und zu Fehlerüberprüfungen 0x133 DPC_WATCHDOG_VIOLATION führen.
Um diese Schmerzpunkte zu vermeiden, ist ein großer Teil von trickreichem Code in Ihrem Treiber erforderlich. 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 Poll-Objekte
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 Poll-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 unterbricht integriert. 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 Treiber die einzelnen Abrufiterationen nicht erneut aktivieren, da der Umfragemodus Ihren Treiber darüber informiert, wann die Abfrage abgeschlossen ist, und es ist an der Zeit, den Interrupt erneut zu aktivieren.
Beim Treffen von Planungsentscheidungen kann das System smart sein, ob es bei 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 Platz von Rx-Paketen mithilfe eines Poll-Objekts anstelle eines DPC verarbeitet.
Wie das DPC-Modell ruft NDIS beim Eintreffen eines Rx-Pakets einen Interrupt auf, ruft NDIS den ISR des Treibers auf, und der Treiber deaktiviert den Interrupt vom ISR. An diesem Punkt unterscheidet sich das Umfragemodusmodell:
Anstatt einen DPC in die Warteschlange zu stellen, wird vom ISR ein Poll -Objekt (das zuvor erstellt wurde) in die Warteschlange gestellt, um NDIS zu benachrichtigen, dass neue Arbeiten verarbeitet werden können.
Irgendwann ruft NDIS den Abfrage-Iterationshandler des Treibers auf, um die Arbeit zu verarbeiten. 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.
NDIS fragt den Treiber weiterhin ab, bis er bewertet, dass der Treiber nicht mehr 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 Enumeration standardisierte INF-Schlüsselwörter weisen die folgenden Attribute auf:
Name des Unterschlüssels
Der Name der Schlüsselworts, die 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ü.
Name des Unterschlüssels | ParamDesc | Wert | EnumDesc |
---|---|---|---|
*NdisPoll | Ndis-Umfragemodus | 0 | Disabled |
1 (Standard) | Aktiviert |
Weitere Informationen zur Verwendung von Enumerationsschlüsselwörtern finden Sie unter Enumerationsschlüsselwörter.
Erstellen eines Poll-Objekts
Zum Erstellen eines Poll-Objekts führt der Miniporttreiber in der MiniportInitializeEx-Rückruffunktion Folgendes aus:
- Weist einen privaten Miniportkontext zu.
- Weist eine NDIS_POLL_CHARACTERISTICS Struktur zu, um Einstiegspunkte für die Rückruffunktionen NdisPoll und NdisSetPollNotification anzugeben.
- 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;
}
Queuing a Poll object for execution
Von einem ISR rufen Miniporttreiber NdisRequestPoll auf, um ein Poll-Objekt für die Ausführung in die Warteschlange zu stellen. Das folgende Beispiel zeigt die Behandlung von Empfangen, ignoriert jedoch die Freigabe von Unterbrechungslinien 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;
}
Implementieren des Abfrage iterationshandlers
NDIS ruft den NdisPoll-Rückruf des Miniporttreibers auf, um Informationen zu empfangen und Fertigstellungen zu senden. 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 Empfangsanzeigen oder Übergängen vornimmt.
Für Empfangsanzeigen sollte der Fahrer in NdisPoll folgendes tun:
- Überprüfen Sie den Empfangsparameter der NDIS_POLL_DATA-Struktur , um die maximale Anzahl von NBLs abzurufen, die angegeben werden können.
- Rufen Sie bis zur maximalen Anzahl von Rx-Paketen ab.
- Initialisieren Sie die NBLs.
- 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 des Parameters NdisPoll PollData).
- Beenden Sie den Rückruf.
Für Übertragungsabschlusse sollte der Treiber die folgenden Schritte in NdisPoll ausführen:
- Überprüfen Sie den Übertragungsparameter der NDIS_POLL_DATA-Struktur , um die maximale Anzahl von NBLs abzurufen, die er abschließen kann.
- Rufen Sie bis zur maximalen Anzahl von Tx-Paketen ab.
- Schließen Sie die NBLs ab.
- Fügen Sie sie der NBL-Warteschlange hinzu, die von der NDIS_POLL_TRANSMIT_DATA Struktur (in der NDIS_POLL_DATA Struktur des Parameters NdisPoll PollData) bereitgestellt wird.
- 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 NdisPoll keinen Fortschritt erzielt. NDIS verwendet NdisSetPollNotification, um dem Treiber mitzuteilen, dass der Aufruf von NdisPoll beendet wird. 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;
}
}