1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxIrpQueue.hpp
8 
9 Abstract:
10 
11     This module implements a common queue structure for the
12     driver frameworks built around the Cancel Safe Queues pattern
13 
14 Author:
15 
16 
17 
18 
19 
20 
21 Environment:
22 
23     Both kernel and user mode
24 
25 Revision History:
26 
27 
28 --*/
29 
30 #ifndef _FXIRPQUEUE_H_
31 #define _FXIRPQUEUE_H_
32 
33 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
34 #include "fxirpkm.hpp"
35 #else
36 #include "fxirpum.hpp"
37 #endif
38 
39 
40 //
41 // IRP DriverContext[] entry used.
42 //
43 // We use the same entry that the CSQ package does
44 // which is OK since we can't use CSQ if we implement the
45 // cancel handler ourselves
46 //
47 #define FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY 3
48 
49 //
50 // FxIrpQueue entry identifier
51 //
52 #define FX_IRP_QUEUE_ENTRY_IDENTIFIER 1
53 
54 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
55 #include "fxirpkm.hpp"
56 #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
57 #include "fxirpum.hpp"
58 #endif
59 
60 
61 //
62 // This is the cancel function callback for the IrpQueue to its caller/parent
63 // This is called holding the lock of the object owning the IrpQueue, and it
64 // is responsible for completing the IRP.
65 //
66 
67 extern "C" {
68 __drv_functionClass(EVT_IRP_QUEUE_CANCEL)
69 __drv_requiresIRQL(DISPATCH_LEVEL)
70 typedef
71 VOID
72 EVT_IRP_QUEUE_CANCEL (
73     __in FxIrpQueue* Queue,
74     __in MdIrp        Irp,
75     __in PMdIoCsqIrpContext pCsqContext,
76     __in KIRQL CallerIrql
77     );
78 
79 typedef EVT_IRP_QUEUE_CANCEL *PFN_IRP_QUEUE_CANCEL;
80 }
81 
82 class FxIrpQueue {
83 
84     friend VOID GetTriageInfo(VOID);
85 
86 private:
87 
88     //
89     // The Queue
90     //
91     LIST_ENTRY        m_Queue;
92 
93     //
94     // The object whose lock controls the queue.
95     // Provided by the client object.
96     //
97     FxNonPagedObject* m_LockObject;
98 
99     //
100     // Callers registered cancel callback
101     //
102     PFN_IRP_QUEUE_CANCEL m_CancelCallback;
103 
104     //
105     // Count of requests in the Queue
106     //
107     LONG              m_RequestCount;
108 
109 public:
110 
111     FxIrpQueue(
112         VOID
113         );
114 
115     ~FxIrpQueue(
116         VOID
117         );
118 
119     VOID
120     Initialize(
121         __in FxNonPagedObject* LockObject,
122         __in PFN_IRP_QUEUE_CANCEL Callback
123         );
124 
125     _Must_inspect_result_
126     NTSTATUS
127     InsertTailRequest(
128         __inout MdIrp Irp,
129         __in_opt PMdIoCsqIrpContext CsqContext,
130         __out_opt ULONG* pRequestCount
131         );
132 
133     _Must_inspect_result_
134     NTSTATUS
135     InsertHeadRequest(
136         __inout MdIrp Irp,
137         __in_opt PMdIoCsqIrpContext CsqContext,
138         __out_opt ULONG* pRequestCount
139         );
140 
141     MdIrp
142     GetNextRequest(
143         __out PMdIoCsqIrpContext* pCsqContext
144         );
145 
146     _Must_inspect_result_
147     NTSTATUS
148     GetNextRequest(
149         __in_opt  PMdIoCsqIrpContext  TagContext,
150         __in_opt  MdFileObject         FileObject,
151         __out FxRequest**          ppOutRequest
152         );
153 
154     _Must_inspect_result_
155     NTSTATUS
156     PeekRequest(
157         __in_opt PMdIoCsqIrpContext  TagContext,
158         __in_opt MdFileObject         FileObject,
159         __out FxRequest**          ppOutRequest
160         );
161 
162     MdIrp
163     RemoveRequest(
164         __in PMdIoCsqIrpContext Context
165         );
166 
167     //
168     // Return whether the queue is empty
169     // (for optimizing the non-pending case in the caller)
170     //
171     inline
172     BOOLEAN
IsQueueEmpty()173     IsQueueEmpty() {
174 
175         if( IsListEmpty(&m_Queue) ) {
176 
177             ASSERT(m_RequestCount == 0);
178 
179             return TRUE;
180         }
181         else {
182             ASSERT(m_RequestCount != 0);
183 
184             return FALSE;
185         }
186     }
187 
188     //
189     // Return count of queued and driver pending requests.
190     //
191     inline
192     LONG
GetRequestCount()193     GetRequestCount() {
194         return m_RequestCount;
195     }
196 
197     BOOLEAN
198     IsIrpInQueue(
199         __in PMdIoCsqIrpContext Context
200         );
201 
202 
203 private:
204 
205     //
206     // Insert an IRP on the cancel list
207     //
208     _Must_inspect_result_
209     NTSTATUS
210     InsertIrpInQueue(
211         __inout   MdIrp                Irp,
212         __in_opt  PMdIoCsqIrpContext Context,
213         __in      BOOLEAN             InsertInHead,
214         __out_opt ULONG*              pRequestCount
215         );
216 
217     // Do not specify argument names
218     FX_DECLARE_VF_FUNCTION_P1(
219     VOID,
220     VerifyRemoveIrpFromQueueByContext,
221         __in PMdIoCsqIrpContext
222         );
223 
224     //
225     // Ask to remove an IRP from the cancel list by Context,
226     // and return NULL if its been cancelled
227     //
228     MdIrp
229     RemoveIrpFromQueueByContext(
230         __in PMdIoCsqIrpContext Context
231         );
232 
233     //
234     // Remove a request from the head of the queue if PeekContext == NULL,
235     // or the next request after PeekContext if != NULL
236     //
237     MdIrp
238     RemoveNextIrpFromQueue(
239         __in_opt  PVOID                PeekContext,
240         __out_opt PMdIoCsqIrpContext* pCsqContext
241         );
242 
243     //
244     // Peek an IRP in the queue
245     //
246     MdIrp
247     PeekNextIrpFromQueue(
248         __in_opt MdIrp  Irp,
249         __in_opt PVOID  PeekContext
250         );
251 
252     //
253     // Unconditionally remove the IRP from the list entry
254     //
255     inline
256     VOID
RemoveIrpFromListEntry(__inout FxIrp * Irp)257     RemoveIrpFromListEntry(
258         __inout FxIrp*   Irp
259         )
260     {
261       PLIST_ENTRY entry = Irp->ListEntry();
262       RemoveEntryList(entry);
263       InitializeListHead(entry);
264       m_RequestCount--;
265       ASSERT(m_RequestCount >= 0);
266     }
267 
268     //
269     // WDM IRP cancel function
270     //
271     static
272     MdCancelRoutineType
273     _WdmCancelRoutineInternal;
274 
275     //
276     // Lock functions accessed from WDM cancel callback
277     //
278     __inline
279     void
LockFromCancel(__out PKIRQL PreviousIrql)280     LockFromCancel(
281         __out PKIRQL PreviousIrql
282         )
283     {
284         m_LockObject->Lock(PreviousIrql);
285     }
286 
287     __inline
288     void
UnlockFromCancel(__in KIRQL PreviousIrql)289     UnlockFromCancel(
290         __in KIRQL PreviousIrql
291         )
292     {
293         m_LockObject->Unlock(PreviousIrql);
294     }
295 };
296 
297 #endif // _FXIRPQUEUE_H
298