Modalità di polling NDIS
Panoramica della modalità di polling NDIS
La modalità di polling NDIS è un modello di esecuzione di polling controllato dal sistema operativo che guida il percorso dati dell'interfaccia di rete.
In precedenza, NDIS non aveva una definizione formale di un contesto di esecuzione del percorso dati. I driver NDIS in genere si basavano sulle chiamate di routine posticipate (DPC) per implementare il modello di esecuzione. Tuttavia, l'uso delle chiamate a procedura differita può sovraccaricare il sistema quando vengono create catene di indicazioni lunghe ed evitare questo problema richiede un sacco di codice complicato da scrivere correttamente. La modalità di polling NDIS offre un'alternativa ai DPC e a strumenti di esecuzione simili.
La modalità di polling NDIS sposta la complessità delle decisioni di pianificazione dai driver della scheda di interfaccia di rete e in NDIS, in cui NDIS imposta i limiti di lavoro per ogni iterazione. Per ottenere ciò, la modalità di polling fornisce:
Un meccanismo del sistema operativo per esercitare pressione sulla scheda di interfaccia di rete.
Un meccanismo del sistema operativo per controllare finemente le interruzioni.
La modalità di polling NDIS è disponibile per i driver miniport NDIS 6.85 e versioni successive.
Problemi con il modello DPC
Il diagramma di sequenza seguente illustra un esempio tipico di come un driver miniport NDIS gestisce un burst di pacchetti Rx usando un DPC. In questo esempio l'hardware è standard in termini di schede di interfaccia di rete PCIe. Ha una coda hardware di ricezione e una maschera di interrupt per tale coda.
Quando non è presente alcuna attività di rete, l'hardware ha l'interrupt Rx abilitato. Quando arriva un pacchetto Rx:
L'hardware genera un interrupt e NDIS chiama la funzione MiniportInterrupt del driver () (ISR).
Il driver svolge un lavoro molto limitato nell'ISR perché quest'ultimo viene eseguito a un IRQL molto elevato. Il driver disabilita l'interrupt dall'ISR e rinvia l'elaborazione hardware a una funzione MiniportInterruptDPC (DPC).
NDIS alla fine chiama il DPC del driver e quindi il driver scarica tutti i completamenti dalla coda hardware e li indica al sistema operativo.
Due punti critici possono influire sullo stack di rete quando il driver differisce le operazioni di I/O a un DPC.
Il driver non sa se il sistema è in grado di elaborare tutti i dati indicati, quindi non ha altra scelta che svuotare il maggior numero possibile di elementi dalla coda hardware e passarli allo stack.
Poiché il driver usa un DPC per rinviare il lavoro dal relativo ISR, tutte le indicazioni vengono effettuate in DISPATCH_LEVEL. Ciò può sovraccaricare il sistema quando vengono effettuate catene di indicazioni lunghe e causare Bug Check 0x133 DPC_WATCHDOG_VIOLATION.
Evitare questi punti critici richiede un sacco di codice complicato nel tuo driver. Sebbene sia possibile verificare se il watchdog DPC è vicino al limite con la funzione KeQueryDpcWatchdogInformation e uscire dal DPC, è comunque necessario creare un'infrastruttura intorno a questo nel driver: è necessario un modo per sospendere per un po', quindi continuare a indicare i pacchetti e allo stesso tempo è necessario sincronizzare tutto questo con la durata del percorso dati.
Introduzione agli oggetti Poll
La modalità di polling NDIS introduce l'oggetto Poll per risolvere le criticità associate ai DPC. Un oggetto Poll rappresenta un costrutto di contesto di esecuzione. I driver Miniport possono usare un oggetto Poll al posto di un DPC quando si gestiscono operazioni sul percorso dati.
Un oggetto Poll offre quanto segue:
Consente a NDIS di impostare limiti di lavoro per ogni iterazione.
È strettamente legato a un meccanismo di notifica. In questo modo il sistema operativo e la scheda di interfaccia di rete vengono sincronizzati per quanto riguarda il momento in cui è necessario elaborare il lavoro.
Ha un concetto di iterazione e interrupt incorporati. Quando si usano i DPC, i driver sono costretti a riabilitare l'interrupt ogni volta che completano un DPC. Quando si usano oggetti Poll, i driver non devono riabilitare l'interruzione di ogni iterazione di polling perché la modalità polling informa il driver quando viene eseguito il polling ed è il momento di riabilitare l'interrupt.
Quando si effettuano decisioni di pianificazione, il sistema può scegliere in modo intelligente se eseguire a DISPATCH_LEVEL o PASSIVE_LEVEL. In questo modo è possibile ottimizzare la definizione delle priorità del traffico da schede di interfaccia di rete diverse e portare a una distribuzione più equa del carico di lavoro nel computer.
Dispone di garanzie di serializzazione. Dopo aver eseguito il codice dall'interno del contesto di esecuzione di un oggetto Poll, si garantisce che non venga eseguito alcun altro codice correlato allo stesso contesto di esecuzione. In questo modo, un driver della scheda di interfaccia di rete può avere un'implementazione senza blocchi del percorso dati.
Modello NDIS in modalità sondaggio
Il diagramma di sequenza seguente illustra come lo stesso driver di interfaccia di rete PCIe ipotetico gestisce un burst di pacchetti Rx usando un oggetto Poll anziché un DPC.
Come il modello DPC, quando arriva un pacchetto Rx, l'hardware genera un interrupt, NDIS chiama l'ISR del driver e il driver disabilita l'interrupt dall'ISR. A questo punto, la modalità di sondaggio del modello si discosta:
Invece di accodare un DPC, il driver accoda un oggetto Poll, che aveva precedentemente creato , dall'ISR al NDIS per notificare che il nuovo lavoro è pronto per l'elaborazione.
A un certo punto nel futuro NDIS chiama il gestore di iterazione del driver per elaborare il lavoro. A differenza di un DPC, il driver non è autorizzato a indicare quanti NBL Rx ci sono quanti sono gli elementi pronti nella sua coda hardware. Il driver deve invece controllare il parametro dei dati di sondaggio del gestore per ottenere il numero massimo di NBL che può indicare.
Una volta che il driver ha recuperato fino al numero massimo di pacchetti Rx, deve inizializzare le NBL, aggiungerle alla coda NBL fornita dal gestore di polling e uscire dal callback. Il driver non deve abilitare l'interrupt prima di uscire.
NDIS continua a sondare il driver fino a quando non valuta che il driver non sta più facendo progressi. A questo punto NDIS interromperà il sondaggio e chiederà al driver di riabilitare l'interrupt.
Parola chiave INF standardizzata per modalità polling NDIS
La parola chiave seguente deve essere usata per abilitare o disabilitare il supporto per la modalità di polling NDIS:
*NdisPoll Le parole chiave INF standardizzate hanno i seguenti attributi per l'enumerazione:
SubkeyName
Nome della parola chiave che è necessario specificare nel file INF e visualizzato nel Registro di sistema.
ParamDesc
Testo visualizzato associato a SubkeyName.
Valore
Valore intero di enumerazione associato a ogni opzione nell'elenco. Questo valore viene archiviato in NDI\params\ SubkeyName\Value.
EnumDesc
Testo visualizzato associato a ogni valore visualizzato nel menu.
Predefinito
Valore predefinito per il menu.
SubkeyName | ParamDesc | Valore | EnumDesc |
---|---|---|---|
*NdisPoll | Modalità di polling Ndis | 0 | Disabile |
1 (impostazione predefinita) | Abilitato |
Per altre informazioni sull'uso delle parole chiave di enumerazione, vedere parole chiave di enumerazione.
Creazione di un oggetto Poll
Per creare un oggetto Poll, il driver miniport esegue le operazioni seguenti nella funzione di callback MiniportInitializeEx:
- Assegna un contesto miniport privato.
- Alloca una struttura NDIS_POLL_CHARACTERISTICS per specificare i punti di ingresso perfunzioni di callbackNdisPoll e NdisSetPollNotification.
- Chiama NdisRegisterPoll per creare l'oggetto Poll e archiviarlo nel contesto miniport.
L'esempio seguente mostra come un driver miniport potrebbe creare un oggetto Poll per un flusso di queue di ricezione. La gestione degli errori viene omessa per semplicità.
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;
}
Accodamento di un oggetto Poll per l'esecuzione
Quando si utilizza un ISR, i driver miniport chiamano NdisRequestPoll per accodare un oggetto Poll per l'esecuzione. Nell'esempio seguente viene illustrata la gestione della ricezione, ma viene ignorata la condivisione delle linee di interrupt per semplicità.
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;
}
Implementazione del gestore di iterazione del sondaggio
NDIS richiama il callback NdisPoll del driver miniport per eseguire il polling per ricevere indicazioni e inviare completamenti. NDIS richiama prima NdisPoll quando il driver chiama NdisRequestPoll per accodare un oggetto Poll. NDIS continuerà a richiamare NdisPoll mentre il driver sta avanzando nei progressi sulle indicazioni di ricezione o sui completamenti di trasmissione.
Per ricevere indicazioni, il driver deve eseguire le operazioni seguenti in NdisPoll:
- Controllare il parametro receive della struttura di NDIS_POLL_DATA per ottenere il numero massimo di NCL che può indicare.
- Recupera fino al numero massimo di pacchetti Rx.
- Inizializzare gli NBLs.
- Aggiungerli alla coda NBL fornita dalla struttura NDIS_POLL_RECEIVE_DATA che si trova nella struttura NDIS_POLL_DATA del parametro NdisPollPollData.
- Chiudere il callback.
Per i completamenti di trasmissione, il driver deve eseguire le operazioni seguenti in NdisPoll:
- Controllare il trasmettere parametro della struttura NDIS_POLL_DATA per ottenere il numero massimo di NRL che può essere completato.
- Recuperare fino al numero massimo di pacchetti Tx.
- Completare i NBL.
- Aggiungerli alla coda NBL fornita dalla struttura NDIS_POLL_TRANSMIT_DATA ,che si trova nella struttura NDIS_POLL_DATA del parametro NdisPollPollData).
- Chiudere il callback.
Il driver non deve abilitare l'interrupt dell'oggetto Poll prima di uscire dalla funzione NdisPoll. NDIS continuerà a eseguire il polling del driver fino a quando non valuta che non venga effettuato alcun avanzamento in avanti. A questo punto NDIS interromperà il polling e chiederà al driver di riabilitare l'interrupt.
Ecco come un driver potrebbe implementare NdisPoll per un flusso della coda di ricezione.
_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);
}
}
Gestione degli interrupt
I driver Miniport implementano il callback NdisSetPollNotification per abilitare o disabilitare l'interrupt associato a un oggetto Poll. NDIS richiama in genere il callback NdisSetPollNotification quando rileva che il driver miniport non sta facendo progressi in NdisPoll. NDIS usa NdisSetPollNotification per indicare al driver che smetterà di richiamare NdisPoll. Il driver deve richiamare NdisRequestPoll quando il nuovo lavoro è pronto per l'elaborazione.
Ecco come un driver potrebbe implementare NdisSetPollNotification per un flusso di coda di ricezione.
_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;
}
}