iniwf

风是温柔的,雨是伤心的,云是快乐的,月是多情的,爱是迷失的,恋是醉人的,情是难忘的,天是长久的,地是永恒的

驱动开发之五 --- TDI之九 【译文】

转自http://hi.baidu.com/combojiang/blog/item/8de47aeca3c0c838269791c8.html

排队和悬而未决

你有权选择排队IRP, 在迟些的时间作处理或者在其他的线程处理。既然你拥有IRP, 当它在你的驱动栈层时,这是被允许的。你需要考虑IRP可以被取消的情况。问题是如果IRP被取消,既然结果将会被扔在一边,你真的不想执行任何处理?另一个问题是我们想要解决的是,如果一个有活动的IRP,他们和一个进程或者线程关联,直到活动的IRP完成,这个进程或者线程才能被完全终止。这是非常狡猾的,对于如何实现,这方面的文档非常少,在这里,我们将给你展示如何实现这些。


夺取你的锁


你需要做的第一件事情就是获取自旋锁保护你的IRP链,这将有助于在你的排队逻辑和取消例程之间同步执行。如果你使用某个系统提供的队列机制,有一种系统的取消自旋锁可以被获取,在一些情况下,是必须的。然而既然取消自旋锁是系统范围的,你所想的也是差不多接近的吗?另外的处理器会获取你的自旋锁,或者获取取消自旋锁吗?最有可能的是它会结束获取取消自旋锁,并且这将成为一种性能打击,在单处理器的机器上,很明显不需要关心你使用哪个自旋锁,但是你应该尝试去实现自己的自旋锁。


设置取消例程


你的取消例程需要获取你的自旋锁来同步执行或者从链中删除IRP. 设置取消例程,如果这个IRP被取消,你会知道并且可以从你的IRP列表中删除。记住,你仍然必须完成这个IRP。

没有其他办法。如果一个IRP被取消,他不会刚好从外面在你的脚下消失。如果是那样,当你在处理这个IRP时,如果它被取消,你会处于巨大的麻烦中。取消例程的作用是当IRP在队列中时,如果取消IRP时没有任何争抢,在任何时候你都可以把它从队列中删除。


检查取消标志


然后你必须检查IRP中的取消标志,如果它没有被取消,你需要调用IoMarkIrpPending以及将IRP排队到你的链表,无论哪种办法,你必须确信从你的驱动中返回了STATUS_PENDING。

如果它已经被取消,我们需要知道它是否调用了你的取消例程。通过把取消例程设置为NULL我们可以做到这点。如果返回值是NULL,那么取消例程被调用。如果返回值非NULL,那么取消例程没有被调用。这意味着在我们设置取消例程之前,它已经被取消。


你现在有两个选择,记住,仅有一个位置可以完成这个IRP. 如果取消例程被调用,那么在取消例程没有完成这个IRP时, 如果它不在你的IRP链中,那么你就可以释放它。如果取消例程已经完成了它,那么你一定不能完成它。如果取消例程没有被调用,那么你必须显式的完成它。不管发生什么事,你一定要记住两件事。第一件事是在你的驱动中某个地方,你必须要完成这个IRP. 第二件事情是记得一定不要完成两次。


同样的事情,当你从IRP链中删除一个IRP时,你需要检查这个IRP是否已经被取消。在删除IRP之前,你也要设置取消例程为NULL. 看下面的代码。


Irp
->Tail.Overlay.DriverContext[0= 

         (PVOID)pTdiExampleContext
->pWriteIrpListHead; 

NtStatus 
= HandleIrp_AddIrp(pTdiExampleContext->pWriteIrpListHead, 

         Irp, TdiExample_CancelRoutine, TdiExample_IrpCleanUp, NULL); 


if(NT_SUCCESS(NtStatus)) 



    KeSetEvent(
&pTdiExampleContext->kWriteIrpReady, 

                                  IO_NO_INCREMENT, FALSE); 

    NtStatus 
= STATUS_PENDING; 

}
 

 




/********************************************************************** 



* HandleIrp_AddIrp 



*    This function adds an IRP to the IRP List. 



*********************************************************************
*/
 

NTSTATUS HandleIrp_AddIrp(PIRPLISTHEAD pIrpListHead, 

                          PIRP pIrp, 

                          PDRIVER_CANCEL pDriverCancelRoutine, 

                          PFNCLEANUPIRP pfnCleanUpIrp, 

                          PVOID pContext) 



    NTSTATUS NtStatus 
= STATUS_UNSUCCESSFUL; 

    KIRQL kOldIrql; 

    PDRIVER_CANCEL pCancelRoutine; 

    PIRPLIST pIrpList; 


    pIrpList 
= (PIRPLIST)KMem_AllocateNonPagedMemory(sizeof(IRPLIST), 

                                            pIrpListHead
->ulPoolTag); 


    
if(pIrpList) 

    


        DbgPrint(
"HandleIrp_AddIrp Allocate Memory = 0x%0x \r\n", pIrpList); 


        pIrpList
