files d’attente IRP Cancel-Safe

Les pilotes qui implémentent leur propre file d’attente IRP doivent utiliser l’infrastructure de file d’attente IRP cancel-safe . Les files d’attente IRP d’annulation sécurisées divisent la gestion des IRP en deux parties :

  1. Le pilote fournit un ensemble de routines de rappel qui implémentent des opérations standard sur la file d’attente IRP du pilote. Les opérations fournies incluent l’insertion et la suppression des irps de la file d’attente, ainsi que le verrouillage et le déverrouillage de la file d’attente. Consultez Implémentation de la file d’attente IRP Cancel-Safe.

  2. Chaque fois que le pilote a besoin d’insérer ou de supprimer un IRP de la file d’attente, il utilise les routines IoCsqXxx fournies par le système. Ces routines gèrent toute la logique de synchronisation et d’annulation IRP pour le pilote.

Les pilotes qui utilisent des files d’attente IRP d’annulation sans échec n’implémentent pas les routines Cancel pour prendre en charge l’annulation des IRP.

L’infrastructure garantit que les pilotes insèrent et suppriment les IRPs de leur file d’attente de manière atomique. Il garantit également que l’annulation des IRP est implémentée correctement. Les pilotes qui n’utilisent pas l’infrastructure doivent verrouiller et déverrouiller manuellement la file d’attente avant d’effectuer des insertions et des suppressions. Ils doivent également éviter les conditions de course qui peuvent en résulter lors de l’implémentation d’une routine Cancel . (Pour obtenir une description des conditions de course qui peuvent survenir, consultez Synchronisation de l’annulation D’IRP.)

L’infrastructure de file d’attente IRP cancel-safe est incluse dans Windows XP et les versions ultérieures de Windows. Les pilotes qui doivent également fonctionner avec Windows 2000 et Windows 98/Me peuvent être liés à la bibliothèque Csq.lib incluse dans le Kit de pilotes Windows (WDK). La bibliothèque Csq.lib fournit une implémentation de cette infrastructure.

Les routines IoCsqXxx sont déclarées dans Windows XP et les versions ultérieures de Wdm.h et Ntddk.h. Les pilotes qui doivent également fonctionner avec Windows 2000 et Windows 98/Me doivent inclure Csq.h pour les déclarations.

Vous pouvez voir une démonstration complète de l’utilisation des files d’attente IRP cancel-safe dans le répertoire \src\general\cancel du WDK. Pour plus d’informations sur ces files d’attente, consultez également le livre blanc Flux de contrôle pour Cancel-Safe IRP Queuing .

Implémentation de la file d’attente IRP Cancel-Safe

Pour implémenter une file d’attente IRP d’annulation sécurisée, les pilotes doivent fournir les routines suivantes :

  • L’une des routines suivantes pour insérer des irps dans la file d’attente : CsqInsertIrp ou CsqInsertIrpEx. CsqInsertIrpEx est une version étendue de CsqInsertIrp ; la file d’attente est implémentée à l’aide de l’un ou de l’autre.

  • Routine CsqRemoveIrp qui supprime l’IRP spécifié de la file d’attente.

  • Routine CsqPeekNextIrp qui retourne un pointeur vers l’IRP suivant l’IRP spécifié dans la file d’attente. C’est là que le système transmet la valeur PeekContext qu’il reçoit d’IoCsqRemoveNextIrp. Le pilote peut interpréter cette valeur de quelque manière que ce soit.

  • Les deux routines suivantes pour permettre au système de verrouiller et de déverrouiller la file d’attente IRP : CsqAcquireLock et CsqReleaseLock.

  • Routine CsqCompleteCanceledIrp qui termine une IRP annulée.

Les pointeurs vers les routines du pilote sont stockés dans la structure IO_CSQ qui décrit la file d’attente. Le pilote alloue le stockage pour la structure IO_CSQ . La structure IO_CSQ est garantie pour rester une taille fixe, de sorte qu’un pilote peut incorporer la structure en toute sécurité à l’intérieur de son extension de périphérique.

