Condividi tramite


Gestione delle code dei dispositivi

Il gestore di I/O in genere (ad eccezione delle unità FSD) crea un oggetto coda di dispositivi associato quando un driver chiama IoCreateDevice. Fornisce anche IoStartPacket e IoStartNextPacket, che i driver possono chiamare affinché il gestore di I/O inserisca gli IRP nella coda del dispositivo associata o chiami le loro routine StartIo.

Di conseguenza, è raramente necessario (o particolarmente utile) per un driver configurare i propri oggetti di coda dei dispositivi per gli IRP. I candidati probabili sono i driver, come il driver della porta SCSI, che devono coordinare gli IRP in ingresso da numerosi driver di classe strettamente associati per dispositivi eterogenei gestiti tramite un unico controller o adattatore bus.

In altre parole, è più probabile che un driver per un controller array di dischi utilizzi un oggetto controller creato dal driver rispetto alla creazione di oggetti coda di dispositivi supplementari, mentre è leggermente più propenso a utilizzare code di dispositivi supplementari un driver per un adattatore bus aggiuntivo e un set di driver di classe.

Uso delle code di dispositivi supplementari con una routine StartIo

Chiamando le routine IoStartPacket e IoStartNextPacket, le routine Dispatch e DpcForIsr (o CustomDpc) del driver sincronizzano le chiamate alla routine StartIo usando la coda di dispositivi creata dal gestore di I/O quando il driver ha creato l'oggetto dispositivo. Per un driver di porta con una routine StartIo, IoStartPacket e IoStartNextPacket inseriscono e rimuovono gli IRP nella coda dei dispositivi per il controller/scheda del dispositivo condiviso del driver di porta. Se il driver di porta configura anche code di dispositivi supplementari per contenere le richieste provenienti dai driver di classe di livello superiore strettamente associati, deve "ordinare" gli IRP in ingresso nelle sue code di dispositivi supplementari, in genere nella routine StartIo.

Il driver di porta deve determinare in quale coda di dispositivi supplementare appartiene ogni IRP prima di tentare di inserire tale IRP nella coda appropriata. Un puntatore all'oggetto dispositivo di destinazione viene passato con l'IRP alla routine di Dispatch del driver. Il driver deve salvare il puntatore per l'uso nel "riordino" degli IRP in ingresso. Si noti che il puntatore all'oggetto dispositivo passato alla routine StartIo è il proprio oggetto dispositivo del driver, che rappresenta il controller/adattatore del dispositivo, quindi non può essere usato a questo scopo.

Dopo aver messo in coda eventuali pacchetti di richiesta I/O (IRP), il driver programma il controller/adattatore condiviso per eseguire la richiesta. Di conseguenza, il driver di porta può elaborare le richieste in ingresso per tutti i dispositivi in base all'ordine di arrivo fino a quando una chiamata a KeInsertDeviceQueue inserisce un IRP nella coda di dispositivi di un particolare driver di classe.

Usando la propria coda di dispositivi per tutti gli IRP da elaborare tramite la routine StartIo, il driver di porta sottostante serializza le operazioni tramite il controller/adattatore del dispositivo condiviso (o bus) a tutti i dispositivi collegati. Talvolta mantenendo gli IRP per ciascun dispositivo supportato in una coda di dispositivi separata, questo port driver inibisce l'elaborazione degli IRP per un dispositivo già occupato, aumentando il throughput di I/O per tutti gli altri dispositivi che eseguono operazioni di I/O attraverso il suo hardware condiviso.

In risposta alla chiamata a IoStartPacket dalla routine Dispatch del driver di porta, il gestore di I/O chiama immediatamente la routine StartIo del driver o inserisce l'IRP nella coda di dispositivi associata all'oggetto dispositivo per il controller/adattatore condiviso del driver di porta.

Il driver di porta deve mantenere le proprie informazioni sullo stato relative a ognuno dei dispositivi eterogenei gestiti tramite il controller/adattatore di dispositivo condiviso.

