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, &params);
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 FxIoTarget::_RequestCompletionRoutine(
2406     MdDeviceObject DeviceObject,
2407     MdIrp Irp,
2408     PVOID Context
2409     )
2410 /*++
2411 
2412 Routine Description:
2413     Generic I/O completion routine for all submitted requests.
2414 
2415 Arguments:
2416     DeviceObject - Our device object.  Most likely NULL since we created the
2417         request and we are the top most driver with respect to it
2418     Irp - Request itself.  Ignored since the context also contains this value
2419     Context - Our context, FxRequestBase*.
2420 
2421 Return Value:
2422     STATUS_MORE_PROCESSING_REQUIRED since the lifetime of the Irp is controlled
2423     by the lifetime of our context which may outlive this function call.
2424 
2425   --*/
2426 {
2427     FxIoTarget* pThis;
2428     FxRequestBase* pRequest;
2429 
2430     FxIrp irp(Irp);
2431 
2432     UNREFERENCED_PARAMETER(DeviceObject);
2433 
2434     pRequest = (FxRequestBase*) Context;
2435     pThis = pRequest->m_Target;
2436 
2437     //
2438     // Only propagate the the pending returned bit in the IRP if this is an
2439     // asynchronous request
2440     //
2441     if (pRequest->m_CompletionRoutine.m_Completion !=
2442                                                     _SyncCompletionRoutine) {
2443         irp.PropagatePendingReturned();
2444     }
2445 
2446     pThis->RequestCompletionRoutine(pRequest);
2447 
2448     return STATUS_MORE_PROCESSING_REQUIRED;
2449 }
2450 
2451 _Must_inspect_result_
2452 NTSTATUS
2453 FxIoTarget::FormatInternalIoctlOthersRequest(
2454     __in FxRequestBase* Request,
2455     __in ULONG Ioctl,
2456     __in FxRequestBuffer* Buffers
2457     )
2458 {
2459     FxInternalIoctlOthersContext *pContext;
2460     PVOID* bufs[FX_REQUEST_NUM_OTHER_PARAMS];
2461     NTSTATUS status;
2462     ULONG i;
2463     FxIrp* irp;
2464 
2465     status = Request->ValidateTarget(this);
2466     if (!NT_SUCCESS(status)) {
2467         return status;
2468     }
2469 
2470     if (Request->HasContextType(FX_RCT_INTERNAL_IOCTL_OTHERS)) {
2471         pContext = (FxInternalIoctlOthersContext*) Request->GetContext();
2472     }
2473     else {
2474         pContext = new(GetDriverGlobals()) FxInternalIoctlOthersContext();
2475 
2476         if (pContext == NULL) {
2477             DoTraceLevelMessage(
2478                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2479                 "Could not allocate context for request");
2480 
2481             return STATUS_INSUFFICIENT_RESOURCES;
2482         }
2483 
2484         Request->SetContext(pContext);
2485     }
2486 
2487     //
2488     // Save away any references to IFxMemory pointers that are passed.
2489     // (StoreAndReferenceMemory can only store one buffer, so it doesn't help).
2490     //
2491     pContext->StoreAndReferenceOtherMemories(&Buffers[0],
2492                                              &Buffers[1],
2493                                              &Buffers[2]);
2494 
2495 
2496     irp = Request->GetSubmitFxIrp();
2497     irp->ClearNextStackLocation();
2498 
2499     irp->SetMajorFunction(IRP_MJ_INTERNAL_DEVICE_CONTROL);
2500     irp->SetParameterIoctlCode(Ioctl);
2501 
2502     CopyFileObjectAndFlags(Request);
2503 
2504     i = 0;
2505     bufs[i] = irp->GetNextStackParameterOthersArgument1Pointer();
2506     bufs[++i] = irp->GetNextStackParameterOthersArgument2Pointer();
2507     bufs[++i] = irp->GetNextStackParameterOthersArgument4Pointer();
2508 
2509     for (i = 0; i < FX_REQUEST_NUM_OTHER_PARAMS; i++) {
2510         status = Buffers[i].GetBuffer(bufs[i]);
2511 
2512         if (!NT_SUCCESS(status)) {
2513             DoTraceLevelMessage(
2514                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2515                 "Could not retrieve buffer %d, status %!STATUS!", i+1, status);
2516 
2517             Request->ContextReleaseAndRestore();
2518 
2519             return status;
2520         }
2521     }
2522 
2523     if (NT_SUCCESS(status)) {
2524         Request->VerifierSetFormatted();
2525     }
2526 
2527     return status;
2528 }
2529 
2530 VOID
2531 FxIoTarget::_RequestCancelled(
2532     __in FxIrpQueue* Queue,
2533     __in MdIrp Irp,
2534     __in PMdIoCsqIrpContext CsqContext,
2535     __in KIRQL CallerIrql
2536     )
2537 {
2538     FxIoTarget* pThis;
2539     FxRequestBase* pRequest;
2540     KIRQL irql;
2541     FxIrp pFxIrp;
2542 
2543     pThis = CONTAINING_RECORD(Queue, FxIoTarget, m_PendedQueue);
2544 
2545     pThis->Unlock(CallerIrql);
2546 
2547     //
2548     // Grab the request out of the irp.  After this call we are done with the
2549     // m_CsqContext field.
2550     //
2551     pRequest = FxRequestBase::_FromCsqContext(CsqContext);
2552 
2553     DoTraceLevelMessage(
2554         pRequest->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
2555         "Pended WDFREQUEST %p canceled", pRequest->GetTraceObjectHandle());
2556 
2557     //
2558     // m_ListEntry is union'ed with m_CsqContext.  m_CsqContext was in use up
2559     // until this function was called.  From this point on, we are going to
2560     // process the request as if it has been completed.  The completed code path
2561     // assumes m_ListEntry is on a list head.  To have a valid m_ListEntry when
2562     // we call RemoveEntryList, initialize it now.  Since we have an outstanding
2563     // irp completion reference count (which is decremented in the call to
2564     // FailPendedRequest later), we can safely initialize this field without
2565     // holding any locks.
2566     //
2567     InitializeListHead(&pRequest->m_ListEntry);
2568 
2569     //
2570     // Undo the affects of the IoSetNextIrpStackLocation made when we
2571     // enqueued the request.
2572     //
2573     pFxIrp.SetIrp(Irp);
2574     pFxIrp.SkipCurrentIrpStackLocation();
2575 
2576     //
2577     // Request is no longer pended
2578     //
2579     pThis->Lock(&irql);
2580     ASSERT(pRequest->GetTargetFlags() & FX_REQUEST_PENDED);
2581     pRequest->ClearTargetFlags(FX_REQUEST_PENDED);
2582     pThis->Unlock(irql);
2583 
2584     //
2585     // Call the driver's completion routine
2586     //
2587     pThis->FailPendedRequest(pRequest, STATUS_CANCELLED);
2588 }
2589 
2590 VOID
2591 FxIoTarget::_SyncCompletionRoutine(
2592     __in WDFREQUEST Request,
2593     __in WDFIOTARGET Target,
2594     __in PWDF_REQUEST_COMPLETION_PARAMS Params,
2595     __in WDFCONTEXT Context
2596     )
2597 {
2598     FxTargetSubmitSyncParams* pParams;
2599 
2600     pParams = (FxTargetSubmitSyncParams*) Context;
2601     pParams->Status = Params->IoStatus.Status;
2602 
2603     if (pParams->OrigTargetCompletionRoutine != NULL) {
2604         pParams->OrigTargetCompletionRoutine(
2605             Request,
2606             Target,
2607             Params,
2608             pParams->OrigTargetCompletionContext
2609             );
2610     }
2611 
2612     pParams->SynchEvent.Set();
2613 }
2614 
2615 
2616 VOID
2617 FxIoTarget::CancelSentIo(
2618     VOID
2619     )
2620 /*++
2621 
2622 Routine Description:
2623     This will be used whenever we send a reset request.
2624     For example if you are sending a reset request
2625     to USB target, you must cancel outstanding I/O before sending a reset
2626     or cycle port request for error recovery.
2627 --*/
2628 
2629 {
2630     SINGLE_LIST_ENTRY sentRequestListHead;
2631     BOOLEAN sentAdded;
2632     KIRQL irql;
2633     PFX_DRIVER_GLOBALS pFxDriverGlobals;
2634 
2635 
2636     pFxDriverGlobals = GetDriverGlobals();
2637     sentRequestListHead.Next = NULL;
2638     Lock(&irql);
2639 
2640     GetSentRequestsListLocked(&sentRequestListHead,
2641                               &m_SentIoListHead,
2642                               &sentAdded);
2643 
2644     Unlock(irql);
2645 
2646     DoTraceLevelMessage(
2647         pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
2648         "Cancelling pending I/O on WDFIOTARGET %p ",
2649         GetHandle());
2650 
2651     if (sentAdded) {
2652         _CancelSentRequests(&sentRequestListHead);
2653     }
2654 }
2655 
2656 _Must_inspect_result_
2657 NTSTATUS
2658 FxIoTarget::SubmitSyncRequestIgnoreTargetState(
2659     __in FxRequestBase* Request,
2660     __in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions
2661     )
2662 /*++
2663 
2664 Routine Description:
2665     Use this for sending a request which ignores target state.
2666 --*/
2667 {
2668     PFX_DRIVER_GLOBALS pFxDriverGlobals;
2669     WDF_REQUEST_SEND_OPTIONS requestOptions;
2670 
2671     pFxDriverGlobals = GetDriverGlobals();
2672     if (RequestOptions != NULL) {
2673 
2674         //
2675         // Do a copy so that the passed in paramters is
2676         // not modified.
2677         //
2678         RtlCopyMemory(&requestOptions,
2679                       RequestOptions,
2680                       sizeof(WDF_REQUEST_SEND_OPTIONS));
2681 
2682         if ((requestOptions.Flags & WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE) == 0) {
2683             DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
2684                                 "Ignoring WDFIOTARGET %p state to send request",
2685                                 GetHandle());
2686             requestOptions.Flags |= WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE;
2687         }
2688     }
2689     else{
2690         WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions,
2691                                       WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
2692     }
2693 
2694     return SubmitSync(Request, &requestOptions);
2695 }
2696 
2697 VOID
2698 FxIoTarget::UpdateTargetIoType(
2699     VOID
2700     )
2701 {
2702     UCHAR ioType = GetTargetIoType();
2703 
2704     //
2705     // m_IoCount is initialized to 1
2706     //
2707     if ((ioType != m_TargetIoType) && (m_IoCount > 1)) {
2708         DoTraceLevelMessage(
2709                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2710                 "WDFIOTARGET %p has changed IoType with outstanding IO",
2711                 GetHandle());
2712     }
2713     m_TargetIoType = (UCHAR) ioType;
2714 }
2715