Gerenciando prioridades de hardware

O IRQL no qual uma rotina de driver é executada determina quais rotinas de suporte ao driver no modo kernel ele pode chamar. Por exemplo, algumas rotinas de suporte ao driver exigem que o chamador esteja em execução em IRQL = DISPATCH_LEVEL. Outros não poderão ser chamados com segurança se o chamador estiver em execução em qualquer IRQL maior que PASSIVE_LEVEL.

A seguir está uma lista de IRQLs nas quais as rotinas de driver padrão mais comumente implementadas são chamadas. Os IRQLs são listados da prioridade mais baixa para a mais alta.

PASSIVE_LEVEL
Interrupções mascaradas — nenhuma.

Rotinas de driver chamadas em PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload routines, most dispatch routines, driver-created threads, worker-thread callbacks.

APC_LEVEL
Interrupções Mascaradas – APC_LEVEL interrupções são mascaradas.

Rotinas de driver chamadas em APC_LEVEL — Algumas rotinas de expedição (consulte Rotinas de expedição e IRQLs).

DISPATCH_LEVEL
Interrupções mascaradas — DISPATCH_LEVEL e APC_LEVEL interrupções são mascaradas. Interrupções de dispositivo, relógio e falha de energia podem ocorrer.

Rotinas de driver chamadas em DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (enquanto mantém o bloqueio de rotação de cancelamento), rotinas DpcForIsr, CustomTimerDpc, CustomDpc .

DIRQL
Interrupções Mascaradas – Todas as interrupções em IRQL <= DIRQL do objeto de interrupção do driver. Interrupções de dispositivo com um valor DIRQL mais alto podem ocorrer, juntamente com interrupções de falha de relógio e energia.

Rotinas de driver chamadas em DIRQL — rotinas InterruptService, SynchCritSection .

A única diferença entre APC_LEVEL e PASSIVE_LEVEL é que um processo em execução em APC_LEVEL não pode obter interrupções de APC. Mas ambas as IRQLs implicam um contexto de thread e ambas implicam que o código pode ser paginado.

Os drivers de nível mais baixo processam IRPs durante a execução em um dos três IRQLs:

  • PASSIVE_LEVEL, sem interrupções mascaradas no processador, nas rotinas de expedição do driver

    As rotinas DriverEntry, AddDevice, Reinitialize e Unload também são executadas em PASSIVE_LEVEL, assim como os threads do sistema criados pelo driver.

  • DISPATCH_LEVEL, com interrupções de DISPATCH_LEVEL e APC_LEVEL mascaradas no processador, na rotina StartIo

    As rotinas AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (embora contenha o bloqueio de rotação de cancelamento) e CustomTimerDpc também são executadas em DISPATCH_LEVEL, assim como as rotinas DpcForIsr e CustomDpc.

  • DIRQL (IRQL do dispositivo), com todas as interrupções em menos ou igual ao SynchronizeIrql dos objetos de interrupção do driver mascarados no processador, nas rotinas ISR e SynchCritSection

A maioria dos drivers de nível superior processa IRPs durante a execução em um dos dois IRQLs:

  • PASSIVE_LEVEL, sem interrupções mascaradas no processador, nas rotinas de expedição do driver

    As rotinas DriverEntry, Reinitialize, AddDevice e Unload também são executadas em PASSIVE_LEVEL, assim como quaisquer threads do sistema criados pelo driver ou rotinas de retorno de chamada de thread de trabalho ou drivers do sistema de arquivos.

  • DISPATCH_LEVEL, com DISPATCH_LEVEL e APC_LEVEL interrupções mascaradas no processador, na rotina IoCompletion do driver

    As rotinas IoTimer, Cancel e CustomTimerDpc também são executadas em DISPATCH_LEVEL.

Em algumas circunstâncias, drivers intermediários e de nível mais baixo de dispositivos de armazenamento em massa são chamados em APC_LEVEL IRQL. Em particular, isso pode ocorrer em uma falha de página para a qual um driver do sistema de arquivos envia uma solicitação IRP_MJ_READ para drivers inferiores.

