Сериализация
Отмена запросов ввода/вывода
Всякий раз, когда запрос ввода/вывода удерживается драйвером в течение продолжительного отрезка времени, драйвер должен быть готов к отмене данного запроса. В случае закрытия потока диспетчер ввода/вывода пытается отменить все запросы ввода/вывода, отправленные этим потоком и еще не завершенные. Пока все такие запросы ввода/вывода не будут завершены устройством, ему не придет запрос IRP_MJ_ CLOSE, и, следовательно, не освободится объект-файл, а впоследствии – и само устройство (драйвер никогда не получит запрос DriverUnload).
Для обеспечения отмены запроса ввода/вывода в пакете IRP, представляющем такой запрос, должен быть указан адрес диспетчерской точки входа драйвера, собственно отменяющей запрос. Для этого служит функция IoSetCancelRoutine().
PDRIVER_CANCEL loSetCancelRoutine (IN PIRP Irp, PDRIVER_CANCEL CancelRoutine);
Где функция CancelRoutine() имеет такой же прототип, как и все диспетчерские функции.
VOID <strong>CancelRoutine</strong> (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
Она вызывается на уровне IRQL DISPATCH_LEVEL в случайном контексте потока, однако перед ее вызовом происходит захват специальной системной спин-блокировки. До тех пор, пока системная спин-блокировка не будет освобождена, функция CancelRoutine() работает на уровне IRQL DISPATCH_LEVEL. Уровень IRQL, на который нужно перейти после освобождения блокировки указывается при вызове IoReleaseCancelSpinLock() (см. ниже). Принцип реализации функции следующий:
- Если указанный пакет IRP не может быть отменен или не принадлежит драйверу, то надо освободить системную спин-блокировку и завершить работу функции.
- В противном случае:
- удалить пакет IRP из любых очередей, в которых он присутствует;
- установить функцию отмены IRP в NULL с помощью IoSetCancelRoutine();
- освободить системную спин-блокировку;
- установить в IRP поле loStatus.Information равном 0, а поле loStatus.Status равном STATUS_CANCELLED;
- завершить IRP с помощью loCompleteRequest().
Системная спин-блокировка отмены IRP освобождается с помощью loRelease CancelSpinLock():
VOID IoReleaseCancelSpinLock(IN KIRQL Irgl);
Где Irql – уровень IRQL, на который система должна вернуться после освобождения спин-блокировки. Это значение хранится в IRP в поле Cancellrql.
Отмена IRP и Системная Очередь
Пример функции отмены IRP драйвера, использующего системную очередь, показан в следующем листинге. Необходимо отметить, что для удаления IRP из системной очереди используется функция KeRemoveEntryDeviceQueue() так, как это показано в листинге.
VOID Cancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { // Обрабатывается ли отменяемый запрос в данный момент? if (Irp == DeviceOb]ect › Current!rp) { // Да. Освободить системную спин-блокировку и указать // диспетчеру ввода/вывода начать обработку следующего // пакета. Отмена IRP – в конце функции loReleaseCancelSpinLock(Irp › CancelIrql); loStartNextPacket(DeviceOb]ect, TRUE); } else { // Нет. Отменяемый IRP находится в очереди. // Удалить его из очереди KeRemoveEntryDeviceQueue(SDeviceOb]ect › DeviceQueue, &Irp › Tail.Overlay.DeviceQueueEntry); loReleaseCancelSpinLock(Irp › CancelIrql); } // Отменить IRP Irp › IoStatus.Status = STATUS_CANCELLED; Irp › IoStatus.Information = 0; loCompleteRequest(Irp, IO_NO_INCREMENT); return; }
Отмена IRP и очереди, управляемые драйвером
VOID Cancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIRP irpToCancel; PDEVICE_EXT devExt; KIRQL oldlrql; // обнулить указатель на функцию отмены loSetCancelRoutine(Irp, NULL); // Освободить системную спин-блокировку // как можно быстрее loReleaseCancelSpinLock(Irp › CancelIrql); devExt = DeviceObject › DeviceExtension; // Захватить спин-блокировку доступа к очереди, // удалить IRP и освободить // спин-блокировку KeAcquireSpinLock(&devExt › QueueLock, soldlrql); RemoveEntryList(&Irp › Tail.Overlay.ListEntry); KeReleaseSpinLock(&devExt › QueueLock, oldlrql); // Отменить IRP Irp › IoStatus Status = STATUS_CANCELLED; Irp › IoStatus.Information = 0; loCompleteRequest(Irp, IO_NO_INCREMENT);