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