挂起的IPR在卸载驱动中的处理
2022-05-31
98
0
有时有一种情况,说是会将上层如CAMERA,HID等USB设备下发的IPR进行入链表LIST_ENTRY,当然在入之前,会将此IRP 挂起pending,并设置取消例程。
不过在驱动卸载的时候,或者某种情况下需要手动清除这些IRP时,会出现和CANCEL例程竞争的情况。故这些需要进行一些判断再处理:
IRP从队列中取出的例程
PIRP DequeueInterruptReadIrp( PHIDCLASS_COLLECTION collection,
PHIDCLASS_FILE_EXTENSION fileExtension)
{
PIRP irp = NULL;
RUNNING_DISPATCH();
while (!irp && !IsListEmpty(&fileExtension->PendingIrpList))
{
PDRIVER_CANCEL oldCancelRoutine;
PLIST_ENTRY listEntry = RemoveHeadList(&fileExtension->PendingIrpList);
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
if (oldCancelRoutine)
{
//因为有oldCancelRoutine,说明尚未CANCEL,故取出,并退出while
ASSERT(oldCancelRoutine == HidpCancelReadIrp);
ASSERT(collection->numPendingReads > 0);
collection->numPendingReads--;
}
else
{
//取消例程已经执行了
/*
* IRP was cancelled and cancel routine was called.
* As soon as we drop the spinlock,
* the cancel routine will dequeue and complete this IRP.
* Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
* Then, don't touch the IRP.
*/
ASSERT(irp->Cancel);
InitializeListHead(&irp->Tail.Overlay.ListEntry);
irp = NULL;
}
}
return irp;
}
IRP入队列的参考例程:
NTSTATUS EnqueueInterruptReadIrp( PHIDCLASS_COLLECTION collection,
PHIDCLASS_FILE_EXTENSION fileExtension,
PIRP Irp)
{
NTSTATUS status;
PDRIVER_CANCEL oldCancelRoutine;
RUNNING_DISPATCH();
/*
* Must set a cancel routine before
* checking the Cancel flag.
*/
oldCancelRoutine = IoSetCancelRoutine(Irp, HidpCancelReadIrp);
ASSERT(!oldCancelRoutine);
/*
* Make sure this Irp wasn't just cancelled.
* Note that there is NO RACE CONDITION here
* because we are holding the fileExtension lock.
*/
if (Irp->Cancel){
/*
* This IRP was cancelled.
*/
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
if (oldCancelRoutine){
//对于此种认为是一个取消的IRP(不正确的请求),收到应直接完成
/*
* The cancel routine was NOT called.
* Return error so that caller completes the IRP.
*/
ASSERT(oldCancelRoutine == HidpCancelReadIrp);
status = STATUS_CANCELLED;
}
else {
/*
* The cancel routine was called.
* As soon as we drop the spinlock it will dequeue
* and complete the IRP.
* Initialize the IRP's listEntry so that the dequeue
* doesn't cause corruption.
* Then don't touch the irp.
*/
//由取消例程完成
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
collection->numPendingReads++; // because cancel routine will decrement
IoMarkIrpPending(Irp);
status = Irp->IoStatus.Status = STATUS_PENDING;
}
}
else {
DBG_RECORD_READ(Irp, IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length, 0, FALSE)
/*
* There are no reports waiting.
* Queue this irp onto the file extension's list of pending irps.
*/
InsertTailList(&fileExtension->PendingIrpList, &Irp->Tail.Overlay.ListEntry);
collection->numPendingReads++;
IoMarkIrpPending(Irp);
status = Irp->IoStatus.Status = STATUS_PENDING;
}
return status;
}
而IRP的取消
/*
********************************************************************************
* HidpCancelReadIrp
********************************************************************************
*
* If a queued read Irp gets cancelled by the user,
* this function removes it from our pending-read list.
*
*/
VOID HidpCancelReadIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
FDO_EXTENSION *fdoExt;
PHIDCLASS_COLLECTION collection;
ULONG collectionIndex;
KIRQL oldIrql;
PIO_STACK_LOCATION irpSp;
PHIDCLASS_FILE_EXTENSION fileExtension;
ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
ASSERT(hidDeviceExtension->isClientPdo);
fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
collectionIndex = hidDeviceExtension->pdoExt.collectionIndex;
collection = &fdoExt->classCollectionArray[collectionIndex];
irpSp = IoGetCurrentIrpStackLocation(Irp);
ASSERT(irpSp->FileObject->Type == IO_TYPE_FILE);
fileExtension = (PHIDCLASS_FILE_EXTENSION)irpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(Irp->CancelIrql);
LockFileExtension(fileExtension, &oldIrql);
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
DBG_RECORD_READ(Irp, 0, 0, TRUE);
ASSERT(collection->numPendingReads > 0);
collection->numPendingReads--;
UnlockFileExtension(fileExtension, oldIrql);
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
而最终的调用函数如下,先将所有的IRP放到一个临时队列,然后逐个完成
VOID CompleteAllPendingReadsForFileExtension(
PHIDCLASS_COLLECTION Collection,
PHIDCLASS_FILE_EXTENSION fileExtension)
{
LIST_ENTRY irpsToComplete;
PIRP irp;
KIRQL oldIrql;
ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
/*
* Move the IRPs to a private queue before completing so they don't
* get requeued on the completion thread, causing us to spin forever.
*/
InitializeListHead(&irpsToComplete);
LockFileExtension(fileExtension, &oldIrql);
while (irp = DequeueInterruptReadIrp(Collection, fileExtension))
{
//
// Irps are created from nonpaged pool,
// so this is ok to call at Dispatch level.
//
InsertTailList(&irpsToComplete, &irp->Tail.Overlay.ListEntry);
}
UnlockFileExtension(fileExtension, oldIrql);
/*
* Complete all the dequeued read IRPs.
*/
while (!IsListEmpty(&irpsToComplete)){
PLIST_ENTRY listEntry = RemoveHeadList(&irpsToComplete);
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
DBGVERBOSE(("Aborting pending read with status=%xh.", irp->IoStatus.Status))
DBG_RECORD_READ(irp, 0, 0, TRUE)
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
}