Gestion des priorités matérielles
L’IRQL auquel une routine de pilote s’exécute détermine le pilote en mode noyau qui prend en charge les routines qu’il peut appeler. Par exemple, certaines routines de prise en charge des pilotes nécessitent que l’appelant s’exécute à IRQL = DISPATCH_LEVEL. D’autres ne peuvent pas être appelées en toute sécurité si l’appelant s’exécute à une valeur IRQL supérieure à PASSIVE_LEVEL.
Voici une liste d’IRQL auxquelles les routines de pilotes standard les plus couramment implémentées sont appelées. Les IRQL sont répertoriés de la priorité la plus basse à la priorité la plus élevée.
PASSIVE_LEVEL
Interruptions masquées : aucune.
Routines de pilotes appelées à PASSIVE_LEVEL : DriverEntry, AddDevice, Reinitialize, Routines de déchargement , routines de répartition la plupart, threads créés par le pilote, rappels de threads de travail.
APC_LEVEL
Interruptions masquées : APC_LEVEL interruptions sont masquées.
Routines de pilotes appelées à APC_LEVEL : certaines routines de distribution (voir Dispatch Routines and IRQLs).
DISPATCH_LEVEL
Interruptions masquées : les interruptions DISPATCH_LEVEL et APC_LEVEL sont masquées. Des interruptions d’appareil, d’horloge et de panne d’alimentation peuvent se produire.
Routines de pilotes appelées à DISPATCH_LEVEL : StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (tout en maintenant le verrou de rotation d’annulation), DpcForIsr, CustomTimerDpc, CustomDpc, routines CustomDpc .
DIRQL
Interruptions masquées : toutes les interruptions à IRQL <= DIRQL de l’objet d’interruption du pilote. Des interruptions d’appareil avec une valeur DIRQL plus élevée peuvent se produire, ainsi que des interruptions d’horloge et de panne de courant.
Routines de pilote appelées au niveau de la DIRQL — Routines InterruptService, SynchCritSection .
La seule différence entre APC_LEVEL et PASSIVE_LEVEL est qu’un processus s’exécutant à APC_LEVEL ne peut pas obtenir d’interruptions APC. Toutefois, les deux IRQL impliquent un contexte de thread et impliquent tous deux que le code peut être paginé.
Les pilotes de niveau le plus bas traitent les IRP lors de l’exécution à l’une des trois IRQL :
PASSIVE_LEVEL, sans interruptions masquées sur le processeur, dans la ou les routines Dispatch du pilote
Les routines DriverEntry, AddDevice, Reinitialize et Unload sont également exécutées à PASSIVE_LEVEL, comme tous les threads système créés par le pilote.
DISPATCH_LEVEL, avec les interruptions DISPATCH_LEVEL et APC_LEVEL masquées sur le processeur, dans la routine StartIo
AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (alors qu’elle contient le verrouillage de rotation d’annulation) et Les routines CustomTimerDpc sont également exécutées au DISPATCH_LEVEL, tout comme les routines DpcForIsr et CustomDpc .
IrQL de l’appareil (DIRQL), avec toutes les interruptions inférieures ou égales à synchronizeIrql du ou des objets d’interruption du pilote masqués sur le processeur, dans les routines ISR et SynchCritSection
La plupart des pilotes de niveau supérieur traitent les IRP lors de l’exécution à l’une des deux IRQL :
PASSIVE_LEVEL, sans interruptions masquées sur le processeur, dans les routines de répartition du pilote
Les routines DriverEntry, Reinitialize, AddDevice et Unload sont également exécutées à PASSIVE_LEVEL, tout comme les threads système créés par le pilote, les routines de rappel de threads de travail ou les pilotes de système de fichiers.
DISPATCH_LEVEL, avec DISPATCH_LEVEL et APC_LEVEL interruptions masquées sur le processeur, dans la ou les routines IoCompletion du pilote
Les routines IoTimer, Cancel et CustomTimerDpc sont également exécutées à DISPATCH_LEVEL.
Dans certaines circonstances, les pilotes intermédiaires et de niveau le plus bas des périphériques de stockage de masse sont appelés au APC_LEVEL IRQL. En particulier, cela peut se produire lors d’une erreur de page pour laquelle un pilote de système de fichiers envoie une demande de IRP_MJ_READ aux pilotes inférieurs.
La plupart des routines de pilotes standard sont exécutées sur un IRQL qui leur permet simplement d’appeler les routines de support appropriées. Par exemple, un pilote de périphérique doit appeler AllocateAdapterChannel lors de l’exécution sur irQL DISPATCH_LEVEL. Étant donné que la plupart des pilotes d’appareil appellent ces routines à partir d’une routine StartIo , ils s’exécutent généralement à DISPATCH_LEVEL déjà.
Notez qu’un pilote de périphérique qui n’a pas de routine StartIo , car il configure et gère ses propres files d’attente d’irps ne s’exécute pas nécessairement à DISPATCH_LEVEL IRQL quand il doit appeler AllocateAdapterChannel. Un tel pilote doit imbriquer son appel à AllocateAdapterChannel entre les appels à KeRaiseIrql et KeLowerIrql afin qu’il s’exécute au niveau de l’IRQL requis lorsqu’il appelle AllocateAdapterChannel et restaure l’IRQL d’origine lorsque la routine appelante reprend le contrôle.
Lorsque vous appelez des routines de support de pilote, tenez compte des points suivants.
L’appel de KeRaiseIrql avec une valeur NewIrql d’entrée inférieure à l’IRQL actuel provoque une erreur irrécupérable. L’appel de KeLowerIrql sauf pour restaurer l’IRQL d’origine (c’est-à-dire, après un appel à KeRaiseIrql) provoque également une erreur irrécupérable.
Lors de l’exécution sur IRQL >= DISPATCH_LEVEL, l’appel de KeWaitForSingleObject ou KeWaitForMultipleObjects pour que les objets de répartiteur définis par le noyau attendent un intervalle différent de zéro entraîne une erreur irrécupérable.
Les seules routines de pilotes qui peuvent attendre en toute sécurité que des événements, des sémaphores, des mutex ou des minuteurs soient définis à l’état signalé sont celles qui s’exécutent dans un contexte de thread non binaire au PASSIVE_LEVEL IRQL, telles que les threads créés par le pilote, les routines DriverEntry et Reinitialize , ou qui distribuent des routines pour des opérations d’E/S intrinsèquement synchrones (telles que la plupart des demandes de contrôle d’E/S de périphérique).
Même lors de l’exécution sur irQL PASSIVE_LEVEL, le code de pilote paginable ne doit pas appeler KeSetEvent, KeReleaseSemaphore ou KeReleaseMutex avec le paramètre Wait d’entrée défini sur TRUE. Un tel appel peut entraîner une erreur de page irrécupérable.
Toute routine qui s’exécute à une valeur supérieure à IRQL APC_LEVEL ne peut ni allouer de la mémoire à partir d’un pool paginé ni accéder à la mémoire dans le pool paginé en toute sécurité. Si une routine s’exécutant à IRQL supérieure à APC_LEVEL provoque une erreur de page, il s’agit d’une erreur irrécupérable.
Un pilote doit s’exécuter sur irQL DISPATCH_LEVEL lorsqu’il appelle KeAcquireSpinLockAtDpcLevel et KeReleaseSpinLockFromDpcLevel.
Un pilote peut s’exécuter sur IRQL <= DISPATCH_LEVEL lorsqu’il appelle KeAcquireSpinLock , mais il doit libérer ce verrou de rotation en appelant KeReleaseSpinLock. En d’autres termes, il s’agit d’une erreur de programmation pour libérer un verrou de rotation acquis avec KeAcquireSpinLock en appelant KeReleaseSpinLockFromDpcLevel.
Un pilote ne doit pas appeler KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock ou KeReleaseSpinLock lors de l’exécution sur irQL > DISPATCH_LEVEL.
L’appel d’une routine de support qui utilise un verrou de rotation, tel qu’une routine ExInterlocked Xxx, déclenche l’IRQL sur le processeur actuel, soit DISPATCH_LEVEL, soit vers DIRQL si l’appelant n’est pas déjà en cours d’exécution à un IRQL élevé.
Le code de pilote qui s’exécute au PASSIVE_LEVEL IRQL > doit s’exécuter aussi rapidement que possible. Plus l’IRQL à laquelle une routine s’exécute est élevée, plus il est important pour de bonnes performances globales d’ajuster cette routine pour qu’elle s’exécute aussi rapidement que possible. Par exemple, tout pilote qui appelle KeRaiseIrql doit effectuer l’appel réciproque à KeLowerIrql dès qu’il le peut.
Pour plus d’informations sur la détermination des priorités, consultez le livre blanc Planification, contexte de thread et IRQL .