Implémentation d’une routine IoCompletion

Lors de l’entrée, une routine IoCompletion reçoit un pointeur de contexte . Lorsqu’une routine de répartition appelle IoSetCompletionRoutine, elle peut fournir un pointeur de contexte . Ce pointeur peut référencer les informations de contexte déterminées par le pilote dont la routine IoCompletion a besoin pour traiter une IRP. Notez que la zone de contexte ne peut pas être paginable, car la routine IoCompletion peut être appelée à l’adresse IRQL = DISPATCH_LEVEL.

Tenez compte des instructions d’implémentation suivantes pour les routines IoCompletion :

  • Une routine IoCompletion peut case activée le bloc de status d’E/S de l’IRP pour déterminer le résultat de l’opération d’E/S.

  • Si l’IRP d’entrée a été alloué par la routine de répartition à l’aide de IoAllocateIrp ou IoBuildAsynchronousFsdRequest, la routine IoCompletion doit appeler IoFreeIrp pour libérer cette IRP, de préférence avant de terminer l’IRP d’origine.

    • La routine IoCompletion doit libérer toutes les ressources par IRP que la routine de répartition allouée pour l’IRP allouée au pilote, de préférence avant de libérer l’IRP correspondante.

      Par exemple, si la routine de répartition alloue un MDL avec IoAllocateMdl et appelle IoBuildPartialMdl pour un IRP de transfert partiel qu’elle alloue, la routine IoCompletion doit libérer la MDL avec IoFreeMdl. S’il alloue des ressources pour conserver l’état de l’IRP d’origine, il doit libérer ces ressources, de préférence avant d’appeler IoCompleteRequest avec l’IRP d’origine et certainement avant qu’il ne retourne le contrôle.

      En général, avant de libérer ou de terminer une IRP, la routine IoCompletion doit libérer toutes les ressources par IRP allouées par la routine Dispatch. Sinon, le pilote doit conserver l’état des ressources à libérer avant que sa routine IoCompletion ne retourne le contrôle de la requête d’origine.

    • Si la routine IoCompletion ne peut pas terminer l’IRP d’origine avec STATUS_SUCCESS, elle doit définir le bloc d’E/S status dans l’IRP d’origine sur la valeur retournée dans l’IRP allouée par le pilote qui a provoqué l’échec de la routine IoCompletion à la requête d’origine.

    • Si la routine IoCompletion termine la requête d’origine avec STATUS_PENDING, elle doit appeler IoMarkIrpPending avec l’IRP d’origine avant d’appeler IoCompleteRequest.

    • Si la routine IoCompletion doit échouer l’IRP d’origine avec une erreur STATUS_XXX, elle peut enregistrer une erreur. Toutefois, il est de la responsabilité du pilote de périphérique sous-jacent de journaliser toutes les erreurs d’E/S d’appareil qui se produisent, de sorte que les routines IoCompletion ne journalisent généralement pas les erreurs.

    • Lorsque la routine IoCompletion a traité et libéré le IRP alloué au pilote, la routine doit retourner le contrôle avec STATUS_MORE_PROCESSING_REQUIRED.

      Le retour STATUS_MORE_PROCESSING_REQUIRED de la routine IoCompletion empêche le traitement d’achèvement du gestionnaire d’E/S pour une IRP allouée et libérée par le pilote. Un deuxième appel à IoCompleteRequest amène le gestionnaire d’E/S à reprendre l’appel des routines d’achèvement de l’IRP, en commençant par la routine d’achèvement immédiatement au-dessus de la routine qui a retourné STATUS_MORE_PROCESSING_REQUIRED.

  • Si la routine IoCompletion réutilise un IRP entrant pour envoyer une ou plusieurs requêtes à des pilotes inférieurs, ou si la routine a échoué, elle doit mettre à jour le contexte que la routine IoCompletion gère pour chaque réutilisation ou nouvelle tentative de l’IRP. Il peut ensuite configurer à nouveau l’emplacement de la pile d’E/S du pilote inférieur suivant, appeler IoSetCompletionRoutine avec son propre point d’entrée et appeler IoCallDriver pour l’IRP.

    • La routine IoCompletion ne doit pas appeler IoMarkIrpPending à chaque réutilisation ou nouvelle tentative de l’IRP.

      La routine de répartition a déjà marqué l’IRP d’origine comme étant en attente. Jusqu’à ce que tous les pilotes de la chaîne terminent l’IRP d’origine avec IoCompleteRequest, il reste en attente.

    • Avant de réessayer une demande, la routine IoCompletion doit réinitialiser le bloc de status d’E/S avec STATUS_SUCCESS pour l’état et zéro pour les informations, éventuellement après avoir enregistré les informations d’erreur retournées.

      Pour chaque nouvelle tentative, la routine IoCompletion décrémente généralement un nombre de nouvelles tentatives configuré par la routine Dispatch. En règle générale, la routine IoCompletion doit appeler IoCompleteRequest pour faire échouer l’IRP en cas d’échec d’un nombre limité de nouvelles tentatives.

    • La routine IoCompletion doit retourner STATUS_MORE_PROCESSING_REQUIRED après avoir appelé IoSetCompletionRoutine et IoCallDriver avec un IRP réutilisé ou retenté.

      Le retour STATUS_MORE_PROCESSING_REQUIRED de la routine IoCompletion empêche le gestionnaire d’E/S de terminer le traitement d’un IRP réutilisé ou retenté.

    • Si la routine IoCompletion ne peut pas terminer l’IRP d’origine avec STATUS_SUCCESS, elle doit laisser le bloc d’E/S status comme retourné par les pilotes inférieurs pour l’opération de réutilisation ou de nouvelle tentative qui entraîne l’échec de la routine IoCompletion.

    • Si la routine IoCompletion termine la requête d’origine avec STATUS_PENDING, elle doit appeler IoMarkIrpPending avec l’IRP d’origine avant d’appeler IoCompleteRequest.

    • Si la routine IoCompletion doit échouer l’IRP d’origine avec une erreur STATUS_XXX, elle peut enregistrer une erreur. Toutefois, il est de la responsabilité du pilote de périphérique sous-jacent de journaliser toutes les erreurs d’E/S d’appareil qui se produisent, de sorte que les routines IoCompletion ne journalisent généralement pas les erreurs.

  • Tout pilote qui définit une routine IoCompletion dans un IRP, puis transmet l’IRP à un pilote inférieur doit case activée l’indicateur IRP-PendingReturned> dans la routine IoCompletion. Si l’indicateur est défini, la routine IoCompletion doit appeler IoMarkIrpPending avec l’IRP. Notez toutefois qu’un pilote qui transmet l’IRP et attend ensuite un événement ne doit pas marquer l’IRP en attente. Au lieu de cela, sa routine IoCompletion doit signaler l’événement et retourner STATUS_MORE_PROCESSING_REQUIRED.

  • La routine IoCompletion doit libérer toutes les ressources que la routine de répartition allouée pour le traitement de l’IRP d’origine, de préférence avant que la routine IoCompletion appelle IoCompleteRequest avec l’IRP d’origine et bien avant que la routine IoCompletion ne retourne le contrôle après avoir terminé l’IRP d’origine.

