Condividi tramite


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 dei CONTROLLER di dominio può sovraccaricare il sistema quando vengono create catene di indicazioni lunghe ed evitare questo problema richiede un sacco di codice difficile da correggere. 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 questa modalità di polling, è possibile:

  1. Un meccanismo per il sistema operativo per esercitare pressione sulla scheda di interfaccia di rete.

  2. Un meccanismo per il sistema operativo per controllare correttamente gli interrupt.

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.

Diagramma che mostra il modello DPC NDIS con pacchetti Rx e una coda hardware di ricezione.

Quando non è presente alcuna attività di rete, l'hardware ha l'interrupt Rx abilitato. Quando arriva un pacchetto Rx:

  1. L'hardware genera un interrupt e NDIS chiama la funzione MiniportInterrupt (ISR) del driver.

  2. Il driver funziona molto poco nell'ISR perché vengono eseguiti a un IRQL molto elevato. Il driver disabilita l'interrupt dall'ISR e rinvia l'elaborazione hardware a una funzione MiniportInterruptDPC (DPC).

  3. NDIS chiama infine il DPC del driver e il driver svuota tutti i completamenti dalla coda hardware e li indica al sistema operativo.

Due punti di dolore possono influire sullo stack di rete quando il driver sfida le operazioni di I/O a un DPC:

  1. Il driver non sa se il sistema è in grado di elaborare tutti i dati indicati, quindi il driver non ha scelta, ma svuotare il maggior numero possibile di elementi dalla coda hardware e indicare che lo stack è in alto.

  2. Poiché il driver usa un DPC per rinviare il lavoro dal relativo ISR, tutte le indicazioni vengono effettuate in DISPATCH_LEVEL. Questo può sovraccaricare il sistema quando vengono effettuate catene di indicazioni lunghe e causare il controllo bug 0x133 DPC_WATCHDOG_VIOLATION.

Evitare questi punti di dolore richiede un sacco di codice complicato nel driver. Anche se è 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 polling

La modalità di polling NDIS introduce l'oggetto Poll per risolvere i punti di dolore associati ai DPC. Un oggetto Poll è 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 controller di rete, i driver vengono forzati a riabilitare l'interrupt ogni volta che terminano 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ò essere intelligente sul fatto che venga eseguito in 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 di modalità poll NDIS

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.

Diagramma che mostra la modalità di polling NDIS con pacchetti Rx e una coda hardware di ricezione.

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 il modello modalità di polling diverge:

  1. Invece di accodare un DPC, il driver accoda un oggetto Poll (creato in precedenza) dall'ISR per notificare a NDIS che il nuovo lavoro è pronto per l'elaborazione.

  2. A un certo punto nel futuro NDIS chiama il gestore di iterazione di polling del driver per elaborare il lavoro. A differenza di un DPC, il driver non è autorizzato a indicare quanti NCL Rx sono pronti nella coda hardware. Il driver deve invece controllare il parametro di dati di polling del gestore per ottenere il numero massimo di NRL che può indicare.

    Una volta che il driver recupera fino al numero massimo di pacchetti Rx, deve inizializzare gli elenchi di numeri di rete, aggiungerli alla coda NBL fornita dal gestore di polling e uscire dal callback. Il driver non deve abilitare l'interrupt prima di uscire.

  3. NDIS continua a eseguire il polling del driver fino a quando non valuta che il driver non sta più avanzando. A questo punto NDIS interromperà il polling e chiederà al driver di riabilitare l'interrupt.

Parola chiave INF standardizzata per la modalità di polling NDIS

La parola chiave seguente deve essere usata per abilitare o disabilitare il supporto per la modalità di polling NDIS:

*Le parole chiave INF standardizzate dell'enumerazione NdisPoll hanno gli attributi seguenti:

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.

Predefiniti
Valore predefinito per il menu.

SubkeyName ParamDesc Valore EnumDesc
*NdisPoll Modalità di polling Ndis 0 Disabilitata
1 (impostazione predefinita) Attivata

Per altre informazioni sull'uso delle parole chiave di enumerazione, vedere Enumeration Keywords.For more information about using enumeration keywords, see Enumeration Keywords.

Creazione di un oggetto Poll

Per creare un oggetto Poll, il driver miniport esegue le operazioni seguenti nella relativa funzione di callback MiniportInitializeEx:

  1. Alloca un contesto miniport privato.
  2. Alloca una struttura NDIS_POLL_CHARACTERISTICS per specificare i punti di ingresso per le funzioni di callback NdisPoll e NdisSetPollNotification.
  3. 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 coda 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

Da 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 righe 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 poll

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 effettuando progressi in avanti in caso di ricezione di indicazioni o di trasmissione dei completamenti.

Per ricevere indicazioni, il driver deve eseguire le operazioni seguenti in NdisPoll:

  1. Controllare il parametro di ricezione della struttura di NDIS_POLL_DATA per ottenere il numero massimo di NRL che può indicare.
  2. Recuperare fino al numero massimo di pacchetti Rx.
  3. Inizializzare gli elenchi di numeri di rete.
  4. Aggiungerli alla coda NBL fornita dalla struttura NDIS_POLL_RECEIVE_DATA ,che si trova nella struttura NDIS_POLL_DATA del parametro NdisPoll PollData.
  5. Chiudere il callback.

Per i completamenti di trasmissione, il driver deve eseguire le operazioni seguenti in NdisPoll:

  1. Controllare il parametro di trasmissione della struttura NDIS_POLL_DATA per ottenere il numero massimo di NRL che può essere completato.
  2. Recuperare fino al numero massimo di pacchetti Tx.
  3. Completare i criteri di rete.
  4. Aggiungerli alla coda NBL fornita dalla struttura NDIS_POLL_TRANSMIT_DATA ,che si trova nella struttura NDIS_POLL_DATA del parametro NdisPoll PollData.
  5. 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 avanzando 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 della 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; 
    } 
}