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