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