Home | History | Annotate | Download | only in ddk
      1 /*
      2  * Cancel-Safe Queue Library
      3  * Created in 2004 by Vizzini (vizzini (at) 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 PIRP Irp,
    160   IN 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(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  */
    291 NTKERNELAPI
    292 NTSTATUS 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  */
    303 NTKERNELAPI
    304 VOID 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  */
    312 NTKERNELAPI
    313 NTSTATUS 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  */
    321 NTKERNELAPI
    322 PIRP NTAPI IoCsqRemoveIrp(PIO_CSQ Csq,
    323                           PIO_CSQ_IRP_CONTEXT Context);
    324 
    325 /*
    326  * Remove the next IRP from the queue
    327  */
    328 NTKERNELAPI
    329 PIRP NTAPI IoCsqRemoveNextIrp(PIO_CSQ Csq,
    330                               PVOID PeekContext);
    331 
    332 #ifdef __cplusplus
    333 }
    334 #endif
    335