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
FxIoTarget(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in USHORT ObjectSize)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
FxIoTarget(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in USHORT ObjectSize,__in USHORT WdfType)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
Construct(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
~FxIoTarget()91 FxIoTarget::~FxIoTarget()
92 {
93 ASSERT(IsListEmpty(&m_SentIoListHead));
94 ASSERT(IsListEmpty(&m_IgnoredIoListHead));
95 ASSERT(m_IoCount == 0);
96 }
97
98 VOID
PrintDisposeMessage(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
WaitForDisposeEvent(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
Dispose(VOID)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
SubmitPendedRequest(__in FxRequestBase * Request)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
SubmitPendedRequests(__in PLIST_ENTRY RequestListHead)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
Start(VOID)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
GotoStartState(__in PLIST_ENTRY RequestListHead,__in BOOLEAN Lock)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
DrainPendedRequestsLocked(__in PLIST_ENTRY RequestListHead,__in BOOLEAN RequestWillBeResent)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
CompletePendedRequest(__in FxRequestBase * Request)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
CompletePendedRequestList(__in PLIST_ENTRY RequestListHead)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
_CancelSentRequest(__in FxRequestBase * Request)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
_CancelSentRequests(__in PSINGLE_LIST_ENTRY RequestListHead)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
GetSentRequestsListLocked(__in PSINGLE_LIST_ENTRY RequestListHead,__in PLIST_ENTRY SendList,__out PBOOLEAN AddedToList)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
GotoStopState(__in WDF_IO_TARGET_SENT_IO_ACTION Action,__in PSINGLE_LIST_ENTRY SentRequestListHead,__out PBOOLEAN Wait,__in BOOLEAN LockSelf)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
Stop(__in WDF_IO_TARGET_SENT_IO_ACTION Action)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
GotoPurgeState(__in WDF_IO_TARGET_PURGE_IO_ACTION Action,__in PLIST_ENTRY PendedRequestListHead,__in PSINGLE_LIST_ENTRY SentRequestListHead,__out PBOOLEAN Wait,__in BOOLEAN LockSelf)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
Purge(__in WDF_IO_TARGET_PURGE_IO_ACTION Action)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
GotoRemoveState(__in WDF_IO_TARGET_STATE NewState,__in PLIST_ENTRY PendedRequestListHead,__in PSINGLE_LIST_ENTRY SentRequestListHead,__in BOOLEAN Lock,__out PBOOLEAN Wait)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
Remove(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
QueryInterface(__inout FxQueryInterfaceParams * Params)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
Init(__in CfxDeviceBase * Device)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
FX_VF_METHOD(FxIoTarget,VerifySubmitLocked)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
SubmitLocked(__in FxRequestBase * Request,__in_opt PWDF_REQUEST_SEND_OPTIONS Options,__in ULONG Flags)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
Submit(__in FxRequestBase * Request,__in_opt PWDF_REQUEST_SEND_OPTIONS Options,__in_opt ULONG Flags)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
SubmitSync(__in FxRequestBase * Request,__in_opt PWDF_REQUEST_SEND_OPTIONS Options,__out_opt PULONG Action)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
FailPendedRequest(__in FxRequestBase * Request,__in NTSTATUS Status)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
RemoveCompletedRequestLocked(__in FxRequestBase * Request)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
PendRequestLocked(__in FxRequestBase * Request)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
TimerCallback(__in FxRequestBase * Request)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
CompleteCanceledRequest(__in FxRequestBase * Request)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
HandleFailedResubmit(__in FxRequestBase * Request)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
RequestCompletionRoutine(__in FxRequestBase * Request)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
_RequestCompletionRoutine(MdDeviceObject DeviceObject,MdIrp Irp,PVOID Context)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
FormatInternalIoctlOthersRequest(__in FxRequestBase * Request,__in ULONG Ioctl,__in FxRequestBuffer * Buffers)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
_RequestCancelled(__in FxIrpQueue * Queue,__in MdIrp Irp,__in PMdIoCsqIrpContext CsqContext,__in KIRQL CallerIrql)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
_SyncCompletionRoutine(__in WDFREQUEST Request,__in WDFIOTARGET Target,__in PWDF_REQUEST_COMPLETION_PARAMS Params,__in WDFCONTEXT Context)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
CancelSentIo(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
SubmitSyncRequestIgnoreTargetState(__in FxRequestBase * Request,__in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions)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
UpdateTargetIoType(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