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 
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 
55 FxIrpQueue::~FxIrpQueue()
56 {
57     ASSERT(IsListEmpty(&m_Queue));
58 }
59 
60 VOID
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
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
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
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
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
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
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
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
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
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
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
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
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
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