1 /*++
2 
3 Copyright (c) Microsoft. All rights reserved.
4 
5 Module Name:
6 
7     EventQueueKm.cpp
8 
9 Abstract:
10 
11     This module implements kernel mode specific functionality of event queue
12 
13     This functionality needed to be separated out since the KM portion takes
14     a reference on driver object because KM MxWorkItem::_Free does not wait for
15     callback to return (i.e. rundown synchronously).
16 
17     MxWorkItem::_Free in UM currently waits for callback to return (i.e. runs
18     down synchronously) hence does not need a reference on driver/devstack
19     object (see comments on top of MxWorkItemUm.h file).
20 
21     In future if UM work-item is made similar to km workitem, UMDF may need to
22     ensure that modules stay loaded, though the mechanism to ensure that would
23     likely be different from a reference on the driver. It would likely be a
24     reference on the devicestack object.
25 
26 Environment:
27 
28     Kernel mode only
29 
30 Revision History:
31 
32 
33 
34 
35 --*/
36 
37 #include "pnppriv.hpp"
38 
39 VOID
40 FxWorkItemEventQueue::QueueWorkItem(
41     VOID
42     )
43 {
44     //
45     // The work item will take a reference on KMDF itself.  This will keep KMDF's
46     // image around (but not necessarily prevent DriverUnload from being called)
47     // so that the code after we set the done event will be in memory and not
48     // unloaded.  We must do this because there is no explicit reference between
49     // the client driver and KMDF, so when the io manager calls the client's
50     // DriverUnload, it has no way of managing KMDF's ref count to stay in memory
51     // when the loader unloads KMDF explicitly in response to DriverUnload.
52     //
53     // We manually take a reference on the client so that we provide the same
54     // functionality that IO workitems do.  The client driver's image will have
55     // a reference on it after it has returned.
56     //
57 
58 
59 
60 
61     Mx::MxReferenceObject(m_PkgPnp->GetDriverGlobals()->Driver->GetDriverObject());
62 
63     //
64     // Prevent FxDriverGlobals from being deleted until the workitem finishes
65     // its work. In case of a bus driver with a PDO in removed
66     // state, if the bus is removed, the removal of PDO may happen in a workitem
67     // and may result in unload routine being called before the PDO package is
68     // deallocated. Since FxPool deallocation code touches FxDriverGlobals
69     // (a pointer of which is located in the header of each FxPool allocated
70     // object), taking this ref prevents the globals from going away until a
71     // corresponding deref at the end of workitem.
72     //
73     m_PkgPnp->GetDriverGlobals()->ADDREF((PVOID)_WorkItemCallback);
74 
75     m_WorkItem.Enqueue(
76         (PMX_WORKITEM_ROUTINE) _WorkItemCallback,
77         (FxEventQueue*) this);
78 }
79 
80 VOID
81 FxWorkItemEventQueue::_WorkItemCallback(
82     __in MdDeviceObject DeviceObject,
83     __in PVOID Context
84     )
85 /*++
86 
87 Routine Description:
88     This is the work item that attempts to run the machine on a thread
89     separate from the one the caller was using.  It implements step 9 above.
90 
91 --*/
92 {
93     FxWorkItemEventQueue* This = (FxWorkItemEventQueue*) Context;
94     PFX_DRIVER_GLOBALS  pFxDriverGlobals;
95 
96     UNREFERENCED_PARAMETER(DeviceObject);
97 
98     MdDriverObject pDriverObject;
99 
100     pFxDriverGlobals = This->m_PkgPnp->GetDriverGlobals();
101 
102     //
103     // Capture the driver object before we call the EventQueueWoker() because
104     // the time it returns, This could be freed.
105 
106 
107 
108     pDriverObject = pFxDriverGlobals->Driver->GetDriverObject();
109 
110     This->EventQueueWorker();
111 
112     //
113     // Release the ref on FxDriverGlobals taken before queuing this workitem.
114     //
115     pFxDriverGlobals->RELEASE((PVOID)_WorkItemCallback);
116 
117     Mx::MxDereferenceObject(pDriverObject);
118 }
119 
120