Processando IRPs em um driver de Intermediate-Level

Os drivers de nível superior têm um conjunto diferente de rotinas padrão do que os drivers de dispositivo de nível mais baixo, com um subconjunto sobreposto de rotinas padrão comuns a ambos os tipos de drivers.

O conjunto de rotinas para drivers intermediários e de nível mais alto também varia de acordo com os seguintes critérios:

  • A natureza do dispositivo físico subjacente

  • Se um driver de dispositivo subjacente configura objetos de dispositivo para E/S direta ou em buffer

  • O design do driver de nível superior individual

A figura a seguir ilustra o caminho que um IRP pode seguir pelas rotinas padrão de um driver de espelho intermediário em camadas em algum lugar sobre o driver de dispositivo de nível mais baixo descrito na seção anterior.

O driver mostrado na figura a seguir tem as seguintes características:

  • O driver está em camadas em mais de um dispositivo físico e possivelmente em mais de um driver de dispositivo.

  • Às vezes, o driver aloca IRPs adicionais para drivers de nível inferior, dependendo da operação solicitada no IRP de entrada.

  • O driver tem pelo menos um driver de sistema de arquivos em camadas acima dele, e esse driver do sistema de arquivos pode estar em camadas sobre outros drivers intermediários em um nível mais alto do que este.

diagrama ilustrando um caminho de irp por meio de rotinas de driver intermediário.

Como mostra a figura, o gerenciador de E/S cria um IRP e o envia para a rotina de expedição do driver para o código de função principal especificado. Supondo que o código da função seja IRP_MJ_WRITE, a rotina de expedição é DDDispatchWrite. O local da pilha de E/S do driver intermediário é mostrado no meio, com um número indefinido de locais de pilha de E/S para drivers de nível superior e inferior mostrados sombreados.

Alocando IRPs

A finalidade do driver espelho é enviar solicitações de gravação para vários dispositivos físicos e enviar solicitações de leitura alternadamente para os drivers desses dispositivos. Para solicitações de gravação, o driver cria IRPs duplicados para cada dispositivo no qual os dados devem ser gravados, supondo que os parâmetros no IRP de entrada sejam válidos.

A figura anterior mostra uma chamada para IoAllocateIrp , mas os drivers de nível superior podem chamar outras rotinas de suporte para alocar IRPs para drivers de nível inferior. Consulte Criando IRPs para drivers de Lower-Level.

Quando a rotina de expedição chama IoAllocateIrp, ela especifica o número de locais de pilha de E/S necessários para o IRP. O driver deve especificar um local de pilha para cada driver inferior na cadeia, obtendo o valor apropriado dos objetos de dispositivo de cada driver logo abaixo do driver espelho. Opcionalmente, o driver pode adicionar um a esse valor quando chama IoAllocateIrp para obter um local de pilha próprio para cada IRP alocado, como faz o driver na figura anterior.

A rotina de expedição desse driver intermediário chama IoGetCurrentIrpStackLocation (não mostrado) com o IRP original para marcar parâmetros.

Ele chama IoSetNextIrpStackLocation porque alocou seu próprio local de pilha em cada IRP e IoGetCurrentIrpStackLocation recém-criados para criar um contexto para si mesmo que ele usa posteriormente na rotina IoCompletion .

Em seguida, ele chama IoGetNextIrpStackLocation com cada IRP recém-criado para que ele possa configurar os próximos locais de pilha de E/S de drivers de nível inferior nos IRPs alocados. A rotina de expedição do driver espelho copia os códigos e parâmetros da função IRP (ponteiro para o buffer de transferência, comprimento em bytes a serem transferidos para IRP_MJ_WRITE) para os locais de pilha de E/S para os drivers mais baixos. Esses drivers, por sua vez, configurarão os locais de pilha de E/S para os drivers logo abaixo deles, se houver.

Chamando IoSetCompletionRoutine e IoCallDriver

A rotina de expedição na figura anterior chama IoSetCompletionRoutine para cada IRP alocado. Como o driver na figura anterior deve descartar os IRPs alocados, esse driver define sua rotina de IoCompletion a ser chamada quando drivers inferiores concluem seus IRPs, se a operação de E/S foi concluída com êxito, falhou ou foi cancelada.

Como o driver na figura anterior é espelhado em paralelo, ele passa os dois IRPs alocados para os drivers de nível inferior seguintes chamando IoCallDriver duas vezes, uma vez para cada objeto de dispositivo de destino que representa uma partição espelhada.

Processando IRPs na rotina IoCompletion do driver

Quando um dos conjuntos de drivers de nível inferior conclui a operação solicitada, o gerente de E/S chama a rotina de IoCompletion do driver de espelho intermediário. O driver espelho mantém uma contagem em seu próprio local de pilha de E/S para o IRP original, para acompanhar quando os drivers inferiores concluíram todos os IRPs duplicados.

Supondo que o bloco de status de E/S indique que um conjunto de drivers inferiores concluiu o IRP duplicado mostrado na figura anterior, a rotina IoCompletion do driver de espelho diminui sua contagem, mas não pode concluir o IRP original até diminuir a contagem para zero. Se a contagem decrementada ainda não for zero, a rotina IoCompletion chamará IoFreeIrp com o IRP retornado pela primeira vez (DupIRP1 na figura anterior) que o driver alocou e retorna STATUS_MORE_PROCESSING_REQUIRED.

Quando a rotina IoCompletion do driver espelho é chamada novamente com o DupIRP2 mostrado na figura anterior, a rotina IoCompletion diminui a contagem no IRP original e determina que ambos os conjuntos de drivers de nível inferior realizaram as operações solicitadas.

Supondo que o bloco de status de E/S no DupIRP2 também esteja definido com STATUS_SUCCESS, a rotina IoCompletion copia o bloco de status de E/S de DupIRP2 para o IRP original e libera DupIRP2. Ele chama IoCompleteRequest com o IRP original e retorna STATUS_MORE_PROCESSING_REQUIRED. Retornar esse status impede que o gerente de E/S tente qualquer processamento de conclusão adicional no DupIRP2; como o IRP não está associado a um thread, seu processamento de conclusão deve terminar com o driver que o criou.

Se um dos conjuntos de drivers de nível inferior não concluir os IRPs do espelho driver com êxito, a rotina IoCompletion do driver de espelho deverá registrar um erro e tentar a recuperação de dados espelhados apropriada. Para obter mais informações, consulte Erros de log.