Condividi tramite


Suddivisione delle richieste di trasferimento DMA

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

  • Numero di registri mappa restituiti da IoGetDmaAdapter

  • Byte di dati da trasferire, contenuti nel membro Length del percorso dello stack I/O del driver per 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 a MDL in Irp-MdlAddress> per ottenere l'indirizzo virtuale iniziale per il buffer. Si noti che un driver non deve tentare di accedere alla memoria usando questo indirizzo virtuale. Il valore restituito da MmGetMdlVirtualAddress è un indice in MDL, non necessariamente un indirizzo valido.

  2. Passare l'indice restituito e il valore di Length nella posizione dello stack di 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 NumberOfMapRegistersrestituito 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.

Si supponga, ad esempio, ADDRESS_AND_SIZE_TO_SPAN_PAGES indica che sono necessari dodici registri mappa per soddisfare una richiesta di trasferimento, ma il valore NumberOfMapRegistersrestituito da IoGetDmaAdapter è solo cinque. Si supponga che non siano previsti 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. Trasferire i dati per tale parte del buffer.

  4. Chiamare MmUnlockPages ed eseguire una delle operazioni seguenti:

    • Se il file MDL allocato nel passaggio 1 è sufficientemente grande per la parte successiva del trasferimento, chiamare MmPrepareMdlForReuse e ripetere i passaggi da 2 a 4.
    • In caso contrario, chiamare IoFreeMdl e ripetere i passaggi da 1 a 4.
  5. Chiama 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 viene 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 all'IRP di trasferimento parziale, se i driver inferiori restituiscono STATUS_PENDING.

  3. Quando recupera il controllo, ripetere i passaggi 1 e 2 fino al trasferimento di tutti i dati e quindi completare l'IRP originale.

Quando un driver della 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 IoCompletion per ogni IRP allocato dal driver, per tenere traccia dello stato della richiesta di trasferimento completa e per liberare i runtime di integrazione allocati dal driver. Invia quindi questi irP al driver di 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 della 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 interne del dispositivo, per passare le informazioni di configurazione sul numero di registri delle mappe 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 le 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.