1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxIoTarget.cpp 8 9 Abstract: 10 11 This module implements the IO Target APIs 12 13 Author: 14 15 Environment: 16 17 Both kernel and user mode 18 19 Revision History: 20 21 --*/ 22 23 24 #include "../fxtargetsshared.hpp" 25 26 extern "C" { 27 #if defined(EVENT_TRACING) 28 #include "FxIoTarget.tmh" 29 #endif 30 } 31 32 const PVOID FxIoTarget::m_SentRequestTag = (PVOID) 'lcnC'; 33 34 FxIoTarget::FxIoTarget( 35 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 36 __in USHORT ObjectSize 37 ) : 38 FxNonPagedObject(FX_TYPE_IO_TARGET, ObjectSize, FxDriverGlobals) 39 { 40 Construct(); 41 } 42 43 FxIoTarget::FxIoTarget( 44 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 45 __in USHORT ObjectSize, 46 __in USHORT WdfType 47 ) : 48 FxNonPagedObject(WdfType, ObjectSize, FxDriverGlobals) 49 { 50 Construct(); 51 } 52 53 VOID 54 FxIoTarget::Construct( 55 VOID 56 ) 57 { 58 InitializeListHead(&m_SentIoListHead); 59 InitializeListHead(&m_IgnoredIoListHead); 60 61 m_InStack = TRUE; 62 63 m_State = WdfIoTargetStarted; 64 65 m_WaitingForSentIo = FALSE; 66 m_Removing = FALSE; 67 m_AddedToDeviceList = FALSE; 68 69 m_Driver = NULL; 70 m_InStackDevice = NULL; 71 m_TargetDevice = NULL; 72 m_TargetPdo = NULL; 73 m_TargetFileObject = NULL; 74 m_TargetStackSize = 0; 75 m_TargetIoType = WdfDeviceIoUndefined; 76 m_IoCount = 1; 77 m_DisposeEvent = NULL; 78 m_TransactionedEntry.SetTransactionedObject(this); 79 80 m_PendedQueue.Initialize(this, _RequestCancelled); 81 82 // 83 // We want to guarantee that the cleanup callback is called at passive level 84 // so the driver writer can override the automatic behavior the target uses 85 // when shutting down with i/o in progress. 86 // 87 MarkPassiveDispose(ObjectDoNotLock); 88 MarkDisposeOverride(ObjectDoNotLock); 89 } 90 91 FxIoTarget::~FxIoTarget() 92 { 93 ASSERT(IsListEmpty(&m_SentIoListHead)); 94 ASSERT(IsListEmpty(&m_IgnoredIoListHead)); 95 ASSERT(m_IoCount == 0); 96 } 97 98 VOID 99 FxIoTarget::PrintDisposeMessage( 100 VOID 101 ) 102 /*++ 103 104 Routine Description: 105 To prevent WPP from reporting incorrect module or line number if the 106 caller is an inline function we use this function to print the message 107 to the IFR. 108 109 Arguments: 110 None 111 112 Return Value: 113 None 114 115 --*/ 116 117 { 118 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 119 "WDFIOTARGET %p, setting Dispose event %p", 120 GetObjectHandle(), m_DisposeEvent->GetEvent()); 121 } 122 123 VOID 124 FxIoTarget::WaitForDisposeEvent( 125 VOID 126 ) 127 { 128 #if (FX_CORE_MODE==FX_CORE_USER_MODE) 129 FxCREvent * event = m_DisposeEventUm.GetSelfPointer(); 130 #else 131 FxCREvent eventOnStack; 132 eventOnStack.Initialize(); 133 FxCREvent * event = eventOnStack.GetSelfPointer(); 134 #endif 135 136 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 137 138 m_DisposeEvent = event; 139 140 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 141 "WDFIOTARGET %p, Waiting on Dispose event %p", 142 GetObjectHandle(),m_DisposeEvent->GetEvent()); 143 144 if (InterlockedDecrement(&m_IoCount) > 0) { 145 event->EnterCRAndWaitAndLeave(); 146 } 147 148 m_DisposeEvent = NULL; 149 ASSERT(m_IoCount == 0); 150 } 151 152 153 BOOLEAN 154 FxIoTarget::Dispose( 155 VOID 156 ) 157 /*++ 158 159 Routine Description: 160 Dispose is overridden so that the driver can have a chance to override the 161 default remove behavior for the target. By default, remove will cancel 162 all sent I/O and then wait for it to complete. For instance, if the driver 163 wants to wait for all i/o to complete rather then be canceled, a cleanup 164 callback should be registered on the target and the wait should be performed 165 there. 166 167 Arguments: 168 None 169 170 Return Value: 171 FALSE, indicating to the FxObject state machine *NOT* to call 172 173 --*/ 174 175 { 176 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 177 178 if (m_AddedToDeviceList) { 179 // 180 // Remove the target from the list of targets that the device keeps track 181 // of. 182 // 183 ASSERT(m_DeviceBase != NULL); 184 m_DeviceBase->RemoveIoTarget(this); 185 } 186 187 // 188 // 189 // Call all of the cleanup callbacks first 190 // 191 CallCleanup(); 192 193 // 194 // Now cancel all sent i/o and shut the target down 195 // 196 Remove(); 197 198 // 199 // By returning FALSE, the object state machine will not attempt to call 200 // cleanup again. 201 // 202 return FALSE; 203 } 204 205 VOID 206 FxIoTarget::SubmitPendedRequest( 207 __in FxRequestBase* Request 208 ) 209 { 210 ULONG action; 211 FxIrp* irp = NULL; 212 213 irp = Request->GetSubmitFxIrp(); 214 action = Submit(Request, NULL, 0); 215 if (action & SubmitSend) { 216 DoTraceLevelMessage( 217 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 218 "Sending Pended WDFREQUEST %p, Irp %p", 219 Request->GetTraceObjectHandle(), 220 irp->GetIrp()); 221 222 Send(irp->GetIrp()); 223 } 224 225 // 226 // If the request was not sent or pended (queued), complete it. 227 // 228 if ((action & (SubmitSend | SubmitQueued)) == 0) { 229 ASSERT(0 == action); 230 DoTraceLevelMessage( 231 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 232 "Completing Pended WDFREQUEST %p, Irp %p, %!STATUS!", 233 Request->GetTraceObjectHandle(), irp->GetIrp(), irp->GetStatus()); 234 235 // 236 // Fail the request. 237 // 238 ASSERT(!NT_SUCCESS(irp->GetStatus())); 239 irp->SetInformation(0); 240 241 // 242 // This function updates the outstanding 'io count' value and 243 // decrement the ref count on Request. 244 // 245 HandleFailedResubmit(Request); 246 } 247 else { 248 // 249 // Submit() updated the 'io count' counter for request that got sent or 250 // queued. Note that the input request was already tracked via this 251 // counter, thus decrement its value. 252 // 253 DecrementIoCount(); 254 255 // 256 // SubmitLocked will add another ref if it needs it. Release the 257 // reference taken when the request was pended. 258 // 259 // Release the reference after submitting it in case the request was deleted. 260 // If it was deleted, the reference taken by the target is the only keep 261 // it alive. 262 // 263 Request->RELEASE(this); 264 } 265 } 266 267 VOID 268 FxIoTarget::SubmitPendedRequests( 269 __in PLIST_ENTRY RequestListHead 270 ) 271 { 272 PLIST_ENTRY ple; 273 274 while (!IsListEmpty(RequestListHead)) { 275 ple = RemoveHeadList(RequestListHead); 276 SubmitPendedRequest(FxRequestBase::_FromListEntry(ple)); 277 } 278 } 279 280 _Must_inspect_result_ 281 NTSTATUS 282 FxIoTarget::Start( 283 VOID 284 ) 285 { 286 LIST_ENTRY head; 287 NTSTATUS status; 288 289 InitializeListHead(&head); 290 291 status = GotoStartState(&head); 292 293 SubmitPendedRequests(&head); 294 295 DoTraceLevelMessage( 296 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 297 "WDFIOTARGET %p started status %!STATUS!", 298 GetHandle(),status); 299 return status; 300 } 301 302 #define START_TAG ((PVOID) ('trtS')) 303 304 _Must_inspect_result_ 305 NTSTATUS 306 FxIoTarget::GotoStartState( 307 __in PLIST_ENTRY RequestListHead, 308 __in BOOLEAN Lock 309 ) 310 { 311 NTSTATUS status; 312 KIRQL irql; 313 314 irql = PASSIVE_LEVEL; 315 316 ADDREF(START_TAG); 317 318 if (Lock) { 319 FxIoTarget::Lock(&irql); 320 } 321 322 CheckState: 323 if (m_State == WdfIoTargetDeleted) { 324 status = STATUS_INVALID_DEVICE_STATE; 325 } 326 else if (m_WaitingForSentIo) { 327 328 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 329 330 DoTraceLevelMessage( 331 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIOTARGET, 332 "WDFIOTARGET %p is being started while it is being stopped or " 333 "purged by another thread. WdfIoTargetStart and " 334 "WdfIoTargetStop/WdfIoTargetPurge must be called synchronously. " 335 "After the driver calls one of these functions, it must not call " 336 "the other function before the first one returns.", 337 GetObjectHandle()); 338 339 if ((pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) && 340 (irql > PASSIVE_LEVEL)) { 341 342 // 343 // We cannont wait for a previous stop to complete if we are at 344 // dispatch level 345 // 346 FxVerifierDbgBreakPoint(pFxDriverGlobals); 347 } 348 349 // 350 // Wait for the stop code to complete 351 // 352 ASSERT(m_State == WdfIoTargetStopped || m_State == WdfIoTargetPurged); 353 354 ASSERT(Lock); 355 Unlock(irql); 356 357 WaitForSentIoToComplete(); 358 359 // 360 // Go back and check our state again since we dropped the lock. 361 // 362 FxIoTarget::Lock(&irql); 363 goto CheckState; 364 } 365 else { 366 status = STATUS_SUCCESS; 367 } 368 369 // 370 // Restart all of the pended i/o. 371 // 372 if (NT_SUCCESS(status)) { 373 m_State = WdfIoTargetStarted; 374 375 m_WaitingForSentIo = FALSE; 376 m_SentIoEvent.Clear(); 377 378 // 379 // TRUE - requests will be resent to the target, so attempt to claim 380 // cancelation ownership 381 // 382 DrainPendedRequestsLocked(RequestListHead, TRUE); 383 } 384 385 if (Lock) { 386 Unlock(irql); 387 } 388 389 RELEASE(START_TAG); 390 391 return status; 392 } 393 394 VOID 395 FxIoTarget::DrainPendedRequestsLocked( 396 __in PLIST_ENTRY RequestListHead, 397 __in BOOLEAN RequestWillBeResent 398 ) 399 { 400 PMdIoCsqIrpContext pContext; 401 MdIrp pIrp; 402 FxIrp* pFxIrp = NULL; 403 pContext = NULL; 404 405 while ((pIrp = m_PendedQueue.GetNextRequest(&pContext)) != NULL) { 406 FxRequestBase* pRequest; 407 BOOLEAN enqueue; 408 409 enqueue = FALSE; 410 411 // 412 // Each FxRequestBase on the pending list has a reference taken against 413 // it already. We will release the reference after we call Submit. 414 // 415 // After this call we are done with the m_CsqContext field. 416 // 417 pRequest = FxRequestBase::_FromCsqContext(pContext); 418 419 // 420 // m_ListEntry and m_CsqContext are a union. Now that we are done with 421 // m_CsqContext, initialize it to be a valid list entry. 422 // 423 InitializeListHead(&pRequest->m_ListEntry); 424 425 // 426 // Undo the affects of the IoSetNextIrpStackLocation made when we 427 // enqueued the request. We want to do this no matter if we can claim 428 // cancellation ownership of the request or not. 429 // 430 pFxIrp = pRequest->GetSubmitFxIrp(); 431 pFxIrp->SkipCurrentIrpStackLocation(); 432 433 // 434 // Request is not longer pended. 435 // 436 ASSERT(pRequest->GetTargetFlags() & FX_REQUEST_PENDED); 437 pRequest->ClearTargetFlags(FX_REQUEST_PENDED); 438 439 if (RequestWillBeResent) { 440 // 441 // Make sure timer callback is not about to run. After the call 442 // below the timer was successfully canceled or the timer callback 443 // already run. 444 // 445 if (pRequest->CancelTimer()) { 446 // 447 // Try to claim cancellation (*) ownership of the request. 448 // CanComplete() decrements the irp completion ref count and 449 // whomever decrements to zero owns the request. Ownership in 450 // this case is resubmission as well as completion (as the name 451 // of the function implies). 452 // 453 // (*) - cancellation as defined by WDF and the myriad of 454 // functions which are calling FxRequestBase::Cancel(). 455 // By this point we have already removed the cancellation 456 // routine by calling m_PendedQueue.GetNextRequest() 457 // 458 if (pRequest->CanComplete()) { 459 460 enqueue = TRUE; 461 } 462 } 463 464 if (FALSE == enqueue) { 465 // 466 // Some other thread is responsible for canceling this request. 467 // We are assuming here that the other thread will complete it 468 // in that thread because we are no longer tracking this request 469 // in any of our lists. 470 // 471 pRequest->m_Irp.SetStatus(STATUS_CANCELLED); 472 473 // 474 // Mark that the request has been completed so that if the other 475 // thread is the timer DPC, it will handle the case properly and 476 // complete the request. 477 // 478 ASSERT((pRequest->GetTargetFlags() & FX_REQUEST_COMPLETED) == 0); 479 pRequest->SetTargetFlags(FX_REQUEST_COMPLETED); 480 481 DoTraceLevelMessage( 482 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 483 "WDFIOTARGET %p, WDFREQUEST %p is being canceled on another thread," 484 " allowing other thread to complete request, not resending", 485 GetObjectHandle(), pRequest->GetTraceObjectHandle()); 486 } 487 } 488 else { 489 // 490 // The caller is going to attempt to complete the requests. To make 491 // the caller's life easier and let it reuse RequestCompletionRoutine, 492 // to handle the completion semantics, keep the completion reference 493 // count != 0 and return the request back to the caller. 494 // 495 enqueue = TRUE; 496 } 497 498 if (enqueue) { 499 ClearCompletedRequestVerifierFlags(pRequest); 500 InsertTailList(RequestListHead, &pRequest->m_ListEntry); 501 } 502 503 pContext = NULL; 504 } 505 } 506 507 VOID 508 FxIoTarget::CompletePendedRequest( 509 __in FxRequestBase* Request 510 ) 511 { 512 // 513 // This will attempt to claim cancelation ownership and call the 514 // request's completion routine. 515 // 516 FailPendedRequest(Request, STATUS_WDF_DEVICE_REMOVED_NOT_SENT); 517 } 518 519 VOID 520 FxIoTarget::CompletePendedRequestList( 521 __in PLIST_ENTRY RequestListHead 522 ) 523 { 524 PLIST_ENTRY ple; 525 526 while (!IsListEmpty(RequestListHead)) { 527 ple = RemoveHeadList(RequestListHead); 528 InitializeListHead(ple); 529 CompletePendedRequest(FxRequestBase::_FromListEntry(ple)); 530 } 531 } 532 533 VOID 534 FxIoTarget::_CancelSentRequest( 535 __in FxRequestBase* Request 536 ) 537 { 538 // 539 // Attempt to cancel the request 540 // 541 Request->Cancel(); 542 543 // 544 // Release the reference taken by GetSentRequestsListLocked 545 // 546 Request->RELEASE(m_SentRequestTag); 547 } 548 549 VOID 550 FxIoTarget::_CancelSentRequests( 551 __in PSINGLE_LIST_ENTRY RequestListHead 552 ) 553 /*++ 554 555 Routine Description: 556 Cancels all FxRequestBases in RequestListHead 557 558 Arguments: 559 RequestListHead - List head containing the requests 560 561 Return Value: 562 None. 563 564 --*/ 565 { 566 PSINGLE_LIST_ENTRY ple; 567 568 while (RequestListHead->Next != NULL) { 569 ple = PopEntryList(RequestListHead); 570 571 // 572 // Set the Next pointer back to NULL so that if it is reinserted into a 573 // cancel list, it will not point to unknown pool. 574 // 575 ple->Next = NULL; 576 577 _CancelSentRequest(FxRequestBase::_FromDrainEntry(ple)); 578 } 579 } 580 581 VOID 582 FxIoTarget::GetSentRequestsListLocked( 583 __in PSINGLE_LIST_ENTRY RequestListHead, 584 __in PLIST_ENTRY SendList, 585 __out PBOOLEAN AddedToList 586 ) 587 { 588 PLIST_ENTRY ple; 589 590 *AddedToList = IsListEmpty(SendList) ? FALSE : TRUE; 591 592 // 593 // Since we are inserting into the head of the single list head, if we walked 594 // over the list from first to last, we would reverse the entries. By walking 595 // the list backwards, we build the single list head in the order of SendList. 596 // 597 for (ple = SendList->Blink; ple != SendList; ple = ple->Blink) { 598 FxRequestBase* pRequest; 599 600 pRequest = FxRequestBase::_FromListEntry(ple); 601 602 // 603 // Add a reference since the request will be touched outside of the 604 // lock being held. 605 // 606 pRequest->ADDREF(m_SentRequestTag); 607 608 // 609 // Add the request at the head of the list. 610 // 611 pRequest->m_DrainSingleEntry.Next = RequestListHead->Next; 612 RequestListHead->Next = &pRequest->m_DrainSingleEntry; 613 } 614 } 615 616 VOID 617 FxIoTarget::GotoStopState( 618 __in WDF_IO_TARGET_SENT_IO_ACTION Action, 619 __in PSINGLE_LIST_ENTRY SentRequestListHead, 620 __out PBOOLEAN Wait, 621 __in BOOLEAN LockSelf 622 ) 623 /*++ 624 625 Routine Description: 626 Accumulates all pending I/O for cancelling out of this function if 627 RequestListHead != NULL. 628 629 Arguments: 630 631 632 Return Value: 633 634 --*/ 635 636 { 637 KIRQL irql; 638 BOOLEAN getSentList, wait, added; 639 640 getSentList = FALSE; 641 wait = FALSE; 642 irql = PASSIVE_LEVEL; 643 644 if (LockSelf) { 645 Lock(&irql); 646 } 647 648 // 649 // The following transitions are allowed: 650 // (1) Started -> Stopped 651 // (2) Purged -> Stopped 652 // (3) Stopped -> Stopped 653 // 654 // A Stopped -> Stopped transition 655 // is feasible if the previous stop left all pending i/o and the current 656 // stop wants to cancel all i/o. 657 // 658 if (m_State == WdfIoTargetStarted || m_State == WdfIoTargetPurged) { 659 m_State = WdfIoTargetStopped; 660 } 661 else if (m_State != WdfIoTargetStopped) { 662 // 663 // Stopping in any state other then stopped or started is not fatal, 664 // but should be logged. 665 // 666 667 // 668 DoTraceLevelMessage( 669 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 670 "WDFIOTARGET %p stopped, but it is currently in the " 671 "%!WDF_IO_TARGET_STATE! state, not started or stopped", 672 GetHandle(), m_State); 673 } 674 675 switch (Action) { 676 case WdfIoTargetCancelSentIo: 677 getSentList = TRUE; 678 // || || Drop through || || 679 // \/ \/ \/ \/ 680 681 case WdfIoTargetWaitForSentIoToComplete: 682 if (IsListEmpty(&m_SentIoListHead)) { 683 // 684 // By using m_WaitingForSentIo as value for wait, we can handle the 685 // case where GotoStopState is called when we are already in the 686 // stopping case (in which case we would have drained 687 // m_SendIoListHead already). 688 // 689 wait = m_WaitingForSentIo; 690 691 if (m_WaitingForSentIo) { 692 DoTraceLevelMessage( 693 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIOTARGET, 694 "WDFIOTARGET %p is already in the process of being stopped " 695 "or purged from another thread. Driver must wait for the " 696 "first WdfIoTargetStop or WdfIoTargetPurge to complete " 697 "before calling it again.", 698 GetObjectHandle()); 699 } 700 } 701 else { 702 wait = TRUE; 703 704 if (getSentList) { 705 // 706 // Ignore the assignment to added since we have already computed 707 // if we need to wait or not. 708 // 709 GetSentRequestsListLocked(SentRequestListHead, 710 &m_SentIoListHead, 711 &added); 712 } 713 } 714 715 break; 716 717 case WdfIoTargetLeaveSentIoPending: 718 wait = FALSE; 719 break; 720 } 721 722 m_WaitingForSentIo = wait; 723 *Wait = wait; 724 725 if (wait) { 726 // 727 // If Stop(leave sent io pending) was previously called, m_SentIoEvent 728 // will be in the signalled state. We need to wait for sent i/o to be 729 // completed, so make sure it is not signalled while holding the lock 730 // 731 m_SentIoEvent.Clear(); 732 } 733 else { 734 // 735 // Even though *Wait is set to FALSE, the caller may wait anyways 736 // if it is aggregating targets to move into the stopped state and 737 // wait on them all. 738 // 739 m_SentIoEvent.Set(); 740 } 741 742 if (LockSelf) { 743 Unlock(irql); 744 } 745 } 746 747 VOID 748 FxIoTarget::Stop( 749 __in WDF_IO_TARGET_SENT_IO_ACTION Action 750 ) 751 { 752 SINGLE_LIST_ENTRY head; 753 BOOLEAN wait; 754 755 head.Next = NULL; 756 wait = FALSE; 757 758 GotoStopState(Action, &head, &wait, TRUE); 759 760 if (head.Next != NULL) { 761 _CancelSentRequests(&head); 762 } 763 764 if (wait) { 765 KIRQL irql; 766 767 // 768 // Under the lock, if wait is set, m_WaitingForSentIo is true, but once 769 // we drop the lock, all pended i/o could have already been canceled 770 // and m_WaitingForSentIo is FALSE at this point. 771 // 772 // ASSERT(m_WaitingForSentIo); 773 774 WaitForSentIoToComplete(); 775 776 // 777 // Mark that we are no longer stopping and waiting for i/o to complete. 778 // 779 Lock(&irql); 780 m_WaitingForSentIo = FALSE; 781 Unlock(irql); 782 } 783 784 DoTraceLevelMessage( 785 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 786 "WDFIOTARGET %p stopped ",GetHandle()); 787 } 788 789 VOID 790 FxIoTarget::GotoPurgeState( 791 __in WDF_IO_TARGET_PURGE_IO_ACTION Action, 792 __in PLIST_ENTRY PendedRequestListHead, 793 __in PSINGLE_LIST_ENTRY SentRequestListHead, 794 __out PBOOLEAN Wait, 795 __in BOOLEAN LockSelf 796 ) 797 /*++ 798 799 Routine Description: 800 Accumulates all pending and sent I/O. The I/O target is moved into 801 the 'purged' state. 802 803 Arguments: 804 805 806 Return Value: 807 808 --*/ 809 810 { 811 KIRQL irql; 812 BOOLEAN wait, added; 813 814 wait = FALSE; 815 irql = PASSIVE_LEVEL; 816 817 if (LockSelf) { 818 Lock(&irql); 819 } 820 821 // 822 // The following transitions are allowed: 823 // (1) Started -> Purged 824 // (2) Stop -> Purged 825 // (3) Purged -> Purged 826 // 827 // A Purged -> Purged transition is feasible if the previous purge didn't 828 // wait for pending i/o to complete and the current purge wants to wait 829 // for them. 830 // 831 if (m_State == WdfIoTargetStarted || m_State == WdfIoTargetStopped) { 832 m_State = WdfIoTargetPurged; 833 } 834 else if (m_State != WdfIoTargetPurged) { 835 // 836 // Purging in any state other then purged or started is not fatal, 837 // but should be logged. 838 // 839 840 // 841 DoTraceLevelMessage( 842 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 843 "WDFIOTARGET %p purged, but it is currently in the " 844 "%!WDF_IO_TARGET_STATE! state, not started, stopped or purged", 845 GetHandle(), m_State); 846 } 847 848 // 849 // FALSE - requests will not be resent to the target. As such, 850 // cancellation ownership will not be claimed b/c the request 851 // will subsequently be passed to FailPendedRequest. 852 // 853 DrainPendedRequestsLocked(PendedRequestListHead, FALSE); 854 855 GetSentRequestsListLocked(SentRequestListHead, 856 &m_SentIoListHead, 857 &added); 858 859 switch (Action) { 860 case WdfIoTargetPurgeIoAndWait: 861 if (added == FALSE) { 862 // 863 // By using m_WaitingForSentIo as value for wait, we can handle the 864 // case where GotoPurgeState is called when we are already in the 865 // purging case (in which case we would have drained 866 // m_SendIoListHead already). 867 // 868 wait = m_WaitingForSentIo; 869 870 if (m_WaitingForSentIo) { 871 DoTraceLevelMessage( 872 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 873 "WDFIOTARGET %p is already in the process of being purged " 874 "or stopped from another thread. Driver must wait for the " 875 "first WdfIoTargetPurge or WdfIoTargetStop to complete " 876 "before calling it again.", 877 GetObjectHandle()); 878 879 FxVerifierDbgBreakPoint(GetDriverGlobals()); 880 } 881 } 882 else { 883 wait = TRUE; 884 } 885 break; 886 887 case WdfIoTargetPurgeIo: 888 wait = FALSE; 889 break; 890 } 891 892 m_WaitingForSentIo = wait; 893 *Wait = wait; 894 895 if (wait) { 896 // 897 // If Stop/Purge(don't wait) was previously called, m_SentIoEvent 898 // will be in the signalled state. We need to wait for sent i/o to be 899 // completed, so make sure it is not signalled while holding the lock 900 // 901 m_SentIoEvent.Clear(); 902 } 903 else { 904 // 905 // Even though *Wait is set to FALSE, the caller may wait anyways 906 // if it is aggregating targets to move into the purged state and 907 // wait on them all. 908 // 909 m_SentIoEvent.Set(); 910 } 911 912 if (LockSelf) { 913 Unlock(irql); 914 } 915 } 916 917 VOID 918 FxIoTarget::Purge( 919 __in WDF_IO_TARGET_PURGE_IO_ACTION Action 920 ) 921 { 922 LIST_ENTRY pendedHead; 923 SINGLE_LIST_ENTRY sentHead; 924 BOOLEAN wait; 925 926 InitializeListHead(&pendedHead); 927 sentHead.Next = NULL; 928 wait = FALSE; 929 930 GotoPurgeState(Action, &pendedHead, &sentHead, &wait, TRUE); 931 932 // 933 // Complete any requests we might have pulled off of our lists 934 // 935 CompletePendedRequestList(&pendedHead); 936 _CancelSentRequests(&sentHead); 937 938 if (wait) { 939 KIRQL irql; 940 941 // 942 // Under the lock, if wait is set, m_WaitingForSentIo is true, but once 943 // we drop the lock, all pended i/o could have already been canceled 944 // and m_WaitingForSentIo is FALSE at this point. 945 // 946 // ASSERT(m_WaitingForSentIo); 947 948 WaitForSentIoToComplete(); 949 950 // 951 // Mark that we are no longer purging and waiting for i/o to complete. 952 // 953 Lock(&irql); 954 m_WaitingForSentIo = FALSE; 955 Unlock(irql); 956 } 957 958 DoTraceLevelMessage( 959 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 960 "WDFIOTARGET %p purged ",GetHandle()); 961 } 962 963 VOID 964 FxIoTarget::GotoRemoveState( 965 __in WDF_IO_TARGET_STATE NewState, 966 __in PLIST_ENTRY PendedRequestListHead, 967 __in PSINGLE_LIST_ENTRY SentRequestListHead, 968 __in BOOLEAN Lock, 969 __out PBOOLEAN Wait 970 ) 971 { 972 KIRQL irql; 973 BOOLEAN sentAdded, ignoredAdded; 974 975 irql = PASSIVE_LEVEL; 976 977 if (Lock) { 978 this->Lock(&irql); 979 } 980 981 if (m_WaitingForSentIo) { 982 DoTraceLevelMessage( 983 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIOTARGET, 984 "WDFIOTARGET %p is being deleted while it is being stopped/purged " 985 "by another thread. Driver must wait for WdfIoTargetStop or " 986 "WdfIoTargetPurge to complete before deleting the object.", 987 GetObjectHandle()); 988 989 ASSERT(Lock); 990 991 Unlock(irql); 992 WaitForSentIoToComplete(); 993 this->Lock(&irql); 994 995 ASSERT(m_WaitingForSentIo == FALSE); 996 } 997 998 *Wait = FALSE; 999 m_State = NewState; 1000 1001 // 1002 // FALSE - requests will not be resent to the target. As such, 1003 // cancellation ownership will not be claimed b/c the request 1004 // will subsequently be passed to FailPendedRequest. 1005 // 1006 DrainPendedRequestsLocked(PendedRequestListHead, FALSE); 1007 1008 // 1009 // Now cancel all of the sent I/O since we are being removed for good. 1010 // 1011 if (NewState == WdfIoTargetDeleted || NewState == WdfIoTargetClosed || 1012 NewState == WdfIoTargetClosedForQueryRemove) { 1013 // 1014 // If the caller is aggregating calls to GotoRemoveState among many 1015 // targets (for instance, WDFUSBDEVICE will do this over its WDFUSBPIPEs 1016 // on remove), we cannot use the state of SentRequestListHead after these 1017 // two calls as a test to see if any requests are being canceled (and the 1018 // diff between removing and removed). Instead, we rely on the added 1019 // state returned by each call. 1020 // 1021 GetSentRequestsListLocked(SentRequestListHead, 1022 &m_SentIoListHead, 1023 &sentAdded); 1024 GetSentRequestsListLocked(SentRequestListHead, 1025 &m_IgnoredIoListHead, 1026 &ignoredAdded); 1027 1028 if (sentAdded || ignoredAdded) { 1029 // 1030 // We will have to wait for the i/o to come back. As such, we are 1031 // in the transition state and must wait for it to complete. In this 1032 // transitionary stage, I/O can still be sent if they are marked to 1033 // ignore the target state. 1034 // 1035 m_Removing = TRUE; 1036 *Wait = TRUE; 1037 1038 // 1039 // If Stop(leave sent io pending) or Purge(do-not-wait) was 1040 // previously called, m_SentIoEvent will be in the signalled state. 1041 // We need to wait for sent i/o to be completed, so make sure it 1042 // is not signalled while hodling the lock 1043 // 1044 m_SentIoEvent.Clear(); 1045 } 1046 else { 1047 ClearTargetPointers(); 1048 1049 // 1050 // Even though *Wait is set to FALSE, the caller may wait anyways 1051 // if it is aggregating targets to move into the remove state and 1052 // wait on all of them. 1053 // 1054 m_SentIoEvent.Set(); 1055 } 1056 } 1057 1058 if (Lock) { 1059 Unlock(irql); 1060 } 1061 1062 return; 1063 } 1064 1065 VOID 1066 FxIoTarget::Remove( 1067 VOID 1068 ) 1069 { 1070 SINGLE_LIST_ENTRY sentHead; 1071 LIST_ENTRY pendedHead; 1072 BOOLEAN wait; 1073 1074 sentHead.Next = NULL; 1075 InitializeListHead(&pendedHead); 1076 1077 GotoRemoveState(WdfIoTargetDeleted, 1078 &pendedHead, 1079 &sentHead, 1080 TRUE, 1081 &wait); 1082 1083 // 1084 // Complete any requests we might have pulled off of our lists 1085 // 1086 CompletePendedRequestList(&pendedHead); 1087 _CancelSentRequests(&sentHead); 1088 1089 if (wait) { 1090 ASSERT(m_State == WdfIoTargetDeleted); 1091 1092 WaitForSentIoToComplete(); 1093 } 1094 1095 WaitForDisposeEvent(); 1096 1097 return; 1098 } 1099 1100 _Must_inspect_result_ 1101 NTSTATUS 1102 FxIoTarget::QueryInterface( 1103 __inout FxQueryInterfaceParams* Params 1104 ) 1105 { 1106 switch (Params->Type) { 1107 case FX_TYPE_IO_TARGET: 1108 *Params->Object = (FxIoTarget*) this; 1109 break; 1110 default: 1111 return FxNonPagedObject::QueryInterface(Params); // __super call 1112 } 1113 1114 return STATUS_SUCCESS; 1115 } 1116 1117 _Must_inspect_result_ 1118 NTSTATUS 1119 FxIoTarget::Init( 1120 __in CfxDeviceBase* Device 1121 ) 1122 { 1123 NTSTATUS status; 1124 1125 status = InitModeSpecific(Device); 1126 if (!NT_SUCCESS(status)) { 1127 return status; 1128 } 1129 1130 SetDeviceBase(Device); 1131 MxDeviceObject deviceObject; 1132 1133 m_InStackDevice = Device->GetDeviceObject(); 1134 1135 // 1136 // Note that AttachedDevice can be NULL for UMDF for 1137 // example when there is only one device in the stack. 1138 // 1139 m_TargetDevice = GetTargetDeviceObject(Device); 1140 m_TargetPdo = Device->GetPhysicalDevice(); 1141 1142 m_Driver = Device->GetDriver(); 1143 1144 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 1145 if (m_InStackDevice == NULL || m_Driver == NULL || 1146 m_TargetDevice == NULL || m_TargetPdo == NULL) { 1147 DoTraceLevelMessage( 1148 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1149 "Init WDFIOTARGET %p, unexpected NULL, m_InStackDevice %p, " 1150 "m_TargetDevice %p, m_TargetPdo %p, m_Driver %p", 1151 GetObjectHandle(), m_InStackDevice, m_TargetDevice, m_TargetPdo, 1152 m_Driver); 1153 1154 return STATUS_UNSUCCESSFUL; 1155 } 1156 #else 1157 if (m_InStackDevice == NULL || m_Driver == NULL) { 1158 DoTraceLevelMessage( 1159 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1160 "Init WDFIOTARGET %p, unexpected NULL, m_InStackDevice %p, " 1161 "m_Driver %p", 1162 GetObjectHandle(), m_InStackDevice, m_Driver); 1163 1164 return STATUS_UNSUCCESSFUL; 1165 } 1166 #endif 1167 1168 // 1169 // For UMDF the target device can be NULL if there is only one driver in the 1170 // stack. In that case m_TargetStackSize retains its initial value (0). 1171 // 1172 if (m_TargetDevice != NULL) { 1173 deviceObject.SetObject(m_TargetDevice); 1174 1175 m_TargetStackSize = deviceObject.GetStackSize(); 1176 1177 m_TargetIoType = GetTargetIoType(); 1178 } 1179 1180 return STATUS_SUCCESS; 1181 } 1182 1183 _Must_inspect_result_ 1184 NTSTATUS 1185 FX_VF_METHOD(FxIoTarget, VerifySubmitLocked) ( 1186 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals, 1187 _In_ FxRequestBase* Request 1188 ) 1189 { 1190 NTSTATUS status = STATUS_SUCCESS; 1191 KIRQL irql; 1192 SHORT flags; 1193 FxIrp* irp; 1194 1195 PAGED_CODE_LOCKED(); 1196 1197 irp = Request->GetSubmitFxIrp(); 1198 Request->Lock(&irql); 1199 flags = Request->GetVerifierFlagsLocked(); 1200 1201 if ((flags & FXREQUEST_FLAG_FORMATTED) == 0x0) { 1202 status = STATUS_REQUEST_NOT_ACCEPTED; 1203 1204 DoTraceLevelMessage( 1205 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1206 "WDFREQUEST %p has not been formatted, cannot send, %!STATUS!", 1207 Request->GetTraceObjectHandle(), status); 1208 } 1209 else if (flags & FXREQUEST_FLAG_SENT_TO_TARGET) { 1210 // 1211 // Technically this is the same check as m_IrpCompletionReferenceCount 1212 // above, but we make this check in many more locations. 1213 // 1214 DoTraceLevelMessage( 1215 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1216 "WDFREQUEST %p is already pending on a WDFIOTARGET", 1217 Request->GetTraceObjectHandle()); 1218 1219 FxVerifierBugCheck(FxDriverGlobals, 1220 WDF_REQUEST_FATAL_ERROR, 1221 WDF_REQUEST_FATAL_ERROR_REQUEST_ALREADY_SENT, 1222 (ULONG_PTR) Request->GetHandle()); 1223 } 1224 else if (HasEnoughStackLocations(irp) == FALSE) { 1225 status = STATUS_REQUEST_NOT_ACCEPTED; 1226 1227 // 1228 // For reasons why we subtract 1 from CurrentLocation, see comments 1229 // in FxIoTarget::HasEnoughStackLocations. 1230 // 1231 DoTraceLevelMessage( 1232 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1233 "WDFREQUEST %p, PIRP %p does not have enough stack locations %d" 1234 " for this WDFIOTARGET %p (requires %d locations), %!STATUS!", 1235 Request->GetTraceObjectHandle(), irp->GetIrp(), irp->GetCurrentIrpStackLocationIndex() - 1, 1236 GetHandle(), m_TargetStackSize, status); 1237 } 1238 1239 Request->Unlock(irql); 1240 return status; 1241 } 1242 1243 ULONG 1244 FxIoTarget::SubmitLocked( 1245 __in FxRequestBase* Request, 1246 __in_opt PWDF_REQUEST_SEND_OPTIONS Options, 1247 __in ULONG Flags 1248 ) 1249 /*++ 1250 1251 Routine Description: 1252 Core processing logic for submitting a request. Will return the send flag 1253 if the request can be submitted immediately. If the flag is not returned, 1254 the pended flag may be set. If neither are set, status in the Request will 1255 be set with the error. 1256 1257 NTSTATUS status; 1258 ULONG action; 1259 1260 Lock(); 1261 action |= SubmitLocked(...); 1262 UnLock(); 1263 1264 if (action & Send) { 1265 // IoCallDriver .... 1266 } 1267 else if (action & Pended) { 1268 // request was pended 1269 } 1270 1271 return ...; 1272 1273 Arguments: 1274 Request - The request that will be submitted to the target 1275 1276 Options - send options associated with the request being sent 1277 1278 Flags - Additional flags to control how the request is being sent 1279 1280 Return Value: 1281 A bit field whose flags are defined by SubmitActionFlags 1282 1283 --*/ 1284 { 1285 PFX_DRIVER_GLOBALS pFxDriverGlobals; 1286 NTSTATUS status; 1287 ULONG action; 1288 BOOLEAN startTimer, stateIgnored, verify; 1289 BOOLEAN addedRef; 1290 FxIrp* irp; 1291 1292 pFxDriverGlobals = GetDriverGlobals(); 1293 1294 action = 0; 1295 startTimer = FALSE; 1296 stateIgnored = FALSE; 1297 addedRef = FALSE; 1298 1299 // 1300 // If the reference count is not zero, the irp has not completed and the 1301 // driver is reusing it before it has returned to the driver. Not good! 1302 // 1303 ASSERT(Request->m_IrpCompletionReferenceCount == 0); 1304 if (Request->m_IrpCompletionReferenceCount != 0) { 1305 DoTraceLevelMessage( 1306 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1307 "WDFREQUEST %p already sent to a target", 1308 Request->GetTraceObjectHandle()); 1309 1310 // 1311 // Last ditch assert 1312 // 1313 ASSERT((Request->GetTargetFlags() & FX_REQUEST_PENDED) == 0); 1314 1315 // no return 1316 FxVerifierBugCheck(pFxDriverGlobals, 1317 WDF_REQUEST_FATAL_ERROR, 1318 WDF_REQUEST_FATAL_ERROR_REQUEST_ALREADY_SENT, 1319 (ULONG_PTR) Request->GetHandle()); 1320 } 1321 1322 irp = Request->GetSubmitFxIrp(); 1323 1324 if (pFxDriverGlobals->FxVerifierOn && 1325 pFxDriverGlobals->FxVerifierIO) { 1326 1327 verify = TRUE; 1328 status = VerifySubmitLocked(pFxDriverGlobals, Request); 1329 if (!NT_SUCCESS(status)){ 1330 goto Done; 1331 } 1332 } 1333 else { 1334 verify = FALSE; 1335 } 1336 1337 // 1338 // if WDF_REQUEST_SEND_OPTION_TIMEOUT is set, Options != NULL 1339 // 1340 if ((Flags & WDF_REQUEST_SEND_OPTION_TIMEOUT) && Options->Timeout != 0) { 1341 // 1342 // Create the timer under the lock 1343 // 1344 status = Request->CreateTimer(); 1345 1346 if (!NT_SUCCESS(status)) { 1347 DoTraceLevelMessage( 1348 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1349 "WDFREQUEST %p, could not create timer, %!STATUS!", 1350 Request->GetTraceObjectHandle(), status); 1351 1352 goto Done; 1353 } 1354 1355 startTimer = TRUE; 1356 } 1357 1358 if (Flags & WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE) { 1359 // 1360 // If we are in the deleted or closed state, we must be in the transitioning 1361 // state to allow I/O at this time. For any other state, always allow 1362 // the i/o to go through. 1363 // 1364 if ((m_State == WdfIoTargetDeleted || 1365 m_State == WdfIoTargetClosed || 1366 m_State == WdfIoTargetClosedForQueryRemove) && 1367 m_Removing == FALSE) { 1368 // 1369 // The target is truly closed or removed, it is not in the 1370 // transitionary state. We don't allow I/O anymore. 1371 // 1372 status = STATUS_INVALID_DEVICE_STATE; 1373 1374 DoTraceLevelMessage( 1375 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 1376 "WDFIOTARGET %p state %!WDF_IO_TARGET_STATE!, sending " 1377 "WDFREQUEST %p cannot ignore current state, %!STATUS!", 1378 GetObjectHandle(), m_State, Request->GetTraceObjectHandle(), 1379 status); 1380 1381 goto Done; 1382 } 1383 else { 1384 action |= SubmitSend; 1385 1386 DoTraceLevelMessage( 1387 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 1388 "ignoring WDFIOTARGET %p state, sending WDFREQUEST %p, state " 1389 "%!WDF_IO_TARGET_STATE!", 1390 GetObjectHandle(), Request->GetTraceObjectHandle(), m_State); 1391 1392 Request->SetTargetFlags(FX_REQUEST_IGNORE_STATE); 1393 1394 status = STATUS_SUCCESS; 1395 stateIgnored = TRUE; 1396 } 1397 } 1398 else { 1399 switch (m_State) { 1400 case WdfIoTargetStarted: 1401 status = STATUS_SUCCESS; 1402 action |= SubmitSend; 1403 break; 1404 1405 case WdfIoTargetStopped: 1406 if (Flags & WDF_REQUEST_SEND_INTERNAL_OPTION_FAIL_ON_PEND) { 1407 status = STATUS_INVALID_DEVICE_STATE; 1408 goto Done; 1409 } 1410 else { 1411 status = STATUS_WDF_QUEUED; 1412 action |= SubmitQueued; 1413 } 1414 break; 1415 1416 case WdfIoTargetClosedForQueryRemove: 1417 case WdfIoTargetClosed: 1418 case WdfIoTargetDeleted: 1419 case WdfIoTargetPurged: 1420 default: 1421 DoTraceLevelMessage( 1422 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1423 "failing WDFREQUEST %p, WDFIOTARGET %p not accepting requests, " 1424 "state %!WDF_IO_TARGET_STATE!", Request->GetTraceObjectHandle(), 1425 GetObjectHandle(), m_State); 1426 1427 status = STATUS_INVALID_DEVICE_STATE; 1428 goto Done; 1429 } 1430 } 1431 1432 // 1433 // Make sure the list entry is initialized so if we call RemoveEntryList 1434 // later, we don't corrupt whatever Flink and Blink point to. 1435 // 1436 InitializeListHead(&Request->m_ListEntry); 1437 1438 ASSERT(((action & SubmitSend) || (action & SubmitQueued)) && NT_SUCCESS(status)); 1439 1440 Done: 1441 if (NT_SUCCESS(status)) { 1442 // 1443 // Request should not be pended 1444 // 1445 ASSERT((Request->GetTargetFlags() & FX_REQUEST_PENDED) == 0); 1446 1447 // 1448 // Set m_Target before setting the reference count to one because as soon 1449 // as it is set to 1, it can be decremented to zero and then it will 1450 // assume that m_Target is valid. 1451 // 1452 Request->SetTarget(this); 1453 1454 // 1455 // Assume we are successful, we will adjust this value in case of error. 1456 // 1457 IncrementIoCount(); 1458 1459 // 1460 // All paths which are pulling the request off of the list must use this 1461 // specific tag. This would include the cancel, timer, and completion 1462 // routines. 1463 // 1464 // We don't add a reference in the error case because the caller 1465 // will just complete the request back to the caller (for a queue 1466 // presented request) or free it (for a driver created request). 1467 // 1468 // This released at the end of FxRequestBase::CompleteSubmitted 1469 // 1470 Request->ADDREF(this); 1471 1472 // 1473 // In case of error, we use this flag to know if the IoCount and 1474 // RequestRef need to be rolled back. 1475 // 1476 addedRef = TRUE; 1477 1478 // 1479 // Set the reference count to one. This reference count guards prevents 1480 // Request::Cancel from touching an invalid PIRP outside of any lock. 1481 // 1482 Request->m_IrpCompletionReferenceCount = 1; 1483 1484 if (Request->m_Canceled) { 1485 // 1486 // CanComplete() decrements the count that was set above. If the 1487 // count goes to zero, CanComplete() returns TRUE and 1488 // FxRequestBase::Cancel will not touch the irp. If it returns 1489 // FALSE, we indicate that the request was sent, in actuality we 1490 // don't send the request b/c FxRequestBase::Cancel will call 1491 // CompleteCanceledRequest, where the irp will complete. 1492 // 1493 if (Request->CanComplete()) { 1494 // 1495 // This thread owns the irp. Set the status to !NT_SUCCESS and 1496 // clear any actions we indicate to the caller. 1497 // 1498 action = 0; 1499 } 1500 else { 1501 // 1502 // There is still an reference count on the completion count, 1503 // let the other thread complete it. 1504 // 1505 1506 // 1507 // Make the caller think that the request was queued. By doing 1508 // this, it will not attempt to call IoCallDriver. SubmitSend 1509 // will be cleared after jump to Done: and evaluate status. 1510 // 1511 action |= SubmitQueued; 1512 } 1513 1514 // 1515 // Either way, we want to set STATUS_CANCELLED in the PIRP when we 1516 // are done. 1517 // 1518 status = STATUS_CANCELLED; 1519 1520 // 1521 // Just jump to the end and avoid any more compares. 1522 // 1523 goto CheckError; 1524 } 1525 1526 if (action & SubmitSend) { 1527 if (stateIgnored) { 1528 InsertTailList(&m_IgnoredIoListHead, &Request->m_ListEntry); 1529 } 1530 else { 1531 // 1532 // Keep track of the request so that we can cancel it later if needed 1533 // 1534 InsertTailList(&m_SentIoListHead, &Request->m_ListEntry); 1535 } 1536 1537 // 1538 // We know we are going to send the request, set the completion 1539 // routine now. Since IoSetCompletionRoutineEx allocates memory 1540 // which is only freed when the completion routine is called when 1541 // the request is completing, we can only set the CR when we *KNOW* 1542 // the request will be sent, ie SubmitSend is set and returned to 1543 // the caller. 1544 // 1545 SetCompletionRoutine(Request); 1546 1547 // 1548 // NOTE: No need to reference the file object before we drop the lock 1549 // because will not deref the file object while there is outstanding 1550 // I/O. 1551 // 1552 } 1553 else { 1554 status = PendRequestLocked(Request); 1555 DoTraceLevelMessage( 1556 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 1557 "Pending WDFREQUEST %p, WDFIOTARGET %p is paused, %!STATUS!", 1558 Request->GetTraceObjectHandle(), GetObjectHandle(), status); 1559 1560 if (!NT_SUCCESS(status)) { 1561 // 1562 // CanComplete() decrements the count that was set above. If the 1563 // count goes to zero, CanComplete() returns TRUE and 1564 // FxRequestBase::Cancel will not touch the irp. If it returns 1565 // FALSE, we indicate that the request was sent, in actuality we 1566 // don't send the request b/c FxRequestBase::Cancel will call 1567 // CompleteCanceledRequest, where the irp will complete. 1568 // 1569 if (Request->CanComplete()) { 1570 // 1571 // This thread owns the irp. 1572 // Clear any actions we indicate to the caller. 1573 // 1574 action = 0; 1575 } 1576 else { 1577 // 1578 // The cancel/timer routine (whoever has ownership of 1579 // request) will complete the request. 1580 // 1581 ASSERT(action & SubmitQueued); 1582 DO_NOTHING(); 1583 } 1584 } 1585 } 1586 1587 if (NT_SUCCESS(status)) { 1588 // 1589 // Delay starting the timer to the last possible moment where we know 1590 // there will be no error and we don't have to deal with any cancel 1591 // logic in the error case. 1592 // 1593 if (startTimer) { 1594 ASSERT(action & (SubmitSend | SubmitQueued)); 1595 1596 // 1597 // Set the timer under the lock 1598 // 1599 DoTraceLevelMessage( 1600 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1601 "Starting timer on WDFREQUEST %p", 1602 Request->GetTraceObjectHandle()); 1603 1604 Request->StartTimer(Options->Timeout); 1605 } 1606 } 1607 } 1608 1609 CheckError: 1610 // 1611 // Not an else clause to the if (NT_SUCCESS(status)) above b/c status can 1612 // be changed within the NT_SUCCESS(status) clause. 1613 // 1614 if (!NT_SUCCESS(status)) { 1615 irp->SetStatus(status); 1616 action &= ~SubmitSend; 1617 } 1618 else if (verify) { 1619 Request->SetVerifierFlags(FXREQUEST_FLAG_SENT_TO_TARGET); 1620 } 1621 1622 // 1623 // Keep the IoCount and Request->AddRef() only if the request is going 1624 // to be sent, or it is queued, or another thread took ownership of its 1625 // cancellation. 1626 // 1627 if (addedRef && (action & (SubmitSend | SubmitQueued)) == 0) { 1628 Request->RELEASE(this); 1629 DecrementIoCount(); 1630 } 1631 1632 return action; 1633 } 1634 1635 ULONG 1636 FxIoTarget::Submit( 1637 __in FxRequestBase* Request, 1638 __in_opt PWDF_REQUEST_SEND_OPTIONS Options, 1639 __in_opt ULONG Flags 1640 ) 1641 { 1642 ULONG result; 1643 KIRQL irql; 1644 1645 Lock(&irql); 1646 result = SubmitLocked(Request, Options, Flags); 1647 Unlock(irql); 1648 1649 return result; 1650 } 1651 1652 _Must_inspect_result_ 1653 NTSTATUS 1654 FxIoTarget::SubmitSync( 1655 __in FxRequestBase* Request, 1656 __in_opt PWDF_REQUEST_SEND_OPTIONS Options, 1657 __out_opt PULONG Action 1658 ) 1659 { 1660 FxTargetSubmitSyncParams params = {0}; 1661 LONGLONG timeout; 1662 ULONG action; 1663 NTSTATUS status; 1664 KIRQL irql; 1665 BOOLEAN clearContext; 1666 1667 status = STATUS_SUCCESS; 1668 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1669 "WDFIOTARGET %p, WDFREQUEST %p", 1670 GetObjectHandle(), Request->GetTraceObjectHandle()); 1671 1672 #if (FX_CORE_MODE == FX_CORE_USER_MODE) 1673 // 1674 // FxCREvent events needs to be initiliazed in UMDF, and failure handled 1675 // gracefully. For KMDF, it will result in double initialization which is 1676 // not a problem. Note that for KMDF, FxCREvent->Initialize will never fail. 1677 // 1678 status = params.SynchEvent.Initialize(); 1679 if (!NT_SUCCESS(status)) { 1680 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 1681 "Failed to initialize sync event for " 1682 "WDFIOTARGET %p, WDFREQUEST %p", 1683 GetObjectHandle(), Request->GetTraceObjectHandle()); 1684 if (Action != NULL) { 1685 *Action = 0; 1686 } 1687 return status; 1688 } 1689 #endif 1690 1691 clearContext = Request->ShouldClearContext(); 1692 1693 if (Action != NULL) { 1694 action = *Action; 1695 } 1696 else { 1697 action = 0; 1698 } 1699 1700 if (Options != NULL && 1701 (Options->Flags & WDF_REQUEST_SEND_OPTION_TIMEOUT) && 1702 Options->Timeout != 0) { 1703 // 1704 // If this flag is set, SubmitLocked will start a timer, which we don't 1705 // want because we will timeout the I/O using KeWaitForSingleObject. 1706 // 1707 // params.Constraints &= ~WDF_REQUEST_SEND_OPTION_TIMEOUT; 1708 timeout = Options->Timeout; 1709 action |= SubmitTimeout; 1710 } 1711 1712 // 1713 // Must set the completion routine before calling Submit() so that in the 1714 // pended or sent case, the completion routine is set in place during 1715 // cancelation or delayed completion. 1716 // 1717 if (action & SubmitSyncCallCompletion) { 1718 params.OrigTargetCompletionContext = Request->m_TargetCompletionContext; 1719 params.OrigTargetCompletionRoutine = 1720 Request->m_CompletionRoutine.m_Completion; 1721 } 1722 else { 1723 params.OrigTargetCompletionContext = NULL; 1724 params.OrigTargetCompletionRoutine = NULL; 1725 } 1726 1727 Request->SetCompletionRoutine(_SyncCompletionRoutine, ¶ms); 1728 1729 // 1730 // SubmitLocked will return whether the request should be sent *right now*. 1731 // If SubmitSend is clear, SubmitQueued must be checked. If set, then 1732 // the request was queued, otherwise, the request has failed and the 1733 // status was already set in the irp. 1734 // 1735 // Clear the WDF_REQUEST_SEND_OPTION_TIMEOUT flag so that SumbitLocked doesn't 1736 // try to allocate a timer 1737 // 1738 action |= Submit( 1739 Request, 1740 Options, 1741 (Options != NULL) ? (Options->Flags & ~WDF_REQUEST_SEND_OPTION_TIMEOUT) : 0); 1742 1743 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1744 "WDFREQUEST %p, Action 0x%x", Request->GetTraceObjectHandle(), 1745 action); 1746 1747 // 1748 // Add reference so that if we call Request->Cancel(), Request is still 1749 // a valid object in between the wait timeout and the cancel call if 1750 // request completes before Cancel is called. 1751 // 1752 Request->ADDREF(&status); 1753 1754 if (action & SubmitSend) { 1755 action |= SubmitSent; 1756 1757 DoTraceLevelMessage( 1758 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1759 "Sending WDFREQUEST %p, Irp %p", Request->GetTraceObjectHandle(), 1760 Request->GetSubmitIrp()); 1761 1762 Send(Request->GetSubmitIrp()); 1763 1764 // 1765 // We always wait, even in the synchronous case. We do this because 1766 // even though the WDM completion routine ran synchronously in this 1767 // thread, the WDF processing of the completion could have been post- 1768 // poned by another thread attempting to cancel the I/O. The postpone- 1769 // ment would occur when the canceling thread has an oustanding reference 1770 // on m_IrpCompletionReferenceCount, which would cause the call to 1771 // CanComplete() in RequestCompletionRoutine() to return FALSE and not 1772 // call _SyncCompletionRoutine in the context of the WDM completion 1773 // routine, but in the context of the canceling thread. 1774 // 1775 action |= SubmitWait; 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 } 1817 else if (action & SubmitQueued) { 1818 // 1819 // To the caller, we say we sent the request (and all the cancel 1820 // semantics of a sent request still work). 1821 // 1822 action |= (SubmitSent | SubmitWait); 1823 } 1824 else if (action & SubmitSyncCallCompletion) { 1825 // 1826 // The request was not sent nor queued, reset the completion routine 1827 // since we overwrote it. 1828 // 1829 Request->m_TargetCompletionContext = params.OrigTargetCompletionContext; 1830 Request->m_CompletionRoutine.m_Completion = 1831 params.OrigTargetCompletionRoutine; 1832 ASSERT(!NT_SUCCESS(Request->GetSubmitFxIrp()->GetStatus())); 1833 } 1834 1835 if (action & SubmitSent) { 1836 if (action & SubmitWait) { 1837 status = params.SynchEvent.EnterCRAndWaitAndLeave( 1838 (action & SubmitTimeout) ? &timeout : NULL 1839 ); 1840 1841 if (status == STATUS_TIMEOUT) { 1842 // 1843 // By setting FX_REQUEST_CANCELLED_FROM_TIMER, we match the 1844 // async timer behavior where we change the completion status 1845 // from STATUS_CANCELLED to STATUS_IO_TIMEOUT. 1846 // 1847 Lock(&irql); 1848 Request->SetTargetFlags(FX_REQUEST_CANCELLED_FROM_TIMER); 1849 Unlock(irql); 1850 1851 Request->Cancel(); 1852 1853 params.SynchEvent.EnterCRAndWaitAndLeave(); 1854 } 1855 } 1856 1857 status = params.Status; 1858 } 1859 else { 1860 status = Request->GetSubmitFxIrp()->GetStatus(); 1861 } 1862 1863 Request->RELEASE(&status); 1864 1865 if (Action != NULL) { 1866 *Action = action; 1867 } 1868 1869 if (clearContext) { 1870 Request->ContextReleaseAndRestore(); 1871 } 1872 1873 return status; 1874 } 1875 1876 VOID 1877 FxIoTarget::FailPendedRequest( 1878 __in FxRequestBase* Request, 1879 __in NTSTATUS Status 1880 ) 1881 /*++ 1882 1883 Routine Description: 1884 Completes a request that has failed due to timer expiration or cancellation. 1885 1886 Assumes: 1887 Assumes that the caller has undone the effects of the 1888 IoSetNextIrpStackLocation made when we enqueued the request. 1889 1890 Arguments: 1891 Request - request that failed 1892 1893 Status - the status to set in the request 1894 1895 TakeReference - add a reference before completing the request 1896 1897 Return Value: 1898 None. 1899 1900 --*/ 1901 { 1902 FxIrp* irp; 1903 1904 irp = Request->GetSubmitFxIrp(); 1905 1906 // 1907 // Simulate failure in the IRP 1908 // 1909 irp->SetStatus(Status); 1910 irp->SetInformation(0); 1911 1912 // 1913 // Manaully process the irp as if it has completed back from the target. 1914 // 1915 RequestCompletionRoutine(Request); 1916 } 1917 1918 BOOLEAN 1919 FxIoTarget::RemoveCompletedRequestLocked( 1920 __in FxRequestBase* Request 1921 ) 1922 /*++ 1923 1924 Routine Description: 1925 Removes a previously sent request from the bookkeeping structures 1926 1927 Arguments: 1928 Request - The request being completed 1929 1930 Assumes: 1931 This object's Lock is being held by the caller. 1932 1933 Return Value: 1934 TRUE if the m_SentIoEvent should be set after the caller has released the 1935 object lock. 1936 1937 --*/ 1938 { 1939 ULONG oldFlags; 1940 1941 // 1942 // We will decrement the pending io count associated with this completed 1943 // request in FxIoTarget::CompleteRequest 1944 // 1945 // DecrementPendingIoCount(); 1946 1947 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 1948 "WDFIOTARGET %p, WDFREQUEST %p", GetObjectHandle(), 1949 Request->GetTraceObjectHandle()); 1950 1951 RemoveEntryList(&Request->m_ListEntry); 1952 1953 // 1954 // The request expects not to be on a list when it is destroyed. 1955 // 1956 InitializeListHead(&Request->m_ListEntry); 1957 1958 // 1959 // By the time we get here, there should never ever be a timer set for the 1960 // request. 1961 // 1962 ASSERT((Request->GetTargetFlags() & FX_REQUEST_TIMER_SET) == 0); 1963 1964 // 1965 // Clear flags that may have been set previously. 1966 // 1967 oldFlags = Request->ClearTargetFlags(FX_REQUEST_COMPLETED | 1968 FX_REQUEST_TIMER_SET | 1969 FX_REQUEST_CANCELLED_FROM_TIMER | 1970 FX_REQUEST_IGNORE_STATE); 1971 1972 ClearCompletedRequestVerifierFlags(Request); 1973 1974 // 1975 // If we are removing, we must wait for *ALL* requests that were sent down 1976 // the stack. 1977 // 1978 // If we are stopping, we only wait for i/o which do not ignore state 1979 // 1980 // NOTE: if we are completing a request which was inserted onto a list 1981 // Cancel()'ed before SubmitLocked was called and the Cancel() 1982 // thread already had a completion reference taken we are going to 1983 // evaluate a state transition even though the request is not a part 1984 // in that transition. I think this is OK b/c the transition would 1985 // have already occurred if the request(s) holding up the transition 1986 // have completed and will not occur here if they are still pending. 1987 // 1988 if (m_Removing) { 1989 if (IsListEmpty(&m_SentIoListHead) && IsListEmpty(&m_IgnoredIoListHead)) { 1990 // 1991 // We are no longer transitioning, do not allow new I/O of any kind 1992 // to come in. 1993 // 1994 m_Removing = FALSE; 1995 1996 // 1997 // Now that all i/o has ceased, clear out our pointers with relation 1998 // to the target itself. 1999 // 2000 ClearTargetPointers(); 2001 2002 return TRUE; 2003 } 2004 } 2005 else if (m_WaitingForSentIo && 2006 (oldFlags & FX_REQUEST_IGNORE_STATE) == 0 && 2007 IsListEmpty(&m_SentIoListHead)) { 2008 m_WaitingForSentIo = FALSE; 2009 return TRUE; 2010 } 2011 2012 return FALSE; 2013 } 2014 2015 _Must_inspect_result_ 2016 NTSTATUS 2017 FxIoTarget::PendRequestLocked( 2018 __in FxRequestBase* Request 2019 ) 2020 { 2021 NTSTATUS status; 2022 FxIrp* irp; 2023 2024 // 2025 // Assumes this object's lock is being held 2026 // 2027 Request->SetTargetFlags(FX_REQUEST_PENDED); 2028 2029 irp = Request->GetSubmitFxIrp(); 2030 2031 // 2032 // Make sure there is a valid current stack location in the irp. If we 2033 // allocated the irp ourself, then the current stack location is not valid. 2034 // Even if we didn't allocate the irp ourself, this will do no harm. In 2035 // every spot where we remove the request, we undo this call with a call to 2036 // IoSkipCurrentIrpStackLocation 2037 // 2038 irp->SetNextIrpStackLocation(); 2039 2040 ASSERT(irp->IsCurrentIrpStackLocationValid()); 2041 2042 status = m_PendedQueue.InsertTailRequest(irp->GetIrp(), &Request->m_CsqContext, NULL); 2043 2044 if (!NT_SUCCESS(status)) { 2045 // 2046 // Undo the affects of the IoSetNextIrpStackLocation made when we 2047 // enqueued the request. 2048 // 2049 irp->SkipCurrentIrpStackLocation(); 2050 2051 // 2052 // Request was not pended. 2053 // 2054 Request->ClearTargetFlags(FX_REQUEST_PENDED); 2055 } 2056 2057 return status; 2058 } 2059 2060 VOID 2061 FxIoTarget::TimerCallback( 2062 __in FxRequestBase* Request 2063 ) 2064 /*++ 2065 2066 Routine Description: 2067 Timer routine for when a request has timed out. This routine will attempt 2068 to cancel the request if it hasn't yet completed or complete it if it has. 2069 2070 Arguments: 2071 Request - The request that has timed out 2072 2073 Return Value: 2074 None. 2075 2076 --*/ 2077 2078 { 2079 KIRQL irql; 2080 BOOLEAN completeRequest, setStopEvent; 2081 LONG completionRefCount; 2082 2083 completeRequest = FALSE; 2084 setStopEvent = FALSE; 2085 2086 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2087 "WDFIOTARGET %p, WDFREQUEST %p", GetObjectHandle(), 2088 Request->GetTraceObjectHandle()); 2089 2090 Lock(&irql); 2091 2092 // 2093 // Clear the flag so that when the completion routine runs, there is no attempt 2094 // to cancel this timer. 2095 // 2096 Request->ClearTargetFlags(FX_REQUEST_TIMER_SET); 2097 2098 if (Request->GetTargetFlags() & FX_REQUEST_COMPLETED) { 2099 // 2100 // Completion routine ran on a separate processor as the same time as 2101 // the timer DPC. The completion routine will have deferred 2102 // completion to the timer DPC or the caller of Request::Cancel(). 2103 // 2104 completeRequest = Request->CanComplete(); 2105 } 2106 else { 2107 // 2108 // Attempt to cancel the request later outside of the lock. By setting 2109 // the cancelled from timer flag, the completion routine can morph the 2110 // status to timeout if the request is returned as cancelled. 2111 // 2112 Request->SetTargetFlags(FX_REQUEST_CANCELLED_FROM_TIMER); 2113 2114 // 2115 // Make sure the completion routine does not complete the request 2116 // while the timer callback is still running, in case the completion 2117 // is invoked in the unlock/lock window below. 2118 // 2119 completionRefCount = FxInterlockedIncrementGTZero( 2120 &Request->m_IrpCompletionReferenceCount); 2121 ASSERT(completionRefCount != 0); 2122 UNREFERENCED_PARAMETER(completionRefCount); 2123 2124 Unlock(irql); 2125 2126 Request->Cancel(); 2127 2128 Lock(&irql); 2129 2130 // 2131 // CanComplete() returns true if completion ownership was claimed. 2132 // 2133 completeRequest = Request->CanComplete(); 2134 } 2135 2136 // 2137 // If completion ownership was claimed, complete request. 2138 // 2139 if (completeRequest) { 2140 ASSERT(Request->GetTargetFlags() & FX_REQUEST_COMPLETED); 2141 2142 setStopEvent = RemoveCompletedRequestLocked(Request); 2143 2144 if (Request->m_Irp.GetStatus() == STATUS_CANCELLED) { 2145 // 2146 // We cancelled the request in another thread and the timer 2147 // fired at the same time. Treat this as if we did the cancel 2148 // from timer directly. 2149 // 2150 // Morph the status code into a timeout status. 2151 // 2152 // Don't muck with the IoStatus.Information field. 2153 // 2154 Request->m_Irp.SetStatus(STATUS_IO_TIMEOUT); 2155 } 2156 } 2157 2158 Unlock(irql); 2159 2160 if (completeRequest) { 2161 DoTraceLevelMessage( 2162 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2163 "WDFREQUEST %p completed in timer callback", 2164 Request->GetTraceObjectHandle()); 2165 CompleteRequest(Request); 2166 } 2167 2168 if (setStopEvent) { 2169 DoTraceLevelMessage( 2170 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 2171 "WDFIOTARGET %p, setting stop event %p in timer callback", 2172 GetObjectHandle(), m_SentIoEvent.GetEvent()); 2173 2174 m_SentIoEvent.Set(); 2175 } 2176 2177 if (completeRequest) { 2178 DecrementIoCount(); 2179 } 2180 } 2181 2182 VOID 2183 FxIoTarget::CompleteCanceledRequest( 2184 __in FxRequestBase* Request 2185 ) 2186 { 2187 KIRQL irql; 2188 BOOLEAN setStopEvent; 2189 2190 Lock(&irql); 2191 2192 // 2193 // RemoveCompletedRequestLocked clears Request->m_TargetFlags, so we must 2194 // do this check before that call. 2195 // 2196 if ((Request->GetTargetFlags() & FX_REQUEST_CANCELLED_FROM_TIMER) && 2197 Request->m_Irp.GetStatus() == STATUS_CANCELLED) { 2198 // 2199 // We cancelled the request from the timer and it has completed with 2200 // cancelled. Morph the status code into a timeout status. 2201 // 2202 // Don't muck with the IoStatus.Information field. 2203 // 2204 Request->m_Irp.SetStatus(STATUS_IO_TIMEOUT); 2205 } 2206 2207 setStopEvent = RemoveCompletedRequestLocked(Request); 2208 Unlock(irql); 2209 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2210 "WDFREQUEST %p completed in from cancel", 2211 Request->GetTraceObjectHandle()); 2212 CompleteRequest(Request); 2213 2214 if (setStopEvent) { 2215 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2216 "WDFIOTARGET %p, setting stop event %p", 2217 GetObjectHandle(), m_SentIoEvent.GetEvent()); 2218 2219 m_SentIoEvent.Set(); 2220 } 2221 2222 DecrementIoCount(); 2223 } 2224 2225 VOID 2226 FxIoTarget::HandleFailedResubmit( 2227 __in FxRequestBase* Request 2228 ) 2229 /*++ 2230 2231 Routine Description: 2232 This function handles the completion of the request when Submit() fails. 2233 Request is tracked by the 'Io Count' counter, caller is responsible for 2234 updating its value. 2235 2236 Arguments: 2237 Request - The request being completed. 2238 2239 Return Value: 2240 None. 2241 2242 --*/ 2243 { 2244 KIRQL irql; 2245 BOOLEAN setStopEvent; 2246 2247 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2248 "WDFREQUEST %p", Request->GetTraceObjectHandle()); 2249 2250 setStopEvent = FALSE; 2251 2252 Lock(&irql); 2253 2254 // 2255 // Flag should be clear until we set it below 2256 // 2257 ASSERT((Request->GetTargetFlags() & FX_REQUEST_COMPLETED) == 0); 2258 2259 // 2260 // Mark that the request has been completed 2261 // 2262 Request->SetTargetFlags(FX_REQUEST_COMPLETED); 2263 2264 // 2265 // Timer should not have been started. 2266 // 2267 ASSERT((Request->GetTargetFlags() & FX_REQUEST_TIMER_SET) == 0); 2268 2269 // 2270 // RemoveCompletedRequestLocked clears Request->m_TargetFlags, so we must 2271 // do this check before that call. 2272 // 2273 if ((Request->GetTargetFlags() & FX_REQUEST_CANCELLED_FROM_TIMER) && 2274 Request->m_Irp.GetStatus() == STATUS_CANCELLED) { 2275 // 2276 // We cancelled the request from the timer and it has completed with 2277 // cancelled. Morph the status code into a timeout status. 2278 // 2279 // Don't muck with the IoStatus.Information field. 2280 // 2281 Request->m_Irp.SetStatus(STATUS_IO_TIMEOUT); 2282 } 2283 2284 setStopEvent = RemoveCompletedRequestLocked(Request); 2285 2286 Unlock(irql); 2287 2288 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2289 "WDFREQUEST %p completed in completion routine", 2290 Request->GetTraceObjectHandle()); 2291 CompleteRequest(Request); 2292 2293 if (setStopEvent) { 2294 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2295 "WDFIOTARGET %p, setting stop event %p", 2296 GetObjectHandle(), m_SentIoEvent.GetEvent()); 2297 m_SentIoEvent.Set(); 2298 } 2299 2300 DecrementIoCount(); 2301 } 2302 2303 VOID 2304 FxIoTarget::RequestCompletionRoutine( 2305 __in FxRequestBase* Request 2306 ) 2307 /*++ 2308 2309 Routine Description: 2310 The previously submitted request has been completed. This function will 2311 handle coordination with the (optional) request timer and the potential 2312 simultaneous call to FxRequest::Cancel as to which function 2313 will actually complete the request. 2314 2315 Arguments: 2316 Request - The request being completed. 2317 2318 Return Value: 2319 None. 2320 2321 --*/ 2322 { 2323 KIRQL irql; 2324 BOOLEAN completeRequest, setStopEvent; 2325 2326 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2327 "WDFREQUEST %p", Request->GetTraceObjectHandle()); 2328 2329 2330 setStopEvent = FALSE; 2331 completeRequest = FALSE; 2332 2333 Lock(&irql); 2334 2335 // 2336 // Flag should be clear until we set it below 2337 // 2338 ASSERT((Request->GetTargetFlags() & FX_REQUEST_COMPLETED) == 0); 2339 2340 // 2341 // Mark that the request has been completed so that the potential timer 2342 // DPC will handle the case properly 2343 // 2344 Request->SetTargetFlags(FX_REQUEST_COMPLETED); 2345 2346 // 2347 // CancelTimer() returns TRUE if the timer was successfully canceled (if 2348 // queued) or if no timer was queued. 2349 // 2350 if (Request->CancelTimer()) { 2351 // 2352 // Sync with Request->Cancel() to make sure we can delete the request. 2353 // 2354 completeRequest = Request->CanComplete(); 2355 } 2356 2357 if (completeRequest) { 2358 // 2359 // RemoveCompletedRequestLocked clears Request->m_TargetFlags, so we must 2360 // do this check before that call. 2361 // 2362 if ((Request->GetTargetFlags() & FX_REQUEST_CANCELLED_FROM_TIMER) && 2363 Request->m_Irp.GetStatus() == STATUS_CANCELLED) { 2364 // 2365 // We cancelled the request from the timer and it has completed with 2366 // cancelled. Morph the status code into a timeout status. 2367 // 2368 // Don't muck with the IoStatus.Information field. 2369 // 2370 Request->m_Irp.SetStatus(STATUS_IO_TIMEOUT); 2371 } 2372 2373 setStopEvent = RemoveCompletedRequestLocked(Request); 2374 } 2375 else { 2376 DoTraceLevelMessage( 2377 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 2378 "WDFREQUEST %p deferring completion due to outstanding completion " 2379 "references", Request->GetTraceObjectHandle()); 2380 } 2381 2382 Unlock(irql); 2383 2384 if (completeRequest) { 2385 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2386 "WDFREQUEST %p completed in completion routine", 2387 Request->GetTraceObjectHandle()); 2388 CompleteRequest(Request); 2389 } 2390 2391 if (setStopEvent) { 2392 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2393 "WDFIOTARGET %p, setting stop event %p", 2394 GetObjectHandle(), m_SentIoEvent.GetEvent()); 2395 m_SentIoEvent.Set(); 2396 } 2397 2398 if (completeRequest) { 2399 DecrementIoCount(); 2400 } 2401 } 2402 2403 _Must_inspect_result_ 2404 NTSTATUS 2405 STDCALL 2406 FxIoTarget::_RequestCompletionRoutine( 2407 MdDeviceObject DeviceObject, 2408 MdIrp Irp, 2409 PVOID Context 2410 ) 2411 /*++ 2412 2413 Routine Description: 2414 Generic I/O completion routine for all submitted requests. 2415 2416 Arguments: 2417 DeviceObject - Our device object. Most likely NULL since we created the 2418 request and we are the top most driver with respect to it 2419 Irp - Request itself. Ignored since the context also contains this value 2420 Context - Our context, FxRequestBase*. 2421 2422 Return Value: 2423 STATUS_MORE_PROCESSING_REQUIRED since the lifetime of the Irp is controlled 2424 by the lifetime of our context which may outlive this function call. 2425 2426 --*/ 2427 { 2428 FxIoTarget* pThis; 2429 FxRequestBase* pRequest; 2430 2431 FxIrp irp(Irp); 2432 2433 UNREFERENCED_PARAMETER(DeviceObject); 2434 2435 pRequest = (FxRequestBase*) Context; 2436 pThis = pRequest->m_Target; 2437 2438 // 2439 // Only propagate the the pending returned bit in the IRP if this is an 2440 // asynchronous request 2441 // 2442 if (pRequest->m_CompletionRoutine.m_Completion != 2443 _SyncCompletionRoutine) { 2444 irp.PropagatePendingReturned(); 2445 } 2446 2447 pThis->RequestCompletionRoutine(pRequest); 2448 2449 return STATUS_MORE_PROCESSING_REQUIRED; 2450 } 2451 2452 _Must_inspect_result_ 2453 NTSTATUS 2454 FxIoTarget::FormatInternalIoctlOthersRequest( 2455 __in FxRequestBase* Request, 2456 __in ULONG Ioctl, 2457 __in FxRequestBuffer* Buffers 2458 ) 2459 { 2460 FxInternalIoctlOthersContext *pContext; 2461 PVOID* bufs[FX_REQUEST_NUM_OTHER_PARAMS]; 2462 NTSTATUS status; 2463 ULONG i; 2464 FxIrp* irp; 2465 2466 status = Request->ValidateTarget(this); 2467 if (!NT_SUCCESS(status)) { 2468 return status; 2469 } 2470 2471 if (Request->HasContextType(FX_RCT_INTERNAL_IOCTL_OTHERS)) { 2472 pContext = (FxInternalIoctlOthersContext*) Request->GetContext(); 2473 } 2474 else { 2475 pContext = new(GetDriverGlobals()) FxInternalIoctlOthersContext(); 2476 2477 if (pContext == NULL) { 2478 DoTraceLevelMessage( 2479 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 2480 "Could not allocate context for request"); 2481 2482 return STATUS_INSUFFICIENT_RESOURCES; 2483 } 2484 2485 Request->SetContext(pContext); 2486 } 2487 2488 // 2489 // Save away any references to IFxMemory pointers that are passed. 2490 // (StoreAndReferenceMemory can only store one buffer, so it doesn't help). 2491 // 2492 pContext->StoreAndReferenceOtherMemories(&Buffers[0], 2493 &Buffers[1], 2494 &Buffers[2]); 2495 2496 2497 irp = Request->GetSubmitFxIrp(); 2498 irp->ClearNextStackLocation(); 2499 2500 irp->SetMajorFunction(IRP_MJ_INTERNAL_DEVICE_CONTROL); 2501 irp->SetParameterIoctlCode(Ioctl); 2502 2503 CopyFileObjectAndFlags(Request); 2504 2505 i = 0; 2506 bufs[i] = irp->GetNextStackParameterOthersArgument1Pointer(); 2507 bufs[++i] = irp->GetNextStackParameterOthersArgument2Pointer(); 2508 bufs[++i] = irp->GetNextStackParameterOthersArgument4Pointer(); 2509 2510 for (i = 0; i < FX_REQUEST_NUM_OTHER_PARAMS; i++) { 2511 status = Buffers[i].GetBuffer(bufs[i]); 2512 2513 if (!NT_SUCCESS(status)) { 2514 DoTraceLevelMessage( 2515 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 2516 "Could not retrieve buffer %d, status %!STATUS!", i+1, status); 2517 2518 Request->ContextReleaseAndRestore(); 2519 2520 return status; 2521 } 2522 } 2523 2524 if (NT_SUCCESS(status)) { 2525 Request->VerifierSetFormatted(); 2526 } 2527 2528 return status; 2529 } 2530 2531 VOID 2532 FxIoTarget::_RequestCancelled( 2533 __in FxIrpQueue* Queue, 2534 __in MdIrp Irp, 2535 __in PMdIoCsqIrpContext CsqContext, 2536 __in KIRQL CallerIrql 2537 ) 2538 { 2539 FxIoTarget* pThis; 2540 FxRequestBase* pRequest; 2541 KIRQL irql; 2542 FxIrp pFxIrp; 2543 2544 pThis = CONTAINING_RECORD(Queue, FxIoTarget, m_PendedQueue); 2545 2546 pThis->Unlock(CallerIrql); 2547 2548 // 2549 // Grab the request out of the irp. After this call we are done with the 2550 // m_CsqContext field. 2551 // 2552 pRequest = FxRequestBase::_FromCsqContext(CsqContext); 2553 2554 DoTraceLevelMessage( 2555 pRequest->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 2556 "Pended WDFREQUEST %p canceled", pRequest->GetTraceObjectHandle()); 2557 2558 // 2559 // m_ListEntry is union'ed with m_CsqContext. m_CsqContext was in use up 2560 // until this function was called. From this point on, we are going to 2561 // process the request as if it has been completed. The completed code path 2562 // assumes m_ListEntry is on a list head. To have a valid m_ListEntry when 2563 // we call RemoveEntryList, initialize it now. Since we have an outstanding 2564 // irp completion reference count (which is decremented in the call to 2565 // FailPendedRequest later), we can safely initialize this field without 2566 // holding any locks. 2567 // 2568 InitializeListHead(&pRequest->m_ListEntry); 2569 2570 // 2571 // Undo the affects of the IoSetNextIrpStackLocation made when we 2572 // enqueued the request. 2573 // 2574 pFxIrp.SetIrp(Irp); 2575 pFxIrp.SkipCurrentIrpStackLocation(); 2576 2577 // 2578 // Request is no longer pended 2579 // 2580 pThis->Lock(&irql); 2581 ASSERT(pRequest->GetTargetFlags() & FX_REQUEST_PENDED); 2582 pRequest->ClearTargetFlags(FX_REQUEST_PENDED); 2583 pThis->Unlock(irql); 2584 2585 // 2586 // Call the driver's completion routine 2587 // 2588 pThis->FailPendedRequest(pRequest, STATUS_CANCELLED); 2589 } 2590 2591 VOID 2592 FxIoTarget::_SyncCompletionRoutine( 2593 __in WDFREQUEST Request, 2594 __in WDFIOTARGET Target, 2595 __in PWDF_REQUEST_COMPLETION_PARAMS Params, 2596 __in WDFCONTEXT Context 2597 ) 2598 { 2599 FxTargetSubmitSyncParams* pParams; 2600 2601 pParams = (FxTargetSubmitSyncParams*) Context; 2602 pParams->Status = Params->IoStatus.Status; 2603 2604 if (pParams->OrigTargetCompletionRoutine != NULL) { 2605 pParams->OrigTargetCompletionRoutine( 2606 Request, 2607 Target, 2608 Params, 2609 pParams->OrigTargetCompletionContext 2610 ); 2611 } 2612 2613 pParams->SynchEvent.Set(); 2614 } 2615 2616 2617 VOID 2618 FxIoTarget::CancelSentIo( 2619 VOID 2620 ) 2621 /*++ 2622 2623 Routine Description: 2624 This will be used whenever we send a reset request. 2625 For example if you are sending a reset request 2626 to USB target, you must cancel outstanding I/O before sending a reset 2627 or cycle port request for error recovery. 2628 --*/ 2629 2630 { 2631 SINGLE_LIST_ENTRY sentRequestListHead; 2632 BOOLEAN sentAdded; 2633 KIRQL irql; 2634 PFX_DRIVER_GLOBALS pFxDriverGlobals; 2635 2636 2637 pFxDriverGlobals = GetDriverGlobals(); 2638 sentRequestListHead.Next = NULL; 2639 Lock(&irql); 2640 2641 GetSentRequestsListLocked(&sentRequestListHead, 2642 &m_SentIoListHead, 2643 &sentAdded); 2644 2645 Unlock(irql); 2646 2647 DoTraceLevelMessage( 2648 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 2649 "Cancelling pending I/O on WDFIOTARGET %p ", 2650 GetHandle()); 2651 2652 if (sentAdded) { 2653 _CancelSentRequests(&sentRequestListHead); 2654 } 2655 } 2656 2657 _Must_inspect_result_ 2658 NTSTATUS 2659 FxIoTarget::SubmitSyncRequestIgnoreTargetState( 2660 __in FxRequestBase* Request, 2661 __in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions 2662 ) 2663 /*++ 2664 2665 Routine Description: 2666 Use this for sending a request which ignores target state. 2667 --*/ 2668 { 2669 PFX_DRIVER_GLOBALS pFxDriverGlobals; 2670 WDF_REQUEST_SEND_OPTIONS requestOptions; 2671 2672 pFxDriverGlobals = GetDriverGlobals(); 2673 if (RequestOptions != NULL) { 2674 2675 // 2676 // Do a copy so that the passed in paramters is 2677 // not modified. 2678 // 2679 RtlCopyMemory(&requestOptions, 2680 RequestOptions, 2681 sizeof(WDF_REQUEST_SEND_OPTIONS)); 2682 2683 if ((requestOptions.Flags & WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE) == 0) { 2684 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET, 2685 "Ignoring WDFIOTARGET %p state to send request", 2686 GetHandle()); 2687 requestOptions.Flags |= WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE; 2688 } 2689 } 2690 else{ 2691 WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, 2692 WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE); 2693 } 2694 2695 return SubmitSync(Request, &requestOptions); 2696 } 2697 2698 VOID 2699 FxIoTarget::UpdateTargetIoType( 2700 VOID 2701 ) 2702 { 2703 UCHAR ioType = GetTargetIoType(); 2704 2705 // 2706 // m_IoCount is initialized to 1 2707 // 2708 if ((ioType != m_TargetIoType) && (m_IoCount > 1)) { 2709 DoTraceLevelMessage( 2710 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 2711 "WDFIOTARGET %p has changed IoType with outstanding IO", 2712 GetHandle()); 2713 } 2714 m_TargetIoType = (UCHAR) ioType; 2715 } 2716