1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 FxIrpQueue.cpp
8
9 Abstract:
10
11 This module implements a common queue structure for the
12 driver frameworks built around the Cancel Safe Queue model
13
14 Author:
15
16
17
18
19
20
21
22 Environment:
23
24 Both kernel and user mode
25
26 Revision History:
27
28
29 --*/
30
31 #include "coreprivshared.hpp"
32
33 // Tracing support
34 extern "C" {
35 // #include "FxIrpQueue.tmh"
36 }
37
38 //
39 // Public constructors
40 //
41
FxIrpQueue(VOID)42 FxIrpQueue::FxIrpQueue(
43 VOID
44 )
45 {
46 InitializeListHead(&m_Queue);
47
48 m_LockObject = NULL;
49
50 m_CancelCallback = NULL;
51
52 m_RequestCount = 0;
53 }
54
~FxIrpQueue()55 FxIrpQueue::~FxIrpQueue()
56 {
57 ASSERT(IsListEmpty(&m_Queue));
58 }
59
60 VOID
Initialize(__in FxNonPagedObject * LockObject,__in PFN_IRP_QUEUE_CANCEL Callback)61 FxIrpQueue::Initialize(
62 __in FxNonPagedObject* LockObject,
63 __in PFN_IRP_QUEUE_CANCEL Callback
64 )
65
66 /*++
67
68 Routine Description:
69
70 Initialize the FxIrpQueue.
71
72 Set the callback for when an IRP gets cancelled.
73
74 The callback function is only called when an IRP
75 gets cancelled, and is called with no locks held.
76
77 The cancel function that the caller registers is
78 responsible for completing the IRP with IoCompleteRequest.
79
80 If no Cancel Callback is set, or is set to NULL, IRP's will
81 be automatically completed with STATUS_CANCELED by
82 the FxIrpQueue when they are canceled.
83
84
85 If the caller supplies a LockObject, this object is used
86 to synchronize cancellation access to the list, but it is
87 expected that the caller be holding the same lock for insert/remove
88 operations. This allows the caller to perform insert/remove operations
89 using its own lock in a race free manner.
90
91 If a LockObject is not supplied, the FxIrpQueue uses its own lock.
92
93 Arguments:
94
95 LockObject - Object whose lock controls the queue
96
97 Callback - Driver callback function
98
99 Returns:
100
101 None
102
103 --*/
104
105 {
106 ASSERT(LockObject != NULL);
107
108 m_CancelCallback = Callback;
109 m_LockObject = LockObject;
110 }
111
112
113 _Must_inspect_result_
114 NTSTATUS
InsertTailRequest(__inout MdIrp Irp,__in_opt PMdIoCsqIrpContext Context,__out_opt ULONG * pRequestCount)115 FxIrpQueue::InsertTailRequest(
116 __inout MdIrp Irp,
117 __in_opt PMdIoCsqIrpContext Context,
118 __out_opt ULONG* pRequestCount
119 )
120
121 /*++
122
123 Routine Description:
124
125 Enqueue a request to the end of the queue (FIFO) and
126 marks it as pending.
127
128 The PMdIoCsqIrpContext is associated with the IRP.
129
130 PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
131 not be released until the IRP is is finally released from
132 the queue.
133
134 Arguments:
135
136 Irp - Pointer to IRP
137
138 Context - Pointer to caller allocated CSQ context
139
140 pRequestCount - Location to return new request count of queue
141 after insertion
142
143 Returns:
144
145 STATUS_SUCCESS - Operation completed.
146
147 STATUS_CANCELLED - Request was cancelled, and not inserted
148 Call is responsible for completing it.
149 --*/
150
151 {
152 NTSTATUS Status;
153
154 // Note: This marks the IRP Pending
155 Status = InsertIrpInQueue(
156 Irp, // Irp to insert
157 Context, // PIO_CSQ_IRP_CONTEXT
158 FALSE, // InsertInHead
159 pRequestCount
160 );
161
162 return Status;
163 }
164
165
166 _Must_inspect_result_
167 NTSTATUS
InsertHeadRequest(__inout MdIrp Irp,__in_opt PMdIoCsqIrpContext Context,__out_opt ULONG * pRequestCount)168 FxIrpQueue::InsertHeadRequest(
169 __inout MdIrp Irp,
170 __in_opt PMdIoCsqIrpContext Context,
171 __out_opt ULONG* pRequestCount
172 )
173
174 /*++
175
176 Routine Description:
177
178 Enqueue a request to the head of the queue and
179 marks it as pending.
180
181 The PIO_CSQ_IRP_CONTEXT is associated with the IRP.
182
183 PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
184 not be released until the IRP is is finally released from
185 the queue.
186
187 Arguments:
188
189 Irp - Pointer to IRP
190
191 Context - Pointer to caller allocated CSQ context
192
193 pRequestCount - Location to return new request count of queue
194 after insertion
195 Returns:
196
197 STATUS_SUCCESS - Operation completed.
198
199 STATUS_CANCELLED - Request was cancelled, and not inserted
200 Call is responsible for completing it.
201 --*/
202
203 {
204 NTSTATUS Status;
205
206 // Note: This marks the IRP Pending
207 Status = InsertIrpInQueue(
208 Irp, // Irp to insert
209 Context, // PIO_CSQ_IRP_CONTEXT
210 TRUE, // InsertInHead
211 pRequestCount
212 );
213
214 return Status;
215 }
216
217
218 MdIrp
GetNextRequest(__out PMdIoCsqIrpContext * pCsqContext)219 FxIrpQueue::GetNextRequest(
220 __out PMdIoCsqIrpContext* pCsqContext
221 )
222
223 /*++
224
225 Routine Description:
226
227 Returns an IRP from the queue, and if successful
228 the IRP is no longer on the CSQ (m_Queue) and
229 is not non-cancellable.
230
231 --*/
232
233 {
234 return RemoveNextIrpFromQueue(NULL, pCsqContext);
235 }
236
237
238 _Must_inspect_result_
239 NTSTATUS
GetNextRequest(__in_opt PMdIoCsqIrpContext TagContext,__in_opt MdFileObject FileObject,__out FxRequest ** ppOutRequest)240 FxIrpQueue::GetNextRequest(
241 __in_opt PMdIoCsqIrpContext TagContext,
242 __in_opt MdFileObject FileObject,
243 __out FxRequest** ppOutRequest
244 )
245
246 /*++
247
248 Routine Description:
249
250 Returns a request from the queue using an optional
251 FileObject or TagContext.
252
253 --*/
254
255 {
256 MdIrp Irp;
257 FxRequest* pRequest;
258 PMdIoCsqIrpContext pCsqContext;
259
260 if( TagContext == NULL ) {
261
262 //
263 // Returns an IRP from the queue, and if successful
264 // the IRP is no longer on the CSQ (m_Queue) and
265 // is not non-cancellable.
266 //
267 Irp = RemoveNextIrpFromQueue(
268 FileObject, // PeekContext
269 &pCsqContext
270 );
271
272 if( Irp != NULL ) {
273
274 pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
275
276 *ppOutRequest = pRequest;
277
278 return STATUS_SUCCESS;
279 }
280 else {
281 return STATUS_NO_MORE_ENTRIES;
282 }
283 }
284 else {
285
286 // Handle TagRequest Case
287 Irp = RemoveIrpFromQueueByContext(
288 TagContext
289 );
290
291 if( Irp != NULL ) {
292 pRequest = FxRequest::RetrieveFromCsqContext(TagContext);
293
294 *ppOutRequest = pRequest;
295
296 return STATUS_SUCCESS;
297 }
298 else {
299 return STATUS_NOT_FOUND;
300 }
301 }
302 }
303
304 _Must_inspect_result_
305 NTSTATUS
PeekRequest(__in_opt PMdIoCsqIrpContext TagContext,__in_opt MdFileObject FileObject,__out FxRequest ** ppOutRequest)306 FxIrpQueue::PeekRequest(
307 __in_opt PMdIoCsqIrpContext TagContext,
308 __in_opt MdFileObject FileObject,
309 __out FxRequest** ppOutRequest
310 )
311
312 /*++
313
314 Routine Description:
315
316 PeekRequest allows a caller to enumerate through requests in
317 a queue, optionally only returning requests that match a specified
318 FileObject.
319
320 The first call specifies TagContext == NULL, and the first request
321 in the queue that matches the FileObject is returned.
322
323 Subsequent requests specify the previous request value as the
324 TagContext, and searching will continue at the request that follows.
325
326 If the queue is empty, there are no requests after TagContext, or no
327 requests match the FileObject, NULL is returned.
328
329 If FileObject == NULL, this matches any FileObject in a request.
330
331 If a WDF_REQUEST_PARAMETERS structure is supplied, the information
332 from the request is returned to allow the driver to further examine
333 the request to decide whether to service it.
334
335 If a TagRequest is specified, and it is not found, the return
336 status STATUS_NOT_FOUND means that the queue should
337 be re-scanned. This is because the TagRequest was cancelled from
338 the queue, or if the queue was active, delivered to the driver.
339 There may still be un-examined requests on the queue that match
340 the drivers search criteria, but the search marker has been lost.
341
342 Re-scanning the queue starting with TagRequest == NULL and
343 continuing until STATUS_NO_MORE_ENTRIES is returned will ensure
344 all requests have been examined.
345
346 Enumerating an active queue with this API could result in the
347 driver frequently having to re-scan.
348
349 If a successful return of a Request object handle occurs, the driver
350 *must* call WdfObjectDereference when done with it.
351
352 NOTE: Synchronization Details
353
354 The driver is allowed to "peek" at requests that are still on
355 the Cancel Safe Queue without removing them. This means that
356 the peek context value used (TagRequest) could go away while
357 still holding it. This does not seem bad in itself, but the request
358 could immediately be re-used by a look aside list and be re-submitted
359 to the queue. At this point, the "tag" value means a completely different
360 request.
361
362 This race is dealt with by reference counting the FxRequest object
363 that contains our PIO_CSQ_IRP_CONTEXT, so its memory remains valid
364 after a cancel, and the driver explicitly releases it with
365 WdfObjectDereference.
366
367 But if this reference is not added under the CSQ's spinlock, there
368 could be a race in which the I/O gets cancelled and the cancel
369 callback completes the request, before we add our reference count.
370 This would then result in attempting to reference count invalid
371 memory. So to close this race, this routine returns the referenced
372 FxRequest object as its result.
373
374 Arguments:
375
376 TagRequest - If !NULL, request to begin search at
377
378 FileObject - If !NULL, FileObject to match in the request
379
380
381 Returns:
382
383 STATUS_NOT_FOUND - TagContext was specified, but not
384 found in the queue. This could be
385 because the request was cancelled,
386 or is part of an active queue and
387 the request was passed to the driver
388 or forwarded to another queue.
389
390 STATUS_NO_MORE_ENTRYS - The queue is empty, or no more requests
391 match the selection criteria of TagRequest
392 and FileObject specified above.
393
394 STATUS_SUCCESS - A request context was returned in
395 pOutRequest.
396
397 --*/
398
399 {
400 PLIST_ENTRY nextEntry;
401 FxIrp nextIrp(NULL);
402 PMdIoCsqIrpContext pCsqContext;
403 BOOLEAN FoundTag = (TagContext == NULL) ? TRUE : FALSE;
404 FxRequest* pRequest;
405
406 for( nextEntry = m_Queue.Flink; nextEntry != &this->m_Queue; nextEntry = nextEntry->Flink) {
407
408 nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
409
410 if(nextIrp.IsCanceled()) {
411 //
412 // This IRP is cancelled and the WdmCancelRoutine is about to run or waiting
413 // for us to drop the lock. So skip this one.
414 //
415 continue;
416 }
417
418 pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
419
420 if( FoundTag ) {
421
422 if( FileObject != NULL ) {
423
424 if(nextIrp.GetFileObject() == FileObject ) {
425
426 pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
427
428 //
429 // Must add the reference here under the protection
430 // of the cancel safe queues spinlock
431 //
432 pRequest->ADDREF(NULL);
433
434 *ppOutRequest = pRequest;
435
436 return STATUS_SUCCESS;
437 }
438 }
439 else {
440
441 pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
442
443 //
444 // Must add the reference here under the protection
445 // of the cancel safe queues spinlock
446 //
447 pRequest->ADDREF(NULL);
448
449 *ppOutRequest = pRequest;
450
451 return STATUS_SUCCESS;
452 }
453 }
454 else {
455
456 // If we found the tag, we want the *next* entry
457 if( pCsqContext == TagContext ) {
458 FoundTag = TRUE;
459 }
460 }
461
462 }
463
464 //
465 // If the caller supplied a tag, and it was
466 // not found, return a different code since
467 // the caller needs to re-scan the queue.
468 //
469 if( (TagContext != NULL) && !FoundTag ) {
470 return STATUS_NOT_FOUND;
471 }
472 else {
473 return STATUS_NO_MORE_ENTRIES;
474 }
475 }
476
477 MdIrp
RemoveRequest(__in PMdIoCsqIrpContext Context)478 FxIrpQueue::RemoveRequest(
479 __in PMdIoCsqIrpContext Context
480 )
481 /*++
482
483 Routine Description:
484
485 Returns a request from the queue.
486
487 --*/
488 {
489 MdIrp Irp;
490
491 ASSERT(Context != NULL);
492
493 //
494 // Returns an IRP from the queue, and if success
495 // the IRP is no longer on the CSQ (m_Queue) and
496 // is not non-cancellable.
497 //
498 Irp = RemoveIrpFromQueueByContext(Context);
499
500 return Irp;
501 }
502
503 _Must_inspect_result_
504 NTSTATUS
InsertIrpInQueue(__inout MdIrp Irp,__in_opt PMdIoCsqIrpContext Context,__in BOOLEAN InsertInHead,__out_opt ULONG * pRequestCount)505 FxIrpQueue::InsertIrpInQueue(
506 __inout MdIrp Irp,
507 __in_opt PMdIoCsqIrpContext Context,
508 __in BOOLEAN InsertInHead,
509 __out_opt ULONG* pRequestCount
510 )
511 /*++
512
513 Routine Description:
514
515 Insert the IRP in the queue. If the IRP is already cancelled
516 it removes the IRP from the queue and returns STATUS_CANCELLED.
517
518 If the IRP is cancelled and the CancelRoutine has already started
519 execution, then this function returns STATUS_SUCCESS.
520
521 Arguments:
522
523 Irp - Pointer to IRP
524
525 Context - Pointer to caller allocated CSQ context
526
527 InsertInHead - TRUE for head, FALSE for tail.
528
529 pRequestCount - Location to return new request count of queue
530 after insertion
531
532 Returns:
533
534 STATUS_SUCCESS - Operation completed.
535
536 STATUS_CANCELLED - if the request is already cancelled.
537
538 --*/
539 {
540 FxIrp irp(Irp);
541 MdCancelRoutine cancelRoutine;
542 NTSTATUS status = STATUS_SUCCESS;
543
544 //
545 // Set the association between the context and the IRP.
546 //
547
548 if (Context) {
549 irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, Context);
550 Context->Irp = Irp;
551 Context->Csq = (PIO_CSQ)this;
552
553
554
555
556
557
558
559
560
561
562 Context->Type = FX_IRP_QUEUE_ENTRY_IDENTIFIER;
563 } else {
564
565 //
566 // Currently always require context, but this will change when we
567 // allow queuing or low level IRP's without FxRequest headers allocated
568 //
569 ASSERT(FALSE);
570
571 irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, this);
572 }
573
574 // See if it's a head insertion
575 if( InsertInHead ) {
576 InsertHeadList(
577 &m_Queue,
578 irp.ListEntry()
579 );
580 }
581 else {
582 InsertTailList(
583 &m_Queue,
584 irp.ListEntry()
585 );
586 }
587
588 m_RequestCount++;
589
590 if( pRequestCount != NULL ) {
591 *pRequestCount = m_RequestCount;
592 }
593
594 irp.MarkIrpPending();
595
596 cancelRoutine = irp.SetCancelRoutine(_WdmCancelRoutineInternal);
597
598 ASSERT(!cancelRoutine);
599 UNREFERENCED_PARAMETER(cancelRoutine);
600
601 if (irp.IsCanceled()) {
602
603 cancelRoutine = irp.SetCancelRoutine(NULL);
604
605 if (cancelRoutine) {
606
607 // Remove the IRP from the list
608 RemoveIrpFromListEntry(&irp);
609
610 if (Context) {
611 Context->Irp = NULL;
612 }
613
614 irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
615
616 //
617 // Caller does not want us to recurse on their lock by invoking
618 // the m_CancelCallback on the insert path. So they will complete
619 // the IRP themselves.
620 //
621 return STATUS_CANCELLED;
622 } else {
623
624 //
625 // The cancel routine beat us to it.
626 //
627 DO_NOTHING();
628 }
629
630 }
631
632 return status;
633 }
634
635 VOID
FX_VF_METHOD(FxIrpQueue,VerifyRemoveIrpFromQueueByContext)636 FX_VF_METHOD(FxIrpQueue, VerifyRemoveIrpFromQueueByContext)(
637 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
638 __in PMdIoCsqIrpContext Context
639 )
640 /*++
641
642 Routine Description:
643
644 Makes sure that the specified Request (context) belongs to this IRP queue.
645
646 --*/
647 {
648 PAGED_CODE_LOCKED();
649
650 if (FxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
651 if (Context->Irp != NULL &&
652 (Context->Type != FX_IRP_QUEUE_ENTRY_IDENTIFIER ||
653 Context->Csq != (PIO_CSQ)this)) {
654
655 //
656 // This should never happen. Bugcheck before corrupting memory.
657 //
658 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGREQUEST,
659 "Irp 0x%p (Context 0x%p) not on IRP queue 0x%p\n",
660 Context->Irp, Context, this);
661
662 FxVerifierBugCheck(FxDriverGlobals,
663 WDF_REQUEST_FATAL_ERROR,
664 WDF_REQUEST_FATAL_ERROR_REQUEST_NOT_IN_QUEUE,
665 (ULONG_PTR) Context);
666 }
667 }
668 }
669
670 MdIrp
RemoveIrpFromQueueByContext(__in PMdIoCsqIrpContext Context)671 FxIrpQueue::RemoveIrpFromQueueByContext(
672 __in PMdIoCsqIrpContext Context
673 )
674 /*++
675
676 Routine Description:
677
678 Using the context it remove the associated IRP from the queue.
679
680 --*/
681 {
682 MdIrp irp;
683 MdCancelRoutine cancelRoutine;
684
685 if (Context->Irp ) {
686 //
687 // Make sure the Irp belongs to this queue.
688 //
689 ASSERT(Context->Csq == (PIO_CSQ)this);
690 VerifyRemoveIrpFromQueueByContext(m_LockObject->GetDriverGlobals(),
691 Context);
692
693 irp = Context->Irp;
694
695 FxIrp fxIrp(irp);
696
697 cancelRoutine = fxIrp.SetCancelRoutine(NULL);
698 if (!cancelRoutine) {
699 return NULL;
700 }
701
702 ASSERT(Context == fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY));
703
704 RemoveIrpFromListEntry(&fxIrp);
705
706 //
707 // Break the association.
708 //
709
710 Context->Irp = NULL;
711 fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
712
713 ASSERT(Context->Csq == (PIO_CSQ)this);
714
715 return irp;
716
717 } else {
718 return NULL;
719 }
720 }
721
722
723 MdIrp
PeekNextIrpFromQueue(__in_opt MdIrp Irp,__in_opt PVOID PeekContext)724 FxIrpQueue::PeekNextIrpFromQueue(
725 __in_opt MdIrp Irp,
726 __in_opt PVOID PeekContext
727 )
728 /*++
729
730 Routine Description:
731
732 This API look up IRP's in the queue.
733
734 If Irp == NULL, it returns one from the head
735 of the queue.
736
737 If Irp != NULL, it is a "peek context", and the
738 routine returns the *next* IRP in the queue.
739
740 --*/
741 {
742 PLIST_ENTRY nextEntry;
743 PLIST_ENTRY listHead;
744 FxIrp irp(Irp);
745 FxIrp nextIrp(NULL);
746
747 listHead = &m_Queue;
748
749 //
750 // If the IRP is NULL, we will start peeking from the listhead, else
751 // we will start from that IRP onwards. This is done under the
752 // assumption that new IRPs are always inserted at the tail.
753 //
754
755 if(Irp == NULL) {
756 nextEntry = listHead->Flink;
757 } else {
758 nextEntry = irp.ListEntry()->Flink;
759 }
760
761 while(nextEntry != listHead) {
762
763 nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
764
765 //
766 // If PeekContext is supplied, it's a search for an IRP associated
767 // with a particular file object.
768 //
769 if(PeekContext) {
770
771 if(nextIrp.GetFileObject() == (MdFileObject) PeekContext) {
772 break;
773 }
774 } else {
775 break;
776 }
777
778 nextIrp.SetIrp(NULL);
779
780 nextEntry = nextEntry->Flink;
781 }
782
783 return nextIrp.GetIrp();
784 }
785
786 MdIrp
RemoveNextIrpFromQueue(__in_opt PVOID PeekContext,__out_opt PMdIoCsqIrpContext * pCsqContext)787 FxIrpQueue::RemoveNextIrpFromQueue(
788 __in_opt PVOID PeekContext,
789 __out_opt PMdIoCsqIrpContext* pCsqContext
790 )
791 /*++
792
793 Routine Description:
794
795 This routine will return a pointer to the next IRP in the queue adjacent to
796 the irp passed as a parameter. If the irp is NULL, it returns the IRP at the head of
797 the queue.
798
799 --*/
800 {
801 PMdIoCsqIrpContext context;
802 MdCancelRoutine cancelRoutine;
803 FxIrp fxIrp(NULL);
804
805 fxIrp.SetIrp(PeekNextIrpFromQueue(NULL, PeekContext));
806
807 for (;;) {
808
809 if (!fxIrp.GetIrp()) {
810 return NULL;
811 }
812
813 cancelRoutine = fxIrp.SetCancelRoutine(NULL);
814 if (!cancelRoutine) {
815 fxIrp.SetIrp(PeekNextIrpFromQueue(fxIrp.GetIrp(), PeekContext));
816 continue;
817 }
818
819 RemoveIrpFromListEntry(&fxIrp); // Remove this IRP from the queue
820
821 break;
822 }
823
824 context = (PMdIoCsqIrpContext)fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
825 if (context->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
826 context->Irp = NULL;
827 ASSERT(context->Csq == (PIO_CSQ)this);
828 }
829
830 if(pCsqContext != NULL) {
831 *pCsqContext = context;
832 }
833
834 fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
835
836 return fxIrp.GetIrp();
837 }
838
839
840 VOID
_WdmCancelRoutineInternal(__inout MdDeviceObject DeviceObject,__in __drv_useCancelIRQL MdIrp Irp)841 FxIrpQueue::_WdmCancelRoutineInternal(
842 __inout MdDeviceObject DeviceObject,
843 __in __drv_useCancelIRQL MdIrp Irp
844 )
845 /*++
846
847 Routine Description:
848
849 This is the function called by WDM on the IRP when a cancel occurs
850
851 --*/
852 {
853 PMdIoCsqIrpContext irpContext;
854 FxIrpQueue* p;
855 KIRQL irql;
856 FxIrp irp(Irp);
857
858 UNREFERENCED_PARAMETER (DeviceObject);
859
860 Mx::ReleaseCancelSpinLock(irp.GetCancelIrql());
861
862 irpContext = (PMdIoCsqIrpContext)irp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
863
864 //
865 // Decide if we have a PIO_CSQ_IRP_CONTEXT or an FxIrpQueue*
866 //
867 if (irpContext->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
868 p = (FxIrpQueue*)irpContext->Csq;
869 } else {
870 ASSERT(FALSE);
871 p = (FxIrpQueue*)irpContext;
872 }
873
874 ASSERT(p);
875
876 p->LockFromCancel(&irql);
877
878 // Remove the IRP from the list
879 p->RemoveIrpFromListEntry(&irp);
880
881 //
882 // Break the association if necessary.
883 //
884
885 if (irpContext != (PMdIoCsqIrpContext)p) {
886 irpContext->Irp = NULL;
887
888 irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
889 }
890
891 //
892 // We are calling cancel-callback of the owning object with the lock
893 // held so that it can successfully deliver the canceled request to the driver
894 // if needed. If we don't hold the lock, we run into a race condition between
895 // thread that's deleting the queue and this routine that's trying to deliver
896 // a request to the queue being deleted. So the way it happens is that the dispose
897 // call of queue waits for the request count to go zero. When we remove the
898 // last Irp from the list above, we end up dropping the count to zero. This causes
899 // the delete thread to run thru and destroy the FxIoQueue object. So to avoid that
900 // after popping the request from the FxIrpQueue, we have to call into the FxIoQueue
901 // with the lock and insert the request back into the FxIoQueue list so that delete
902 // thread will wait until the request is delivered to the driver.
903 //
904 if( p->m_CancelCallback != NULL ) {
905 p->m_CancelCallback(p, Irp, irpContext, irql);
906 }
907 else {
908
909 p->UnlockFromCancel(irql);
910
911 //
912 // Dispose of the IRP ourselves
913 //
914 irp.SetStatus(STATUS_CANCELLED);
915 irp.SetInformation(0);
916
917 DoTraceLevelMessage(p->m_LockObject->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
918 "Irp 0x%p on Queue 0x%p Cancelled\n", Irp, p);
919
920 //
921 // Breakpoint for now. This usually means that someone
922 // is going to leak some driver frameworks state...
923 //
924 FxVerifierDbgBreakPoint(p->m_LockObject->GetDriverGlobals());
925
926 irp.CompleteRequest(IO_NO_INCREMENT);
927 }
928 }
929
930 BOOLEAN
IsIrpInQueue(__in PMdIoCsqIrpContext Context)931 FxIrpQueue::IsIrpInQueue(
932 __in PMdIoCsqIrpContext Context
933 )
934 /*++
935
936 Routine Description:
937 Enumerates the list to see if any of the IRPs there has
938 a context that matches the input one.
939
940 --*/
941 {
942 PLIST_ENTRY nextEntry;
943 FxIrp nextIrp(NULL);
944 PMdIoCsqIrpContext pCsqContext;
945
946 nextEntry = m_Queue.Flink;
947
948 while( nextEntry != &m_Queue ) {
949 nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
950
951 pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
952
953 if( pCsqContext == Context ) {
954 ASSERT(Context->Irp == nextIrp.GetIrp());
955 return TRUE;
956 }
957
958 nextEntry = nextEntry->Flink;
959 }
960
961 return FALSE;
962 }
963
964
965