ReactOS  0.4.14-dev-608-gd495a4f
csq.c
Go to the documentation of this file.
1 /*
2  * ReactOS Cancel-Safe Queue library
3  * Copyright (c) 2004, Vizzini (vizzini@plasmic.com)
4  * Licensed under the GNU GPL for the ReactOS project
5  *
6  * This file implements the ReactOS CSQ library. For background and overview
7  * information on these routines, read csq.h. For the authoritative reference
8  * to using these routines, see the current DDK (IoCsqXXX and CsqXxx callbacks).
9  *
10  * There are a couple of subtle races that this library is designed to avoid.
11  * Please read the code (particularly IoCsqInsertIrpEx and IoCsqRemoveIrp) for
12  * some details.
13  *
14  * In general, we try here to avoid the race between these queue/dequeue
15  * interfaces and our own cancel routine. This library supplies a cancel
16  * routine that is used in all IRPs that are queued to it. The major race
17  * conditions surround the proper handling of in-between cases, such as in-progress
18  * queue and de-queue operations.
19  *
20  * When you're thinking about these operations, keep in mind that three or four
21  * processors can have queue and dequeue operations in progress simultaneously,
22  * and a user thread may cancel any IRP at any time. Also, these operations don't
23  * all happen at DISPATCH_LEVEL all of the time, so thread switching on a single
24  * processor can create races too.
25  */
26 
27 #include <ntdef.h>
28 #undef DECLSPEC_IMPORT
29 #define DECLSPEC_IMPORT
30 #include <ntifs.h>
31 
32 
49 _Function_class_(DRIVER_CANCEL)
50 static
51 VOID
52 NTAPI
53 IopCsqCancelRoutine(
56 {
57  PIO_CSQ Csq;
58  KIRQL Irql;
59 
60  /* First things first: */
61  IoReleaseCancelSpinLock(Irp->CancelIrql);
62 
63  /* We could either get a context or just a csq */
64  Csq = (PIO_CSQ)Irp->Tail.Overlay.DriverContext[3];
65 
67  {
69  Csq = Context->Csq;
70 
71  /* clean up context while we're here */
72  Context->Irp = NULL;
73  }
74 
75  /* Now that we have our CSQ, complete the IRP */
79 
81 }
82 
83 
101 NTSTATUS
102 NTAPI
104  _Out_ PIO_CSQ Csq,
111 {
112  Csq->Type = IO_TYPE_CSQ;
120 
121  return STATUS_SUCCESS;
122 }
123 
124 
141 NTSTATUS
142 NTAPI
144  _Out_ PIO_CSQ Csq,
151 {
160 
161  return STATUS_SUCCESS;
162 }
163 
164 
175 VOID
176 NTAPI
179  _Inout_ PIRP Irp,
181 {
183 }
184 
185 
203 NTSTATUS
204 NTAPI
207  _Inout_ PIRP Irp,
210 {
211  NTSTATUS Retval = STATUS_SUCCESS;
212  KIRQL Irql;
213 
215 
216  do
217  {
218  /* mark all irps pending -- says so in the cancel sample */
220 
221  /* set up the context if we have one */
222  if(Context)
223  {
225  Context->Irp = Irp;
226  Context->Csq = Csq;
227  Irp->Tail.Overlay.DriverContext[3] = Context;
228  }
229  else
230  Irp->Tail.Overlay.DriverContext[3] = Csq;
231 
232  /*
233  * NOTE! This is very sensitive to order. If you set the cancel routine
234  * *before* you queue the IRP, our cancel routine will get called back for
235  * an IRP that isn't in its queue.
236  *
237  * There are three possibilities:
238  * 1) We get an IRP, we queue it, and it is valid the whole way
239  * 2) We get an IRP, and the IO manager cancels it before we're done here
240  * 3) We get an IRP, queue it, and the IO manager cancels it.
241  *
242  * #2 is is a booger.
243  *
244  * When the IO manger receives a request to cancel an IRP, it sets the cancel
245  * bit in the IRP's control byte to TRUE. Then, it looks to see if a cancel
246  * routine is set. If it isn't, the IO manager just returns to the caller.
247  * If there *is* a routine, it gets called.
248  *
249  * If we test for cancel first and then set the cancel routine, there is a spot
250  * between test and set that the IO manager can cancel us without our knowledge,
251  * so we miss a cancel request. That is bad.
252  *
253  * If we set a routine first and then test for cancel, we race with our completion
254  * routine: We set the routine, the IO Manager sets cancel, we test cancel and find
255  * it is TRUE. Meanwhile the IO manager has called our cancel routine already, so
256  * we can't complete the IRP because it'll rip it out from under the cancel routine.
257  *
258  * The IO manager does us a favor though: it nulls out the cancel routine in the IRP
259  * before calling it. Therefore, if we test to see if the cancel routine is NULL
260  * (after we have just set it), that means our own cancel routine is already working
261  * on the IRP, and we can just return quietly. Otherwise, we have to de-queue the
262  * IRP and cancel it ourselves.
263  *
264  * We have to go through all of this mess because this API guarantees that we will
265  * never return having left a canceled IRP in the queue.
266  */
267 
268  /* Step 1: Queue the IRP */
269  if(Csq->Type == IO_TYPE_CSQ)
270  Csq->CsqInsertIrp(Csq, Irp);
271  else
272  {
274  Retval = pCsqInsertIrpEx(Csq, Irp, InsertContext);
275  if(Retval != STATUS_SUCCESS)
276  break;
277  }
278 
279  /* Step 2: Set our cancel routine */
280  (void)IoSetCancelRoutine(Irp, IopCsqCancelRoutine);
281 
282  /* Step 3: Deal with an IRP that is already canceled */
283  if(!Irp->Cancel)
284  break;
285 
286  /*
287  * Since we're canceled, see if our cancel routine is already running
288  * If this is NULL, the IO Manager has already called our cancel routine
289  */
291  break;
292 
293 
294  Irp->Tail.Overlay.DriverContext[3] = 0;
295 
296  /* OK, looks like we have to de-queue and complete this ourselves */
297  Csq->CsqRemoveIrp(Csq, Irp);
299 
300  if(Context)
301  Context->Irp = NULL;
302  }
303  while(0);
304 
306 
307  return Retval;
308 }
309 
310 
324 PIRP
325 NTAPI
329 {
330  KIRQL Irql;
331  PIRP Irp = NULL;
332 
334 
335  do
336  {
337  /* It's possible that this IRP could have been canceled */
338  Irp = Context->Irp;
339 
340  if(!Irp)
341  break;
342 
343  ASSERT(Context->Csq == Csq);
344 
345  /* Unset the cancel routine and see if it has already been canceled */
347  {
348  /*
349  * already gone, return NULL --> NOTE that we cannot touch this IRP *or* the context,
350  * since the context is being simultaneously twiddled by the cancel routine
351  */
352  Irp = NULL;
353  break;
354  }
355 
356  ASSERT(Context == Irp->Tail.Overlay.DriverContext[3]);
357 
358  /* This IRP is valid and is ours. Dequeue it, fix it up, and return */
359  Csq->CsqRemoveIrp(Csq, Irp);
360 
361  Context = (PIO_CSQ_IRP_CONTEXT)InterlockedExchangePointer(&Irp->Tail.Overlay.DriverContext[3], NULL);
362 
363  if (Context && Context->Type == IO_TYPE_CSQ_IRP_CONTEXT)
364  {
365  Context->Irp = NULL;
366 
367  ASSERT(Context->Csq == Csq);
368  }
369 
370  Irp->Tail.Overlay.DriverContext[3] = 0;
371  }
372  while(0);
373 
375 
376  return Irp;
377 }
378 
396 PIRP
397 NTAPI
401 {
402  KIRQL Irql;
403  PIRP Irp = NULL;
405 
407 
408  while((Irp = Csq->CsqPeekNextIrp(Csq, Irp, PeekContext)))
409  {
410  /*
411  * If the cancel routine is gone, we're already canceled,
412  * and are spinning on the queue lock in our own cancel
413  * routine. Move on to the next candidate. It'll get
414  * removed by the cance routine.
415  */
417  continue;
418 
419  Csq->CsqRemoveIrp(Csq, Irp);
420 
421  /* Unset the context stuff and return */
422  Context = (PIO_CSQ_IRP_CONTEXT)InterlockedExchangePointer(&Irp->Tail.Overlay.DriverContext[3], NULL);
423 
424  if (Context && Context->Type == IO_TYPE_CSQ_IRP_CONTEXT)
425  {
426  Context->Irp = NULL;
427 
428  ASSERT(Context->Csq == Csq);
429  }
430 
431  Irp->Tail.Overlay.DriverContext[3] = 0;
432 
433  break;
434  }
435 
437 
438  return Irp;
439 }
440 
_In_ PIO_CSQ_INSERT_IRP CsqInsertIrp
Definition: iofuncs.h:1884
PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock
Definition: csq.h:227
struct png_info_def **typedef void(__cdecl typeof(png_destroy_read_struct))(struct png_struct_def **
Definition: typeof.h:49
_In_ PIRP Irp
Definition: csq.h:116
IO_CSQ_REMOVE_IRP * PIO_CSQ_REMOVE_IRP
Definition: csq.h:135
#define IO_TYPE_CSQ_EX
Definition: csq.h:238
LONG NTSTATUS
Definition: precomp.h:26
NTSTATUS NTAPI IoCsqInitializeEx(_Out_ PIO_CSQ Csq, _In_ PIO_CSQ_INSERT_IRP_EX CsqInsertIrpEx, _In_ PIO_CSQ_REMOVE_IRP CsqRemoveIrp, _In_ PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp, _In_ PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock, _In_ PIO_CSQ_RELEASE_LOCK CsqReleaseLock, _In_ PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp)
Set up a CSQ struct to initialize the queue (extended version)
Definition: csq.c:143
IO_CSQ_ACQUIRE_LOCK * PIO_CSQ_ACQUIRE_LOCK
Definition: csq.h:180
_In_ PIO_CSQ_INSERT_IRP _In_ PIO_CSQ_REMOVE_IRP _In_ PIO_CSQ_PEEK_NEXT_IRP _In_ PIO_CSQ_ACQUIRE_LOCK _In_ PIO_CSQ_RELEASE_LOCK CsqReleaseLock
Definition: iofuncs.h:1884
IoSetCancelRoutine(Irp, CancelRoutine)
_Out_ PKIRQL Irql
Definition: csq.h:179
#define _In_opt_
Definition: no_sal2.h:213
UCHAR KIRQL
Definition: env_spec_w32.h:591
NTSTATUS NTAPI IoCsqInsertIrpEx(_Inout_ PIO_CSQ Csq, _Inout_ PIRP Irp, _Out_opt_ PIO_CSQ_IRP_CONTEXT Context, _In_opt_ PVOID InsertContext)
Insert an IRP into the CSQ, with additional tracking context.
Definition: csq.c:205
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
struct _IO_CSQ * PIO_CSQ
Definition: csq.h:69
PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp
Definition: csq.h:226
#define IO_TYPE_CSQ
Definition: csq.h:79
smooth NULL
Definition: ftsmooth.c:416
ULONG Type
Definition: csq.h:223
#define _Out_
Definition: no_sal2.h:323
IO_CSQ_PEEK_NEXT_IRP * PIO_CSQ_PEEK_NEXT_IRP
Definition: csq.h:161
NTSTATUS NTAPI CsqInsertIrpEx(PIO_CSQ Csq, PIRP Irp, PVOID InsertContext)
Definition: csqtest.c:82
_In_ PIO_CSQ_INSERT_IRP _In_ PIO_CSQ_REMOVE_IRP _In_ PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp
Definition: iofuncs.h:1884
_In_ PIO_CSQ_INSERT_IRP _In_ PIO_CSQ_REMOVE_IRP CsqRemoveIrp
Definition: iofuncs.h:1884
_In_ PIRP _In_ PVOID InsertContext
Definition: csq.h:257
#define _Out_opt_
Definition: no_sal2.h:339
#define InterlockedExchangePointer(Target, Value)
Definition: dshow.h:45
#define IO_TYPE_CSQ_IRP_CONTEXT
Definition: csq.h:78
PIRP NTAPI IoCsqRemoveNextIrp(_Inout_ PIO_CSQ Csq, _In_opt_ PVOID PeekContext)
IoCsqRemoveNextIrp - Removes the next IRP from the queue.
Definition: csq.c:398
PIO_CSQ_REMOVE_IRP CsqRemoveIrp
Definition: csq.h:225
_In_ PIO_CSQ_INSERT_IRP _In_ PIO_CSQ_REMOVE_IRP _In_ PIO_CSQ_PEEK_NEXT_IRP _In_ PIO_CSQ_ACQUIRE_LOCK _In_ PIO_CSQ_RELEASE_LOCK _In_ PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp
Definition: iofuncs.h:1884
VOID NTAPI IoCsqInsertIrp(_Inout_ PIO_CSQ Csq, _Inout_ PIRP Irp, _Out_opt_ PIO_CSQ_IRP_CONTEXT Context)
Insert an IRP into the CSQ.
Definition: csq.c:177
#define _Inout_
Definition: no_sal2.h:244
_Function_class_(DRIVER_CANCEL)
Cancel routine that is installed on any IRP that this library manages.
Definition: csq.c:49
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
NTSTATUS NTAPI IoCsqInitialize(_Out_ PIO_CSQ Csq, _In_ PIO_CSQ_INSERT_IRP CsqInsertIrp, _In_ PIO_CSQ_REMOVE_IRP CsqRemoveIrp, _In_ PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp, _In_ PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock, _In_ PIO_CSQ_RELEASE_LOCK CsqReleaseLock, _In_ PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp)
Set up a CSQ struct to initialize the queue.
Definition: csq.c:103
PVOID ReservePointer
Definition: csq.h:230
PIO_CSQ_INSERT_IRP CsqInsertIrp
Definition: csq.h:224
PIO_CSQ_RELEASE_LOCK CsqReleaseLock
Definition: csq.h:228
IO_CSQ_INSERT_IRP * PIO_CSQ_INSERT_IRP
Definition: csq.h:117
PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp
Definition: csq.h:229
VOID NTAPI IoReleaseCancelSpinLock(IN KIRQL Irql)
Definition: util.c:150
struct _IO_CSQ_IRP_CONTEXT * PIO_CSQ_IRP_CONTEXT
#define _In_
Definition: no_sal2.h:204
Definition: csq.h:222
IN PDEVICE_OBJECT DeviceObject
Definition: fatprocs.h:1560
PIRP NTAPI IoCsqRemoveIrp(_Inout_ PIO_CSQ Csq, _Inout_ PIO_CSQ_IRP_CONTEXT Context)
Remove anb IRP from the queue.
Definition: csq.c:326
IO_CSQ_RELEASE_LOCK * PIO_CSQ_RELEASE_LOCK
Definition: csq.h:196
_In_opt_ PIRP _In_opt_ PVOID PeekContext
Definition: csq.h:159
IO_CSQ Csq
Definition: csqrtns.c:46
IO_CSQ_INSERT_IRP_EX * PIO_CSQ_INSERT_IRP_EX
Definition: csq.h:259
struct tagContext Context
Definition: acpixf.h:1030
IO_CSQ_COMPLETE_CANCELED_IRP * PIO_CSQ_COMPLETE_CANCELED_IRP
Definition: csq.h:217
return STATUS_SUCCESS
Definition: btrfs.c:2938
IoMarkIrpPending(Irp)
_In_ PIO_CSQ_INSERT_IRP _In_ PIO_CSQ_REMOVE_IRP _In_ PIO_CSQ_PEEK_NEXT_IRP _In_ PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock
Definition: iofuncs.h:1884
#define _IRQL_uses_cancel_
Definition: no_sal2.h:657