Condividi tramite


Suddivisione delle richieste di trasferimento DMA

Qualsiasi driver potrebbe dover suddividere una richiesta di trasferimento ed eseguire più operazioni di trasferimento DMA per soddisfare un determinato IRP, a seconda dei seguenti:

  • Numero di registri mappa restituiti da IoGetDmaAdapter

  • Byte di dati da trasferire, contenuti nel Length, nella posizione dello stack di I/O del driver per l'IRP.

  • Numero di limiti di pagina, nella memoria fisica di sistema, per il buffer in cui o da cui il driver deve trasferire i dati

  • Vincoli specifici del dispositivo sulle operazioni DMA del driver. Ad esempio, il driver del disco "AT" di sistema deve suddividere le richieste di trasferimento per più di 256 settori a causa delle limitazioni del controller del disco.

Un driver può determinare il numero di registri mappa necessari per trasferire tutti i dati specificati da un IRP come indicato di seguito:

  1. Chiamare MmGetMdlVirtualAddress, passando un puntatore all'MDL in Irp->MdlAddress, per ottenere l'indirizzo virtuale iniziale del buffer. Si noti che un driver non deve tentare di accedere alla memoria usando questo indirizzo virtuale. Il valore restituito da MmGetMdlVirtualAddress è un indice nel file MDL, non necessariamente un indirizzo valido.

  2. Trasmetti l'indice restituito e il valore di Length nella posizione dello stack I/O del driver dell'IRP alla macro ADDRESS_AND_SIZE_TO_SPAN_PAGES.

Se il valore restituito da ADDRESS_AND_SIZE_TO_SPAN_PAGES è maggiore del valore NumberOfMapRegisters restituito da IoGetDmaAdapter, il driver non può trasferire tutti i dati richiesti per questo IRP in una singola operazione DMA. Al contrario, deve eseguire le operazioni seguenti:

  1. Suddividere il buffer in parti ridimensionate in base al numero di registri mappa disponibili (ed eventuali vincoli DMA specifici del dispositivo).

  2. Eseguire tutte le operazioni DMA necessarie per soddisfare la richiesta di trasferimento.

Supponiamo, ad esempio, che ADDRESS_AND_SIZE_TO_SPAN_PAGES indichi che sono necessari dodici registri mappa per soddisfare una richiesta di trasferimento, ma il valore NumberOfMapRegisters restituito da IoGetDmaAdapter sono solo cinque. Si supponga che non siano presenti vincoli DMA specifici del dispositivo. In questo caso, il driver deve eseguire tre operazioni di trasferimento DMA, chiamando MapTransfer tre volte per trasferire tutti i dati richiesti dall'IRP.

I driver di dispositivo DMA del sistema usano varie tecniche per suddividere un trasferimento DMA quando non sono presenti registri mappa sufficienti per soddisfare un IRP con una singola operazione di I/O. Una tecnica da usare è la seguente:

  1. Chiamare IoAllocateMdl per allocare un MDL che descrive una parte del buffer utente.

  2. Chiamare MmProbeAndLockPages per bloccare tale parte del buffer utente.

  3. Trasferisci i dati per quella parte del buffer.

  4. Chiamare MmUnlockPages ed eseguire una delle operazioni seguenti:

    • Se il MDL che il driver ha allocato nel passaggio 1 è sufficientemente grande per il prossimo pezzo del trasferimento, utilizzare MmPrepareMdlForReuse e ripetere i passaggi da 2 a 4.
    • In caso contrario, chiamare IoFreeMdl e ripetere i passaggi da 1 a 4.
  5. Chiamare MmUnlockPages e IoFreeMdl quando tutti i dati sono stati trasferiti.

Se un driver di livello più alto non può bloccare l'intero buffer utente con MmProbeAndLockPages in un computer con memoria limitata, può eseguire le operazioni seguenti:

  1. Chiamare IoBuildSynchronousFsdRequest per allocare un IRP di trasferimento parziale e bloccare una parte del buffer utente. L'area bloccata è in genere un multiplo di PAGE_SIZE o è ridimensionata in base alla capacità di trasferimento del dispositivo sottostante.

  2. Chiamare IoCallDriver per l'IRP di trasferimento parziale e chiamare KeWaitForSingleObject per attendere che un oggetto evento, configurato dal driver, sia associato al relativo IRP di trasferimento parziale, se i driver inferiori restituiscono STATUS_PENDING.

  3. Quando recupera il controllo, ripetere i passaggi 1 e 2 fino a quando non vengono trasferiti tutti i dati e quindi completare l'IRP originale.

Quando un driver di classe di archiviazione suddivide le richieste di trasferimento di grandi dimensioni per i driver porta/miniport SCSI sottostanti, alloca un IRP aggiuntivo per ogni parte della richiesta di trasferimento. Registra una routine IoCompletionper ogni IRP allocato dal driver, per tenere traccia dello stato della richiesta di trasferimento completa e per liberare gli IRP allocati dal driver. Invia quindi questi IRP al driver della porta usando IoCallDriver.

Altri driver di classe/porta possono usare questa tecnica solo se il driver di classe può determinare il numero di registri mappa disponibili per il driver di porta. Il driver di porta deve archiviare queste informazioni di configurazione nel Registro di sistema per il driver di classe associato oppure i driver associati devono definire un'interfaccia privata, usando le richieste di controllo di I/O del dispositivo interno, per passare le informazioni di configurazione sul numero di registri mappa disponibili dal driver di porta al driver di classe.

Un driver monolitico (ovvero un driver non parte di una coppia di classi/porte) per un dispositivo DMA deve suddividere richieste di trasferimento di grandi dimensioni per se stesso. Tali driver di solito suddivideno una richiesta di grandi dimensioni in parti ed eseguono una sequenza di operazioni DMA per soddisfare l'IRP.

Se una richiesta di trasferimento è troppo grande per il driver di dispositivo sottostante da gestire, un driver di livello superiore può chiamare MmGetMdlVirtualAddress e IoBuildPartialMdl, quindi configurare una sequenza di IRP di trasferimento parziale per i driver di dispositivo sottostanti.