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