Le pilote utilise IoCsqInitialize ou IoCsqInitializeEx pour initialiser la structure. Utilisez IoCsqInitialize si la file d’attente implémente CsqInsertIrp, ou IoCsqInitializeEx si la file d’attente implémente CsqInsertIrpEx.

Les pilotes doivent uniquement fournir les fonctionnalités essentielles dans chaque routine de rappel. Par exemple, seules les routines CsqAcquireLock et CsqReleaseLock implémentent la gestion des verrous. Le système appelle automatiquement ces routines pour verrouiller et déverrouiller la file d’attente si nécessaire.

Vous pouvez implémenter n’importe quel type de mécanisme de mise en file d’attente IRP dans votre pilote, à condition que les routines de répartition appropriées soient fournies. Par exemple, le pilote peut implémenter la file d’attente en tant que liste liée ou en tant que file d’attente prioritaire.

CsqInsertIrpEx fournit une interface plus flexible à la file d’attente que CsqInsertIrp. Le pilote peut utiliser sa valeur de retour pour indiquer le résultat de l’opération ; si elle retourne un code d’erreur, l’insertion a échoué. Une routine CsqInsertIrp ne retourne pas de valeur. Il n’existe donc pas de moyen simple d’indiquer qu’une insertion a échoué. En outre, CsqInsertIrpEx prend un paramètre InsertContext défini par le pilote supplémentaire qui peut être utilisé pour spécifier des informations supplémentaires spécifiques au pilote à utiliser par l’implémentation de la file d’attente.

Les pilotes peuvent utiliser CsqInsertIrpEx pour implémenter une gestion IRP plus sophistiquée. Par exemple, s’il n’y a pas d’IRP en attente, la routine CsqInsertIrpEx peut retourner un code d’erreur et le pilote peut traiter l’IRP immédiatement. De même, si les IRP ne peuvent plus être mis en file d’attente, CsqInsertIrpEx peut retourner un code d’erreur pour indiquer ce fait.

Le pilote est isolé de toute gestion de l’annulation IRP. Le système fournit une routine d’annulation pour les IRPs dans la file d’attente. Cette routine appelle CsqRemoveIrp pour supprimer l’IRP de la file d’attente, et CsqCompleteCanceledIrp pour terminer l’annulation de l’IRP.

Le diagramme suivant illustre le flux de contrôle pour l’annulation IRP.

diagramme illustrant le flux de contrôle pour l’annulation d’irp.

Une implémentation de base de CsqCompleteCanceledIrp est la suivante.

