ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

csq.c
Go to the documentation of this file.
00001 /*
00002  * ReactOS Cancel-Safe Queue library
00003  * Copyright (c) 2004, Vizzini (vizzini@plasmic.com)
00004  * Licensed under the GNU GPL for the ReactOS project
00005  *
00006  * This file implements the ReactOS CSQ library.  For background and overview
00007  * information on these routines, read csq.h.   For the authoritative reference
00008  * to using these routines, see the current DDK (IoCsqXXX and CsqXxx callbacks).
00009  *
00010  * There are a couple of subtle races that this library is designed to avoid.
00011  * Please read the code (particularly IoCsqInsertIrpEx and IoCsqRemoveIrp) for
00012  * some details.
00013  *
00014  * In general, we try here to avoid the race between these queue/dequeue
00015  * interfaces and our own cancel routine.  This library supplies a cancel
00016  * routine that is used in all IRPs that are queued to it.  The major race
00017  * conditions surround the proper handling of in-between cases, such as in-progress
00018  * queue and de-queue operations.
00019  *
00020  * When you're thinking about these operations, keep in mind that three or four
00021  * processors can have queue and dequeue operations in progress simultaneously,
00022  * and a user thread may cancel any IRP at any time.  Also, these operations don't
00023  * all happen at DISPATCH_LEVEL all of the time, so thread switching on a single
00024  * processor can create races too.
00025  */
00026 /* $Id: csq.c 42461 2009-08-07 10:45:59Z sginsberg $ */
00027 
00028 #include <ntdef.h>
00029 #undef DECLSPEC_IMPORT
00030 #define DECLSPEC_IMPORT
00031 #include <ntifs.h>
00032 
00033 
00034 static VOID NTAPI IopCsqCancelRoutine(PDEVICE_OBJECT DeviceObject,
00035                                PIRP Irp)
00036 /*
00037  * FUNCTION: Cancel routine that is installed on any IRP that this library manages
00038  * ARGUMENTS:
00039  *     [Called back by the system]
00040  * NOTES:
00041  *     - We assume that Irp->Tail.Overlay.DriverContext[3] has either a IO_CSQ
00042  *       or an IO_CSQ_IRP_CONTEXT in it, but we have to figure out which it is
00043  *     - By the time this routine executes, the I/O Manager has already cleared
00044  *       the cancel routine pointer in the IRP, so it will only be canceled once
00045  *     - Because of this, we're guaranteed that Irp is valid the whole time
00046  *     - Don't forget to release the cancel spinlock ASAP --> #1 hot lock in the
00047  *       system
00048  *     - May be called at high IRQL
00049  */
00050 {
00051     PIO_CSQ Csq;
00052     KIRQL Irql;
00053 
00054     /* First things first: */
00055     IoReleaseCancelSpinLock(Irp->CancelIrql);
00056 
00057     /* We could either get a context or just a csq */
00058     Csq = (PIO_CSQ)Irp->Tail.Overlay.DriverContext[3];
00059 
00060     if(Csq->Type == IO_TYPE_CSQ_IRP_CONTEXT)
00061         {
00062             PIO_CSQ_IRP_CONTEXT Context = (PIO_CSQ_IRP_CONTEXT)Csq;
00063             Csq = Context->Csq;
00064 
00065             /* clean up context while we're here */
00066             Context->Irp = NULL;
00067         }
00068 
00069     /* Now that we have our CSQ, complete the IRP */
00070     Csq->CsqAcquireLock(Csq, &Irql);
00071         {
00072             Csq->CsqRemoveIrp(Csq, Irp);
00073             Csq->CsqCompleteCanceledIrp(Csq, Irp);
00074         }
00075     Csq->CsqReleaseLock(Csq, Irql);
00076 }
00077 
00078 
00079 NTSTATUS NTAPI IoCsqInitialize(PIO_CSQ Csq,
00080                                PIO_CSQ_INSERT_IRP CsqInsertIrp,
00081                                PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
00082                                PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
00083                                PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
00084                                PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
00085                                PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp)
00086 /*
00087  * FUNCTION: Set up a CSQ struct to initialize the queue
00088  * ARGUMENTS:
00089  *     Csq: Caller-allocated non-paged space for our IO_CSQ to be initialized
00090  *     CsqInsertIrp: Insert routine
00091  *     CsqRemoveIrp: Remove routine
00092  *     CsqPeekNextIrp: Routine to paeek at the next IRP in queue
00093  *     CsqAcquireLock: Acquire the queue's lock
00094  *     CsqReleaseLock: Release the queue's lock
00095  *     CsqCompleteCanceledIrp: Routine to complete IRPs when they are canceled
00096  * RETURNS:
00097  *     - STATUS_SUCCESS in all cases
00098  * NOTES:
00099  *     - Csq must be non-paged, as the queue is manipulated with a held spinlock
00100  */
00101 {
00102     Csq->Type = IO_TYPE_CSQ;
00103     Csq->CsqInsertIrp = CsqInsertIrp;
00104     Csq->CsqRemoveIrp = CsqRemoveIrp;
00105     Csq->CsqPeekNextIrp = CsqPeekNextIrp;
00106     Csq->CsqAcquireLock = CsqAcquireLock;
00107     Csq->CsqReleaseLock = CsqReleaseLock;
00108     Csq->CsqCompleteCanceledIrp = CsqCompleteCanceledIrp;
00109     Csq->ReservePointer = NULL;
00110 
00111     return STATUS_SUCCESS;
00112 }
00113 
00114 
00115 NTSTATUS NTAPI IoCsqInitializeEx(PIO_CSQ Csq,
00116                                  PIO_CSQ_INSERT_IRP_EX CsqInsertIrpEx,
00117                                  PIO_CSQ_REMOVE_IRP CsqRemoveIrp,
00118                                  PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp,
00119                                  PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock,
00120                                  PIO_CSQ_RELEASE_LOCK CsqReleaseLock,
00121                                  PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp)
00122 /*
00123  * FUNCTION: Set up a CSQ struct to initialize the queue (extended version)
00124  * ARGUMENTS:
00125  *     Csq: Caller-allocated non-paged space for our IO_CSQ to be initialized
00126  *     CsqInsertIrpEx: Extended insert routine
00127  *     CsqRemoveIrp: Remove routine
00128  *     CsqPeekNextIrp: Routine to paeek at the next IRP in queue
00129  *     CsqAcquireLock: Acquire the queue's lock
00130  *     CsqReleaseLock: Release the queue's lock
00131  *     CsqCompleteCanceledIrp: Routine to complete IRPs when they are canceled
00132  * RETURNS:
00133  *     - STATUS_SUCCESS in all cases
00134  * NOTES:
00135  *     - Csq must be non-paged, as the queue is manipulated with a held spinlock
00136  */
00137 {
00138     Csq->Type = IO_TYPE_CSQ_EX;
00139     Csq->CsqInsertIrp = (PIO_CSQ_INSERT_IRP)CsqInsertIrpEx;
00140     Csq->CsqRemoveIrp = CsqRemoveIrp;
00141     Csq->CsqPeekNextIrp = CsqPeekNextIrp;
00142     Csq->CsqAcquireLock = CsqAcquireLock;
00143     Csq->CsqReleaseLock = CsqReleaseLock;
00144     Csq->CsqCompleteCanceledIrp = CsqCompleteCanceledIrp;
00145     Csq->ReservePointer = NULL;
00146 
00147     return STATUS_SUCCESS;
00148 }
00149 
00150 
00151 VOID NTAPI IoCsqInsertIrp(PIO_CSQ Csq,
00152                           PIRP Irp,
00153                           PIO_CSQ_IRP_CONTEXT Context)
00154 /*
00155  * FUNCTION: Insert an IRP into the CSQ
00156  * ARGUMENTS:
00157  *     Csq: Pointer to the initialized CSQ
00158  *     Irp: Pointer to the IRP to queue
00159  *     Context: Context record to track the IRP while queued
00160  * NOTES:
00161  *     - Just passes through to IoCsqInsertIrpEx, with no InsertContext
00162  */
00163 {
00164     IoCsqInsertIrpEx(Csq, Irp, Context, 0);
00165 }
00166 
00167 
00168 NTSTATUS NTAPI IoCsqInsertIrpEx(PIO_CSQ Csq,
00169                                 PIRP Irp,
00170                                 PIO_CSQ_IRP_CONTEXT Context,
00171                                 PVOID InsertContext)
00172 /*
00173  * FUNCTION: Insert an IRP into the CSQ, with additional tracking context
00174  * ARGUMENTS:
00175  *     Csq: Pointer to the initialized CSQ
00176  *     Irp: Pointer to the IRP to queue
00177  *     Context: Context record to track the IRP while queued
00178  *     InsertContext: additional data that is passed through to CsqInsertIrpEx
00179  * NOTES:
00180  *     - Passes the additional context through to the driver-supplied callback,
00181  *       which can be used with more sophistocated queues
00182  *     - Marks the IRP pending in all cases
00183  *     - Guaranteed to not queue a canceled IRP
00184  *     - This is complicated logic, and is patterend after the Microsoft library.
00185  *       I'm sure I have gotten the details wrong on a fine point or two, but
00186  *       basically this works with the MS-supplied samples.
00187  */
00188 {
00189     NTSTATUS Retval = STATUS_SUCCESS;
00190     KIRQL Irql;
00191 
00192     Csq->CsqAcquireLock(Csq, &Irql);
00193 
00194     do
00195         {
00196             /* mark all irps pending -- says so in the cancel sample */
00197             IoMarkIrpPending(Irp);
00198 
00199             /* set up the context if we have one */
00200             if(Context)
00201                 {
00202                     Context->Type = IO_TYPE_CSQ_IRP_CONTEXT;
00203                     Context->Irp = Irp;
00204                     Context->Csq = Csq;
00205                     Irp->Tail.Overlay.DriverContext[3] = Context;
00206                 }
00207             else
00208                 Irp->Tail.Overlay.DriverContext[3] = Csq;
00209 
00210             /*
00211              * NOTE!  This is very sensitive to order.  If you set the cancel routine
00212              * *before* you queue the IRP, our cancel routine will get called back for
00213              * an IRP that isn't in its queue.
00214              *
00215              * There are three possibilities:
00216              * 1) We get an IRP, we queue it, and it is valid the whole way
00217              * 2) We get an IRP, and the IO manager cancels it before we're done here
00218              * 3) We get an IRP, queue it, and the IO manager cancels it.
00219              *
00220              * #2 is is a booger.
00221              *
00222              * When the IO manger receives a request to cancel an IRP, it sets the cancel
00223              * bit in the IRP's control byte to TRUE.  Then, it looks to see if a cancel
00224              * routine is set.  If it isn't, the IO manager just returns to the caller.
00225              * If there *is* a routine, it gets called.
00226              *
00227              * If we test for cancel first and then set the cancel routine, there is a spot
00228              * between test and set that the IO manager can cancel us without our knowledge,
00229              * so we miss a cancel request.  That is bad.
00230              *
00231              * If we set a routine first and then test for cancel, we race with our completion
00232              * routine:  We set the routine, the IO Manager sets cancel, we test cancel and find
00233              * it is TRUE.  Meanwhile the IO manager has called our cancel routine already, so
00234              * we can't complete the IRP because it'll rip it out from under the cancel routine.
00235              *
00236              * The IO manager does us a favor though: it nulls out the cancel routine in the IRP
00237              * before calling it.  Therefore, if we test to see if the cancel routine is NULL
00238              * (after we have just set it), that means our own cancel routine is already working
00239              * on the IRP, and we can just return quietly.  Otherwise, we have to de-queue the
00240              * IRP and cancel it ourselves.
00241              *
00242              * We have to go through all of this mess because this API guarantees that we will
00243              * never return having left a canceled IRP in the queue.
00244              */
00245 
00246             /* Step 1: Queue the IRP */
00247             if(Csq->Type == IO_TYPE_CSQ)
00248                 Csq->CsqInsertIrp(Csq, Irp);
00249             else
00250                 {
00251                     PIO_CSQ_INSERT_IRP_EX pCsqInsertIrpEx = (PIO_CSQ_INSERT_IRP_EX)Csq->CsqInsertIrp;
00252                     Retval = pCsqInsertIrpEx(Csq, Irp, InsertContext);
00253                     if(Retval != STATUS_SUCCESS)
00254                         break;
00255                 }
00256 
00257             /* Step 2: Set our cancel routine */
00258             (void)IoSetCancelRoutine(Irp, IopCsqCancelRoutine);
00259 
00260             /* Step 3: Deal with an IRP that is already canceled */
00261             if(!Irp->Cancel)
00262                 break;
00263 
00264             /*
00265              * Since we're canceled, see if our cancel routine is already running
00266              * If this is NULL, the IO Manager has already called our cancel routine
00267              */
00268             if(!IoSetCancelRoutine(Irp, NULL))
00269                 break;
00270 
00271             /* OK, looks like we have to de-queue and complete this ourselves */
00272             Csq->CsqRemoveIrp(Csq, Irp);
00273             Csq->CsqCompleteCanceledIrp(Csq, Irp);
00274 
00275             if(Context)
00276                 Context->Irp = NULL;
00277         }
00278     while(0);
00279 
00280     Csq->CsqReleaseLock(Csq, Irql);
00281 
00282     return Retval;
00283 }
00284 
00285 
00286 PIRP NTAPI IoCsqRemoveIrp(PIO_CSQ Csq,
00287                           PIO_CSQ_IRP_CONTEXT Context)
00288 /*
00289  * FUNCTION: Remove anb IRP from the queue
00290  * ARGUMENTS:
00291  *     Csq: Queue to remove the IRP from
00292  *     Context: Context record containing the IRP to be dequeued
00293  * RETURNS:
00294  *     - Pointer to an IRP if we found it
00295  * NOTES:
00296  *     - Don't forget that we can be canceled any time up to the point
00297  *       where we unset our cancel routine
00298  */
00299 {
00300     KIRQL Irql;
00301     PIRP Irp = NULL;
00302 
00303     Csq->CsqAcquireLock(Csq, &Irql);
00304 
00305     do
00306         {
00307             /* It's possible that this IRP could have been canceled */
00308             Irp = Context->Irp;
00309 
00310             if(!Irp)
00311                 break;
00312 
00313             /* Unset the cancel routine and see if it has already been canceled */
00314             if(!IoSetCancelRoutine(Irp, NULL))
00315                 {
00316                     /*
00317                      * already gone, return NULL  --> NOTE  that we cannot touch this IRP *or* the context,
00318                      * since the context is being simultaneously twiddled by the cancel routine
00319                      */
00320                     Irp = NULL;
00321                     break;
00322                 }
00323 
00324             /* This IRP is valid and is ours.  Dequeue it, fix it up, and return */
00325             Csq->CsqRemoveIrp(Csq, Irp);
00326 
00327             Context = (PIO_CSQ_IRP_CONTEXT)InterlockedExchangePointer(&Irp->Tail.Overlay.DriverContext[3], NULL);
00328 
00329             if(Context && Context->Type == IO_TYPE_CSQ_IRP_CONTEXT)
00330                 Context->Irp = NULL;
00331         }
00332     while(0);
00333 
00334     Csq->CsqReleaseLock(Csq, Irql);
00335 
00336     return Irp;
00337 }
00338 
00339 PIRP NTAPI IoCsqRemoveNextIrp(PIO_CSQ Csq,
00340                               PVOID PeekContext)
00341 /*
00342  * FUNCTION: IoCsqRemoveNextIrp - Removes the next IRP from the queue
00343  * ARGUMENTS:
00344  *     Csq: Queue to remove the IRP from
00345  *     PeekContext: Identifier of the IRP to be removed
00346  * RETURNS:
00347  *     Pointer to the IRP that was removed, or NULL if one
00348  *     could not be found
00349  * NOTES:
00350  *     - This function is sensitive to yet another race condition.
00351  *       The basic idea is that we have to return the first IRP that
00352  *       we get that matches the PeekContext >that is not already canceled<.
00353  *       Therefore, we have to do a trick similar to the one done in Insert
00354  *       above.
00355  */
00356 {
00357     KIRQL Irql;
00358     PIRP Irp = NULL;
00359     PIO_CSQ_IRP_CONTEXT Context;
00360 
00361     Csq->CsqAcquireLock(Csq, &Irql);
00362 
00363     while((Irp = Csq->CsqPeekNextIrp(Csq, Irp, PeekContext)))
00364         {
00365             /*
00366              * If the cancel routine is gone, we're already canceled,
00367              * and are spinning on the queue lock in our own cancel
00368              * routine.  Move on to the next candidate.  It'll get
00369              * removed by the cance routine.
00370              */
00371             if(!IoSetCancelRoutine(Irp, NULL))
00372                 continue;
00373 
00374             Csq->CsqRemoveIrp(Csq, Irp);
00375 
00376             /* Unset the context stuff and return */
00377             Context = (PIO_CSQ_IRP_CONTEXT)InterlockedExchangePointer(&Irp->Tail.Overlay.DriverContext[3], NULL);
00378 
00379             if(Context && Context->Type == IO_TYPE_CSQ_IRP_CONTEXT)
00380                 Context->Irp = NULL;
00381 
00382             break;
00383         }
00384 
00385     Csq->CsqReleaseLock(Csq, Irql);
00386 
00387     return Irp;
00388 }
00389 

Generated on Sat May 26 2012 04:34:53 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.