Windows驱动
+ -

挂起的IPR在卸载驱动中的处理

2022-05-31 81 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);
    }

}

0 篇笔记 写笔记

自定义IRP的回收利用
前面说到,我们有时需要自已生成新的IPR并派发到下层驱动,所以这里的IRP派发一般的情况下是频繁的,所以上节中会出现频敏的IoAllocateIrp和IoFreeIrp。这样会造成性能上的损失,那有没有更好的处理方式了。答案是肯定的:在说答案之前,我们先介绍一个函数:IoReuseIrp,其REAC......
IRP的完成IoCompleteRequest
每当一个IRP在下层设备层完成时,是需要调用IoCompleteRequest来实现IRP的完成,这个完成其实是实现对执行的IRP的善后操作,这个操作其实是一个宏,真实函数数是IofCompleteRequest。#define IoCompleteRequest IofCompleteReque......
Windows IRP结构字段解释
 比如说你开发了一个USB接口的设备, 那么为了在windows上使用它, 你需要开发一个驱动程序,也许还需要一个应用程序.有些硬件是"免驱"的.但事实上不是不需要驱动,而是他所需要的驱动已经存在在windows中了,不需要你另外安装而已.  你使用应用程序可以控制该硬件的行为,过程是应用程序发出......
自定义IRP的完成与处理
一般情况下,当我们在windows驱动中收到IRP时,驱动根据实际情况分为几种情况:第一种是直接完成填充相关的参数后,直接调用IoCompleteRequest完成。第二种情况是该层做相关的设置,如设置完成例程,然后调用IoCallDriver进行下发。在第二种情况下,一般是当在IRP_MN_STA......
IRP与LIST_ENTRY的关联使用
IRP是Windows内核中的一个很重要的概念,代表着应用或内核对设备的请求。LIST_ENTRY又是Windows内核提供的一个链表。IRP挂入LIST_ENTRYVOID RecyleFrame(PFDO_DEVICE_EXTENSION deviceExtension, FrameHead......
IoCopyCurrentIrpStackLocationToNext和IoSkipCurrentIrpStackLocation操作的IO_STACK_LOCATION有什么区别
在Windows驱动中,传递IPR一般有两种操作:一种是调用IoSkipCurrentIrpStackLocation,表示跳过本层驱动的操作,直接转发至下层: IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(FDODeviceEx......
IRP完成APC执行函数IopCompleteRequest
IRP在完成时调用IoCompleteRequest,其最终会执行一个APC调用,该调用的函数名为IopCompleteRequest。其调用APC调用时的代码如下:KeInitializeApc(&Irp->Tail.Apc, &......
IRP完成与IO_STACK_LOCATION
IRP与IO_STACK_LOCATION的关系IPR完成时,有时为了获取数据,我们经常要这样干 IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, Comple......
IRP无法取消的无限等待问题
有时有这样一种功能,就是当上层设备或应用和我们开发的驱动的时候,而我们这时根本无法完成本次的IRP,所以根据WINDOWS 驱动开发规范,我们这时需要对该IRP进行MarkPending,然后返回Pending。NTSTATUSMyDispatch( PDEVICE_OBJECT Dev......
IRP完成例程IoSetCompletionRoutine的设计和实现原理
在进行IRP下层传递时,通过上一节可知道,一种中使用IoCopyCurrentIrpStackLocationToNext,另一种是IoSkipCurrentIrpStackLocation。其中在使用IoCopyCurrentIrpStackLocationToNext表示的是对当前的IRP当留当......
IRP的取消机制IoCancelIrp
在驱动层,可以使用取消例程实现对IRP的取消。IRP的取消主要涉及几个函数,分别是:IoSetCancelRoutine 设置取消IRP例程IoCancelIrp函数看起来比较简单,但过程却是并不简单。REACTOS提供的IoCancelIrp的源代码如下:BOOLEAN NTAPI IoC......
Windows x64 IRP结构体成员偏移地址
4: kd> dt _IRPntdll!_IRP +0x000 Type : Int2B +0x002 Size : Uint2B +0x004 AllocationProcessorNumber : Uint2B +0......
PDO设备的动态创建与卸载IRP_MN_SURPRISE_REMOVAL和IRP_MN_REMOVE_DEVICE
总线驱动创建的PDO在卸载时,因卸载方式不同而不同如果是直接卸载总线,会只调用IRP_MN_REMOVE_DEVICE而如果是动态创建与卸载,在卸载的时候调用IoInvalidateDeviceRelations会导致IRP_MN_SURPRISE_REMOVAL的调用,然后调用IRP_MN_R......
挂起的IPR在卸载驱动中的处理
有时有一种情况,说是会将上层如CAMERA,HID等USB设备下发的IPR进行入链表LIST_ENTRY,当然在入之前,会将此IRP 挂起pending,并设置取消例程。不过在驱动卸载的时候,或者某种情况下需要手动清除这些IRP时,会出现和CANCEL例程竞争的情况。故这些需要进行一些判断再处理:......
IPR超时手动取消
手动发一个IRP下底层,可以设置超时时间,当超时后,可以手动取消该IRP.NTSTATUS HidpCallDriverSynchronous(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp){ KEVENT event; NTST......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!