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
 60extern "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
 69typedef 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 */
 84typedef 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 */
113typedef VOID
114(NTAPI IO_CSQ_INSERT_IRP)(
115  IN struct _IO_CSQ *Csq,
116  IN PIRP Irp);
117typedef 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 */
131typedef VOID
132(NTAPI IO_CSQ_REMOVE_IRP)(
133  IN struct _IO_CSQ *Csq,
134  IN PIRP Irp);
135typedef 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 */
156typedef PIRP
157(NTAPI IO_CSQ_PEEK_NEXT_IRP)(
158  IN struct _IO_CSQ *Csq,
159  IN PIRP Irp,
160  IN PVOID PeekContext);
161typedef 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 */
176typedef VOID
177(NTAPI IO_CSQ_ACQUIRE_LOCK)(
178  IN struct _IO_CSQ *Csq,
179  OUT PKIRQL Irql);
180typedef 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 */
192typedef VOID
193(NTAPI IO_CSQ_RELEASE_LOCK)(
194  IN struct _IO_CSQ *Csq,
195  IN KIRQL Irql);
196typedef 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 */
213typedef VOID
214(NTAPI IO_CSQ_COMPLETE_CANCELED_IRP)(
215  IN struct _IO_CSQ *Csq,
216  IN PIRP Irp);
217typedef IO_CSQ_COMPLETE_CANCELED_IRP *PIO_CSQ_COMPLETE_CANCELED_IRP;
218
219/*
220 * IO_CSQ - Queue control structure
221 */
222typedef 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 */
254typedef NTSTATUS
255(NTAPI IO_CSQ_INSERT_IRP_EX)(
256  IN struct _IO_CSQ *Csq,
257  IN PIRP Irp,
258  IN PVOID InsertContext);
259typedef 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 */
277NTKERNELAPI
278NTSTATUS NTAPI IoCsqInitialize(PIO_CSQ Csq,
279                               PIO_CSQ_INSERT_IRP CsqInsertIrp,
280                               PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
281                               PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
282                               PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
283                               PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
284                               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 */
291NTKERNELAPI
292NTSTATUS NTAPI IoCsqInitializeEx(PIO_CSQ Csq,
293                                 PIO_CSQ_INSERT_IRP_EX CsqInsertIrpEx,
294                                 PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
295                                 PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
296                                 PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
297                                 PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
298                                 PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp);
299
300/*
301 * Insert an IRP into the queue
302 */
303NTKERNELAPI
304VOID NTAPI IoCsqInsertIrp(PIO_CSQ Csq,
305                          PIRP Irp,
306                          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 */
312NTKERNELAPI
313NTSTATUS NTAPI IoCsqInsertIrpEx(PIO_CSQ Csq,
314                                PIRP Irp,
315                                PIO_CSQ_IRP_CONTEXT Context,
316                                PVOID InsertContext);
317
318/*
319 * Remove a particular IRP from the queue
320 */
321NTKERNELAPI
322PIRP NTAPI IoCsqRemoveIrp(PIO_CSQ Csq,
323                          PIO_CSQ_IRP_CONTEXT Context);
324
325/*
326 * Remove the next IRP from the queue
327 */
328NTKERNELAPI
329PIRP NTAPI IoCsqRemoveNextIrp(PIO_CSQ Csq,
330                              PVOID PeekContext);
331
332#ifdef __cplusplus
333}
334#endif