xref: /reactos/sdk/include/ddk/csq.h (revision c2c66aff)
1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * Cancel-Safe Queue Library
3*c2c66affSColin Finck  * Created in 2004 by Vizzini (vizzini@plasmic.com)
4*c2c66affSColin Finck  *
5*c2c66affSColin Finck  * THIS SOFTWARE IS NOT COPYRIGHTED
6*c2c66affSColin Finck  *
7*c2c66affSColin Finck  * This source code is offered for use in the public domain. You may
8*c2c66affSColin Finck  * use, modify or distribute it freely.
9*c2c66affSColin Finck  *
10*c2c66affSColin Finck  * This code is distributed in the hope that it will be useful but
11*c2c66affSColin Finck  * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
12*c2c66affSColin Finck  * DISCLAIMED. This includes but is not limited to warranties of
13*c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14*c2c66affSColin Finck  *
15*c2c66affSColin Finck  *
16*c2c66affSColin Finck  * This header defines the interface to the ReactOS Cancel-Safe Queue library.
17*c2c66affSColin Finck  * This interface is based on and is similar to the Microsoft Cancel-Safe
18*c2c66affSColin Finck  * Queue interface.
19*c2c66affSColin Finck  *
20*c2c66affSColin Finck  * BACKGROUND
21*c2c66affSColin Finck  *
22*c2c66affSColin Finck  * IRP queuing is a royal pain in the butt, due to the fact that there are
23*c2c66affSColin Finck  * tons of built-in race conditions.  IRP handling is difficult in general,
24*c2c66affSColin Finck  * but the cancel logic has been particularly complicated due to some subtle
25*c2c66affSColin Finck  * races, coupled with the fact that the system interfaces have changed over
26*c2c66affSColin Finck  * time.
27*c2c66affSColin Finck  *
28*c2c66affSColin Finck  * Walter Oney (2nd. Ed. of Programming the Windows Driver Model) states a
29*c2c66affSColin Finck  * common opinion among driver developers when he says that it is foolish
30*c2c66affSColin Finck  * to try to roll your own cancel logic.  There are only a very few people
31*c2c66affSColin Finck  * who have gotten it right in the past.  He suggests, instead, that you
32*c2c66affSColin Finck  * either use his own well-tested code, or use the code in the Microsoft
33*c2c66affSColin Finck  * Cancel-Safe Queue Library.
34*c2c66affSColin Finck  *
35*c2c66affSColin Finck  * We cannot do either, of course, due to copyright issues.  I have therefore
36*c2c66affSColin Finck  * created this clone of the Microsoft library in order to concentrate all
37*c2c66affSColin Finck  * of the IRP-queuing bugs in one place.  I'm quite sure there are problems
38*c2c66affSColin Finck  * here, so if you are a driver writer, I'd be glad to hear your feedback.
39*c2c66affSColin Finck  *
40*c2c66affSColin Finck  * Apart from that, please try to use these routines, rather than building
41*c2c66affSColin Finck  * your own.  If you think you have found a bug, please bring it up with me
42*c2c66affSColin Finck  * or on-list, as this is complicated and non-obvious stuff.  Don't just
43*c2c66affSColin Finck  * change this and hope for the best!
44*c2c66affSColin Finck  *
45*c2c66affSColin Finck  * USAGE
46*c2c66affSColin Finck  *
47*c2c66affSColin Finck  * This library follows exactly the same interface as the Microsoft Cancel-Safe
48*c2c66affSColin Finck  * Queue routines (IoCsqXxx()).  As such, the authoritative reference is the
49*c2c66affSColin Finck  * current DDK.  There is also a DDK sample called "cancel" that has an
50*c2c66affSColin Finck  * example of how to use this code.  I have also provided a sample driver
51*c2c66affSColin Finck  * that makes use of this queue. Finally, please do read the header and the
52*c2c66affSColin Finck  * source if you're curious about the inner workings of these routines.
53*c2c66affSColin Finck  */
54*c2c66affSColin Finck 
55*c2c66affSColin Finck #pragma once
56*c2c66affSColin Finck 
57*c2c66affSColin Finck #define _CSQ_H_
58*c2c66affSColin Finck 
59*c2c66affSColin Finck #ifdef __cplusplus
60*c2c66affSColin Finck extern "C" {
61*c2c66affSColin Finck #endif
62*c2c66affSColin Finck 
63*c2c66affSColin Finck /*
64*c2c66affSColin Finck  * Prevent including the CSQ definitions twice. They're present in NTDDK
65*c2c66affSColin Finck  * now too, except the *_EX versions.
66*c2c66affSColin Finck  */
67*c2c66affSColin Finck #ifndef IO_TYPE_CSQ_IRP_CONTEXT
68*c2c66affSColin Finck 
69*c2c66affSColin Finck typedef struct _IO_CSQ IO_CSQ, *PIO_CSQ;
70*c2c66affSColin Finck 
71*c2c66affSColin Finck /*
72*c2c66affSColin Finck  * STRUCTURES
73*c2c66affSColin Finck  *
74*c2c66affSColin Finck  * NOTE:  Please do not use these directly.  You will make incompatible code
75*c2c66affSColin Finck  * if you do.  Always only use the documented IoCsqXxx() interfaces and you
76*c2c66affSColin Finck  * will amass much Good Karma.
77*c2c66affSColin Finck  */
78*c2c66affSColin Finck #define IO_TYPE_CSQ_IRP_CONTEXT 1
79*c2c66affSColin Finck #define IO_TYPE_CSQ 2
80*c2c66affSColin Finck 
81*c2c66affSColin Finck /*
82*c2c66affSColin Finck  * IO_CSQ_IRP_CONTEXT - Context used to track an IRP in the CSQ
83*c2c66affSColin Finck  */
84*c2c66affSColin Finck typedef struct _IO_CSQ_IRP_CONTEXT {
85*c2c66affSColin Finck   ULONG Type;
86*c2c66affSColin Finck   PIRP Irp;
87*c2c66affSColin Finck   PIO_CSQ Csq;
88*c2c66affSColin Finck } IO_CSQ_IRP_CONTEXT, *PIO_CSQ_IRP_CONTEXT;
89*c2c66affSColin Finck 
90*c2c66affSColin Finck /*
91*c2c66affSColin Finck  * CSQ Callbacks
92*c2c66affSColin Finck  *
93*c2c66affSColin Finck  * The cancel-safe queue is implemented as a set of IoCsqXxx() OS routines
94*c2c66affSColin Finck  * copuled with a set of driver callbacks to handle the basic operations of
95*c2c66affSColin Finck  * the queue.  You need to supply one of each of these functions in your own
96*c2c66affSColin Finck  * driver.  These routines are also documented in the DDK under CsqXxx().
97*c2c66affSColin Finck  * That is the authoritative documentation.
98*c2c66affSColin Finck  */
99*c2c66affSColin Finck 
100*c2c66affSColin Finck /*
101*c2c66affSColin Finck  * Function to insert an IRP in the queue.  No need to worry about locking;
102*c2c66affSColin Finck  * just tack it onto your list or something.
103*c2c66affSColin Finck  *
104*c2c66affSColin Finck  * Sample implementation:
105*c2c66affSColin Finck  *
106*c2c66affSColin Finck   VOID NTAPI CsqInsertIrp(PIO_CSQ Csq, PIRP Irp)
107*c2c66affSColin Finck   {
108*c2c66affSColin Finck     KdPrint(("Inserting IRP 0x%x into CSQ\n", Irp));
109*c2c66affSColin Finck     InsertTailList(&IrpQueue, &Irp->Tail.Overlay.ListEntry);
110*c2c66affSColin Finck   }
111*c2c66affSColin Finck  *
112*c2c66affSColin Finck  */
113*c2c66affSColin Finck typedef VOID
114*c2c66affSColin Finck (NTAPI IO_CSQ_INSERT_IRP)(
115*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
116*c2c66affSColin Finck   _In_ PIRP Irp);
117*c2c66affSColin Finck typedef IO_CSQ_INSERT_IRP *PIO_CSQ_INSERT_IRP;
118*c2c66affSColin Finck 
119*c2c66affSColin Finck /*
120*c2c66affSColin Finck  * Function to remove an IRP from the queue.
121*c2c66affSColin Finck  *
122*c2c66affSColin Finck  * Sample:
123*c2c66affSColin Finck  *
124*c2c66affSColin Finck   VOID NTAPI CsqRemoveIrp(PIO_CSQ Csq, PIRP Irp)
125*c2c66affSColin Finck   {
126*c2c66affSColin Finck     KdPrint(("Removing IRP 0x%x from CSQ\n", Irp));
127*c2c66affSColin Finck     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
128*c2c66affSColin Finck   }
129*c2c66affSColin Finck  *
130*c2c66affSColin Finck  */
131*c2c66affSColin Finck typedef VOID
132*c2c66affSColin Finck (NTAPI IO_CSQ_REMOVE_IRP)(
133*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
134*c2c66affSColin Finck   _In_ PIRP Irp);
135*c2c66affSColin Finck typedef IO_CSQ_REMOVE_IRP *PIO_CSQ_REMOVE_IRP;
136*c2c66affSColin Finck 
137*c2c66affSColin Finck /*
138*c2c66affSColin Finck  * Function to look for an IRP in the queue
139*c2c66affSColin Finck  *
140*c2c66affSColin Finck  * Sample:
141*c2c66affSColin Finck  *
142*c2c66affSColin Finck   PIRP NTAPI CsqPeekNextIrp(PIO_CSQ Csq, PIRP Irp, PVOID PeekContext)
143*c2c66affSColin Finck   {
144*c2c66affSColin Finck     KdPrint(("Peeking for next IRP\n"));
145*c2c66affSColin Finck 
146*c2c66affSColin Finck     if(Irp)
147*c2c66affSColin Finck       return CONTAINING_RECORD(&Irp->Tail.Overlay.ListEntry.Flink, IRP, Tail.Overlay.ListEntry);
148*c2c66affSColin Finck 
149*c2c66affSColin Finck     if(IsListEmpty(&IrpQueue))
150*c2c66affSColin Finck       return NULL;
151*c2c66affSColin Finck 
152*c2c66affSColin Finck     return CONTAINING_RECORD(IrpQueue.Flink, IRP, Tail.Overlay.ListEntry);
153*c2c66affSColin Finck   }
154*c2c66affSColin Finck  *
155*c2c66affSColin Finck  */
156*c2c66affSColin Finck typedef PIRP
157*c2c66affSColin Finck (NTAPI IO_CSQ_PEEK_NEXT_IRP)(
158*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
159*c2c66affSColin Finck   _In_opt_ PIRP Irp,
160*c2c66affSColin Finck   _In_opt_ PVOID PeekContext);
161*c2c66affSColin Finck typedef IO_CSQ_PEEK_NEXT_IRP *PIO_CSQ_PEEK_NEXT_IRP;
162*c2c66affSColin Finck 
163*c2c66affSColin Finck /*
164*c2c66affSColin Finck  * Lock the queue.  This can be a spinlock, a mutex, or whatever
165*c2c66affSColin Finck  * else floats your boat.
166*c2c66affSColin Finck  *
167*c2c66affSColin Finck  * Sample:
168*c2c66affSColin Finck  *
169*c2c66affSColin Finck   VOID NTAPI CsqAcquireLock(PIO_CSQ Csq, PKIRQL Irql)
170*c2c66affSColin Finck   {
171*c2c66affSColin Finck     KdPrint(("Acquiring spin lock\n"));
172*c2c66affSColin Finck     KeAcquireSpinLock(&IrpQueueLock, Irql);
173*c2c66affSColin Finck   }
174*c2c66affSColin Finck  *
175*c2c66affSColin Finck  */
176*c2c66affSColin Finck typedef VOID
177*c2c66affSColin Finck (NTAPI IO_CSQ_ACQUIRE_LOCK)(
178*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
179*c2c66affSColin Finck   _Out_ PKIRQL Irql);
180*c2c66affSColin Finck typedef IO_CSQ_ACQUIRE_LOCK *PIO_CSQ_ACQUIRE_LOCK;
181*c2c66affSColin Finck 
182*c2c66affSColin Finck /*
183*c2c66affSColin Finck  * Unlock the queue:
184*c2c66affSColin Finck  *
185*c2c66affSColin Finck   VOID NTAPI CsqReleaseLock(PIO_CSQ Csq, KIRQL Irql)
186*c2c66affSColin Finck   {
187*c2c66affSColin Finck     KdPrint(("Releasing spin lock\n"));
188*c2c66affSColin Finck     KeReleaseSpinLock(&IrpQueueLock, Irql);
189*c2c66affSColin Finck   }
190*c2c66affSColin Finck  *
191*c2c66affSColin Finck  */
192*c2c66affSColin Finck typedef VOID
193*c2c66affSColin Finck (NTAPI IO_CSQ_RELEASE_LOCK)(
194*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
195*c2c66affSColin Finck   _In_ KIRQL Irql);
196*c2c66affSColin Finck typedef IO_CSQ_RELEASE_LOCK *PIO_CSQ_RELEASE_LOCK;
197*c2c66affSColin Finck 
198*c2c66affSColin Finck /*
199*c2c66affSColin Finck  * Finally, this is called by the queue library when it wants to complete
200*c2c66affSColin Finck  * a canceled IRP.
201*c2c66affSColin Finck  *
202*c2c66affSColin Finck  * Sample:
203*c2c66affSColin Finck  *
204*c2c66affSColin Finck   VOID NTAPI CsqCompleteCancelledIrp(PIO_CSQ Csq, PIRP Irp)
205*c2c66affSColin Finck   {
206*c2c66affSColin Finck     KdPrint(("cancelling irp 0x%x\n", Irp));
207*c2c66affSColin Finck     Irp->IoStatus.Status = STATUS_CANCELLED;
208*c2c66affSColin Finck     Irp->IoStatus.Information = 0;
209*c2c66affSColin Finck     IoCompleteRequest(Irp, IO_NO_INCREMENT);
210*c2c66affSColin Finck   }
211*c2c66affSColin Finck  *
212*c2c66affSColin Finck  */
213*c2c66affSColin Finck typedef VOID
214*c2c66affSColin Finck (NTAPI IO_CSQ_COMPLETE_CANCELED_IRP)(
215*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
216*c2c66affSColin Finck   _In_ PIRP Irp);
217*c2c66affSColin Finck typedef IO_CSQ_COMPLETE_CANCELED_IRP *PIO_CSQ_COMPLETE_CANCELED_IRP;
218*c2c66affSColin Finck 
219*c2c66affSColin Finck /*
220*c2c66affSColin Finck  * IO_CSQ - Queue control structure
221*c2c66affSColin Finck  */
222*c2c66affSColin Finck typedef struct _IO_CSQ {
223*c2c66affSColin Finck   ULONG Type;
224*c2c66affSColin Finck   PIO_CSQ_INSERT_IRP CsqInsertIrp;
225*c2c66affSColin Finck   PIO_CSQ_REMOVE_IRP CsqRemoveIrp;
226*c2c66affSColin Finck   PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp;
227*c2c66affSColin Finck   PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock;
228*c2c66affSColin Finck   PIO_CSQ_RELEASE_LOCK CsqReleaseLock;
229*c2c66affSColin Finck   PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp;
230*c2c66affSColin Finck   PVOID ReservePointer; /* must be NULL */
231*c2c66affSColin Finck } IO_CSQ, *PIO_CSQ;
232*c2c66affSColin Finck 
233*c2c66affSColin Finck #endif /* IO_TYPE_CSQ_IRP_CONTEXT */
234*c2c66affSColin Finck 
235*c2c66affSColin Finck #ifndef IO_TYPE_CSQ_EX
236*c2c66affSColin Finck 
237*c2c66affSColin Finck /* See IO_TYPE_CSQ_* above */
238*c2c66affSColin Finck #define IO_TYPE_CSQ_EX 3
239*c2c66affSColin Finck 
240*c2c66affSColin Finck /*
241*c2c66affSColin Finck  * Function to insert an IRP into the queue with extended context information.
242*c2c66affSColin Finck  * This is useful if you need to be able to de-queue particular IRPs more
243*c2c66affSColin Finck  * easily in some cases.
244*c2c66affSColin Finck  *
245*c2c66affSColin Finck  * Same deal as above; sample implementation:
246*c2c66affSColin Finck  *
247*c2c66affSColin Finck   NTSTATUS NTAPI CsqInsertIrpEx(PIO_CSQ Csq, PIRP Irp, PVOID InsertContext)
248*c2c66affSColin Finck   {
249*c2c66affSColin Finck     CsqInsertIrp(Csq, Irp);
250*c2c66affSColin Finck     return STATUS_PENDING;
251*c2c66affSColin Finck   }
252*c2c66affSColin Finck  *
253*c2c66affSColin Finck  */
254*c2c66affSColin Finck typedef NTSTATUS
255*c2c66affSColin Finck (NTAPI IO_CSQ_INSERT_IRP_EX)(
256*c2c66affSColin Finck   _In_ struct _IO_CSQ *Csq,
257*c2c66affSColin Finck   _In_ PIRP Irp,
258*c2c66affSColin Finck   _In_ PVOID InsertContext);
259*c2c66affSColin Finck typedef IO_CSQ_INSERT_IRP_EX *PIO_CSQ_INSERT_IRP_EX;
260*c2c66affSColin Finck 
261*c2c66affSColin Finck #endif /* IO_TYPE_CSQ_EX */
262*c2c66affSColin Finck 
263*c2c66affSColin Finck /*
264*c2c66affSColin Finck  * CANCEL-SAFE QUEUE DDIs
265*c2c66affSColin Finck  *
266*c2c66affSColin Finck  * These device driver interfaces are called to make use of the queue.  Again,
267*c2c66affSColin Finck  * authoritative documentation for these functions is in the DDK.  The csqtest
268*c2c66affSColin Finck  * driver also makes use of some of them.
269*c2c66affSColin Finck  */
270*c2c66affSColin Finck 
271*c2c66affSColin Finck 
272*c2c66affSColin Finck /*
273*c2c66affSColin Finck  * Call this in DriverEntry or similar in order to set up the Csq structure.
274*c2c66affSColin Finck  * As long as the Csq struct and the functions you pass in are resident,
275*c2c66affSColin Finck  * there are no IRQL restrictions.
276*c2c66affSColin Finck  */
277*c2c66affSColin Finck NTKERNELAPI
278*c2c66affSColin Finck NTSTATUS NTAPI IoCsqInitialize(_Out_ PIO_CSQ Csq,
279*c2c66affSColin Finck                                _In_ PIO_CSQ_INSERT_IRP CsqInsertIrp,
280*c2c66affSColin Finck                                _In_ PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
281*c2c66affSColin Finck                                _In_ PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
282*c2c66affSColin Finck                                _In_ PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
283*c2c66affSColin Finck                                _In_ PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
284*c2c66affSColin Finck                                _In_ PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp);
285*c2c66affSColin Finck 
286*c2c66affSColin Finck /*
287*c2c66affSColin Finck  * Same as above, except you provide a CsqInsertIrpEx routine instead of
288*c2c66affSColin Finck  * CsqInsertIrp.  This eventually allows you to supply extra tracking
289*c2c66affSColin Finck  * information for use with the queue.
290*c2c66affSColin Finck  */
291*c2c66affSColin Finck NTKERNELAPI
292*c2c66affSColin Finck NTSTATUS NTAPI IoCsqInitializeEx(_Out_ PIO_CSQ Csq,
293*c2c66affSColin Finck                                  _In_ PIO_CSQ_INSERT_IRP_EX CsqInsertIrpEx,
294*c2c66affSColin Finck                                  _In_ PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
295*c2c66affSColin Finck                                  _In_ PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
296*c2c66affSColin Finck                                  _In_ PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
297*c2c66affSColin Finck                                  _In_ PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
298*c2c66affSColin Finck                                  _In_ PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp);
299*c2c66affSColin Finck 
300*c2c66affSColin Finck /*
301*c2c66affSColin Finck  * Insert an IRP into the queue
302*c2c66affSColin Finck  */
303*c2c66affSColin Finck NTKERNELAPI
304*c2c66affSColin Finck VOID NTAPI IoCsqInsertIrp(_Inout_ PIO_CSQ Csq,
305*c2c66affSColin Finck                           _Inout_ PIRP Irp,
306*c2c66affSColin Finck                           _Out_opt_ PIO_CSQ_IRP_CONTEXT Context);
307*c2c66affSColin Finck 
308*c2c66affSColin Finck /*
309*c2c66affSColin Finck  * Insert an IRP into the queue, with special context maintained that
310*c2c66affSColin Finck  * makes it easy to find IRPs in the queue
311*c2c66affSColin Finck  */
312*c2c66affSColin Finck NTKERNELAPI
313*c2c66affSColin Finck NTSTATUS NTAPI IoCsqInsertIrpEx(_Inout_ PIO_CSQ Csq,
314*c2c66affSColin Finck                                 _Inout_ PIRP Irp,
315*c2c66affSColin Finck                                 _Out_opt_ PIO_CSQ_IRP_CONTEXT Context,
316*c2c66affSColin Finck                                 _In_opt_ PVOID InsertContext);
317*c2c66affSColin Finck 
318*c2c66affSColin Finck /*
319*c2c66affSColin Finck  * Remove a particular IRP from the queue
320*c2c66affSColin Finck  */
321*c2c66affSColin Finck NTKERNELAPI
322*c2c66affSColin Finck PIRP NTAPI IoCsqRemoveIrp(_Inout_ PIO_CSQ Csq,
323*c2c66affSColin Finck                           _Inout_ PIO_CSQ_IRP_CONTEXT Context);
324*c2c66affSColin Finck 
325*c2c66affSColin Finck /*
326*c2c66affSColin Finck  * Remove the next IRP from the queue
327*c2c66affSColin Finck  */
328*c2c66affSColin Finck NTKERNELAPI
329*c2c66affSColin Finck PIRP NTAPI IoCsqRemoveNextIrp(_Inout_ PIO_CSQ Csq,
330*c2c66affSColin Finck                               _In_opt_ PVOID PeekContext);
331*c2c66affSColin Finck 
332*c2c66affSColin Finck #ifdef __cplusplus
333*c2c66affSColin Finck }
334*c2c66affSColin Finck #endif
335