驱动的卸载流程IRP_MN_REMOVE_DEVICE与其余IRP竞争
2023-07-28
41
0
USB通用总线驱动程序源代码USBC_Dispatch函数中有这一段代码:
if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE)))
{
IncrementPendingActionCount(parentFdoExt);
}
这段代码应该完全可以写成:
if (!(minorFunction == IRP_MN_REMOVE_DEVICE))
{
IncrementPendingActionCount(parentFdoExt);
}
或
if (minorFunction != IRP_MN_REMOVE_DEVICE)
{
IncrementPendingActionCount(parentFdoExt);
}
最终简化为:当该IRP不是主功能号为:IRP_MJ_PNP,次功能号为IRP_MN_REMOVE_DEVICE,计数加1。
当然,在IRP完成后,应:
/*
* Balance the increment above
*/
if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE)))
{
DecrementPendingActionCount(parentFdoExt);
}
所以,对于该驱动中的所有IRP,除过IRP_MN_REMOVE_DEVICE,都进行了计数平衡。
driverObj->MajorFunction[IRP_MJ_CREATE] =
driverObj->MajorFunction[IRP_MJ_CLOSE] =
driverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
driverObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
driverObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
driverObj->MajorFunction[IRP_MJ_PNP] =
driverObj->MajorFunction[IRP_MJ_POWER] = USBC_Dispatch;
那么,为什么要讲行计数平衡了,它的意义是什么。微软给出的解释为:
/*
* For all IRPs except REMOVE, we increment the PendingActionCount
* across the dispatch routine in order to prevent a race condition with
* the REMOVE_DEVICE IRP (without this increment, if REMOVE_DEVICE
* preempted another IRP, device object and extension might get
* freed while the second thread was still using it).
*/
它的大概含义是由于IPR竞争。这主要是因为内核是多线程的。个人感觉是应用层的线程可能还有携带IPR,但系统卸载线程的卸载已经下发。这时由于多线程或时间片关系,可能会产生竞争(2个IRP时间片交叉)。即可能IRP_MN_REMOVE_DEVICE已经执行,但其它的线程还占用设备驱动。故在卸载时同步一下。
对于该计数pendingActionCount,我们可以搜索驱动整个代码:
发现计数减1时的代码如下:
ASSERT(parentFdoExt->pendingActionCount >= 0);
InterlockedDecrement(&parentFdoExt->pendingActionCount);
if (parentFdoExt->pendingActionCount < 0)
{
/*
* All pending actions have completed and we've gotten
* the REMOVE_DEVICE IRP.
* Set the removeEvent so we'll stop waiting on REMOVE_DEVICE.
*/
ASSERT((parentFdoExt->state == STATE_REMOVING) ||
(parentFdoExt->state == STATE_REMOVED));
KeSetEvent(&parentFdoExt->removeEvent, 0, FALSE);
}
所以关联的是removeEvent事件。而该事件关联于函数IRP_MN_REMOVE_DEVICE的PrepareParentFDOForRemove。
case IRP_MN_REMOVE_DEVICE:
/*
* Cancel downward IO
* Set default status to SUCCESS
* Send the IRP down
* Detach
* Cleanup
*/
PrepareParentFDOForRemove(parentFdoExt);
irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(parentFdoExt->topDevObj, irp);
irpAlreadyCompleted = TRUE;
IoDetachDevice(parentFdoExt->topDevObj);
FreeParentFDOResources(parentFdoExt);
break;
而PrepareParentFDOForRemove中:
- 卸载子设备FreeFunctionPDOResources
- DecrementPendingActionCount 后,再事件(必达)
最后总结一下:
- 在该驱动中,必要的所有IPR请求计数,以标识驱动是否为空闲态。
- 卸载例程中,首先释放的是子设备的资源,其次计数减1,等待事件(一般必达),但也有可能减1后的线程切换,导致别的IPR还在工作,故待一个事件,这个事件在别的IPR完成后会减计数。当收到事件后,再发下给下层设备等其完成后再释放FDO总线资源。