VOID CsqCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) {
  Irp->IoStatus.Status = STATUS_CANCELLED;
  Irp->IoStatus.Information = 0;

  IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

Les pilotes peuvent utiliser n’importe quelle primitive de synchronisation du système d’exploitation pour implémenter leurs routines CsqAcquireLock et CsqReleaseLock . Les primitives de synchronisation disponibles incluent les verrous de rotation et lesobjets mutex.

Voici un exemple de la façon dont un pilote peut implémenter le verrouillage à l’aide de verrous de rotation.

/* 
  The driver has previously initialized the SpinLock variable with
  KeInitializeSpinLock.
 */

VOID CsqAcquireLock(PIO_CSQ IoCsq, PKIRQL PIrql)
{
    KeAcquireSpinLock(SpinLock, PIrql);
}

VOID CsqReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
{
    KeReleaseSpinLock(SpinLock, Irql);
}

Le système transmet un pointeur vers une variable IRQL vers CsqAcquireLock et CsqReleaseLock. Si le pilote utilise un verrou de rotation pour implémenter le verrouillage de la file d’attente, il peut utiliser cette variable pour stocker l’IRQL actuel lorsque la file d’attente est verrouillée.

Les pilotes ne sont pas obligés d’utiliser des verrous de rotation. Par exemple, le pilote peut utiliser un mutex pour verrouiller la file d’attente. Pour obtenir une description des techniques de synchronisation disponibles pour les pilotes, consultez Techniques de synchronisation.

Utilisation de la file d’attente IRP Cancel-Safe

Les pilotes utilisent les routines système suivantes lors de la mise en file d’attente et de la mise en file d’attente des IRP :

  • L’un des éléments suivants pour insérer un IRP dans la file d’attente : IoCsqInsertIrp ou IoCsqInsertIrpEx.

  • IoCsqRemoveNextIrp pour supprimer le IRP suivant dans la file d’attente. Le pilote peut éventuellement spécifier une valeur de clé.

Le diagramme suivant illustre le flux de contrôle pour IoCsqRemoveNextIrp.

diagramme illustrant le flux de contrôle pour iocsqremovenextirp.

  • IoCsqRemoveIrp pour supprimer l’IRP spécifié de la file d’attente.

Le diagramme suivant illustre le flux de contrôle pour IoCsqRemoveIrp.

diagramme illustrant le flux de contrôle pour iocsqremoveirp.

Ces routines, à leur tour, sont distribuées aux routines fournies par le conducteur.

La routine IoCsqInsertIrpEx permet d’accéder aux fonctionnalités étendues d’une routine CsqInsertIrpEx . Il retourne la valeur status qui a été retournée par CsqInsertIrpEx. L’appelant peut utiliser cette valeur pour déterminer si l’IRP a été correctement mis en file d’attente ou non. IoCsqInsertIrpEx permet également à l’appelant de spécifier une valeur pour le paramètre InsertContext de CsqInsertIrpEx.

Notez que ioCsqInsertIrp et IoCsqInsertIrpEx peuvent être appelés sur n’importe quelle file d’attente cancel-safe, que la file d’attente ait une routine CsqInsertIrp ou une routine CsqInsertIrpEx . IoCsqInsertIrp se comporte de la même façon dans les deux cas. Si IoCsqInsertIrpEx reçoit une file d’attente avec une routine CsqInsertIrp , il se comporte de manière identique à IoCsqInsertIrp.

Le diagramme suivant illustre le flux de contrôle pour IoCsqInsertIrp.

diagramme illustrant le flux de contrôle pour iocsqinsertirp.

Le diagramme suivant illustre le flux de contrôle pour IoCsqInsertIrpEx.

diagramme illustrant le flux de contrôle pour iocsqinsertirpex.

Il existe plusieurs façons naturelles d’utiliser les routines IoCsqXxx pour mettre en file d’attente et mettre en file d’attente les IRP. Par exemple, un pilote peut simplement mettre en file d’attente les IRP à traiter dans l’ordre dans lequel ils sont reçus. Le pilote peut mettre en file d’attente un IRP comme suit :

    status = IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);

Si le pilote n’est pas tenu de faire la distinction entre des IRP particuliers, il peut alors simplement les mettre en file d’attente dans l’ordre dans lequel ils ont été mis en file d’attente, comme suit :

    IoCsqRemoveNextIrp(IoCsq, NULL);

Le pilote peut également mettre en file d’attente et mettre en file d’attente certains IRP. Les routines utilisent la structure opaque IO_CSQ_IRP_CONTEXT pour identifier des IRP particuliers dans la file d’attente. Le pilote met en file d’attente l’IRP comme suit :

    IO_CSQ_IRP_CONTEXT ParticularIrpInQueue;
    IoCsqInsertIrp(IoCsq, Irp, &ParticularIrpInQueue);

Le pilote peut ensuite mettre en file d’attente le même IRP à l’aide de la valeur IO_CSQ_IRP_CONTEXT .

    IoCsqRemoveIrp(IoCsq, Irp, &ParticularIrpInQueue);

Le pilote peut également être tenu de supprimer les IRP de la file d’attente en fonction d’un critère particulier. Par exemple, le pilote peut associer une priorité à chaque IRP, de sorte que les IRP de priorité supérieure sont d’abord supprimés. Le pilote peut passer une valeur PeekContext à IoCsqRemoveNextIrp, que le système transmet au pilote lorsqu’il demande l’IRP suivant dans la file d’attente.