Exemplos de sincronização
Os exemplos a seguir ilustram o que um minidriver precisa fazer em relação à sincronização e incluem exemplos de quando a sincronização não deve ser usada:
Exemplo um: Minidriver com um ISR funcional
Se a sincronização de classe de fluxo estiver ativada, todos os pontos de entrada do minidriver serão chamados no IRQL gerado, usando KeSynchronizeExecution, o que significa que o nível de IRQ do adaptador e todos os níveis de IRQ inferiores são mascarados quando o minidriver está executando seu código. Portanto, é imperativo que o minidriver faça apenas uma pequena quantidade de trabalho nesse modo.
O minidriver não deve executar código que normalmente leva mais de 10 a 20 microssegundos em IRQL gerado. Se você usar o build de depuração do stream.sys, a classe de fluxo registrará a quantidade de tempo gasto no IRQL gerado e declarará se o driver está gastando muito tempo lá. Se o minidriver simplesmente precisar programar registros de DMA de hardware para uma solicitação ou apenas precisar ler portas em seu ISR, geralmente é aceitável fazer todo o processamento no IRQL gerado.
Se o minidriver precisar fazer um processamento que leva mais do que alguns microssegundos, como um minidriver que transfere dados por meio do PIO, o minidriver deverá usar StreamClassCallAtNewPriority para agendar um retorno de chamada DISPATCH_LEVEL. No retorno de chamada, o minidriver pode levar até cerca de 1/2 a 1 milissegundo para fazer seu processamento. Uma coisa a lembrar quando estiver nesse modo é que o retorno de chamada DISPATCH_LEVEL não está sincronizado com o ISR.
Essa falta de sincronização não será um problema se o hardware permanecer estável quando o minidriver acessar recursos (por exemplo, portas ou uma fila) durante o retorno de chamada, bem como no ISR. Mas se a instabilidade puder ser um problema, o minidriver deverá usar StreamClassCallAtNewPriority para agendar um retorno de chamada de prioridade ALTA em que o retorno de chamada DISPATCH_LEVEL toca recursos compartilhados com os recursos usados pelo ISR.
Observe que um retorno de chamada de prioridade ALTA é equivalente a chamar KeSynchronizeExecution. KeSynchronizeExecution requer que o minidriver faça referência a vários parâmetros que StreamClassCallAtNewPriority não faz, mas, em geral, os dois resultam no mesmo comportamento.
Se o minidriver precisar executar apenas ocasionalmente um código que leva mais de 1/2 a 1 milissegundo ou ocasionalmente precisar chamar serviços em PASSIVE_LEVEL (como no momento da inicialização), definir StreamClassCallAtNewPriority como prioridade LOW poderá ser usado para adquirir um thread de trabalho PASSIVE_LEVEL. Observe que um retorno de chamada de baixa prioridade não é sincronizado com nada e que o minidriver pode receber novas solicitações (supondo que o parâmetro NotificationType ReadyForNextRequest esteja pendente) ou uma chamada ISR ao executar um retorno de chamada de baixa prioridade.
Exemplo dois: Minidriver sem um ISR
Se a sincronização de classe de fluxo estiver ativada, os pontos de entrada do minidriver serão todos chamados em DISPATCH_LEVEL. O minidriver pode fazer processamento de até cerca de 1/2 a 1 milissegundo de duração sem precisar ajustar a prioridade. Se o minidriver precisar executar apenas ocasionalmente um código que leva mais de 1/2 milissegundo ou ocasionalmente precisar chamar serviços em PASSIVE_LEVEL (como no momento da inicialização), StreamClassCallAtNewPriority com prioridade LOW poderá ser usado para adquirir um thread de trabalho PASSIVE_LEVEL. Observe que um retorno de chamada de baixa prioridade não é sincronizado com nada e o minidriver pode receber novas solicitações (supondo que o parâmetro NotificationType ReadyForNextRequest esteja pendente) ao executar um retorno de chamada de baixa prioridade.
Quando a sincronização de classe de fluxo não deve ser usada
Veja a seguir exemplos de situações em que a sincronização de classe de fluxo não deve ser usada. Estão incluídos:
Quando os drivers com frequência (mais de cerca de 20% das solicitações que o minidriver recebe) precisam fazer um processamento que leva mais de 1 milissegundo ou precisam chamar com frequência PASSIVE_LEVEL serviços, como os serviços do Microsoft DirectDraw. Ao usar a versão de depuração do stream.sys, a classe de fluxo declarará esses dois casos e será interrompida se eles forem detectados com a sincronização ativada.
Quando o minidriver é um filtro sem hardware associado. Esse minidriver deve estar em execução no PASSIVE_LEVEL pois não há hardware subjacente para sincronizar e o minidriver normalmente faz muito processamento. É mais fácil fazer sua própria sincronização nesse caso do que desperdiçar sobrecarga usando a sincronização de classe de fluxo. Se necessário, use mutexes para proteger suas filas.
Bugs no código de sincronização geralmente podem ser difíceis de encontrar e, em determinados ambientes (como sistemas operacionais baseados em NT executados em sistemas multiprocessadores), os bugs podem aparecer somente após muitas horas de estresse. Com base na experiência com fornecedores, esses não são os tipos de coisas que a maioria dos fornecedores tem a capacidade ou o desejo de depurar. Somente gravadores de driver familiarizados com a escrita de drivers de dispositivo WDM totalmente assíncronos devem tentar fazer sua própria sincronização.
Quando o minidriver é um driver do tipo barramento em barramento (por exemplo, um driver periférico USB ou 1394) que realmente não se preocupa com a sincronização do hardware real, mas apenas chama solicitações para a próxima camada em PASSIVE_LEVEL e recebe retornos de chamada normalmente em DISPATCH_LEVEL.