A maioria das rotinas de driver padrão é executada em um IRQL que permite que eles simplesmente chamem as rotinas de suporte apropriadas. Por exemplo, um driver de dispositivo deve chamar AllocateAdapterChannel durante a execução no IRQL DISPATCH_LEVEL. Como a maioria dos drivers de dispositivo chama essas rotinas de uma rotina StartIo , geralmente eles já estão em execução em DISPATCH_LEVEL.

Observe que um driver de dispositivo que não tem rotina StartIo porque ele configura e gerencia suas próprias filas de IRPs não está necessariamente em execução em DISPATCH_LEVEL IRQL quando deve chamar AllocateAdapterChannel. Esse driver deve aninhar sua chamada para AllocateAdapterChannel entre chamadas para KeRaiseIrql e KeLowerIrql para que ele seja executado no IRQL necessário quando chamar AllocateAdapterChannel e restaurar o IRQL original quando a rotina de chamada recuperar o controle.

Ao chamar rotinas de suporte ao driver, esteja ciente do seguinte.

  • Chamar KeRaiseIrql com um valor NewIrql de entrada menor que o IRQL atual causa um erro fatal. Chamar KeLowerIrql , exceto para restaurar o IRQL original (ou seja, após uma chamada para KeRaiseIrql) também causa um erro fatal.

  • Durante a execução em IRQL >= DISPATCH_LEVEL, chamar KeWaitForSingleObject ou KeWaitForMultipleObjects para objetos dispatcher definidos por kernel para aguardar um intervalo diferente de zero causa um erro fatal.

  • As únicas rotinas de driver que podem aguardar com segurança eventos, semáforos, mutexes ou temporizadores a serem definidos para o estado sinalizado são aquelas executadas em um contexto de thread não-bitrário no IRQL PASSIVE_LEVEL, como threads criados pelo driver, as rotinas DriverEntry e Reinitialize ou rotinas de expedição para operações de E/S inerentemente síncronas (como a maioria das solicitações de controle de E/S do dispositivo).

  • Mesmo em execução no IRQL PASSIVE_LEVEL, o código de driver paginável não deve chamar KeSetEvent, KeReleaseSemaphore ou KeReleaseMutex com o parâmetro wait de entrada definido como TRUE. Essa chamada pode causar uma falha fatal na página.

  • Qualquer rotina em execução em maior que o IRQL APC_LEVEL não pode alocar memória do pool de páginas nem acessar a memória no pool de páginas com segurança. Se uma rotina em execução no IRQL maior que APC_LEVEL causar uma falha de página, será um erro fatal.

  • Um driver deve estar em execução no IRQL DISPATCH_LEVEL quando chama KeAcquireSpinLockAtDpcLevel e KeReleaseSpinLockFromDpcLevel.

    Um driver pode estar em execução em IRQL <= DISPATCH_LEVEL quando chama KeAcquireSpinLock , mas deve liberar esse bloqueio de rotação chamando KeReleaseSpinLock. Em outras palavras, é um erro de programação liberar um bloqueio de rotação adquirido com KeAcquireSpinLock chamando KeReleaseSpinLockFromDpcLevel.

    Um driver não deve chamar KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock ou KeReleaseSpinLock durante a execução no IRQL > DISPATCH_LEVEL.

  • Chamar uma rotina de suporte que usa um bloqueio de rotação, como uma rotina Xxx ExInterlocked, eleva o IRQL no processador atual para DISPATCH_LEVEL ou para DIRQL se o chamador ainda não estiver em execução em um IRQL gerado.

  • O código do driver executado no IRQL > PASSIVE_LEVEL deve ser executado o mais rápido possível. Quanto maior o IRQL em que uma rotina é executada, mais importante é que o bom desempenho geral ajuste essa rotina para executar o mais rápido possível. Por exemplo, qualquer driver que chame KeRaiseIrql deve fazer a chamada recíproca para KeLowerIrql assim que possível.

Para obter mais informações sobre como determinar prioridades, consulte o white paper Agendamento, Contexto de Thread e IRQL .