Quando si progettano driver di classe/porta con code di dispositivi supplementari, tenere presente quanto segue:

  • Un driver non può facilmente ottenere un puntatore a un oggetto di dispositivo creato da qualsiasi driver a livelli superiori, ad eccezione dell'oggetto dispositivo nella parte superiore dello stack di dispositivi.

    Per impostazione predefinita, il gestore di I/O non fornisce una routine di supporto per ottenere un puntatore di questo tipo. Inoltre, l'ordine in cui vengono caricati i driver rende impossibile per i driver più bassi ottenere puntatori per gli oggetti dispositivo dei driver di livello superiore, che non sono ancora stati creati quando un driver di livello inferiore aggiunge il dispositivo.

    Anche se IoGetAttachedDeviceReference restituisce un puntatore all'oggetto dispositivo di livello più alto nello stack di un driver, un driver deve usare questo puntatore solo per designare una destinazione per le richieste di I/O allo stack. Un driver non deve tentare di leggere o scrivere l'oggetto dispositivo.

  • Un driver non può usare un puntatore a un oggetto dispositivo creato da driver posizionati sopra di esso, tranne che per inviare richieste alla cima del proprio stack di dispositivi.

    Non è possibile sincronizzare l'accesso a un singolo oggetto dispositivo (e l'estensione del dispositivo) tra due driver in modo sicuro per più processi. Nessuno dei due driver può fare ipotesi su quale elaborazione di I/O l'altro driver stia attualmente eseguendo.

Anche per i driver di classe/porta strettamente associati, ogni driver di classe deve usare il puntatore agli oggetti dispositivo del driver di porta solo per trasmettere i pacchetti di richiesta I/O (IRP) usando IoCallDriver. Il driver di porta sottostante deve mantenere il proprio stato, probabilmente nell'estensione del dispositivo del driver di porta, sulle richieste che elabora per qualsiasi dispositivo dei driver di classe strettamente integrati.

Gestione delle code di dispositivi aggiuntivi nelle routine dei driver

Qualsiasi driver di porta che accoda gli IRP nelle code di dispositivi supplementari per un set di driver di classe strettamente accoppiato deve gestire la seguente situazione in modo efficiente:

  1. Le routine Dispatch hanno inserito i pacchetti di richiesta I/O per uno specifico dispositivo nella coda di dispositivi creata dal driver per quel dispositivo.

  2. Le Richieste di Pacchetto I/O per altri dispositivi continuano a entrare in coda alla routine StartIo del driver con IoStartPacket e vengono elaborate tramite il controller del dispositivo condiviso.

  3. Il controller del dispositivo non diventa inattivo, ma ogni IRP mantenuto nella coda di dispositivi creata dal driver deve anche essere accodato alla routine StartIo del driver il prima possibile.

Di conseguenza, la routine DpcForIsr del driver di porta deve tentare di trasferire un IRP dalla coda di dispositivi interni del driver per un determinato dispositivo nella coda di dispositivi per l'adattatore/controller condiviso ogni volta che il driver di porta completa un IRP, come indicato di seguito:

  1. La routine DpcForIsr chiama IoStartNextPacket per fare in modo che la routine StartIo inizi l'elaborazione del successivo IRP accodato al controller del dispositivo condiviso.

  2. La routine DpcForIsr chiama KeRemoveDeviceQueue per estrarre il successivo IRP (se presente) che sta trattenendo nella sua coda interna per il dispositivo per cui sta per completare un IRP.

  3. Se KeRemoveDeviceQueue restituisce un puntatore non NULL, la routine DpcForIsr chiama IoStartPacket con l'IRP appena rimosso dalla coda, per accodarlo al controller/adattatore del dispositivo condiviso. In caso contrario, la chiamata a KeRemoveDeviceQueue reimposta semplicemente lo stato dell'oggetto coda del dispositivo su Not-Busy e la routine DpcForIsr omette la chiamata a IoStartPacket.

  4. Quindi, la routine DpcForIsr chiama IoCompleteRequest con l'IRP di input per cui il driver di porta ha appena completato l'elaborazione di I/O, impostando il blocco di stato di I/O con un errore o soddisfando la richiesta di I/O.

Si noti che la sequenza precedente implica che la routine DpcForIsr deve anche determinare il dispositivo per il quale sta completando l'IRP corrente (input) per gestire in modo efficiente l'accodamento interno degli IRP.

Se il driver di porta tenta di attendere fino a quando il controller o l'adattatore condiviso non è inattivo prima di estrarre le IRP trattenute nelle code dei dispositivi supplementari, potrebbe privare un dispositivo per il quale vi era una forte domanda di I/O, mentre gestisce più rapidamente ogni altro dispositivo per cui la domanda di I/O corrente era effettivamente molto più leggera.