Partager via


Gestion des files d’attente d’appareils

Le gestionnaire d’E/S crée généralement (à l’exception des FSD) un objet de file d’attente d’appareils associé lorsqu’un pilote appelle IoCreateDevice. Il fournit également IoStartPacket et IoStartNextPacket, que les pilotes peuvent appeler pour que le gestionnaire d’E/S insère des irps dans la file d’attente d’appareils associée ou appelle leurs routines StartIo .

Par conséquent, il est rarement nécessaire (ou particulièrement utile) pour un pilote de configurer ses propres objets de file d’attente d’appareils pour les IRP. Les candidats probables sont des pilotes, tels que le pilote de port SCSI, qui doivent coordonner les IRP entrants à partir d’un certain nombre de pilotes de classe étroitement couplés pour les périphériques hétérogènes qui sont pris en charge via un contrôleur unique ou un adaptateur de bus.

En d’autres termes, un pilote pour un contrôleur de groupe de disques est plus susceptible d’utiliser un objet contrôleur créé par un pilote que de configurer des objets de file d’attente de périphériques supplémentaires, tandis qu’un pilote pour un adaptateur de bus de module complémentaire et d’un ensemble de pilotes de classe est légèrement plus susceptible d’utiliser des files d’attente de périphériques supplémentaires.

Utilisation de files d’attente d’appareils supplémentaires avec une routine StartIo

En appelant IoStartPacket et IoStartNextPacket, les routines Dispatch et DpcForIsr (ou CustomDpc) d’un pilote synchronisent les appels à sa routine StartIo à l’aide de la file d’attente d’appareils que le gestionnaire d’E/S a créée lorsque le pilote a créé l’objet d’appareil. Pour un pilote de port avec une routine StartIo , IoStartPacket et IoStartNextPacket insérez et supprimez des irps dans la file d’attente d’appareils pour le contrôleur/adaptateur d’appareil partagé du pilote de port. Si le pilote de port configure également des files d’attente d’appareils supplémentaires pour contenir les demandes provenant de pilotes de classe de niveau supérieur étroitement couplés, il doit « trier » les irps entrants dans ses files d’attente de périphériques supplémentaires, généralement dans sa routine StartIo .

Le pilote de port doit déterminer la file d’attente de périphériques supplémentaire dans laquelle appartient chaque IRP avant d’essayer d’insérer cette IRP dans la file d’attente appropriée. Un pointeur vers l’objet de périphérique cible est passé avec l’IRP à la routine Dispatch du pilote. Le pilote doit enregistrer le pointeur pour l’utiliser dans le « tri » des irps entrants. Notez que le pointeur d’objet d’appareil passé à la routine StartIo est le propre objet de périphérique du pilote, qui représente le contrôleur/adaptateur de périphérique, il ne peut donc pas être utilisé à cet effet.

Après avoir mis en file d’attente tous les IRP, le pilote programme son contrôleur/adaptateur partagé pour exécuter la demande. Ainsi, le pilote de port peut traiter les demandes entrantes pour tous les appareils sur la base du premier arrivé, premier servi jusqu’à ce qu’un appel à KeInsertDeviceQueue place un IRP dans la file d’attente de périphériques d’un pilote de classe spécifique.

En utilisant sa propre file d’attente d’appareils pour que tous les IRP soient traités via sa routine StartIo , le pilote de port sous-jacent sérialise les opérations via le contrôleur/adaptateur de périphérique partagé (ou bus) à tous les appareils attachés. En conservant parfois des irps pour chaque appareil pris en charge dans une file d’attente d’appareils distincte, ce pilote de port empêche le traitement des IRP pour un appareil déjà occupé tout en augmentant le débit d’E/S pour tous les autres appareils qui effectuent des E/S via son matériel partagé.

En réponse à l’appel à IoStartPacket à partir de la routine Dispatch du pilote de port, le gestionnaire d’E/S appelle immédiatement la routine StartIo de ce pilote ou place l’IRP dans la file d’attente d’appareils associée à l’objet de périphérique pour le contrôleur/adaptateur partagé du pilote de port.

Le pilote de port doit conserver ses propres informations d’état sur chacun des appareils hétérogènes qu’il dessert via le contrôleur/adaptateur de périphérique partagé.