Si un pilote de niveau supérieur a défini sa routine IoCompletion dans l’IRP d’origine, la routine IoCompletion de ce pilote n’est pas appelée tant que les routines IoCompletion de tous les pilotes de niveau inférieur n’ont pas été appelées.

Fourniture d’un renforcement de priorité dans les appels à IoCompleteRequest

Si un pilote de périphérique de niveau inférieur peut effectuer une IRP dans sa routine de répartition, il appelle IoCompleteRequest avec un PriorityBoost de IO_NO_INCREMENT. Aucune augmentation de la priorité au moment de l’exécution n’est nécessaire, car le pilote peut supposer que le demandeur d’origine n’a pas attendu la fin de son opération d’E/S.

Sinon, le pilote de niveau le plus bas fournit une valeur définie par le système et spécifique au type d’appareil qui augmente la priorité d’exécution du demandeur pour compenser le temps d’attente du demandeur sur sa demande d’E/S d’appareil. Pour connaître les valeurs boost, consultez Wdm.h ou Ntddk.h.

Les pilotes de niveau supérieur appliquent le même PriorityBoost que leurs pilotes de périphériques sous-jacents respectifs lorsqu’ils appellent IoCompleteRequest.

Effet de l’appel d’IoCompleteRequest

Lorsqu’un pilote appelle IoCompleteRequest, le gestionnaire d’E/S remplit l’emplacement de la pile d’E/S de ce pilote avec des zéros avant d’appeler le pilote de niveau supérieur suivant, le cas échéant, qui a configuré une routine IoCompletion à appeler pour l’IRP.

La routine IoCompletion d’un pilote de niveau supérieur peut case activée uniquement le bloc de status d’E/S de l’IRP pour déterminer comment tous les pilotes inférieurs ont géré la requête.

L’appelant d’IoCompleteRequest ne doit pas tenter d’accéder à l’IRP juste terminé. Une telle tentative est une erreur de programmation qui provoque un plantage du système.