Implementando uma rotina IoCompletion
Na entrada, uma rotina IoCompletion recebe um ponteiro contextual . Quando uma rotina de expedição chama IoSetCompletionRoutine, ela pode fornecer um ponteiro contextual . Esse ponteiro pode fazer referência a qualquer informação de contexto determinada pelo driver que a rotina IoCompletion exige para processar um IRP. Observe que a área de contexto não pode ser paginável porque a rotina IoCompletion pode ser chamada em IRQL = DISPATCH_LEVEL.
Considere as seguintes diretrizes de implementação para rotinas de IoCompletion:
Uma rotina IoCompletion pode marcar o bloco de status de E/S do IRP para determinar o resultado da operação de E/S.
Se o IRP de entrada foi alocado pela rotina de expedição usando IoAllocateIrp ou IoBuildAsynchronousFsdRequest, a rotina IoCompletion deve chamar IoFreeIrp para liberar esse IRP, preferencialmente antes de concluir o IRP original.
A rotina IoCompletion deve liberar todos os recursos por IRP que a rotina de expedição alocada para o IRP alocado pelo driver, preferencialmente antes de liberar o IRP correspondente.
Por exemplo, se a rotina de expedição alocar um MDL com IoAllocateMdl e chamar IoBuildPartialMdl para um IRP de transferência parcial alocado, a rotina IoCompletion deverá liberar o MDL com IoFreeMdl. Se alocar recursos para manter o estado sobre o IRP original, ele deverá liberar esses recursos, preferencialmente antes de chamar IoCompleteRequest com o IRP original e, definitivamente, antes de retornar o controle.
Em geral, antes de liberar ou concluir um IRP, a rotina IoCompletion deve liberar todos os recursos por IRP alocados pela rotina Dispatch. Caso contrário, o driver deve manter o estado sobre os recursos a serem liberados antes que sua rotina IoCompletion retorne o controle da conclusão da solicitação original.
Se a rotina IoCompletion não puder concluir o IRP original com STATUS_SUCCESS, ela deverá definir o bloco de status de E/S no IRP original para o valor retornado no IRP alocado pelo driver que fez com que a rotina IoCompletion falhasse na solicitação original.
Se a rotina IoCompletion concluir a solicitação original com STATUS_PENDING, ela deverá chamar IoMarkIrpPending com o IRP original antes de chamar IoCompleteRequest.
Se a rotina IoCompletion precisar falhar no IRP original com um erro STATUS_XXX, ele poderá registrar um erro. No entanto, é responsabilidade do driver de dispositivo subjacente registrar todos os erros de E/S do dispositivo que ocorrem, portanto, as rotinas de IoCompletion geralmente não registram erros.
Quando a rotina IoCompletion tiver processado e liberado o IRP alocado pelo driver, a rotina deverá retornar o controle com STATUS_MORE_PROCESSING_REQUIRED.
Retornar STATUS_MORE_PROCESSING_REQUIRED da floresta de rotina IoCompletion faz com que o processamento de conclusão do gerenciador de E/S seja para um IRP alocado e liberado pelo driver. Uma segunda chamada para IoCompleteRequest faz com que o gerente de E/S retome a chamar as rotinas de conclusão do IRP, começando com a rotina de conclusão imediatamente acima da rotina que retornou STATUS_MORE_PROCESSING_REQUIRED.
Se a rotina IoCompletion reutilizar um IRP de entrada para enviar uma ou mais solicitações para drivers inferiores ou se a rotina repetir operações com falha, ela deverá atualizar qualquer contexto que a rotina IoCompletion mantenha sobre cada reutilização ou repetição do IRP. Em seguida, ele pode configurar o local da pilha de E/S do próximo driver inferior novamente, chamar IoSetCompletionRoutine com seu próprio ponto de entrada e chamar IoCallDriver para o IRP.
A rotina IoCompletion não deve chamar IoMarkIrpPending em cada reutilização ou repetição do IRP.
A rotina de expedição já marcou o IRP original como pendente. Até que todos os drivers na cadeia concluam o IRP original com IoCompleteRequest, ele permanece pendente.
Antes de tentar novamente uma solicitação, a rotina IoCompletion deve redefinir o bloco de status de E/S com STATUS_SUCCESS para Status e zero para Informações, possivelmente depois de salvar as informações de erro retornadas.
Para cada repetição, a rotina IoCompletion geralmente diminui uma contagem de repetições configurada pela rotina Dispatch. Normalmente, a rotina IoCompletion deve chamar IoCompleteRequest para falhar no IRP quando algum número limitado de repetições tiver falhado.
A rotina IoCompletion deve retornar STATUS_MORE_PROCESSING_REQUIRED depois de chamar IoSetCompletionRoutine e IoCallDriver com um IRP que está sendo reutilizado ou repetido.
Retornar STATUS_MORE_PROCESSING_REQUIRED da floresta de rotina IoCompletion faz com que o processamento de conclusão do gerenciador de E/S de um IRP reutilizado ou repetido seja repetido.
Se a rotina IoCompletion não puder concluir o IRP original com STATUS_SUCCESS, ela deverá deixar o bloco de status de E/S como retornado por drivers inferiores para a operação de reutilização ou repetição que faz com que a rotina IoCompletion falhe no IRP.
Se a rotina IoCompletion concluir a solicitação original com STATUS_PENDING, ela deverá chamar IoMarkIrpPending com o IRP original antes de chamar IoCompleteRequest.
Se a rotina IoCompletion precisar falhar no IRP original com um erro STATUS_XXX, ele poderá registrar um erro. No entanto, é responsabilidade do driver de dispositivo subjacente registrar todos os erros de E/S do dispositivo que ocorrem, portanto, as rotinas de IoCompletion geralmente não registram erros.
Qualquer driver que define uma rotina IoCompletion em um IRP e, em seguida, passa o IRP para baixo para um driver inferior deve marcar o sinalizador IRP-PendingReturned> na rotina IoCompletion. Se o sinalizador estiver definido, a rotina IoCompletion deverá chamar IoMarkIrpPending com o IRP. Observe, no entanto, que um driver que passa o IRP e aguarda um evento não deve marcar o IRP pendente. Em vez disso, sua rotina IoCompletion deve sinalizar o evento e retornar STATUS_MORE_PROCESSING_REQUIRED.
A rotina IoCompletion deve liberar todos os recursos alocados pela rotina de expedição para processar o IRP original, preferencialmente antes que a rotina IoCompletion chame IoCompleteRequest com o IRP original e, definitivamente, antes que a rotina IoCompletion retorne o controle de concluir o IRP original.
Se qualquer driver de nível superior tiver definido sua rotina IoCompletion no IRP original, a rotina IoCompletion desse driver não será chamada até que as rotinas IoCompletion de todos os drivers de nível inferior sejam chamadas.
Fornecendo um aumento de prioridade em chamadas para IoCompleteRequest
Se um driver de dispositivo de nível mais baixo puder concluir um IRP em sua rotina de expedição, ele chamará IoCompleteRequest com um PriorityBoost de IO_NO_INCREMENT. Nenhum aumento de prioridade em tempo de execução é necessário porque o driver pode assumir que o solicitante original não esperou a conclusão da operação de E/S.
Caso contrário, o driver de nível mais baixo fornece um valor específico do tipo de dispositivo e definido pelo sistema que aumenta a prioridade de tempo de execução do solicitante para compensar o tempo que o solicitante esperou em sua solicitação de E/S do dispositivo. Consulte Wdm.h ou Ntddk.h para obter os valores de aumento.
Os drivers de nível superior aplicam o mesmo PriorityBoost que seus respectivos drivers de dispositivo subjacentes quando chamam IoCompleteRequest.
Efeito de chamar IoCompleteRequest
Quando um driver chama IoCompleteRequest, o gerenciador de E/S preenche o local da pilha de E/S do driver com zeros antes de chamar o próximo driver de nível superior, se houver, que configurou uma rotina de IoCompletion a ser chamada para o IRP.
A rotina de IoCompletion de um driver de nível superior pode marcar apenas o bloco de status de E/S do IRP para determinar como todos os drivers inferiores lidaram com a solicitação.
O chamador de IoCompleteRequest não deve tentar acessar o IRP concluído. Essa tentativa é um erro de programação que causa uma falha no sistema.