Administración de colas de dispositivos
El administrador de E/S normalmente (excepto los FSD) crea un objeto de cola de dispositivos asociado cuando un controlador llama a IoCreateDevice. También proporciona IoStartPacket e IoStartNextPacket, que los controladores pueden llamar para que el administrador de E/S inserte IRP en la cola de dispositivos asociada o llame a sus rutinas StartIo .
Por lo tanto, rara vez es necesario (o especialmente útil) para que un controlador configure sus propios objetos de cola de dispositivos para IRP. Los candidatos probables son controladores, como el controlador de puerto SCSI, que deben coordinar los IRP entrantes de algún número de controladores de clase estrechamente acoplados para dispositivos heterogéneos que se proporcionan a través de un único controlador o adaptador de bus.
Es decir, es más probable que un controlador para un controlador de matriz de discos use un objeto de controlador creado por el controlador que para configurar objetos de cola de dispositivos complementarios, mientras que un controlador para un adaptador de bus de complemento y de un conjunto de controladores de clase es ligeramente más probable que use colas de dispositivos complementarias.
Uso de colas de dispositivos complementarias con una rutina StartIo
Al llamar a IoStartPacket e IoStartNextPacket, las rutinas Dispatch y DpcForIsr (o CustomDpc) de un controlador sincronizan las llamadas a su rutina StartIo mediante la cola de dispositivos que creó el administrador de E/S cuando el controlador creó el objeto de dispositivo. Para un controlador de puerto con una rutina StartIo , IoStartPacket e IoStartNextPacket insertan y quitan IRP en la cola de dispositivos para el controlador de dispositivo o adaptador compartido del controlador de puerto. Si el controlador de puerto también configura colas de dispositivos complementarias para contener solicitudes procedentes de controladores de clase de nivel superior estrechamente acoplados, debe "ordenar" los IRP entrantes en sus colas de dispositivos complementarias, normalmente en su rutina StartIo .
El controlador de puerto debe determinar en qué cola de dispositivos complementaria pertenece cada IRP antes de intentar insertar ese IRP en la cola adecuada. Un puntero al objeto de dispositivo de destino se pasa con el IRP a la rutina Dispatch del controlador. El controlador debe guardar el puntero para usarlo en "ordenar" los IRP entrantes. Tenga en cuenta que el puntero de objeto de dispositivo pasado a la rutina StartIo es el propio objeto de dispositivo del controlador, que representa el controlador o adaptador del dispositivo, por lo que no se puede usar con este fin.
Después de poner en cola los IRP, el controlador programa su controlador compartido o adaptador para llevar a cabo la solicitud. Por lo tanto, el controlador de puerto puede procesar las solicitudes entrantes de todos los dispositivos de primera llegada, en primer lugar, hasta que una llamada a KeInsertDeviceQueue coloca un IRP en una cola de dispositivos de controlador de clase determinada.
Mediante el uso de su propia cola de dispositivos para que todos los IRP se procesen a través de su rutina StartIo , el controlador de puerto subyacente serializa las operaciones a través del controlador o adaptador del dispositivo compartido (o bus) en todos los dispositivos conectados. Al a veces mantener IRP para cada dispositivo compatible en una cola de dispositivos independiente, este controlador de puerto impide el procesamiento de IRP para un dispositivo ya ocupado mientras aumenta el rendimiento de E/S para cada otro dispositivo que realiza E/S a través de su hardware compartido.
En respuesta a la llamada a IoStartPacket desde la rutina Dispatch del controlador de puerto, el administrador de E/S llama a la rutina StartIo del controlador inmediatamente o coloca el IRP en la cola de dispositivos asociada al objeto de dispositivo para el controlador de puerto controlador compartido o adaptador.
El controlador de puerto debe mantener su propia información de estado sobre cada uno de los dispositivos heterogéneos que ofrece a través del controlador o adaptador de dispositivo compartido.
Tenga en cuenta lo siguiente al diseñar controladores de clase/puerto con colas de dispositivos complementarias:
Un controlador no puede obtener fácilmente un puntero a un objeto de dispositivo creado por cualquier controlador por encima de sí mismo, excepto para el objeto de dispositivo en la parte superior de su pila de dispositivos.
Por diseño, el administrador de E/S no proporciona una rutina de soporte técnico para obtener este puntero. Además, el orden en que se cargan los controladores hace imposible que los controladores inferiores obtengan punteros para los objetos de dispositivo de los controladores de nivel superior, que aún no se han creado cuando cualquier controlador de nivel inferior agrega su dispositivo.
Aunque IoGetAttachedDeviceReference devuelve un puntero al objeto de dispositivo de nivel más alto de la pila de un controlador, un controlador solo debe usar este puntero para designar un destino para las solicitudes de E/S a su pila. Un controlador no debe intentar leer o escribir el objeto de dispositivo.
Un controlador no puede usar un puntero a un objeto de dispositivo creado por ningún controlador por encima de sí mismo, excepto para enviar solicitudes a la parte superior de su propia pila de dispositivos.
No hay ninguna manera de sincronizar el acceso a un único objeto de dispositivo (y su extensión de dispositivo) entre dos controladores de una manera segura para varios procesadores. Ninguno de los controladores puede hacer suposiciones sobre qué procesamiento de E/S está haciendo el otro controlador.
Incluso para controladores de puerto o clase estrechamente acoplados, cada controlador de clase debe usar el puntero a los objetos de dispositivo del controlador de puerto solo para pasar irP mediante IoCallDriver. El controlador de puerto subyacente debe mantener su propio estado, probablemente en la extensión del dispositivo del controlador de puerto, sobre las solicitudes que procesa para los dispositivos de clase estrechamente acoplados.
Administración de colas de dispositivos complementarias entre rutinas de controladores
Cualquier controlador de puerto que pone en cola los IRP en colas de dispositivos complementarios para un conjunto estrechamente acoplado de controladores de clase también debe controlar la siguiente situación de forma eficaz:
Sus rutinas de distribución han insertado IRP para un dispositivo determinado en la cola de dispositivos creada por el controlador para ese dispositivo.
Las IRP para otros dispositivos siguen en cola, para poner en cola la rutina StartIo del controlador con IoStartPacket y procesarse a través del controlador de dispositivo compartido.
El controlador de dispositivo no se vuelve inactivo, pero cada IRP mantenido en la cola de dispositivos creado por el controlador también debe ponerse en cola en la rutina StartIo del controlador lo antes posible.
Por lo tanto, la rutina DpcForIsr del controlador de puerto debe intentar transferir un IRP desde la cola de dispositivos internos del controlador para un dispositivo determinado en la cola del dispositivo para el adaptador o controlador compartido siempre que el controlador de puerto complete un IRP, como se indica a continuación:
La rutina DpcForIsr llama a IoStartNextPacket para que la rutina StartIo comience a procesar el siguiente IRP en cola en el controlador de dispositivo compartido.
La rutina DpcForIsr llama a KeRemoveDeviceQueue para poner en cola el siguiente IRP (si existe) que contiene en su cola de dispositivos interna para el dispositivo en cuyo nombre está a punto de completar un IRP.
Si KeRemoveDeviceQueue devuelve un puntero distinto de NULL, la rutina DpcForIsr llama a IoStartPacket con el IRP que acaba de poner en cola al controlador o adaptador de dispositivo compartido. De lo contrario, la llamada a KeRemoveDeviceQueue simplemente restablece el estado del objeto de cola del dispositivo en Not-Busy y la rutina DpcForIsr omite la llamada a IoStartPacket.
A continuación, la rutina DpcForIsr llama a IoCompleteRequest con el IRP de entrada para el que el controlador de puerto acaba de completar el procesamiento de E/S, ya sea estableciendo el bloque de estado de E/S con un error o al satisfacer la solicitud de E/S.
Tenga en cuenta que la secuencia anterior implica que la rutina DpcForIsr también debe determinar el dispositivo para el que está completando el IRP actual (entrada) para administrar la puesta en cola interna de los IRP de forma eficaz.
Si el controlador de puerto intenta esperar hasta que su controlador o adaptador compartido esté inactivo antes de poner en cola los IRP que se mantienen en sus colas de dispositivos adicionales, el controlador podría estrellar un dispositivo para el que había mucha demanda de E/S mientras que se atenía rápidamente todos los demás dispositivos para los que la demanda de E/S actual era realmente mucho más ligera.