Gerenciando filas de dispositivos
O gerenciador de E/S geralmente (exceto FSDs) cria um objeto de fila de dispositivo associado quando um driver chama IoCreateDevice. Ele também fornece IoStartPacket e IoStartNextPacket, que os drivers podem chamar para que o gerenciador de E/S insira IRPs na fila de dispositivos associada ou chame suas rotinas StartIo .
Consequentemente, raramente é necessário (ou particularmente útil) para um driver configurar seus próprios objetos de fila de dispositivos para IRPs. Os candidatos prováveis são drivers, como o driver de porta SCSI, que devem coordenar IRPs de entrada de algum número de drivers de classe estreitamente acoplados para dispositivos heterogêneos que são atendidos por meio de um único controlador ou adaptador de barramento.
Em outras palavras, é mais provável que um driver para um controlador de matriz de disco use um objeto de controlador criado pelo driver do que configurar objetos de fila de dispositivo suplementares, enquanto um driver para um adaptador de barramento de complemento e de um conjunto de drivers de classe é um pouco mais provável de usar filas de dispositivo suplementares.
Usando filas de dispositivo suplementares com uma rotina StartIo
Chamando IoStartPacket e IoStartNextPacket, as rotinas Dispatch e DpcForIsr (ou CustomDpc) de um driver sincronizam chamadas para sua rotina StartIo usando a fila de dispositivos que o gerenciador de E/S criou quando o driver criou o objeto de dispositivo. Para um driver de porta com uma rotina StartIo , IoStartPacket e IoStartNextPacket inserem e removem IRPs na fila do dispositivo para o controlador/adaptador de dispositivo compartilhado do driver de porta. Se o driver de porta também configurar filas de dispositivo suplementares para manter solicitações provenientes de drivers de classe de nível superior estreitamente acoplados, ele deverá "classificar" IRPs de entrada em suas filas de dispositivo suplementares, geralmente em sua rotina StartIo .
O driver de porta deve determinar em qual fila de dispositivo suplementar cada IRP pertence antes de tentar inserir esse IRP na fila apropriada. Um ponteiro para o objeto de dispositivo de destino é passado com o IRP para a rotina de Expedição do driver. O driver deve salvar o ponteiro para uso na "classificação" dos IRPs de entrada. Observe que o ponteiro do objeto do dispositivo passado para a rotina StartIo é o próprio objeto de dispositivo do driver, que representa o controlador/adaptador do dispositivo, portanto, ele não pode ser usado para essa finalidade.
Depois de enfileirar qualquer IRPs, o driver programa seu controlador/adaptador compartilhado para realizar a solicitação. Assim, o driver de porta pode processar solicitações de entrada para todos os dispositivos por ordem de chegada até que uma chamada para KeInsertDeviceQueue coloque um IRP em uma fila de dispositivos de um driver de classe específico.
Usando sua própria fila de dispositivos para que todos os IRPs sejam processados por meio de sua rotina StartIo , o driver de porta subjacente serializa as operações por meio do controlador/adaptador do dispositivo compartilhado (ou barramento) para todos os dispositivos anexados. Às vezes, mantendo IRPs para cada dispositivo com suporte em uma fila de dispositivos separada, esse driver de porta inibe o processamento de IRPs para um dispositivo já ocupado, aumentando a taxa de transferência de E/S para todos os outros dispositivos que fazem E/S por meio de seu hardware compartilhado.
Em resposta à chamada para IoStartPacket da rotina dispatch do driver de porta, o gerente de E/S chama a rotina StartIo desse driver imediatamente ou coloca o IRP na fila do dispositivo associada ao objeto de dispositivo para o controlador/adaptador compartilhado do driver de porta.
O driver de porta deve manter suas próprias informações de estado sobre cada um dos dispositivos heterogêneos que ele atende por meio do controlador/adaptador de dispositivo compartilhado.
Tenha em mente o seguinte ao projetar drivers de classe/porta com filas de dispositivo suplementares:
Um driver não pode facilmente obter um ponteiro para um objeto de dispositivo criado por qualquer driver em camadas acima de si mesmo, exceto para o objeto de dispositivo na parte superior de sua pilha de dispositivos.
Por design, o gerenciador de E/S não fornece uma rotina de suporte para obter esse ponteiro. Além disso, a ordem na qual os drivers são carregados torna impossível para drivers inferiores obter ponteiros para objetos de dispositivo de drivers de nível superior, que ainda não foram criados quando qualquer driver de nível inferior está adicionando seu dispositivo.
Embora IoGetAttachedDeviceReference retorne um ponteiro para o objeto de dispositivo de nível mais alto na pilha de um driver, um driver deve usar esse ponteiro apenas para designar um destino para solicitações de E/S para sua pilha. Um driver não deve tentar ler ou gravar o objeto do dispositivo.
Um driver não pode usar um ponteiro para um objeto de dispositivo criado por qualquer driver em camadas acima de si mesmo, exceto para enviar solicitações para a parte superior de sua própria pilha de dispositivos.
Não há como sincronizar o acesso a um único objeto de dispositivo (e sua extensão de dispositivo) entre dois drivers de maneira segura para vários processadores. Nenhum dos motoristas pode fazer suposições sobre o que o processamento de E/S do outro driver está fazendo no momento.
Mesmo para drivers de classe/porta estreitamente acoplados, cada driver de classe deve usar o ponteiro para os objetos de dispositivo do driver de porta apenas para passar IRPs usando IoCallDriver. O driver de porta subjacente deve manter seu próprio estado, provavelmente na extensão de dispositivo do driver de porta, sobre as solicitações que processa para qualquer(s) dispositivo(s) de driver(s) de classe estreitamente acoplado(s).
Gerenciando filas de dispositivo suplementares entre rotinas de driver
Qualquer driver de porta que enfileira IRPs em filas de dispositivo suplementares para um conjunto estreitamente acoplado de drivers de classe também deve lidar com a seguinte situação com eficiência:
Suas rotinas de expedição inseriram IRPs para um dispositivo específico na fila de dispositivos criada pelo driver para esse dispositivo.
Os IRPs para outros dispositivos continuam a entrar, a serem enfileirados na rotina StartIo do driver com IoStartPacket e a serem processados por meio do controlador de dispositivo compartilhado.
O controlador de dispositivo não fica ocioso, mas cada IRP mantido na fila de dispositivos criada pelo driver também deve ser enfileirado para a rotina StartIo do driver assim que possível.
Consequentemente, a rotina DpcForIsr do driver de porta deve tentar transferir um IRP da fila de dispositivos interna do driver para um dispositivo específico para a fila do dispositivo para o adaptador/controlador compartilhado sempre que o driver de porta concluir um IRP, da seguinte maneira:
A rotina DpcForIsr chama IoStartNextPacket para que a rotina StartIo comece a processar o próximo IRP enfileirado para o controlador de dispositivo compartilhado.
A rotina DpcForIsr chama KeRemoveDeviceQueue para remover a fila do próximo IRP (se houver) que ele está mantendo em sua fila de dispositivos interna para o dispositivo em cujo nome ele está prestes a concluir um IRP.
Se KeRemoveDeviceQueue retornar um ponteiro não NULL, a rotina DpcForIsrchamará IoStartPacket com o IRP dequeued apenas para que ele seja enfileirado para o controlador/adaptador de dispositivo compartilhado. Caso contrário, a chamada para KeRemoveDeviceQueue simplesmente redefine o estado do objeto de fila do dispositivo como Not-Busy e a rotina DpcForIsr omite a chamada para IoStartPacket.
Em seguida, a rotina DpcForIsr chama IoCompleteRequest com o IRP de entrada para o qual o driver de porta acabou de concluir o processamento de E/S, definindo o bloco de E/S status com um erro ou atendendo à solicitação de E/S.
Observe que a sequência anterior implica que a rotina DpcForIsr também deve determinar o dispositivo para o qual ele está concluindo o IRP atual (entrada) para gerenciar o enfileiramento interno de IRPs com eficiência.
Se o driver de porta tentar aguardar até que seu controlador/adaptador compartilhado esteja ocioso antes de remover o remoção de IRPs mantidos em suas filas de dispositivos suplementares, o driver poderá morrer de fome em um dispositivo para o qual havia uma demanda pesada de E/S enquanto ele prontamente ateava a todos os outros dispositivos para os quais a demanda de E/S atual era realmente muito mais leve.