->pContext      = pContext; 

        pIrpList
->pfnCleanUpIrp = pfnCleanUpIrp; 

        pIrpList
->pIrp          = pIrp; 

        pIrpList
->pfnCancelRoutine = pDriverCancelRoutine; 


        
/* 

         * The first thing we need to to is acquire our spin lock. 

         * 

         * The reason for this is a few things. 

         * 

         *     1. All access to this list is synchronized, the obvious reason 

         *     2. This will synchronize adding this IRP to the 

         *             list with the cancel routine. 

         
*/
 

     

        KeAcquireSpinLock(
&pIrpListHead->kspIrpListLock, &kOldIrql); 

     

        
/* 

         * We will now attempt to set the cancel routine which will be called 

         * when (if) the IRP is ever canceled. This allows us to remove an IRP 

         * from the queue that is no longer valid. 

         * 

         * A potential misconception is that if the IRP is canceled it is no 

         * longer valid. This is not true the IRP does not self-destruct.   

         * The IRP is valid as long as it has not been completed. Once it 

         * has been completed this is when it is no longer valid (while we 

         * own it). So, while we own the IRP we need to complete it at some 

         * point. The reason for setting a cancel routine is to realize 

         * that the IRP has been canceled and complete it immediately and 

         * get rid of it. We don't want to do processing for an IRP that 

         * has been canceled as the result will just be thrown away. 

         * 

         * So, if we remove an IRP from this list for processing and 

         * it's canceled the only problem is that we did processing on it.   

         * We complete it at the end and there's no problem. 

         * 

         * There is a problem however if your code is written in a way 

         * that allows your cancel routine to complete the IRP unconditionally. 

         * This is fine as long as you have some type of synchronization 

         * since you DO NOT WANT TO COMPLETE AN IRP TWICE!!!!!! 

         
*/
 

     

        IoSetCancelRoutine(pIrp, pIrpList
->pfnCancelRoutine); 

     

        
/* 

         * We have set our cancel routine. Now, check if the IRP has 

         *                         already been canceled. 

         * We must set the cancel routine before checking this to ensure 

         * that once we queue the IRP it will definately be called if the 

         * IRP is ever canceled. 

         
*/
 

     

        
if(pIrp->Cancel) 

        


            
/* 

             * If the IRP has been canceled we can then check if our 

             * cancel routine has been called. 

             
*/
 

            pCancelRoutine 
= IoSetCancelRoutine(pIrp, NULL); 

     

            
/* 

             * if pCancelRoutine == 

             *             NULL then our cancel routine has been called. 

             * if pCancelRoutine != 

             *              NULL then our cancel routine has not been called. 

             * 

             * The I/O Manager will set the cancel routine to NULL 

             * before calling the cancel routine. 

             * We have a decision to make here, we need to write the code 

             * in a way that we only complete and clean up the IRP once.   

             * We either allow the cancel routine to do it or we do it here. 

             * Now, we will already have to clean up the IRP here if the 

             * pCancelRoutine != NULL. 

             * 

             * The solution we are going with here is that we will only clean 

             * up IRP's in the cancel routine if the are in the list.   

             * So, we will not add any IRP to the list if it has 

             * already been canceled once we get to this location. 

             * 

             
*/
 

            KeReleaseSpinLock(
&pIrpListHead->kspIrpListLock, kOldIrql); 

     

            
/* 

             * We are going to allow the clean up function to complete the IRP. 

             
*/
 

            pfnCleanUpIrp(pIrp, pContext); 

            DbgPrint(
"HandleIrp_AddIrp Complete Free Memory = 0x%0x \r\n"

                                                          pIrpList); 

            KMem_FreeNonPagedMemory(pIrpList); 

        }
 

        
else 

        


            
/* 

             * The IRP has not been canceled, so we can simply queue it! 

             
*/
 



            pIrpList
->pNextIrp      = NULL; 

             

            IoMarkIrpPending(pIrp); 


            
if(pIrpListHead->pListBack) 

            


               pIrpListHead
->pListBack->pNextIrp = pIrpList; 

               pIrpListHead
->pListBack           = pIrpList; 

            }
 

            
else 

            


               pIrpListHead
->pListFront = pIrpListHead->pListBack = 

                                                                pIrpList; 

            }
 


            KeReleaseSpinLock(
&pIrpListHead->kspIrpListLock, 

                                                            kOldIrql); 


            NtStatus 
= STATUS_SUCCESS; 

        }
 

    }
 

    
else 

    


        
/* 

         * We are going to allow the clean up function to complete the IRP. 

         
*/
 

        pfnCleanUpIrp(pIrp, pContext); 

    }
 

     


    
return NtStatus;       

}
 

posted on 2009-03-23 22:10 iniwf 阅读(229) 评论(0)  编辑 收藏 引用 所属分类: 驱动


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


导航

统计

常用链接

留言簿(2)

随笔分类

随笔档案

收藏夹

IT技术

积分与排名

最新评论

阅读排行榜

评论排行榜