Отмена запросов в/в
Всякий раз, когда запрос в/в удерживается драйвером в течение продолжительного отрезка времени, драйвер должен быть готов к отмене данного запроса. В случае закрытия потока диспетчер в/в пытается отменить все запросы в/в, отправленные этим потоком и еще не завершенные. Пока все такие запросы в/в не будут завершены устройством, ему не придет запрос IRP_MJ_CLOSE, и, следовательно, не освободится объект-файл, а впоследствии – и само устройство (драйвер никогда не получит запрос DriverUnload).
Для обеспечения отмены запроса в/в в пакете IRP, представляющем такой запрос, должен быть указан адрес диспетчерской точки входа драйвера, собственно отменяющей запрос. Для этого служит функция IoSetCancelRoutine().
PDRIVER_CANCEL IoSetCancelRoutine (IN PIRP Irp, PDRIVER_CANCEL CancelRoutine);
Функция CancelRoutine() имеет такой же прототип, как и все диспетчерские функции.
VOID CancelRoutine (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
Она вызывается на уровне IRQL DISPATCH_LEVEL в случайном контексте потока, однако перед ее вызовом происходит захват специальной системной спин-блокировки. До тех пор, пока системная спин-блокировка не будет освобождена, функция CancelRoutine() работает на уровне IRQL DISPATCH_LEVEL. Уровень IRQL, на который нужно перейти после освобождения блокировки указывается при вызове IoReleaseCancelSpinLock() (см. ниже). Принцип реализации функции следующий:
|
|
· Если указанный пакет IRP не может быть отменен или не принадлежит драйверу, освободить системную спин-блокировку и завершить работу функции.
· В противном случае:
· удалить пакет IRP из любых очередей, в которых он присутствует
· установить функцию отмены IRP в NULL с помощью IoSetCancelRoutine()
· освободить системную спин-блокировку
· установить в IRP поле IoStatus.Information=0, IoStatus.Status=STATUS_CANCELLED
· завершить IRP с помощью IoCompleteRequest().
Системная спин-блокировка отмены IRP освобождается с помощью IoReleaseCancelSpinLock()
VOID IoReleaseCancelSpinLock(IN KIRQL Irql);
где Irql – уровень IRQL, на который система должна вернуться после освобождения спин-блокировки. Это значение хранится в IRP в поле CancelIrql.
Пример функции отмены IRP драйвера, использующего системную очередь, показан в следующем листинге. Необходимо отметить, что для удаления IRP из системной очереди используется функция KeRemoveEntryDeviceQueue() так, как это показано в листинге.
VOID Cancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
// Обрабатывается ли отменяемый запрос в данный момент?
if (Irp == DeviceOb]ect->CurrentIrp)
{
// Да. Освободить системную спин-блокировку и указать диспетчеру в/в
// начать обработку следующего пакета. Отмена IRP – в конце функции
IoReleaseCancelSpinLock(Irp->CancelIrql);
IoStartNextPacket(DeviceOb]ect, TRUE);
}
else
{
// Нет. Отменяемый IRP находится в очереди. Удалить его из очереди
KeRemoveEntryDeviceQueue(&DeviceOb]ect->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
}
// Отменить IRP
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return;
}