Gardez à l’esprit les points suivants lors de la conception de pilotes de classe/de port avec des files d’attente de périphériques supplémentaires :

  • Un pilote ne peut pas facilement obtenir un pointeur vers un objet de périphérique créé par n’importe quel pilote superposé, à l’exception de l’objet de périphérique situé en haut de sa pile de périphériques.

    Par conception, le gestionnaire d’E/S ne fournit pas de routine de support pour obtenir un tel pointeur. En outre, l’ordre dans lequel les pilotes sont chargés empêche les pilotes inférieurs d’obtenir des pointeurs pour les objets de périphérique des pilotes de niveau supérieur, qui n’ont pas encore été créés lorsqu’un pilote de niveau inférieur ajoute son périphérique.

    Bien qu’IoGetAttachedDeviceReference retourne un pointeur vers l’objet d’appareil de niveau supérieur dans la pile d’un pilote, un pilote doit utiliser ce pointeur uniquement pour désigner une cible pour les demandes d’E/S sur sa pile. Un pilote ne doit pas tenter de lire ou d’écrire l’objet de périphérique.

  • Un pilote ne peut pas utiliser un pointeur vers un objet de périphérique créé par un pilote superposé, sauf pour envoyer des requêtes en haut de sa propre pile de périphériques.

    Il n’existe aucun moyen de synchroniser l’accès à un seul objet de périphérique (et à son extension de périphérique) entre deux pilotes de manière multiprocesseur-sécurisé. Aucun pilote ne peut faire d’hypothèses sur ce que fait actuellement le traitement des E/S de l’autre pilote.

Même pour les pilotes de classe/de port étroitement couplés, chaque pilote de classe doit utiliser le pointeur vers le ou les objets de périphérique du pilote de port uniquement pour passer des irPs à l’aide d’IoCallDriver. Le pilote de port sous-jacent doit conserver son propre état, probablement dans l’extension de périphérique du pilote de port, concernant les demandes qu’il traite pour tout périphérique de pilote de classe étroitement couplé.

Gestion des files d’attente d’appareils supplémentaires entre les routines des pilotes

Tout pilote de port qui met en file d’attente les IRP dans des files d’attente d’appareils supplémentaires pour un ensemble étroitement couplé de pilotes de classe doit également gérer efficacement la situation suivante :

  1. Ses routines dispatch ont inséré des IRP pour un appareil particulier dans la file d’attente d’appareils créée par le pilote pour cet appareil.

  2. Les irps d’autres appareils continuent d’entrer, d’être mis en file d’attente à la routine StartIo du pilote avec IoStartPacket et d’être traités via le contrôleur d’appareil partagé.

  3. Le contrôleur de périphérique ne devient pas inactif, mais chaque IRP conservé dans la file d’attente d’appareils créée par le pilote doit également être mis en file d’attente dans la routine StartIo du pilote dès que possible.

Par conséquent, la routine DpcForIsr du pilote de port doit tenter de transférer un IRP de la file d’attente de périphériques interne du pilote pour un périphérique particulier vers la file d’attente de périphérique pour l’adaptateur/contrôleur partagé chaque fois que le pilote de port effectue une IRP, comme suit :

  1. La routine DpcForIsr appelle IoStartNextPacket pour que la routine StartIo commence à traiter la prochaine IRP en file d’attente vers le contrôleur d’appareil partagé.

  2. La routine DpcForIsr appelle KeRemoveDeviceQueue pour mettre en file d’attente l’IRP suivante (le cas échéant) qu’elle conserve dans sa file d’attente d’appareils interne pour l’appareil au nom duquel il est sur le point d’effectuer une IRP.

  3. Si KeRemoveDeviceQueue renvoie un pointeur non NULL, la routine DpcForIsr appelle IoStartPacket avec l’IRP qui vient d’être supprimée pour qu’elle soit mise en file d’attente vers l’adaptateur/contrôleur d’appareil partagé. Sinon, l’appel à KeRemoveDeviceQueue réinitialise simplement l’état de l’objet de file d’attente de l’appareil sur Not-Busy, et la routine DpcForIsr omet l’appel à IoStartPacket.

  4. Ensuite, la routine DpcForIsr appelle IoCompleteRequest avec l’IRP d’entrée pour laquelle le pilote de port vient de terminer le traitement des E/S, soit en définissant le bloc d’E/S status avec une erreur, soit en satisfaisant la demande d’E/S.

Notez que la séquence précédente implique que la routine DpcForIsr doit également déterminer l’appareil pour lequel elle termine le IRP actuel (entrée) afin de gérer efficacement la mise en file d’attente interne des IRP.

Si le pilote de port tente d’attendre que son contrôleur/adaptateur partagé soit inactif avant de mettre fin à la file d’attente des IRP contenues dans ses files d’attente d’appareils supplémentaires, le pilote peut affamer un appareil pour lequel il y avait une forte demande d’E/S alors qu’il a rapidement mis en service tous les autres appareils pour lesquels la demande d’E/S actuelle était en fait beaucoup plus légère.