1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 FxIoQueue.cpp
8
9 Abstract:
10
11 This module implements the FxIoQueue object and C interfaces
12
13 Author:
14
15
16
17
18 Revision History:
19
20
21
22
23 --*/
24
25 #include "ioprivshared.hpp"
26 #include "fxioqueue.hpp"
27
28 extern "C" {
29 #if defined(EVENT_TRACING)
30 #include "FxIoQueue.tmh"
31 #endif
32 }
33
34 //
35 // Public constructors
36 //
FxIoQueue(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxPkgIo * PkgIo)37 FxIoQueue::FxIoQueue(
38 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
39 __in FxPkgIo* PkgIo
40 ) :
41 FxNonPagedObject(FX_TYPE_QUEUE, sizeof(FxIoQueue), FxDriverGlobals),
42 m_CallbackSpinLock(FxDriverGlobals),
43 m_CallbackMutexLock(FxDriverGlobals),
44 m_IoPkgListNode(FxIoQueueNodeTypeQueue)
45 {
46 m_Configured = FALSE;
47 m_Disposing = FALSE;
48 m_PowerManaged = FALSE;
49 m_PowerState = FxIoQueuePowerOn;
50 m_PowerReferenced = FALSE;
51 m_AllowZeroLengthRequests = FALSE;
52 m_IsDevicePowerPolicyOwner = FALSE;
53 m_Type = WdfIoQueueDispatchSequential;
54
55 // A newly created queue can not accept requests until initialized
56 m_QueueState = (FX_IO_QUEUE_STATE)0;
57
58 //
59 // Set our Cancel callbacks
60 //
61 m_Queue.Initialize(this, _IrpCancelForQueue);
62
63 m_DriverCancelable.Initialize(this, _IrpCancelForDriver);
64
65 InitializeListHead(&m_Cancelled);
66
67 InitializeListHead(&m_CanceledOnQueueList);
68
69 InitializeListHead(&m_DriverOwned);
70
71 InitializeListHead(&m_PowerNotify);
72
73 InitializeListHead(&m_PowerDriverNotified);
74
75 m_PowerSListEntry.Next = NULL;
76
77 //
78 // We do not reference count the I/O package instance
79 // since it contains us. The fact we exist, the I/O
80 // package instance exists.
81 //
82 m_PkgIo = PkgIo;
83 m_CxDeviceInfo = NULL;
84
85 m_Device = PkgIo->GetDevice();
86
87 m_IsDevicePowerPolicyOwner = (m_Device->IsPnp() &&
88 m_Device->m_PkgPnp->IsPowerPolicyOwner());
89
90 m_Dispatching = 0L;
91
92 m_TransitionFromEmpty = FALSE;
93 m_ForceTransitionFromEmptyWhenAddingNewRequest = FALSE;
94
95 m_DriverIoCount = 0L;
96 m_TwoPhaseCompletions = 0L;
97
98 m_SystemWorkItem = NULL;
99
100 m_IdleComplete.Method = NULL;
101 m_IdleCompleteContext = NULL;
102
103 m_PurgeComplete.Method = NULL;
104 m_PurgeCompleteContext = NULL;
105
106 m_ReadyNotify.Method = NULL;
107 m_ReadyNotifyContext = NULL;
108
109 m_CallbackLockPtr = NULL;
110 m_CallbackLockObjectPtr = NULL;
111
112 #if FX_IS_KERNEL_MODE
113
114 // Initialize the DPC used for deferrals
115 KeInitializeDpc(
116 &m_Dpc,
117 _DeferredDispatchDpcThunk,
118 this
119 );
120 #endif
121
122 m_DpcQueued = FALSE;
123
124 m_WorkItemQueued = FALSE;
125
126 m_RequeueDeferredDispatcher = FALSE;
127
128 m_Deleted = FALSE;
129 m_SupportForwardProgress = FALSE;
130 m_PassiveLevel = FALSE;
131
132 m_ExecutionLevel = WdfExecutionLevelInheritFromParent;
133 m_SynchronizationScope = WdfSynchronizationScopeInheritFromParent;
134
135 m_FwdProgContext = NULL;
136 MarkPassiveDispose(ObjectDoNotLock);
137 m_MaxParallelQueuePresentedRequests = (ULONG)-1;
138
139 return;
140 }
141
~FxIoQueue()142 FxIoQueue::~FxIoQueue()
143 {
144 ASSERT(m_SystemWorkItem == NULL);
145
146 if (m_PkgIo != NULL) {
147 m_PkgIo = NULL;
148 }
149
150 ASSERT(IsListEmpty(&m_Cancelled));
151 ASSERT(IsListEmpty(&m_CanceledOnQueueList));
152 ASSERT(IsListEmpty(&m_DriverOwned));
153 ASSERT(IsListEmpty(&m_PowerNotify));
154 ASSERT(IsListEmpty(&m_PowerDriverNotified));
155 ASSERT(!m_PowerReferenced);
156 ASSERT(!m_DpcQueued);
157 ASSERT(!m_WorkItemQueued);
158 ASSERT(!m_RequeueDeferredDispatcher);
159 ASSERT(m_TwoPhaseCompletions == 0);
160 }
161
162 _Must_inspect_result_
163 NTSTATUS
_Create(__in PFX_DRIVER_GLOBALS DriverGlobals,__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,__in PWDF_IO_QUEUE_CONFIG Config,__in_opt FxDriver * Caller,__in FxPkgIo * PkgIo,__in BOOLEAN InitialPowerStateOn,__deref_out FxIoQueue ** Object)164 FxIoQueue::_Create(
165 __in PFX_DRIVER_GLOBALS DriverGlobals,
166 __in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
167 __in PWDF_IO_QUEUE_CONFIG Config,
168 __in_opt FxDriver* Caller,
169 __in FxPkgIo* PkgIo,
170 __in BOOLEAN InitialPowerStateOn,
171 __deref_out FxIoQueue** Object
172 )
173 {
174 NTSTATUS status;
175 FxIoQueue* pQueue;
176
177 *Object = NULL;
178
179 pQueue = new(DriverGlobals, Attributes) FxIoQueue(DriverGlobals, PkgIo);
180
181 if (pQueue == NULL) {
182 status = STATUS_INSUFFICIENT_RESOURCES;
183 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
184 "Memory allocation failed: %!STATUS!", status);
185 return status;
186 }
187
188 //
189 // Initialize it, creating the handle to pass the driver
190 // and configuring its callbacks and queue type
191 //
192 status = pQueue->Initialize(Config,
193 Attributes,
194 Caller,
195 InitialPowerStateOn);
196 if (!NT_SUCCESS(status)) {
197 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
198 "Could not configure queue: %!STATUS!", status);
199 goto Done;
200 }
201 Done:
202 if (NT_SUCCESS(status)) {
203 *Object = pQueue;
204 }
205 else {
206 //
207 // Release our newly allocated Queue object
208 //
209 pQueue->DeleteFromFailedCreate();
210 }
211
212 return status;
213 }
214
215 _Must_inspect_result_
216 NTSTATUS
Initialize(__in PWDF_IO_QUEUE_CONFIG pConfig,__in_opt PWDF_OBJECT_ATTRIBUTES QueueAttributes,__in_opt FxDriver * Caller,__in BOOLEAN InitialPowerStateOn)217 FxIoQueue::Initialize(
218 __in PWDF_IO_QUEUE_CONFIG pConfig,
219 __in_opt PWDF_OBJECT_ATTRIBUTES QueueAttributes,
220 __in_opt FxDriver* Caller,
221 __in BOOLEAN InitialPowerStateOn
222 )
223
224 /*++
225
226 Routine Description:
227
228 Initialize the IoQueue after creating.
229
230 This creates the handle required for passing to the driver.
231
232 Arguments:
233
234 Returns:
235
236 NTSTATUS
237
238 --*/
239
240 {
241 NTSTATUS Status;
242 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
243
244 Status = m_PowerIdle.Initialize(NotificationEvent, FALSE);
245 if (!NT_SUCCESS(Status)) {
246 return Status;
247 }
248
249 Status = m_FinishDisposing.Initialize(NotificationEvent, FALSE);
250 if (!NT_SUCCESS(Status)) {
251 return Status;
252 }
253
254 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
255 Status = m_RequestWaitEventUm.Initialize(SynchronizationEvent, FALSE);
256 if (!NT_SUCCESS(Status)) {
257 return Status;
258 }
259 #endif
260
261 MarkDisposeOverride(ObjectDoNotLock);
262
263 //
264 // Set the execution level and callback synchronization based on
265 // configuration
266 //
267 Status = ConfigureConstraints(QueueAttributes, Caller);
268 if (!NT_SUCCESS(Status)) {
269 return Status;
270 }
271
272 //
273 // Validate dispatch type.
274 //
275 if (pConfig->DispatchType <= WdfIoQueueDispatchInvalid ||
276 pConfig->DispatchType >= WdfIoQueueDispatchMax) {
277 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
278 "Invalid dispatch type "
279 "specified %d, Queue 0x%p %!STATUS!",
280 pConfig->DispatchType,
281 GetObjectHandle(),
282 STATUS_INVALID_PARAMETER);
283 return STATUS_INVALID_PARAMETER;
284 }
285
286 //
287 // If not a manual queue, must set at least IoStart, or one of
288 // read|write|devicecontrol
289 //
290 if ((pConfig->DispatchType != WdfIoQueueDispatchManual) &&
291 (pConfig->EvtIoDefault == NULL)) {
292
293 if ((pConfig->EvtIoDefault == NULL) &&
294 (pConfig->EvtIoRead == NULL) &&
295 (pConfig->EvtIoWrite == NULL) &&
296 (pConfig->EvtIoDeviceControl == NULL) &&
297 (pConfig->EvtIoInternalDeviceControl == NULL)) {
298
299 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
300 "At least one of EvtIoDefault|EvtIoRead|EvtIoWrite|"
301 "EvtIoDeviceControl|EvtIoInternalDeviceControl "
302 "must be set %!STATUS!", STATUS_WDF_NO_CALLBACK);
303 return STATUS_WDF_NO_CALLBACK;
304 }
305 }
306
307 //
308 // A manual queue should not set any callback function
309 // pointers since they will not get invoked.
310 //
311 if (pConfig->DispatchType == WdfIoQueueDispatchManual) {
312
313 if ((pConfig->EvtIoDefault != NULL) ||
314 (pConfig->EvtIoRead != NULL) ||
315 (pConfig->EvtIoWrite != NULL) ||
316 (pConfig->EvtIoDeviceControl != NULL) ||
317 (pConfig->EvtIoInternalDeviceControl != NULL)) {
318
319 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
320 "Cannot set io callback events "
321 "on a manual WDFQUEUE 0x%p %!STATUS!",
322 GetObjectHandle(),
323 STATUS_INVALID_PARAMETER);
324 return STATUS_INVALID_PARAMETER;
325 }
326 }
327
328 //
329 // For version less than v1.9 m_MaxParallelQueuePresentedRequests is set to
330 // -1 by the FxIoQueue Constructor.
331 // By checking > below we mean v1.9 and above (public API already did the official
332 // validation).
333 //
334 if (pConfig->Size > sizeof(WDF_IO_QUEUE_CONFIG_V1_7)) {
335 if (pConfig->Settings.Parallel.NumberOfPresentedRequests != 0 &&
336 (pConfig->DispatchType == WdfIoQueueDispatchSequential ||
337 pConfig->DispatchType == WdfIoQueueDispatchManual)) {
338 Status = STATUS_INVALID_PARAMETER;
339 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
340 "Cannot have NumberOfPresentedRequests other "
341 "than 0 on a Sequential or manual WDFQUEUE 0x%p."
342 "Make Sure you set NumberOfPresentedRequests"
343 " to 0, %!STATUS!",
344 GetObjectHandle(),
345 Status
346 );
347 return Status;
348
349 }
350 else{
351 m_MaxParallelQueuePresentedRequests =
352 pConfig->Settings.Parallel.NumberOfPresentedRequests;
353 }
354 }
355
356 //
357 // Initialize our workitem if we have to support passive callbacks
358 //
359 if (m_PassiveLevel) {
360
361 Status = FxSystemWorkItem::_Create(FxDriverGlobals,
362 m_Device->GetDeviceObject(),
363 &m_SystemWorkItem
364 );
365
366 if (!NT_SUCCESS(Status)) {
367 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
368 "Could not allocate workitem: %!STATUS!", Status);
369 return Status;
370 }
371 }
372
373 m_Type = pConfig->DispatchType;
374
375 switch(pConfig->PowerManaged) {
376
377 case WdfUseDefault:
378 if(m_Device->IsFilter()){
379 m_PowerManaged = FALSE;
380 } else {
381 m_PowerManaged = TRUE;
382 }
383 break;
384
385 case WdfTrue:
386 m_PowerManaged = TRUE;
387 break;
388
389 case WdfFalse:
390 m_PowerManaged = FALSE;
391 break;
392 default:
393 ASSERTMSG("Invalid value in WDF_IO_QUEUE_CONFIG PowerManaged field\n", FALSE);
394 break;
395 }
396
397 //
398 // Queues for NonPnp devices can't be power managed.
399 //
400 if(m_Device->IsLegacy()) {
401 m_PowerManaged = FALSE;
402 }
403
404 //
405 // If power managed queue, ensure its initial power state
406 // is same as the device.
407 //
408 if (m_PowerManaged) {
409 if (InitialPowerStateOn) {
410 m_PowerState = FxIoQueuePowerOn;
411 }
412 else {
413 m_PowerState = FxIoQueuePowerOff;
414 }
415 } else {
416 m_PowerState = FxIoQueuePowerOn;
417 }
418
419 m_AllowZeroLengthRequests = pConfig->AllowZeroLengthRequests;
420
421 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
422 "EvtIoDefault 0x%p, EvtIoRead 0x%p, EvtIoWrite 0x%p, "
423 "EvtIoDeviceControl 0x%p for WDFQUEUE 0x%p",
424 pConfig->EvtIoDefault,
425 pConfig->EvtIoRead,
426 pConfig->EvtIoWrite,
427 pConfig->EvtIoDeviceControl, GetObjectHandle());
428
429 m_IoDefault.Method = pConfig->EvtIoDefault;
430 m_IoStop.Method = pConfig->EvtIoStop;
431 m_IoResume.Method = pConfig->EvtIoResume;
432 m_IoRead.Method = pConfig->EvtIoRead;
433 m_IoWrite.Method = pConfig->EvtIoWrite;
434 m_IoDeviceControl.Method = pConfig->EvtIoDeviceControl;
435 m_IoInternalDeviceControl.Method = pConfig->EvtIoInternalDeviceControl;
436 m_IoCanceledOnQueue.Method = pConfig->EvtIoCanceledOnQueue;
437
438
439 // A newly created queue can accept and dispatch requests once initialized
440 SetState((FX_IO_QUEUE_SET_STATE)(FxIoQueueSetAcceptRequests|FxIoQueueSetDispatchRequests));
441
442 m_Configured = TRUE;
443
444 return STATUS_SUCCESS;
445 }
446
447 BOOLEAN
Dispose()448 FxIoQueue::Dispose(
449 )
450 /*++
451
452 Routine Description:
453
454 Should be called at PASSIVE_LEVEL because of the synchronous call
455 to drain requests, workitems, and dpcs associated with this queue.
456
457 Arguments:
458
459 Returns:
460
461 TRUE or FALSE
462
463 --*/
464 {
465 KIRQL irql;
466
467 if (IsCommitted() == FALSE) {
468 //
469 // We called DeleteFromFailedCreate because we couldn't commit the
470 // object.
471 //
472 goto End;
473 }
474
475 //
476 // If object is commited means we are added to the FxPkgIo queue list.
477 //
478 //
479 // Purge the queue asynchrnously without providing any callback. That way,
480 // we allow the driver to have an outstanding purge request while the delete
481 // is in progress.
482 //
483 (VOID) QueuePurge(TRUE, TRUE, NULL, NULL);
484
485 Lock(&irql);
486
487 //
488 // Mark that this queue is disposing
489 //
490
491 ASSERT(m_Disposing == FALSE);
492
493 m_Disposing = TRUE;
494
495 //
496 // Just make sure the state hasn't changed after the purge.
497 //
498 ASSERT(IsState(WdfIoQueueAcceptRequests) == FALSE);
499
500 //
501 // Call the FxPkgIo to remove its references to this queue
502 //
503 // Note: We are holding the queue lock to prevent races, and
504 // FxPkgIo never calls FxIoQueue methods while holding
505 // its lock.
506 //
507 m_PkgIo->RemoveQueueReferences(this);
508
509 DispatchEvents(irql);
510
511 //
512 // Wait for the finished event to be signalled. This event will
513 // be signalled when the queue is in a disposed state and there
514 // are no more pending events.
515 //
516 GetDriverGlobals()->WaitForSignal(m_FinishDisposing.GetSelfPointer(),
517 "waiting for the queue to be deleted, WDFQUEUE", GetHandle(),
518 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
519 WaitSignalBreakUnderVerifier);
520
521
522 ASSERT(m_Deleted == TRUE);
523
524 ASSERT(m_Queue.GetRequestCount() == 0);
525 ASSERT(m_DriverIoCount == 0);
526
527 if (IsForwardProgressQueue()) {
528 FreeAllReservedRequests(TRUE);
529 ASSERT(IsListEmpty(&m_FwdProgContext->m_ReservedRequestList));
530 ASSERT(IsListEmpty(&m_FwdProgContext->m_PendedIrpList));
531 }
532
533 if (m_FwdProgContext != NULL) {
534 m_FwdProgContext->m_PendedReserveLock.Uninitialize();
535 FxPoolFree(m_FwdProgContext);
536 m_FwdProgContext = NULL;
537 }
538
539 //
540 // Rundown the workitem.
541 //
542 if (m_SystemWorkItem != NULL) {
543 m_SystemWorkItem->DeleteObject();
544 m_SystemWorkItem = NULL;
545 }
546
547 //
548 // Rundown the DPCs
549 //
550 if (m_DpcQueued) {
551 FlushQueuedDpcs();
552 }
553
554 //
555 // All callbacks into the device driver acquire and release a
556 // reference on the queue. This ensures that the queue will
557 // not actually complete deleting until return from any
558 // outstanding event callbacks into the device driver.
559 //
560 // See DispatchRequestToDriver()
561 //
562 End:
563
564 FxNonPagedObject::Dispose(); // __super call
565
566 return TRUE;
567 }
568
569 _Must_inspect_result_
570 NTSTATUS
ConfigureConstraints(__in_opt PWDF_OBJECT_ATTRIBUTES ObjectAttributes,__in_opt FxDriver * Caller)571 FxIoQueue::ConfigureConstraints(
572 __in_opt PWDF_OBJECT_ATTRIBUTES ObjectAttributes,
573 __in_opt FxDriver* Caller
574 )
575
576 /*++
577
578 Routine Description:
579
580 Configures the locking and threading model for the
581 Queue according to parameters specified by the device
582 driver when it initialized.
583
584 Arguments:
585
586 Returns:
587
588 NTSTATUS
589
590 --*/
591
592 {
593 WDF_EXECUTION_LEVEL ParentLevel;
594 WDF_SYNCHRONIZATION_SCOPE ParentScope;
595 BOOLEAN AutomaticLockingRequired;
596
597 AutomaticLockingRequired = FALSE;
598 ASSERT(m_Device != NULL);
599
600 //
601 // Initialize both spin and mutex locks
602 //
603 m_CallbackSpinLock.Initialize(this);
604 m_CallbackMutexLock.Initialize(this);
605
606 //
607 // If WDF_OBJECT_ATTRIBUTES is specified, these override any
608 // default settings.
609 //
610 if (ObjectAttributes != NULL) {
611 m_ExecutionLevel = ObjectAttributes->ExecutionLevel;
612 m_SynchronizationScope = ObjectAttributes->SynchronizationScope;
613 }
614
615 //
616 // If no WDFQUEUE specific attributes are specified, we
617 // get them from WDFDEVICE, which allows WDFDEVICE to
618 // provide a default for all WDFQUEUE's created.
619 //
620 m_Device->GetConstraints(&ParentLevel, &ParentScope);
621 ASSERT(ParentLevel != WdfExecutionLevelInheritFromParent);
622 ASSERT(ParentScope != WdfSynchronizationScopeInheritFromParent);
623
624 if (m_ExecutionLevel == WdfExecutionLevelInheritFromParent) {
625 m_ExecutionLevel = ParentLevel;
626 }
627
628 if (m_SynchronizationScope == WdfSynchronizationScopeInheritFromParent) {
629 m_SynchronizationScope = ParentScope;
630 }
631
632 //
633 // For backward compatibility purposes always have a lock associated with the
634 // object even for WdfSynchronizationScopeNone. This is so that we return a non-null
635 // presentation lock for the WDFQUEUE object.
636 //
637 if (m_ExecutionLevel == WdfExecutionLevelPassive) {
638 //
639 // Mark FxObject as passive level to ensure that Dispose and Destroy
640 // callbacks are passive to the driver
641 //
642 MarkPassiveCallbacks(ObjectDoNotLock);
643 m_PassiveLevel = TRUE;
644
645 //
646 // Passive Callbacks constraint has been set, we use a mutex for the
647 // callback lock.
648 //
649 m_CallbackLockPtr = &m_CallbackMutexLock;
650 m_CallbackLockObjectPtr = this;
651 }
652 else {
653 //
654 // If no passive level constraint is specified, then spinlocks
655 // are used for callbacks since they are lightweight and work with
656 // DPC's and Timer's
657 //
658 m_CallbackLockPtr = &m_CallbackSpinLock;
659 m_CallbackLockObjectPtr = this;
660 }
661
662 //
663 // Configure synchronization scope
664 //
665 if (m_SynchronizationScope == WdfSynchronizationScopeDevice) {
666 NTSTATUS status;
667
668 //
669 // WDF extensions are not allowed to use this type of synchronization.
670 //
671 if (Caller != NULL && Caller != GetDriverGlobals()->Driver) {
672 status = STATUS_INVALID_PARAMETER;
673 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
674 "WDFQUEUE 0x%p Synchronization scope is set to "
675 "device; WDF extension drivers are not allowed "
676 "to use this type of synchronization, %!STATUS!",
677 GetObjectHandle(), status);
678 return status;
679 }
680
681 //
682 // If we inherit the Sync. scope from parent or device
683 // and if the parent/device has Exec. Level different from Queue
684 // then disallow that case.
685 // FUTURE PROOF NOTE: Adding a new Execution Level will need reevaluation
686 // of the check below.
687 //
688 if (ParentLevel != m_ExecutionLevel) {
689 status = STATUS_INVALID_PARAMETER;
690 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
691 "WDFQUEUE 0x%p Synchronization scope is set to device"
692 " but the Device ExecutionLevel: 0x%x"
693 " doesn't match Queue ExecutionLevel: 0x%x, %!STATUS!",
694 GetObjectHandle(), ParentLevel,
695 m_ExecutionLevel, status);
696 return status;
697 }
698 //
699 // Per device automatic callback synchronization, so we update our
700 // callback lock ptr to point to the devices lock
701 //
702 AutomaticLockingRequired = TRUE;
703
704 //
705 // Get the callback lock and object from the device
706 //
707 m_CallbackLockPtr = m_Device->GetCallbackLockPtr(&m_CallbackLockObjectPtr);
708 }
709 else if (m_SynchronizationScope == WdfSynchronizationScopeQueue) {
710 //
711 // Per object automatic serialization
712 //
713 AutomaticLockingRequired = TRUE;
714
715 // m_CallbackLockPtr has been set above in execution level constraint
716 }
717
718
719 if (AutomaticLockingRequired) {
720 //
721 // If automatic locking has been configured, set the lock
722 // on the FxCallback object delegates
723 //
724 m_IoDefault.SetCallbackLockPtr(m_CallbackLockPtr);
725 m_IoStop.SetCallbackLockPtr(m_CallbackLockPtr);
726 m_IoResume.SetCallbackLockPtr(m_CallbackLockPtr);
727 m_IoRead.SetCallbackLockPtr(m_CallbackLockPtr);
728 m_IoWrite.SetCallbackLockPtr(m_CallbackLockPtr);
729 m_IoDeviceControl.SetCallbackLockPtr(m_CallbackLockPtr);
730 m_IoInternalDeviceControl.SetCallbackLockPtr(m_CallbackLockPtr);
731 m_PurgeComplete.SetCallbackLockPtr(m_CallbackLockPtr);
732 m_ReadyNotify.SetCallbackLockPtr(m_CallbackLockPtr);
733 m_IoCanceledOnQueue.SetCallbackLockPtr(m_CallbackLockPtr);
734
735 m_IoCancelCallbackLockPtr = m_CallbackLockPtr;
736 }
737 else {
738 //
739 // No automatic locking specified
740 //
741 m_IoDefault.SetCallbackLockPtr(NULL);
742 m_IoStop.SetCallbackLockPtr(NULL);
743 m_IoResume.SetCallbackLockPtr(NULL);
744 m_IoRead.SetCallbackLockPtr(NULL);
745 m_IoWrite.SetCallbackLockPtr(NULL);
746 m_IoDeviceControl.SetCallbackLockPtr(NULL);
747 m_IoInternalDeviceControl.SetCallbackLockPtr(NULL);
748 m_PurgeComplete.SetCallbackLockPtr(NULL);
749 m_ReadyNotify.SetCallbackLockPtr(NULL);
750 m_IoCanceledOnQueue.SetCallbackLockPtr(NULL);
751
752 m_IoCancelCallbackLockPtr = NULL;
753
754 }
755
756 return STATUS_SUCCESS;
757 }
758
759 WDF_IO_QUEUE_STATE
GetState(__out_opt PULONG pQueueCount,__out_opt PULONG pDriverCount)760 FxIoQueue::GetState(
761 __out_opt PULONG pQueueCount,
762 __out_opt PULONG pDriverCount
763 )
764 {
765 int stat;
766 ULONG QueueCount, DriverCount;
767
768 // Get request counts
769 GetRequestCount(&QueueCount, &DriverCount);
770
771 if (pQueueCount ) *pQueueCount = QueueCount;
772
773 if (pDriverCount ) *pDriverCount = DriverCount;
774
775 //
776 // First fill in the values that are kept up to date at runtime
777 //
778 stat = (int)m_QueueState & (int)(WdfIoQueueAcceptRequests | WdfIoQueueDispatchRequests);
779
780 //
781 // Set additional information bits from information retrieved
782 // from other sources. It's cheaper to get this info at the infrequent
783 // GetStatus time, rather than keep the bits up to date at each
784 // request and queue transition.
785 //
786 if (QueueCount == 0) {
787 stat = stat | (int)WdfIoQueueNoRequests;
788 }
789
790 if (DriverCount == 0) {
791 stat = stat | (int)WdfIoQueueDriverNoRequests;
792 }
793
794 if(m_PowerManaged) {
795
796 if (m_PowerState != FxIoQueuePowerOn) {
797 stat = stat | (int)WdfIoQueuePnpHeld;
798 }
799 }
800
801 return (WDF_IO_QUEUE_STATE)stat;
802 }
803
804 VOID
SetState(__in FX_IO_QUEUE_SET_STATE NewStatus)805 FxIoQueue::SetState(
806 __in FX_IO_QUEUE_SET_STATE NewStatus
807 )
808 {
809 int AllowedBits;
810
811 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
812
813 //
814 // Only allow setting of valid bits
815 //
816 AllowedBits = (int)(FxIoQueueSetAcceptRequests |
817 FxIoQueueClearAcceptRequests |
818 FxIoQueueSetDispatchRequests |
819 FxIoQueueClearDispatchRequests |
820 FxIoQueueSetShutdown |
821 FxIoQueueClearShutdown
822 );
823
824 if ((int)NewStatus & ~AllowedBits) {
825 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
826 "Invalid WDFQUEUE 0x%p state",
827 GetObjectHandle());
828 FxVerifierDbgBreakPoint(FxDriverGlobals);
829 return;
830 }
831
832 //
833 // Clear the high bit used to prevent accidental mixing of
834 // WDF_IO_QUEUE_STATE and FX_IO_QUEUE_SET_STATE
835 //
836 NewStatus = (FX_IO_QUEUE_SET_STATE)((int)NewStatus & 0x7FFFFFFF);
837
838 if (NewStatus & (int)FxIoQueueClearShutdown) {
839 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState & ~(int)FxIoQueueShutdown);
840 }
841
842 if (NewStatus & (int)FxIoQueueSetShutdown) {
843 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState | (int)FxIoQueueShutdown);
844 }
845
846 if (NewStatus & (int)FxIoQueueSetAcceptRequests) {
847 if (IsState(FxIoQueueShutdown) == FALSE) {
848 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState | (int)WdfIoQueueAcceptRequests);
849 }
850 else {
851 DoTraceLevelMessage(FxDriverGlobals,
852 TRACE_LEVEL_INFORMATION, TRACINGIO,
853 "WDFQUEUE 0x%p is shut down, preventing queue "
854 "from accepting requests",
855 GetObjectHandle());
856 }
857 }
858
859 if (NewStatus & (int)FxIoQueueClearAcceptRequests) {
860 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState & ~(int)WdfIoQueueAcceptRequests);
861 }
862
863 if (NewStatus & (int)FxIoQueueSetDispatchRequests) {
864 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState | (int)WdfIoQueueDispatchRequests);
865 //
866 // If the queue is allowed to dispatch new requests, we must clear this flag.
867 // See also WdfIoQueueStopAndPurge for more info about the flag.
868 //
869 m_CancelDispatchedRequests = FALSE;
870 }
871
872 if (NewStatus & (int)FxIoQueueClearDispatchRequests) {
873 m_QueueState = (FX_IO_QUEUE_STATE)((int)m_QueueState & ~(int)WdfIoQueueDispatchRequests);
874 }
875
876 return;
877 }
878
879 _Must_inspect_result_
880 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyGetRequestUpdateFlags)881 FX_VF_METHOD(FxIoQueue, VerifyGetRequestUpdateFlags) (
882 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
883 _In_ FxRequest* TagRequest
884 )
885 {
886 KIRQL irql;
887 NTSTATUS status;
888
889 PAGED_CODE_LOCKED();
890
891 if (TagRequest != NULL) {
892 //
893 // WdfIoQueueRetrieveFoundRequest is only valid on manual queues.
894 // v1.11 and above: driver is not required to find the request
895 // using WdfIoQueueFindRequest.
896 // v1.9 and below: driver is required to find the request
897 // using WdfIoQueueFindRequest.
898 //
899 if (FxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11)) {
900 if (m_Type != WdfIoQueueDispatchManual) {
901 status = STATUS_INVALID_DEVICE_REQUEST;
902 DoTraceLevelMessage(
903 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
904 "WdfIoQueueRetrieveFoundRequest is allowed "
905 "only on a manual queue 0x%p, %!STATUS!",
906 GetHandle(), status);
907 FxVerifierDbgBreakPoint(FxDriverGlobals);
908 return status;
909 }
910 }
911 else {
912 //
913 // Legacy validation.
914 //
915 TagRequest->Lock(&irql);
916 status = TagRequest->VerifyRequestIsTagRequest(FxDriverGlobals);
917 TagRequest->Unlock(irql);
918 if (!NT_SUCCESS(status)) {
919 return status;
920 }
921 }
922 }
923
924 Lock(&irql);
925 if ((m_Type == WdfIoQueueDispatchSequential) && (m_DriverIoCount == 0)) {
926
927 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
928 "Driver called WdfIoQueueRetrieveNextRequest on a sequential WDFQUEUE 0x%p with no "
929 "outstanding requests. This can cause a race with automatically dispatched "
930 "requests. Call WdfIoQueueRetrieveNextRequest before completing the current request(s)",
931 GetObjectHandle());
932
933 FxVerifierDbgBreakPoint(FxDriverGlobals);
934
935 // Allow them to continue, though this is a race condition in their driver
936 }
937 Unlock(irql);
938
939 return STATUS_SUCCESS;
940 }
941
942 VOID
FX_VF_METHOD(FxIoQueue,VerifyGetRequestRestoreFlags)943 FX_VF_METHOD(FxIoQueue, VerifyGetRequestRestoreFlags)(
944 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
945 _In_ FxRequest* pRequest
946 )
947 {
948 UNREFERENCED_PARAMETER(FxDriverGlobals);
949 KIRQL irql;
950
951 PAGED_CODE_LOCKED();
952 pRequest->Lock(&irql);
953
954 pRequest->ClearVerifierFlagsLocked(FXREQUEST_FLAG_TAG_REQUEST);
955 pRequest->SetVerifierFlagsLocked(FXREQUEST_FLAG_DRIVER_OWNED);
956
957 pRequest->Unlock(irql);
958 }
959
960 _Must_inspect_result_
961 NTSTATUS
GetRequest(__in_opt MdFileObject FileObject,__in_opt FxRequest * TagRequest,__deref_out FxRequest ** pOutRequest)962 FxIoQueue::GetRequest(
963 __in_opt MdFileObject FileObject,
964 __in_opt FxRequest* TagRequest,
965 __deref_out FxRequest** pOutRequest
966 )
967 /*++
968
969 Routine Description:
970
971 This method is called by
972
973 WdfIoQueueRetrieveNextRequest
974 WdfIoQueueRetrieveRequestByFileObject
975 WdfIoQueueRetrieveFoundRequest
976
977 to retrieve a request from the queue.
978
979 Arguments:
980
981 Returns:
982
983 NTSTATUS
984
985 --*/
986 {
987 NTSTATUS status;
988 FxRequest* pRequest = NULL;
989 FxRequestCompletionState oldState;
990 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
991 KIRQL irql;
992
993 status = VerifyGetRequestUpdateFlags(pFxDriverGlobals, TagRequest);
994 if(!NT_SUCCESS(status)){
995 return status;
996 }
997
998 //
999 // Don't allow on parallel queues
1000 //
1001 if ((m_Type != WdfIoQueueDispatchManual) &&
1002 (m_Type != WdfIoQueueDispatchSequential)) {
1003 status = STATUS_INVALID_DEVICE_STATE;
1004 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1005 "Cannot be called on a parallel WDFQUEUE 0x%p, %!STATUS!",
1006 GetObjectHandle(), status);
1007 return status;
1008 }
1009
1010 Lock(&irql);
1011
1012 //
1013 // Only if the queue state allows requests to be retrieved.
1014 // It's okay to retrieve requests while the queue is in a transitioning state.
1015 //
1016 if (m_PowerState == FxIoQueuePowerOff) {
1017 status = STATUS_WDF_PAUSED;
1018 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1019 "WDFQUEUE 0x%p is powered off, %!STATUS!",
1020 GetObjectHandle(), status);
1021 Unlock(irql);
1022 return status;
1023 }
1024
1025 //
1026 // See if the queue is (still) processing requests
1027 //
1028 if (!IsState(WdfIoQueueDispatchRequests)) {
1029 status = STATUS_WDF_PAUSED;
1030 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1031 "WDFQUEUE 0x%p is stopped, %!STATUS!",
1032 GetObjectHandle(), status);
1033 Unlock(irql);
1034 return status;
1035 }
1036
1037 #pragma warning(disable:4127)
1038 while (TRUE) {
1039
1040 #pragma warning(default:4127)
1041 //
1042 // Get the next FxRequest from the cancel safe queue
1043 //
1044 status = FxRequest::GetNextRequest(&m_Queue, FileObject, TagRequest, &pRequest);
1045 if (!NT_SUCCESS(status)) {
1046 //
1047 // This code address the following race condition:
1048 // 1) Queue has only one request (count 1).
1049 // 2) Request in queue is cancelled.
1050 // 3) Request's cancellation logic starts to run on thread 1.
1051 // 4) But before cancellation logic gets the queue's lock
1052 // thread 2 calls WdfIoQueueRetrieveNextRequest.
1053 // 5) WdfIoQueueRetrieveNextRequest returns no more requests.
1054 // Driver waits for the ReadyNotify callback. (count 1)
1055 // 6) Thread 3 adds a new request in queue. (count 1->2)
1056 // 7) Thread 1 finally runs. (count 2->1).
1057 // 8) At this point driver stops responding b/c it never receives ReadyNotify.
1058 //
1059 // This code below forces the queue logic to send a ReadyNotify
1060 // callback the next time a new request is added (in step 6 above).
1061 //
1062 if (STATUS_NO_MORE_ENTRIES == status &&
1063 NULL == FileObject && // WdfIoQueueRetrieveNextRequest
1064 NULL == TagRequest && // WdfIoQueueRetrieveNextRequest
1065 m_Queue.GetRequestCount() > 0L) {
1066
1067 m_ForceTransitionFromEmptyWhenAddingNewRequest = TRUE;
1068 }
1069
1070 Unlock(irql);
1071 return status;
1072 }
1073
1074 //
1075 // If we don't allow zero length read/write's to the driver,
1076 // complete it now with success and attempt to get another
1077 // request from the queue.
1078 //
1079 if (!m_AllowZeroLengthRequests) {
1080
1081
1082
1083
1084
1085 (VOID)pRequest->GetCurrentIrpStackLocation();
1086
1087 FxIrp* pIrp = pRequest->GetFxIrp();
1088 UCHAR majorFunction = pIrp->GetMajorFunction();
1089
1090 if ((majorFunction == IRP_MJ_READ) &&
1091 (pIrp->GetParameterReadLength() == 0)) {
1092
1093 Unlock(irql);
1094 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
1095 "Zero length WDFREQUEST 0x%p completed automatically by WDFQUEUE 0x%p",
1096 pRequest->GetHandle(),GetObjectHandle());
1097 pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
1098 pRequest->RELEASE(FXREQUEST_COMPLETE_TAG);
1099
1100 Lock(&irql);
1101
1102 // Get another request from the queue
1103 continue;
1104 }
1105 else if ((majorFunction == IRP_MJ_WRITE) &&
1106 (pIrp->GetParameterWriteLength() == 0)) {
1107
1108 Unlock(irql);
1109 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
1110 "Zero length WDFREQUEST 0x%p completed automatically by WDFQUEUE 0x%p",
1111 pRequest->GetHandle(), GetObjectHandle());
1112
1113 pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
1114 pRequest->RELEASE(FXREQUEST_COMPLETE_TAG);
1115
1116 Lock(&irql);
1117
1118 // Get another request from the queue
1119 continue;
1120 }
1121 }
1122
1123 break;
1124 }
1125
1126 // Increase the driver owned request count
1127 InsertInDriverOwnedList(pRequest);
1128
1129 Unlock(irql);
1130
1131 //
1132 // We don't need to check for PurgeComplete since
1133 // we are giving the request to the driver
1134 //
1135
1136 // pRequest is not cancellable now
1137
1138 //
1139 // We are now going to return the request
1140 // to the driver, and it must complete it.
1141 //
1142
1143 //
1144 // Set a completion event, this takes a reference
1145 //
1146 oldState = pRequest->SetCompletionState(FxRequestCompletionStateQueue);
1147 ASSERT(oldState == FxRequestCompletionStateNone);
1148 UNREFERENCED_PARAMETER(oldState);
1149
1150 //
1151 // Track that we have given the request to the driver
1152 //
1153 VerifyGetRequestRestoreFlags(pFxDriverGlobals, pRequest);
1154
1155 pRequest->SetPresented();
1156
1157 //
1158 // Release our original reference. The FxRequest::Complete
1159 // will release the final one since we have registered a completion
1160 // callback handler
1161 //
1162 // We now have one reference count on the FxRequest object until
1163 // its completion routine runs since the completion event made
1164 // an extra reference, and will dereference it when it fires, or
1165 // its canceled.
1166 //
1167
1168 pRequest->RELEASE(FXREQUEST_STATE_TAG);
1169
1170 // Return it to the driver
1171 *pOutRequest = pRequest;
1172
1173 return STATUS_SUCCESS;
1174 }
1175
1176 _Must_inspect_result_
1177 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyPeekRequest)1178 FX_VF_METHOD(FxIoQueue, VerifyPeekRequest) (
1179 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1180 _In_ FxRequest* TagRequest
1181 )
1182 {
1183 NTSTATUS status;
1184 KIRQL irql;
1185
1186 PAGED_CODE_LOCKED();
1187
1188 TagRequest->Lock(&irql);
1189 status = TagRequest->VerifyRequestIsTagRequest(FxDriverGlobals);
1190 TagRequest->Unlock(irql);
1191
1192 return status;
1193 }
1194
1195 _Must_inspect_result_
1196 NTSTATUS
PeekRequest(__in_opt FxRequest * TagRequest,__in_opt MdFileObject FileObject,__out_opt PWDF_REQUEST_PARAMETERS Parameters,__deref_out FxRequest ** pOutRequest)1197 FxIoQueue::PeekRequest(
1198 __in_opt FxRequest* TagRequest,
1199 __in_opt MdFileObject FileObject,
1200 __out_opt PWDF_REQUEST_PARAMETERS Parameters,
1201 __deref_out FxRequest** pOutRequest
1202 )
1203 /*++
1204
1205 Routine Description:
1206
1207 This method is called by WdfIoQueueFindRequest to
1208 look for a specific request from the queue. If tagrequest
1209 is not specified then this method will return the very
1210 first request from the queue.
1211
1212 If the fileobject is specified then fileobject is also
1213 used as one of the constrain for returing the request.
1214
1215 Important point to remember is that only request information
1216 is returned to the caller. The request is still present in
1217 the queue.
1218
1219 If the request is returned, there is an additional reference
1220 taken on the queue to prevent it from deletion while the
1221 caller is using the request handle. The caller has to
1222 explicitly drop the reference once he is done using the
1223 request handle.
1224
1225 Arguments:
1226
1227 Returns:
1228
1229 NTSTATUS
1230
1231 --*/
1232
1233 {
1234 NTSTATUS status;
1235 FxRequest* pRequest = NULL;
1236 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
1237 KIRQL irql;
1238
1239 //
1240 // FindRequest is allowed only on a manual queue.
1241 //
1242 if (m_Type != WdfIoQueueDispatchManual) {
1243 status = STATUS_INVALID_DEVICE_REQUEST;
1244 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1245 "FindRequest is allowed only on a manaul queue 0x%p, %!STATUS!",
1246 GetHandle(), status);
1247 FxVerifierDbgBreakPoint(pFxDriverGlobals);
1248 return status;
1249 }
1250
1251 if (TagRequest != NULL) {
1252 status = VerifyPeekRequest(pFxDriverGlobals, TagRequest);
1253 if (!NT_SUCCESS(status)) {
1254 return status;
1255 }
1256 }
1257
1258 //
1259 // Get the next FxRequest from the cancel safe queue
1260 //
1261 // If success, it will return a referenced FxRequest in
1262 // which the caller must release the reference.
1263 //
1264 Lock(&irql);
1265
1266 status = FxRequest::PeekRequest(
1267 &m_Queue,
1268 TagRequest,
1269 FileObject,
1270 Parameters,
1271 &pRequest
1272 );
1273
1274 //
1275 // This code address the following potential race condition:
1276 // 1) Queue has only one request (count 1).
1277 // 2) Request in queue is cancelled.
1278 // 3) Request's cancellation logic starts to run on thread 1.
1279 // 4) But before cancellation logic gets the queue's lock
1280 // thread 2 calls WdfIoQueueFindRequest to find any request.
1281 // 5) WdfIoQueueFindRequest returns no more requests.
1282 // Driver waits for the ReadyNotify callback. (count 1)
1283 // 6) Thread 3 adds a new request in queue. (count 1->2)
1284 // 7) Thread 1 finally runs. (count 2->1).
1285 // 8) At this point driver stops responding b/c it never receives ReadyNotify.
1286 //
1287 // This code below forces the queue logic to send a ReadyNotify
1288 // callback the next time a new request is added (in step 6 above).
1289 //
1290 if (STATUS_NO_MORE_ENTRIES == status &&
1291 NULL == FileObject && // WdfIoQueueFindRequest(any request)
1292 NULL == TagRequest && // WdfIoQueueFindRequest(any request)
1293 m_Queue.GetRequestCount() > 0L) {
1294
1295 m_ForceTransitionFromEmptyWhenAddingNewRequest = TRUE;
1296 }
1297
1298 Unlock(irql);
1299
1300 if (!NT_SUCCESS(status)) {
1301 return status;
1302 }
1303
1304 //
1305 // Mark it as a tag request to detect abuse since its not
1306 // driver owned.
1307 //
1308 if (pFxDriverGlobals->FxVerifierOn) {
1309 pRequest->SetVerifierFlags(FXREQUEST_FLAG_TAG_REQUEST);
1310 }
1311
1312 // Return it to the driver
1313 *pOutRequest = pRequest;
1314
1315 return status;
1316 }
1317
1318 SHORT
FX_VF_METHOD(FxIoQueue,VerifyForwardRequestUpdateFlags)1319 FX_VF_METHOD(FxIoQueue, VerifyForwardRequestUpdateFlags) (
1320 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1321 _In_ FxRequest* Request
1322 )
1323 {
1324 UNREFERENCED_PARAMETER(FxDriverGlobals);
1325 SHORT OldFlags = 0;
1326 KIRQL irql;
1327
1328 PAGED_CODE_LOCKED();
1329
1330 Request->Lock(&irql);
1331
1332 // Save old flags to put them back if forward fails
1333 OldFlags = Request->GetVerifierFlagsLocked();
1334
1335 //
1336 // Set that the request was forwarded. This effects
1337 // cancel behavior.
1338 //
1339 Request->SetVerifierFlagsLocked(FXREQUEST_FLAG_FORWARDED);
1340
1341 ASSERT((Request->GetVerifierFlagsLocked() & FXREQUEST_FLAG_DRIVER_OWNED) != 0);
1342
1343 // Set that the request is no longer driver owned
1344 Request->ClearVerifierFlagsLocked(
1345 FXREQUEST_FLAG_DRIVER_OWNED | FXREQUEST_FLAG_DRIVER_DISPATCH);
1346
1347 Request->Unlock(irql);
1348
1349 return OldFlags;
1350 }
1351
1352 _Must_inspect_result_
1353 NTSTATUS
ForwardRequestWorker(__in FxRequest * Request,__in FxIoQueue * DestQueue)1354 FxIoQueue::ForwardRequestWorker(
1355 __in FxRequest* Request,
1356 __in FxIoQueue* DestQueue
1357 )
1358 {
1359 NTSTATUS status;
1360 FxRequestCompletionState oldState;
1361 PLIST_ENTRY ple;
1362 SHORT OldFlags;
1363 KIRQL irql;
1364
1365 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
1366
1367 OldFlags = 0;
1368
1369 //
1370 // The request has only one reference, held by the completion
1371 // callback function. We need to take another one before cancelling
1372 // this function, otherwise we will lose the request object
1373 //
1374 Request->ADDREF(FXREQUEST_STATE_TAG);
1375
1376 //
1377 // Cancel its current completion event for this queue
1378 //
1379 oldState = Request->SetCompletionState(FxRequestCompletionStateNone);
1380 ASSERT(oldState == FxRequestCompletionStateQueue);
1381
1382 OldFlags = VerifyForwardRequestUpdateFlags(FxDriverGlobals, Request);
1383
1384 //
1385 // Remove it from this queues driver owned list.
1386 //
1387 // This must be done before forward since new queue will
1388 // use the list entry in the FxRequest
1389 //
1390 // We can't use RemoveFromDriverOwnedList since we want the
1391 // m_DriverIoCount to be left alone in case the forward fails.
1392 // If we don't, another thread can run when we drop the lock, notice
1393 // that there are no more requests, and raise the purged and empty
1394 // events. But if the forward fails, the request will wind up back
1395 // on the queue! So m_DriverIoCount is used as a gate to prevent
1396 // these events from firing until we are really sure this queue
1397 // is done with the request.
1398 //
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410 Lock(&irql);
1411 ple = Request->GetListEntry(FxListEntryDriverOwned);
1412 RemoveEntryList(ple);
1413 InitializeListHead(ple);
1414 Unlock(irql);
1415
1416 //
1417 // Attempt to pass the request onto the target queue
1418 //
1419 status = DestQueue->QueueRequestFromForward(Request);
1420 if (!NT_SUCCESS(status)) {
1421
1422 //
1423 // Target queue did not accept the request, so we
1424 // restore the original completion callback function
1425 // and flags
1426 //
1427 oldState = Request->SetCompletionState(oldState);
1428 ASSERT(oldState == FxRequestCompletionStateNone);
1429 UNREFERENCED_PARAMETER(oldState);
1430
1431 if (FxDriverGlobals->FxVerifierOn) {
1432 Request->SetVerifierFlags(OldFlags);
1433 }
1434
1435 // Release the extra reference we took
1436 Request->RELEASE(FXREQUEST_STATE_TAG);
1437
1438 Lock(&irql);
1439 // Place it back on the driver owned list
1440 InsertTailList(&m_DriverOwned, ple);
1441 Unlock(irql);
1442 }
1443 else {
1444
1445 Lock(&irql);
1446
1447 // Request is no longer part of the I/O count for this queue
1448 m_DriverIoCount--;
1449
1450 ASSERT(m_DriverIoCount >= 0);
1451
1452 //
1453 // Don't run the event dispatcher if we are called from a
1454 // dispath routine in order to prevent stack recursion.
1455 // Since some other thread (possibly this thread higher on
1456 // the stack) is running the dispatcher, no events will get lost.
1457 //
1458 //
1459 // This returns with the IoQueue lock released
1460 //
1461 DispatchInternalEvents(irql);
1462
1463 //
1464 // We don't dereference the request object since the new IoQueue
1465 // will release it when it is done.
1466 //
1467 }
1468
1469 return status;
1470 }
1471
1472 _Must_inspect_result_
1473 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyForwardRequestToParent)1474 FX_VF_METHOD(FxIoQueue, VerifyForwardRequestToParent) (
1475 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1476 _In_ FxIoQueue* DestQueue,
1477 _In_ FxRequest* Request
1478 )
1479 {
1480 KIRQL irql;
1481 NTSTATUS status;
1482
1483 PAGED_CODE_LOCKED();
1484
1485 if (m_Device->m_ParentDevice == NULL) {
1486 status = STATUS_INVALID_DEVICE_REQUEST;
1487 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1488 "No parent device for WDFQUEUE 0x%p Device, %!STATUS!",
1489 DestQueue->m_Device->GetHandle(), status);
1490 FxVerifierDbgBreakPoint(FxDriverGlobals);
1491 goto Done;
1492 }
1493
1494 Request->Lock(&irql);
1495
1496 status = Request->VerifyRequestIsDriverOwned(FxDriverGlobals);
1497
1498 if (NT_SUCCESS(status)) {
1499 status = Request->VerifyRequestIsNotCancelable(FxDriverGlobals);
1500 }
1501
1502 Request->Unlock(irql);
1503
1504 if (!NT_SUCCESS(status)) {
1505 goto Done;
1506 }
1507
1508 if (DestQueue == this) {
1509 status = STATUS_INVALID_DEVICE_REQUEST;
1510 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1511 "Cannot forward a request to the same WDFQUEUE 0x%p"
1512 " %!STATUS!", GetObjectHandle(), status);
1513 FxVerifierDbgBreakPoint(FxDriverGlobals);
1514 goto Done;
1515 }
1516
1517 if (m_Device->m_ParentDevice != DestQueue->m_Device) {
1518 status = STATUS_INVALID_DEVICE_REQUEST;
1519 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1520 "Cannot forward a request to "
1521 "a different WDFDEVICE 0x%p which is not the "
1522 "parent, %!STATUS!",
1523 DestQueue->m_Device->GetHandle(),
1524 status);
1525 FxVerifierDbgBreakPoint(FxDriverGlobals);
1526 goto Done;
1527 }
1528
1529 if (Request->IsReserved()) {
1530 status = STATUS_INVALID_DEVICE_REQUEST;
1531 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1532 "Cannot forward reserved WDFREQUEST 0x%p to a "
1533 "parent WDFDEVICE 0x%p, %!STATUS!",
1534 Request->GetHandle(),
1535 DestQueue->m_Device->GetHandle(),
1536 status);
1537 FxVerifierDbgBreakPoint(FxDriverGlobals);
1538 goto Done;
1539 }
1540
1541 //
1542 // Make sure the child device is a PDO
1543 //
1544 ASSERT(m_Device->IsPdo());
1545
1546 //
1547 // Check if the WdfPdoInitSetForwardRequestToParent was called to increase
1548 // the StackSize of the child Device to include the stack size of the
1549 // parent Device
1550 //
1551 if (m_Device->IsPnp()
1552 &&
1553 m_Device->GetPdoPkg()->m_AllowForwardRequestToParent == FALSE) {
1554 status = STATUS_INVALID_DEVICE_REQUEST;
1555 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1556 "WdfPdoInitSetForwardRequestToParent not called on "
1557 "WDFDEVICE 0x%p, %!STATUS!", m_Device->GetHandle(),
1558 status);
1559 FxVerifierDbgBreakPoint(FxDriverGlobals);
1560 goto Done;
1561 }
1562
1563 Done:
1564 return status;
1565 }
1566
1567 _Must_inspect_result_
1568 NTSTATUS
ForwardRequestToParent(__in FxIoQueue * DestQueue,__in FxRequest * Request,__in PWDF_REQUEST_FORWARD_OPTIONS ForwardOptions)1569 FxIoQueue::ForwardRequestToParent(
1570 __in FxIoQueue* DestQueue,
1571 __in FxRequest* Request,
1572 __in PWDF_REQUEST_FORWARD_OPTIONS ForwardOptions
1573 )
1574
1575 /*++
1576
1577 Routine Description:
1578
1579 ForwardRequest is called from the drivers EvtIoDefault routine
1580 and the following conditions apply:
1581
1582 Request is not on a CSQ and not cancellable
1583
1584 Request is FXREQUEST_FLAG_DRIVER_OWNED
1585
1586 m_DriverIoCount has been incremented to reflect the request
1587
1588 Request has an I/O completion callback function pointing to
1589 FxIoQueueRequestComplete with the context for this Queue
1590
1591 The Request has one reference count from the I/O completion callback.
1592
1593 If a driver calls this API, it will not complete the request
1594 as a result of this queues EvtIoDefault, and does not own
1595 the request until it has been re-presented by the Destination
1596 Queue.
1597
1598 Arguments:
1599
1600 Returns:
1601
1602 NTSTATUS
1603
1604 --*/
1605 {
1606 NTSTATUS status;
1607 PFX_DRIVER_GLOBALS pFxDriverGlobals;
1608 BOOLEAN forwardRequestToParent;
1609 FxIrp* pIrp;
1610
1611 UNREFERENCED_PARAMETER(ForwardOptions);
1612
1613 pFxDriverGlobals = GetDriverGlobals();
1614
1615 forwardRequestToParent = Request->m_ForwardRequestToParent;
1616
1617 status = VerifyForwardRequestToParent(pFxDriverGlobals,
1618 DestQueue,
1619 Request);
1620 if(!NT_SUCCESS(status)){
1621 return status;
1622 }
1623
1624 pIrp = Request->GetFxIrp();
1625
1626 pIrp->CopyCurrentIrpStackLocationToNext();
1627 pIrp->SetNextIrpStackLocation();
1628
1629 //
1630 // Save a pointer to the device object for this request so that it can
1631 // be used later in completion.
1632 //
1633 pIrp->SetCurrentDeviceObject(m_Device->m_ParentDevice->GetDeviceObject());
1634
1635 Request->SetDeviceBase((CfxDeviceBase *)m_Device->m_ParentDevice);
1636 Request->m_ForwardRequestToParent = TRUE;
1637
1638 status = ForwardRequestWorker(Request, DestQueue);
1639
1640 //
1641 // Undo the actions of changing the FxDevice and
1642 // changing the deviceObject and stack location in the IRP
1643 //
1644 if (!NT_SUCCESS(status)) {
1645 Request->SetDeviceBase((CfxDeviceBase *)m_Device);
1646 pIrp = Request->GetFxIrp();
1647 pIrp->SkipCurrentIrpStackLocation();
1648 ASSERT(pIrp->GetDeviceObject() == m_Device->GetDeviceObject());
1649
1650 //
1651 // Set the value of m_ForwardRequestToParent to the previous
1652 // value so that if the Request has been forwarded to Parent
1653 // successfully but fails to be forwarded to the grandparent
1654 // from the parent then we free it back using ExFreePool
1655 // instead of the Lookaside buffer .
1656 //
1657 Request->m_ForwardRequestToParent = forwardRequestToParent;
1658 }
1659
1660 return status;
1661 }
1662
1663 _Must_inspect_result_
1664 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyForwardRequest)1665 FX_VF_METHOD(FxIoQueue, VerifyForwardRequest) (
1666 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1667 _In_ FxIoQueue* pDestQueue,
1668 _In_ FxRequest* pRequest
1669 )
1670 {
1671 NTSTATUS status;
1672 KIRQL irql;
1673
1674 PAGED_CODE_LOCKED();
1675
1676 pRequest->Lock(&irql);
1677
1678 status = pRequest->VerifyRequestIsDriverOwned(FxDriverGlobals);
1679 if (NT_SUCCESS(status)) {
1680 status = pRequest->VerifyRequestIsNotCancelable(FxDriverGlobals);
1681 }
1682
1683 pRequest->Unlock(irql);
1684
1685 if (!NT_SUCCESS(status)) {
1686 return status;
1687 }
1688
1689 if (pDestQueue == this) {
1690 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1691 "Cannot forward a request to the same WDFQUEUE 0x%p"
1692 " %!STATUS!",
1693 GetObjectHandle(),
1694 STATUS_INVALID_DEVICE_REQUEST);
1695 FxVerifierDbgBreakPoint(FxDriverGlobals);
1696 return STATUS_INVALID_DEVICE_REQUEST;
1697 }
1698
1699 if ((m_Device != pDestQueue->m_Device)) {
1700 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1701 "Cannot forward a request to a different WDFDEVICE 0x%p",
1702 pDestQueue->m_Device->GetHandle());
1703 FxVerifierDbgBreakPoint(FxDriverGlobals);
1704 return STATUS_INVALID_DEVICE_REQUEST;
1705 }
1706
1707 return status;
1708 }
1709
1710 _Must_inspect_result_
1711 NTSTATUS
ForwardRequest(__in FxIoQueue * pDestQueue,__in FxRequest * pRequest)1712 FxIoQueue::ForwardRequest(
1713 __in FxIoQueue* pDestQueue,
1714 __in FxRequest* pRequest
1715 )
1716 /*++
1717
1718 Routine Description:
1719
1720 ForwardRequest is called from the drivers EvtIoDefault routine
1721 and the following conditions apply:
1722
1723 Request is not on a CSQ and not cancellable
1724
1725 Request is FXREQUEST_FLAG_DRIVER_OWNED
1726
1727 m_DriverIoCount has been incremented to reflect the request
1728
1729 Request has an I/O completion callback function pointing to
1730 FxIoQueueRequestComplete with the context for this Queue
1731
1732 The Request has one reference count from the I/O completion callback.
1733
1734 If a driver calls this API, it will not complete the request
1735 as a result of this queues EvtIoDefault, and does not own
1736 the request until it has been re-presented by the Destination
1737 Queue.
1738
1739 Arguments:
1740
1741 Returns:
1742
1743 NTSTATUS
1744
1745 --*/
1746 {
1747 NTSTATUS status;
1748
1749 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
1750
1751 status = VerifyForwardRequest(FxDriverGlobals, pDestQueue, pRequest);
1752 if (!NT_SUCCESS(status)) {
1753 return status;
1754 }
1755
1756 status = ForwardRequestWorker(pRequest, pDestQueue);
1757 return status;
1758 }
1759
1760 _Must_inspect_result_
1761 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyQueueDriverCreatedRequest)1762 FX_VF_METHOD(FxIoQueue, VerifyQueueDriverCreatedRequest) (
1763 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1764 _In_ FxRequest* Request,
1765 _Inout_ SHORT* OldFlags
1766 )
1767 {
1768 NTSTATUS status;
1769 KIRQL irql;
1770
1771 PAGED_CODE_LOCKED();
1772
1773 Request->Lock(&irql);
1774
1775 *OldFlags = Request->GetVerifierFlagsLocked();
1776 ASSERT((FXREQUEST_FLAG_DRIVER_DISPATCH & (*OldFlags)) == 0);
1777
1778 status = Request->VerifyRequestIsNotCancelable(FxDriverGlobals);
1779 if (NT_SUCCESS(status)) {
1780 // Clear the driver owned flag.
1781 ASSERT((FXREQUEST_FLAG_DRIVER_OWNED & (*OldFlags)) != 0);
1782 Request->ClearVerifierFlagsLocked(FXREQUEST_FLAG_DRIVER_OWNED);
1783 }
1784
1785 Request->Unlock(irql);
1786 return status;
1787 }
1788
1789 _Must_inspect_result_
1790 NTSTATUS
QueueDriverCreatedRequest(__in FxRequest * Request,__in BOOLEAN ParentQueue)1791 FxIoQueue::QueueDriverCreatedRequest(
1792 __in FxRequest* Request,
1793 __in BOOLEAN ParentQueue
1794 )
1795 /*++
1796
1797 Routine Description:
1798
1799 Insert a driver-created-request into this queue.
1800 The following conditions apply:
1801
1802 Request is not on a CSQ and not cancellable.
1803
1804 Request doesn't have the FXREQUEST_FLAG_DRIVER_OWNED set yet.
1805
1806 Request doesn't have an I/O completion callback function pointing to
1807 FxIoQueueRequestComplete since the original queue is NULL.
1808
1809 The Request has one reference count from WdfRequestCreate[FromIrp].
1810
1811 On a successful return, the request is owned by the queue. Driver can complete
1812 this request only after it has been re-presented to the driver.
1813
1814 Arguments:
1815
1816 Request - Driver created request (already validated by public API) to
1817 insert in queue.
1818
1819 ParentQueue - TRUE if the queue is owned by the parent device.
1820
1821 Returns:
1822
1823 NTSTATUS
1824
1825 --*/
1826 {
1827 NTSTATUS status;
1828 CfxDeviceBase * origDeviceBase;
1829 SHORT oldFlags = 0;
1830 FxIrp* fxIrp;
1831
1832 PFX_DRIVER_GLOBALS fxDriverGlobals = GetDriverGlobals();
1833 fxIrp = Request->GetFxIrp();
1834
1835 status = VerifyQueueDriverCreatedRequest(fxDriverGlobals, Request, &oldFlags);
1836 if(!NT_SUCCESS(status)) {
1837 return status;
1838 }
1839
1840 ASSERT(Request->SetCompletionState(FxRequestCompletionStateNone) ==
1841 FxRequestCompletionStateNone);
1842
1843 //
1844 // If this is the parent queue, we need to adjust the IRP's stack.
1845 //
1846 if (ParentQueue) {
1847
1848 //
1849 // IRP should not have a completion routine set yet.
1850 //
1851
1852 ASSERT(fxIrp->GetNextCompletionRoutine() == NULL);
1853
1854 fxIrp->CopyCurrentIrpStackLocationToNext();
1855 fxIrp->SetNextIrpStackLocation();
1856
1857 //
1858 // Save a pointer to the device object for this request so that it can
1859 // be used later in completion.
1860 //
1861 fxIrp->SetCurrentDeviceObject(m_Device->GetDeviceObject());
1862 }
1863
1864 origDeviceBase = Request->GetDeviceBase();
1865 Request->SetDeviceBase((CfxDeviceBase *)m_Device);
1866
1867 //
1868 // Attempt to insert the request into the queue
1869 //
1870 status = QueueRequestFromForward(Request);
1871 if (!NT_SUCCESS(status)) {
1872 //
1873 // Request was not accepted, restore the original DeviceBase and flags.
1874 //
1875 ASSERT(Request->SetCompletionState(FxRequestCompletionStateNone) ==
1876 FxRequestCompletionStateNone);
1877
1878 //
1879 // Restore original device/info.
1880 //
1881 Request->SetDeviceBase(origDeviceBase);
1882
1883 if (fxDriverGlobals->FxVerifierOn) {
1884 Request->SetVerifierFlags(oldFlags);
1885 }
1886
1887 //
1888 // If this is the parent queue, we need to adjust the IRP's stack.
1889 //
1890 if (ParentQueue) {
1891 fxIrp->SkipCurrentIrpStackLocation();
1892 //
1893 // There is no completion routine. See above assert.
1894 //
1895 Request->m_Irp.ClearNextStack();
1896 }
1897 }
1898
1899 return status;
1900 }
1901
1902 _Must_inspect_result_
1903 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyRequeue)1904 FX_VF_METHOD(FxIoQueue, VerifyRequeue) (
1905 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
1906 _In_ FxRequest* pRequest
1907 )
1908 {
1909 NTSTATUS status = STATUS_SUCCESS;
1910 KIRQL irql;
1911
1912 PAGED_CODE_LOCKED();
1913
1914 pRequest->Lock(&irql);
1915
1916 status = pRequest->VerifyRequestIsDriverOwned(FxDriverGlobals);
1917 if (NT_SUCCESS(status)) {
1918 status = pRequest->VerifyRequestIsNotCancelable(FxDriverGlobals);
1919 }
1920
1921 if (NT_SUCCESS(status)) {
1922 pRequest->ClearVerifierFlagsLocked(FXREQUEST_FLAG_DRIVER_OWNED |
1923 FXREQUEST_FLAG_DRIVER_DISPATCH);
1924 }
1925 pRequest->Unlock(irql);
1926
1927 return status;
1928 }
1929
1930
1931 _Must_inspect_result_
1932 NTSTATUS
Requeue(__in FxRequest * pRequest)1933 FxIoQueue::Requeue(
1934 __in FxRequest* pRequest
1935 )
1936 {
1937 NTSTATUS status;
1938 FxRequestCompletionState oldState;
1939 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
1940 KIRQL irql;
1941
1942 status = VerifyRequeue(FxDriverGlobals, pRequest);
1943 if (!NT_SUCCESS(status)) {
1944 return status;
1945 }
1946
1947 //
1948 // Requeue is allowed only on Manual queue.
1949 //
1950 if(pRequest->GetCurrentQueue()->m_Type != WdfIoQueueDispatchManual) {
1951
1952 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1953 "Requeue is allowed only for "
1954 "a manual queue, WDFREQUEST 0x%p "
1955 "%!STATUS!",
1956 pRequest,
1957 STATUS_INVALID_DEVICE_REQUEST);
1958 FxVerifierDbgBreakPoint(FxDriverGlobals);
1959 return STATUS_INVALID_DEVICE_REQUEST;
1960 }
1961
1962 //
1963 // The request has only one reference, held by the completion
1964 // callback function. We need to take another one before cancelling
1965 // this function, otherwise we will lose the request object
1966 //
1967 pRequest->ADDREF(FXREQUEST_STATE_TAG);
1968
1969 // Cancel the request complete callback (deletes a reference)
1970 oldState = pRequest->SetCompletionState(FxRequestCompletionStateNone);
1971 ASSERT(oldState == FxRequestCompletionStateQueue);
1972 UNREFERENCED_PARAMETER(oldState);
1973
1974 Lock(&irql);
1975
1976 //
1977 // We are going to place the request back on the queue
1978 //
1979
1980
1981 // Driver did not accept the I/O
1982 RemoveFromDriverOwnedList(pRequest);
1983
1984 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
1985 "WDFREQUEST 0x%p", pRequest->GetHandle());
1986
1987 //
1988 // Check if we need to delete this request.
1989 //
1990 if (m_CancelDispatchedRequests) {
1991 //
1992 // Do not requeue this request.
1993 //
1994 status = STATUS_CANCELLED;
1995 }
1996 else {
1997 //
1998 // Place the request back at the head of the main queue
1999 // so as not to re-order requests
2000 //
2001 status = pRequest->InsertHeadIrpQueue(&m_Queue, NULL);
2002 }
2003
2004 if (!NT_SUCCESS(status)) {
2005
2006 // Request did not get placed in queue
2007 ASSERT(status == STATUS_CANCELLED);
2008 //
2009 // Let the caller think the request is requeued successfully
2010 // because this is no different from the request cancelling
2011 // while it's in the queue. By returning STATUS_CANCELLED
2012 // the caller can't take any recovery action anyways
2013 // because the request is gone.
2014 //
2015 status = STATUS_SUCCESS;
2016
2017 //
2018 // We must add a reference since the CancelForQueue path
2019 // assumes we were on the FxIrpQueue with the extra reference
2020 //
2021 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
2022
2023 //
2024 // Mark the request as cancelled, place it on the cancel list,
2025 // and schedule the cancel event to the driver
2026 //
2027 CancelForQueue(pRequest, irql);
2028
2029 Lock(&irql);
2030 }
2031 else {
2032 // Check if went from no requests to have requests
2033 CheckTransitionFromEmpty();
2034 }
2035
2036 //
2037 // Visit the DispatchEvent so that we can deliver EvtIoReadyNotify
2038 //
2039 DispatchEvents(irql);
2040
2041 return status;
2042 }
2043
2044 _Must_inspect_result_
2045 NTSTATUS
FX_VF_METHOD(FxIoQueue,VerifyRequestCancelable)2046 FX_VF_METHOD(FxIoQueue, VerifyRequestCancelable) (
2047 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
2048 _In_ FxRequest* pRequest,
2049 _In_ BOOLEAN Cancelable
2050 )
2051 {
2052 NTSTATUS status;
2053 KIRQL irql;
2054
2055 PAGED_CODE_LOCKED();
2056
2057 pRequest->Lock(&irql);
2058
2059 // Make sure the driver owns the request
2060 status = pRequest->VerifyRequestIsDriverOwned(FxDriverGlobals);
2061 if (!NT_SUCCESS(status)) {
2062 goto Done;
2063 }
2064
2065 if (Cancelable) {
2066 //
2067 // Make sure the request is not cancelable for it to be made
2068 // cancelable.
2069 //
2070 status = pRequest->VerifyRequestIsNotCancelable(FxDriverGlobals);
2071 if (!NT_SUCCESS(status)) {
2072 goto Done;
2073 }
2074 }
2075 else {
2076 //
2077 // Make sure the request is cancelable for it to be made
2078 // uncancelable.
2079 //
2080 status = pRequest->VerifyRequestIsCancelable(FxDriverGlobals);
2081 if (!NT_SUCCESS(status)) {
2082 goto Done;
2083 }
2084 }
2085
2086 Done:
2087 pRequest->Unlock(irql);
2088 return status;
2089 }
2090
2091 _Must_inspect_result_
2092 NTSTATUS
RequestCancelable(__in FxRequest * pRequest,__in BOOLEAN Cancelable,__in_opt PFN_WDF_REQUEST_CANCEL EvtRequestCancel,__in BOOLEAN FailIfIrpIsCancelled)2093 FxIoQueue::RequestCancelable(
2094 __in FxRequest* pRequest,
2095 __in BOOLEAN Cancelable,
2096 __in_opt PFN_WDF_REQUEST_CANCEL EvtRequestCancel,
2097 __in BOOLEAN FailIfIrpIsCancelled
2098 )
2099 /*++
2100
2101 Routine Description:
2102
2103 This is called to mark or unmark the request cancelable.
2104
2105 Arguments:
2106
2107 FxRequest* - Request that is completing
2108
2109 Cancelable - if TRUE, mark the request cancellable
2110 if FALSE, mark the request not cancelable
2111 if it's previously marked canceelable.
2112
2113 EvtRequestCancel - points to driver provided cancel routine
2114 if the cancelable flag is TRUE.
2115
2116 FailIfIrpIsCancelled - if FALSE and the IRP is already cancelled,
2117 call the provided cancel routine and
2118 return success.
2119 if TRUE and the IRP is already cancelled,
2120 return STATUS_CANCELLED.
2121
2122 Returns:
2123
2124 NTSTATUS
2125
2126 --*/
2127 {
2128 NTSTATUS status;
2129 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
2130 KIRQL irql;
2131
2132 status = VerifyRequestCancelable(FxDriverGlobals, pRequest, Cancelable);
2133 if(!NT_SUCCESS(status)) {
2134 return status;
2135 }
2136
2137 if (Cancelable) {
2138
2139 if (FxDriverGlobals->FxVerifierOn) {
2140 pRequest->SetVerifierFlags(FXREQUEST_FLAG_DRIVER_CANCELABLE);
2141 }
2142 //
2143 // Set the Request for cancel status by inserting in the driver owned
2144 // CSQ. Note: This could fire the cancel callback right away
2145 // if the IRP was already cancelled.
2146 //
2147
2148 ASSERT(EvtRequestCancel);
2149
2150 Lock(&irql);
2151
2152 pRequest->m_CancelRoutine.m_Cancel = EvtRequestCancel;
2153
2154 //
2155 // Check if we need to delete this request.
2156 //
2157 if (m_CancelDispatchedRequests) {
2158 //
2159 // Purge is in progress, cancel this request.
2160 //
2161 status = STATUS_CANCELLED;
2162 }
2163 else {
2164 status = pRequest->InsertTailIrpQueue(&m_DriverCancelable, NULL);
2165 }
2166
2167 if (NT_SUCCESS(status)) {
2168 Unlock(irql);
2169 }
2170 else if (FailIfIrpIsCancelled == FALSE) {
2171
2172 ASSERT(status == STATUS_CANCELLED);
2173
2174 // This is not an error to the driver
2175 status = STATUS_SUCCESS;
2176
2177 pRequest->m_Canceled = TRUE;
2178
2179 Unlock(irql);
2180
2181 //
2182 // We must add a reference since the CancelForDriver path
2183 // assumes we were on the FxIrpQueue with the extra reference
2184 //
2185 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
2186
2187 //
2188 // Mark the request as cancelled, place it on the cancel list,
2189 // and schedule the cancel event to the driver
2190 //
2191 CancelForDriver(pRequest);
2192 }
2193 else {
2194
2195 ASSERT(status == STATUS_CANCELLED);
2196
2197 pRequest->m_CancelRoutine.m_Cancel = NULL;
2198
2199 //
2200 // Let the caller complete the request with STATUS_CANCELLED.
2201 //
2202 Unlock(irql);
2203
2204 if (FxDriverGlobals->FxVerifierOn) {
2205 pRequest->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_CANCELABLE);
2206 }
2207 }
2208
2209 return status;
2210 }
2211 else {
2212 //
2213 // This can return STATUS_CANCELLED if the request
2214 // has been canceled already
2215 //
2216 Lock(&irql);
2217 status = pRequest->RemoveFromIrpQueue(&m_DriverCancelable);
2218
2219 if (NT_SUCCESS(status)) {
2220 pRequest->m_CancelRoutine.m_Cancel = NULL;
2221 }
2222 else {
2223 //
2224 // In the failure case, the cancel routine has won the race and will
2225 // be invoked on another thread.
2226 //
2227 DO_NOTHING();
2228 }
2229 Unlock(irql);
2230
2231 if (FxDriverGlobals->FxVerifierOn) {
2232
2233 // We got the request back, can clear the cancelable flag
2234 if (NT_SUCCESS(status)) {
2235 pRequest->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_CANCELABLE);
2236 }
2237 }
2238
2239 return status;
2240 }
2241 }
2242
2243 _Must_inspect_result_
2244 NTSTATUS
QueueRequest(__in FxRequest * pRequest)2245 FxIoQueue::QueueRequest(
2246 __in FxRequest* pRequest
2247 )
2248
2249 /*++
2250
2251 Routine Description:
2252
2253 Enqueue a request to the end of the queue.
2254
2255 Note: This routine owns the final disposition of
2256 the Request object, and must handle it and
2257 dereference even if the driver does not.
2258
2259 Arguments:
2260
2261 pRequest - Pointer to Request object
2262
2263 Returns:
2264
2265 NTSTATUS
2266 --*/
2267
2268 {
2269 NTSTATUS Status;
2270 KIRQL irql;
2271 MdIrp pIrp;
2272 FxIrp* pFxIrp;
2273
2274 // Get IoQueue Object Lock
2275 Lock(&irql);
2276
2277 ASSERT(pRequest->GetRefCnt() == 1);
2278
2279 //
2280 // If the request is reserved, take an additional reference. This reference
2281 // will be released when the request is completed. This additional reference
2282 // enables us to detect 2 to 1 transition in the completion path so that
2283 // we can reclaim the reserved request for reuse.
2284 //
2285 if (pRequest->IsReserved()) {
2286 pRequest->ADDREF(FXREQUEST_FWDPRG_TAG);
2287 }
2288
2289 //
2290 // If queue is not taking new requests, fail now
2291 //
2292 if (!IsState(WdfIoQueueAcceptRequests)) {
2293
2294 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
2295 "WDFQUEUE 0x%p is not accepting requests, "
2296 "state is %!WDF_IO_QUEUE_STATE!, %s"
2297 "completing WDFREQUEST 0x%p %!STATUS!",
2298 GetObjectHandle(), m_QueueState,
2299 IsState(FxIoQueueShutdown) ?
2300 "power stopping (Drain) in progress," : "",
2301 pRequest->GetHandle(),
2302 STATUS_INVALID_DEVICE_STATE);
2303
2304 // Must release IoQueue object Lock
2305 Unlock(irql);
2306
2307 Status = STATUS_INVALID_DEVICE_STATE;
2308
2309 // Complete it with error
2310 pRequest->CompleteWithInformation(Status, 0);
2311
2312 // Dereference request object
2313 pRequest->RELEASE(FXREQUEST_COMPLETE_TAG);
2314
2315 return Status;
2316 }
2317
2318 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
2319 "Queuing WDFREQUEST 0x%p on WDFQUEUE 0x%p",
2320 pRequest->GetHandle(),GetObjectHandle());
2321
2322 (VOID)pRequest->GetIrp(&pIrp);
2323
2324 pFxIrp = pRequest->GetFxIrp();
2325
2326 pFxIrp->MarkIrpPending();
2327
2328 //
2329 // If the request is reserved, we may be called to dispatch
2330 // a pending reserved IRP from within the context of the completion routine.
2331 // So to avoid recursion, we will insert the request in the queue and try
2332 // to dispatch in the return path. If the request is not reserved then we
2333 // will dispatch it directly because this path is meant for dispatching new
2334 // incoming I/O. There is no concern for running into recursion in that
2335 // scenario.
2336 //
2337 if (pRequest->IsReserved() && m_Dispatching != 0) {
2338 InsertNewRequestLocked(&pRequest, irql);
2339 Unlock(irql);
2340 }
2341 else {
2342 DispatchEvents(irql, pRequest);
2343 }
2344
2345 // We always return status pending through the frameworks
2346 return STATUS_PENDING;
2347 }
2348
2349 _Must_inspect_result_
2350 NTSTATUS
QueueRequestFromForward(__in FxRequest * pRequest)2351 FxIoQueue::QueueRequestFromForward(
2352 __in FxRequest* pRequest
2353 )
2354
2355 /*++
2356
2357 Routine Description:
2358
2359 Enqueue a request to the end of the queue.
2360
2361 This is an internal version that does not fail
2362 the request if it can not be enqueued.
2363
2364 Arguments:
2365
2366 pRequest - Pointer to Request object
2367
2368 Returns:
2369
2370 STATUS_SUCCESS on success
2371 --*/
2372
2373 {
2374 NTSTATUS status;
2375 KIRQL irql;
2376 BOOLEAN fromIo;
2377
2378 // Get IoQueue Object Lock
2379 Lock(&irql);
2380
2381 //
2382 // If queue is not taking new requests, fail now
2383 //
2384 if (!IsState(WdfIoQueueAcceptRequests)) {
2385
2386 status = STATUS_WDF_BUSY;
2387
2388 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIO,
2389 "WDFQUEUE 0x%p is not accepting requests "
2390 "state is %!WDF_IO_QUEUE_STATE!, %s"
2391 "WDFREQUEST 0x%p %!STATUS!",
2392 GetObjectHandle(), m_QueueState,
2393 IsState(FxIoQueueShutdown) ?
2394 "power stopping (Drain) in progress," : "",
2395 pRequest->GetHandle(), status);
2396
2397 Unlock(irql);
2398
2399 return status;
2400 }
2401 #if FX_VERBOSE_TRACE
2402 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
2403 "Queuing WDFREQUEST 0x%p on WDFQUEUE 0x%p",
2404 pRequest->GetHandle(), GetObjectHandle());
2405 #endif
2406 //
2407 // The Request has one reference count, and no completion
2408 // callback function. It has been completely removed from
2409 // its previous queue.
2410 //
2411
2412 //
2413 // Cache this info b/c the request can be delete and freed by the time we use it.
2414 //
2415 fromIo = pRequest->IsAllocatedFromIo();
2416
2417 //
2418 // Insert it in the Cancel Safe Queue
2419 //
2420 // This will mark the IRP pending
2421 //
2422 status = pRequest->InsertTailIrpQueue(&m_Queue, NULL);
2423
2424 if (!NT_SUCCESS(status)) {
2425
2426 pRequest->SetCurrentQueue(this);
2427
2428 ASSERT(status == STATUS_CANCELLED);
2429
2430 //
2431 // We must add a reference since the CancelForQueue path
2432 // assumes we were on the FxIrpQueue with the extra reference
2433 //
2434 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
2435
2436 //
2437 // Mark the request as cancelled, place it on the cancel list,
2438 // and schedule the cancel event to the driver
2439 //
2440 CancelForQueue(pRequest, irql);
2441
2442 Lock(&irql);
2443 }
2444 else {
2445 pRequest->SetCurrentQueue(this);
2446
2447 // Check if went from no requests to have requests
2448 CheckTransitionFromEmpty();
2449 }
2450
2451 //
2452 // If the request is driver-created, we may be called to dispatch
2453 // a request from within the context of the completion routine.
2454 // So to avoid recursion, we will try to dispatch in the return path.
2455 // If the request is not driver-created then we will dispatch it directly because
2456 // this path is meant for dispatching new incoming I/O. There is no concern for
2457 // running into recursion in that scenario.
2458 //
2459 if (fromIo == FALSE && m_Dispatching != 0) {
2460 Unlock(irql);
2461 }
2462 else {
2463 //
2464 // Attempt to dispatch any new requests.
2465 //
2466 // This releases, and re-acquires the IoQueue lock
2467 //
2468 DispatchEvents(irql);
2469 }
2470
2471 return STATUS_SUCCESS;
2472 }
2473
2474 VOID
DeferredDispatchRequestsFromDpc()2475 FxIoQueue::DeferredDispatchRequestsFromDpc(
2476 )
2477
2478 /*++
2479
2480 Routine Description:
2481
2482 Dispatch requests from the queue to the driver
2483 from within the m_Dpc
2484
2485 Arguments:
2486
2487 Returns:
2488
2489 --*/
2490
2491 {
2492 KIRQL irql;
2493
2494 Lock(&irql);
2495
2496 ASSERT(m_DpcQueued != FALSE);
2497
2498 m_RequeueDeferredDispatcher = FALSE;
2499
2500 DispatchEvents(irql);
2501
2502 //
2503 // DispatchEvents drops the lock before returning. So reacquire the lock.
2504 //
2505 Lock(&irql);
2506
2507 if (m_Deleted == FALSE && m_RequeueDeferredDispatcher) {
2508 InsertQueueDpc();
2509 } else {
2510 m_RequeueDeferredDispatcher = FALSE;
2511 m_DpcQueued = FALSE;
2512 }
2513
2514 Unlock(irql);
2515
2516 return;
2517 }
2518
2519 VOID
DeferredDispatchRequestsFromWorkerThread()2520 FxIoQueue::DeferredDispatchRequestsFromWorkerThread(
2521 )
2522
2523 /*++
2524
2525 Routine Description:
2526
2527 Dispatch requests from the queue to the driver
2528 from within the m_WorkItem.
2529
2530 Arguments:
2531
2532 Returns:
2533
2534 --*/
2535
2536 {
2537 KIRQL irql;
2538
2539 Lock(&irql);
2540
2541 ASSERT(m_WorkItemQueued != FALSE);
2542
2543 m_RequeueDeferredDispatcher = FALSE;
2544
2545 DispatchEvents(irql);
2546
2547 //
2548 // DispatchEvents drops the lock before returning. So reacquire
2549 // the lock.
2550 //
2551 Lock(&irql);
2552
2553 if (m_Deleted == FALSE &&
2554 m_RequeueDeferredDispatcher &&
2555 m_SystemWorkItem->Enqueue(_DeferredDispatchThreadThunk, this)) {
2556 //
2557 // Workitem is queued.
2558 //
2559 DO_NOTHING();
2560 } else {
2561 m_RequeueDeferredDispatcher = FALSE;
2562 m_WorkItemQueued = FALSE;
2563 }
2564
2565 Unlock(irql);
2566
2567 return;
2568 }
2569
2570 NTSTATUS
InsertNewRequestLocked(__deref_in FxRequest ** Request,__in KIRQL PreviousIrql)2571 FxIoQueue::InsertNewRequestLocked(
2572 __deref_in FxRequest** Request,
2573 __in KIRQL PreviousIrql
2574 )
2575 /*++
2576
2577 Routine Description:
2578
2579 Purpose of this function is to insert the request that's dispatched
2580 by the IoPkg into FxIrpQueue. This function has been added to improve
2581 the performance of queueing logic. Prior to version 1.7, when a
2582 request is dispatched to a queue, it was first inserted into queue,
2583 various checks for the readiness of queue made, and then the request
2584 is removed from the queue to be presented to the driver.
2585
2586 To improve the I/O performance, dispatching logic has been changed
2587 such that the request will not be inserted into the queue if the queue
2588 is ready to dispatch the request. If the queue is not ready or if there
2589 are other events to be dispatched before dispatching the new incoming request,
2590 we will queue the request first using this function before releasing the lock
2591 so that we don't change the ordering of requests in the queue.
2592
2593 --*/
2594 {
2595 NTSTATUS status;
2596
2597 status = (*Request)->InsertTailIrpQueue(&m_Queue, NULL);
2598
2599 if (!NT_SUCCESS(status)) {
2600 //
2601 // Request was never presented to the driver
2602 // so there is no need to call CancelForQueue
2603 // in this case.
2604 //
2605 ASSERT(status == STATUS_CANCELLED);
2606
2607 Unlock(PreviousIrql);
2608
2609 (*Request)->CompleteWithInformation(status, 0);
2610
2611 (*Request)->RELEASE(FXREQUEST_COMPLETE_TAG);
2612
2613 Lock(&PreviousIrql);
2614 }
2615 else {
2616 (*Request)->SetCurrentQueue(this);
2617
2618 // Check if went from no requests to have requests
2619 CheckTransitionFromEmpty();
2620 }
2621
2622 //
2623 // Request is either inserted into the queue or completed. Clear
2624 // the field to prevent touching the request.
2625 //
2626 *Request = NULL;
2627
2628 return status;
2629 }
2630
2631 _Must_inspect_result_
2632 BOOLEAN
CanThreadDispatchEventsLocked(__in KIRQL PreviousIrql)2633 FxIoQueue::CanThreadDispatchEventsLocked(
2634 __in KIRQL PreviousIrql
2635 )
2636 /*++
2637
2638 Routine Description:
2639
2640 Dispatch events and requests from the queue to the driver.
2641
2642 The IoQueue object lock must be held on entry. This routine
2643 should not drop and reacquire the lock to ensure the request
2644 is not queued out of order.
2645
2646 Returns:
2647
2648 TRUE - if the thread meets all the sychronization and
2649 execution contraints to dispatch the events.
2650 FALSE - if the dispatching of events to be defered to
2651 another thread - either DPC or workitem.
2652 --*/
2653 {
2654 //
2655 // If the current irql is not at passive-level and the queue is configured
2656 // to receive events only at passive-level then we should queue a
2657 // workitem to defer the processing.
2658 //
2659 if ((PreviousIrql > PASSIVE_LEVEL) && m_PassiveLevel) {
2660 ASSERT(PreviousIrql <= DISPATCH_LEVEL);
2661 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIO,
2662 "Current thread 0x%p is not at the passive-level"
2663 " %!irql!, posting to worker thread for WDFQUEUE"
2664 " 0x%p",
2665 Mx::MxGetCurrentThread(),
2666 PreviousIrql,
2667 GetObjectHandle());
2668 //
2669 // We only need to post this once
2670 //
2671 if (m_WorkItemQueued == FALSE) {
2672
2673 m_WorkItemQueued = TRUE;
2674
2675 if (!m_SystemWorkItem->Enqueue(_DeferredDispatchThreadThunk, this)) {
2676 ASSERT(FALSE);
2677 m_WorkItemQueued = FALSE;
2678 }
2679 }
2680
2681 return FALSE;
2682 }
2683
2684 //
2685 // If the current thread is holding the presentation lock, we
2686 // must defer to a DPC or work item.
2687 // This is the result of the device driver calling
2688 // WdfRequestForwardToIoQueue, or WdfIoQueueStart/Stop from
2689 // within I/O dispatch handler. This can also occur if a driver
2690 // attempts to forward a request among a circular series of Queues
2691 // that are configured to have locking constraints.
2692 //
2693 if (m_CallbackLockPtr && m_CallbackLockPtr->IsOwner()) {
2694
2695 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIO,
2696 "Presentation lock for WDFQUEUE 0x%p is "
2697 "already held, deferring to dpc or workitem",
2698 GetObjectHandle());
2699
2700 if (m_PassiveLevel) {
2701
2702 if(m_WorkItemQueued == FALSE) {
2703
2704 m_WorkItemQueued = TRUE;
2705
2706 if (!m_SystemWorkItem->Enqueue(_DeferredDispatchThreadThunk, this)) {
2707 ASSERT(FALSE);
2708 m_WorkItemQueued = FALSE;
2709 }
2710 }
2711 }
2712 else {
2713 //
2714 // We only need to post this once
2715 //
2716 if (m_DpcQueued == FALSE) {
2717
2718 m_DpcQueued = TRUE;
2719
2720 InsertQueueDpc();
2721 }
2722 }
2723
2724 return FALSE;
2725 }
2726
2727 return TRUE;
2728 }
2729
2730 _Releases_lock_(this->m_SpinLock.m_Lock)
__drv_requiresIRQL(DISPATCH_LEVEL)2731 __drv_requiresIRQL(DISPATCH_LEVEL)
2732 BOOLEAN
2733 FxIoQueue::DispatchEvents(
2734 __in __drv_restoresIRQL KIRQL PreviousIrql,
2735 __in_opt FxRequest* NewRequest
2736 )
2737 /*++
2738
2739 Routine Description:
2740
2741 Dispatch events and requests from the queue to the driver.
2742
2743 The IoQueue object lock must be held on entry, but this routine can release
2744 and re-acquire the lock multiple times while processing.
2745
2746 It returns to the caller with the lock released, but queue state may have
2747 changed.
2748
2749 The main processing loop checks for various Queue state change events
2750 delivering them to the driver, and then finally any WDFREQUEST objects
2751 that are pending in the Queue.
2752
2753 The design also handles the recursive case with the m_Dispatching
2754 field so that a driver that completes requests from within the
2755 callback does not cause a stack or lock recursion.
2756
2757 All event callbacks to the device driver are provided though the
2758 FxCallback object which manages lock acquire and release as required
2759 by the locking model.
2760
2761 In addition these may be passive or dispatch level locks.
2762 If configured for passive level callbacks,
2763 must defer to a work item if current thread is DISPATCH_LEVEL
2764 when not owning the current FxIoQueue lock
2765
2766 Arguments:
2767
2768 NewRequest - This is a new incoming request from the driver above.
2769 It will be either presented to the driver or saved into
2770 a queue if the conditions are not right to dispatch.
2771
2772 Returns:
2773
2774 FALSE if the queue is in a deleted state else TRUE.
2775 Caller should check for return value only if it's waiting
2776 on the some events to be invoked by this call.
2777
2778 --*/
2779 {
2780 FxRequest* pRequest;
2781 ULONG totalIoCount;
2782 NTSTATUS status;
2783 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
2784
2785 if (m_Deleted) {
2786 ASSERT(NewRequest == NULL);
2787 Unlock(PreviousIrql);
2788 return FALSE;
2789 }
2790
2791 //
2792 // The design of the I/O Queue allows all "events" to notify the driver of
2793 // to be deferred until the opportune time to deliver them.
2794 // Depending on the drivers configured locking and threading
2795 // mode, this may have to be deferred to a worker thread or a DPC
2796 // to be in a compatible IRQL level, or to prevent a lock recursion
2797 // when a parent objects lock is in effect.
2798 //
2799
2800 if (CanThreadDispatchEventsLocked(PreviousIrql) == FALSE) {
2801 //
2802 // Previous workitem or Dpc might be running the DispatchEvents right now.
2803 // But it may be at a point where it might miss out to process the event
2804 // that we have been asked to dispatch. This is possible because the
2805 // DispatchEvent is reentrant as it acquires and drops lock along
2806 // the way. So we make a note of this, so that when the current Dpc or
2807 // workItem runs to completion, it will requeue itself to handle our message.
2808 //
2809 m_RequeueDeferredDispatcher = TRUE;
2810
2811 //
2812 // Queue the request in to FxIrpQueue and return.
2813 //
2814 InsertNewRequest(&NewRequest, PreviousIrql);
2815 Unlock(PreviousIrql);
2816 return TRUE;
2817 }
2818
2819 //
2820 // This must be incremented before attempting to deliver any
2821 // events to the driver. This prevents recursion on the presentation lock,
2822 // and limits the stack depth in a Start/Complete/Start/... recursion
2823 //
2824 m_Dispatching++;
2825
2826 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
2827 "Thread %p is processing WDFQUEUE 0x%p",
2828 Mx::MxGetCurrentThread(), GetObjectHandle());
2829
2830 //
2831 // At this point all constaints such as IRQL level, locks held,
2832 // and stack recursion protection has been satisfied, and we can
2833 // make callbacks into the device driver.
2834 //
2835 // Process events and requests until we either have an empty queue,
2836 // the driver stops taking requests, or some queue state does not
2837 // allow the driver to take new requests
2838 //
2839 #pragma warning(disable:4127)
2840 while (TRUE) {
2841 #pragma warning(default:4127)
2842 //
2843 // totoalIoCount is sum of requests pending in the queue and requests
2844 // currently owned by the driver.
2845 //
2846 totalIoCount = m_Queue.GetRequestCount() + m_DriverIoCount;
2847
2848 //
2849 // Increment the count if there is a new request to be dispatched.
2850 //
2851 totalIoCount += ((NewRequest != NULL) ? 1 : 0);
2852
2853 if (!IsListEmpty(&this->m_Cancelled)) {
2854 status = InsertNewRequest(&NewRequest, PreviousIrql);
2855 if (!NT_SUCCESS(status)) {
2856 continue; // totalIoCount may be zero now.
2857 }
2858
2859 //
2860 // This can drop and re-acquire the queue lock
2861 // ProcessCancelledRequests returns FALSE if the queue is
2862 // notifying driver about power state changes.
2863 //
2864 if(ProcessCancelledRequests(&PreviousIrql)) {
2865 continue;
2866 }
2867 }
2868
2869 if (!IsListEmpty(&this->m_CanceledOnQueueList)) {
2870 status = InsertNewRequest(&NewRequest, PreviousIrql);
2871 if (!NT_SUCCESS(status)) {
2872 continue; // totalIoCount may be zero now.
2873 }
2874
2875 //
2876 // This can drop and re-acquire the queue lock
2877 // ProcessCancelledRequests returns FALSE if the queue is
2878 // notifying driver about power state changes.
2879 //
2880 if (ProcessCancelledRequestsOnQueue(&PreviousIrql)) {
2881 continue;
2882 }
2883 }
2884
2885 if (m_IdleComplete.Method != NULL &&
2886 m_Dispatching == 1L &&
2887 m_DriverIoCount == 0L) {
2888
2889 InsertNewRequest(&NewRequest, PreviousIrql);
2890
2891 // no more driver owned requests, we can clear the following flag:
2892 m_CancelDispatchedRequests = FALSE;
2893
2894 // This can drop and re-acquire the queue lock
2895 ProcessIdleComplete(&PreviousIrql);
2896 continue;
2897 }
2898
2899 if (m_PurgeComplete.Method != NULL &&
2900 totalIoCount == 0L &&
2901 m_Dispatching == 1L) {
2902
2903 InsertNewRequest(&NewRequest, PreviousIrql);
2904
2905 // no more driver owned requests, we can clear the following flag:
2906 m_CancelDispatchedRequests = FALSE;
2907
2908 // This can drop and re-acquire the queue lock
2909 ProcessPurgeComplete(&PreviousIrql);
2910 continue;
2911 }
2912
2913 if (m_IsDevicePowerPolicyOwner &&
2914 m_PowerManaged &&
2915 m_PowerReferenced &&
2916 totalIoCount == 0L &&
2917 m_Dispatching == 1L) {
2918
2919 //
2920 // Queue has no requests, and is going idle. Notify
2921 // PNP/Power.
2922 //
2923 m_Device->m_PkgPnp->PowerDereference();
2924 m_PowerReferenced = FALSE;
2925 continue;
2926 }
2927
2928 //
2929 // Look for power state transitions
2930 //
2931 if (m_PowerState != FxIoQueuePowerOn &&
2932 m_PowerState != FxIoQueuePowerOff) {
2933
2934 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
2935 "WDFQUEUE 0x%p Power Transition State "
2936 "%!FxIoQueuePowerState!", GetObjectHandle(),
2937 m_PowerState);
2938
2939 status = InsertNewRequest(&NewRequest, PreviousIrql);
2940 if (!NT_SUCCESS(status)) {
2941 continue; // totalIoCount may be zero now.
2942 }
2943
2944 // Process intermediate power state
2945 // This can drop and re-acquire the queue lock
2946 if (ProcessPowerEvents(&PreviousIrql)) {
2947 continue;
2948 }
2949 else {
2950
2951 //
2952 // Return, awaiting some response from the driver
2953 //
2954 goto Done;
2955 }
2956 }
2957 else {
2958 // Queue is either in PowerOn or PowerOff state
2959 DO_NOTHING();
2960 }
2961
2962 //
2963 // Check for queue disposing should be made after processing all
2964 // the events.
2965 //
2966 if (m_Disposing &&
2967 totalIoCount == 0L &&
2968 m_Dispatching == 1L) {
2969
2970 m_Deleted = TRUE;
2971
2972 //
2973 // After this point, no other thread will be able to dispatch
2974 // events from this queue. Also threads that are about to call
2975 // this function as soon as we drop the lock below should have
2976 // a reference on the queue to prevent queue object from being
2977 // freed when we signal the dispose thread to run through.
2978 //
2979 Unlock(PreviousIrql);
2980
2981 m_FinishDisposing.Set();
2982 return TRUE;
2983 }
2984
2985
2986 //
2987 // Return if power is off, can't deliver any request oriented events
2988 // to the driver.
2989 //
2990 if (m_PowerState == FxIoQueuePowerOff) {
2991 status = InsertNewRequest(&NewRequest, PreviousIrql);
2992 if (!NT_SUCCESS(status)) {
2993 continue; // totalIoCount may be zero now.
2994 }
2995
2996 goto Done;
2997 }
2998
2999 //
3000 // See if the queue is (still) processing requests
3001 //
3002 if (!IsState(WdfIoQueueDispatchRequests)) {
3003
3004 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIO,
3005 "WDFQUEUE 0x%p not in dispatching state, "
3006 "current state is %!WDF_IO_QUEUE_STATE!",
3007 GetObjectHandle(), m_QueueState);
3008
3009 status = InsertNewRequest(&NewRequest, PreviousIrql);
3010 if (!NT_SUCCESS(status)) {
3011 continue; // totalIoCount may be zero now.
3012 }
3013
3014 goto Done;
3015 }
3016
3017 //
3018 // A manual dispatch queue can have a request ready notification
3019 //
3020 if (m_Type == WdfIoQueueDispatchManual) {
3021
3022 status = InsertNewRequest(&NewRequest, PreviousIrql);
3023 if (!NT_SUCCESS(status)) {
3024 continue; // totalIoCount may be zero now.
3025 }
3026
3027 if (m_ReadyNotify.Method != NULL && m_TransitionFromEmpty) {
3028
3029 // This can drop and re-acquire the lock to callback to the driver
3030 ProcessReadyNotify(&PreviousIrql);
3031 continue;
3032 }
3033
3034 goto Done;
3035 }
3036
3037 if (m_Type == WdfIoQueueDispatchSequential && m_DriverIoCount > 0) {
3038 status = InsertNewRequest(&NewRequest, PreviousIrql);
3039 if (!NT_SUCCESS(status)) {
3040 continue; // totalIoCount may be zero now.
3041 }
3042
3043 goto Done;
3044 }
3045
3046 //
3047 // For counted Queue's dont dispatch request to driver if the
3048 // m_DriverIoCount exceeds the one set by the driver writer.
3049 //
3050 if (m_Type == WdfIoQueueDispatchParallel &&
3051 (ULONG)m_DriverIoCount >= m_MaxParallelQueuePresentedRequests) {
3052 status = InsertNewRequest(&NewRequest, PreviousIrql);
3053 if (!NT_SUCCESS(status)) {
3054 continue; // totalIoCount may be zero now.
3055 }
3056
3057 goto Done;
3058 }
3059
3060 //
3061 // If there is a request in the queue, then retrieve that.
3062 //
3063 pRequest = NULL;
3064 if (m_Queue.GetRequestCount() > 0L) {
3065 pRequest = FxRequest::GetNextRequest(&m_Queue);
3066 }
3067
3068 //
3069 // The request from the queue should be dispatched first
3070 // to preserve the ordering.
3071 //
3072 if (pRequest != NULL) {
3073 InsertNewRequest(&NewRequest, PreviousIrql);
3074 }
3075 else {
3076 //
3077 // If there is no request in the queue then dispatch
3078 // the incoming one.
3079 //
3080 pRequest = NewRequest;
3081 if (pRequest != NULL) {
3082 pRequest->SetCurrentQueue(this);
3083 SetTransitionFromEmpty();
3084 NewRequest = NULL;
3085 }
3086 else {
3087 goto Done;
3088 }
3089 }
3090
3091 //
3092 // pRequest is not cancellable now
3093 //
3094 InsertInDriverOwnedList(pRequest);
3095
3096 Unlock(PreviousIrql);
3097
3098 DispatchRequestToDriver(pRequest);
3099
3100 Lock(&PreviousIrql);
3101 }
3102
3103 Done:
3104 m_Dispatching--;
3105 Unlock(PreviousIrql);
3106 return TRUE;
3107 }
3108
3109 VOID
DispatchRequestToDriver(__in FxRequest * pRequest)3110 FxIoQueue::DispatchRequestToDriver(
3111 __in FxRequest* pRequest
3112 )
3113
3114 /*++
3115
3116 Routine Description:
3117
3118 Dispatch the next request to the driver.
3119
3120 The IoQueue object lock is *not* held.
3121
3122 It returns to the caller with the lock *not* held.
3123
3124 This is called by DispatchRequests(), and should not be
3125 called directly in order to maintain queue processing model.
3126
3127 Arguments:
3128
3129 Returns:
3130
3131 --*/
3132
3133 {
3134 PFX_DRIVER_GLOBALS FxDriverGlobals;
3135 NTSTATUS Status;
3136 FxRequestCompletionState oldState;
3137 WDFREQUEST hRequest;
3138 FxIrp* pIrp;
3139
3140 FxDriverGlobals = GetDriverGlobals();
3141
3142
3143
3144
3145
3146 (VOID)pRequest->GetCurrentIrpStackLocation();
3147
3148 pIrp = pRequest->GetFxIrp();
3149
3150 // The Irp does not have a cancel function right now
3151 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
3152 ASSERT(pIrp->GetCurrentIrpStackLocation() != NULL);
3153 #endif
3154
3155 //
3156 // Set our completion callback on the request now before
3157 // calling the driver since the driver can complete the
3158 // request in the callback handler, and to avoid races with
3159 // the drivers completion thread.
3160 //
3161 // This takes a reference on the request object.
3162 //
3163 oldState = pRequest->SetCompletionState(FxRequestCompletionStateQueue);
3164 ASSERT(oldState == FxRequestCompletionStateNone);
3165 UNREFERENCED_PARAMETER(oldState);
3166
3167 if (FxDriverGlobals->FxVerifierOn) {
3168 //
3169 // If the verifier is on, we do not release the extra
3170 // reference so we can mark the request as no longer
3171 // being dispatched to the driver on return from the
3172 // event callback to the driver
3173 //
3174
3175 ASSERT((pRequest->GetVerifierFlags() & FXREQUEST_FLAG_DRIVER_OWNED) == 0);
3176
3177 // Mark the request as being "owned" by the driver
3178 pRequest->SetVerifierFlags(FXREQUEST_FLAG_DRIVER_OWNED |
3179 FXREQUEST_FLAG_DRIVER_DISPATCH);
3180 }
3181 else {
3182
3183 //
3184 // Release our original reference. The FxRequest::Complete
3185 // will release the final one since we have registered a completion
3186 // callback handler
3187 //
3188 // We now have one reference count on the FxRequest object until
3189 // its completion routine runs since the completion event made
3190 // an extra reference, and will dereference it when it fires, or
3191 // its canceled.
3192 //
3193
3194 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3195 }
3196
3197 //
3198 // Attempt to dispatch it to the driver
3199 //
3200
3201 //
3202 // Note: A driver that changes its callback pointers at runtime
3203 // could run into a race here since we released the queue
3204 // lock. Currently, changing parameters on a processing
3205 // queue is undefined.
3206 //
3207 // The C DDI's force the callbacks to be registered at
3208 // queue creation time and avoid this race.
3209 //
3210
3211 hRequest = pRequest->GetHandle();
3212
3213 UCHAR majorFunction = pIrp->GetMajorFunction();
3214
3215 if ((majorFunction == IRP_MJ_READ) && m_IoRead.Method) {
3216 ULONG readLength = pIrp->GetParameterReadLength();
3217
3218 //
3219 // Complete zero length reads with STATUS_SUCCESS unless the
3220 // driver specified it wants them delivered.
3221 //
3222 if ((readLength == 0) &&
3223 !m_AllowZeroLengthRequests) {
3224
3225 DoTraceLevelMessage(
3226 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3227 "Zero length WDFREQUEST 0x%p completed automatically "
3228 "by WDFQUEUE 0x%p", hRequest, GetObjectHandle());
3229
3230 pRequest->Complete(STATUS_SUCCESS);
3231 if (FxDriverGlobals->FxVerifierOn) {
3232 //
3233 // Release the reference taken in the call to SetCompletionState
3234 // at the top of the function.
3235 //
3236 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3237 }
3238 return;
3239 }
3240
3241 pRequest->SetPresented();
3242
3243 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3244 "Calling driver EvtIoRead for WDFREQUEST 0x%p",
3245 hRequest);
3246
3247 m_IoRead.Invoke(
3248 GetHandle(),
3249 hRequest,
3250 readLength
3251 );
3252 }
3253 else if ((majorFunction == IRP_MJ_WRITE) && m_IoWrite.Method) {
3254 ULONG writeLength = pIrp->GetParameterWriteLength();
3255
3256 //
3257 // Complete zero length writes with STATUS_SUCCESS unless the
3258 // driver specified it wants them delivered.
3259 //
3260 if ((writeLength == 0) &&
3261 !m_AllowZeroLengthRequests) {
3262
3263 DoTraceLevelMessage(
3264 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3265 "Zero length WDFREQUEST 0x%p completed automatically "
3266 "by WDFQUEUE 0x%p", hRequest, GetObjectHandle());
3267
3268 pRequest->Complete(STATUS_SUCCESS);
3269
3270 if (FxDriverGlobals->FxVerifierOn) {
3271 //
3272 // Release the reference taken in the call to SetCompletionState
3273 // at the top of the function.
3274 //
3275 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3276 }
3277 return;
3278 }
3279
3280 pRequest->SetPresented();
3281
3282 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3283 "Calling driver EvtIoWrite for WDFREQUEST 0x%p",
3284 pRequest->GetObjectHandle());
3285
3286 m_IoWrite.Invoke(
3287 GetHandle(),
3288 hRequest,
3289 writeLength
3290 );
3291 }
3292 else if ((majorFunction == IRP_MJ_DEVICE_CONTROL) && m_IoDeviceControl.Method) {
3293
3294 pRequest->SetPresented();
3295
3296 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3297 "Calling driver EvtIoDeviceControl for "
3298 "WDFREQUEST 0x%p", hRequest);
3299
3300 m_IoDeviceControl.Invoke(
3301 GetHandle(),
3302 hRequest,
3303 pIrp->GetParameterIoctlOutputBufferLength(),
3304 pIrp->GetParameterIoctlInputBufferLength(),
3305 pIrp->GetParameterIoctlCode()
3306 );
3307 }
3308
3309 else if ( (majorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && m_IoInternalDeviceControl.Method) {
3310
3311 pRequest->SetPresented();
3312
3313 DoTraceLevelMessage(
3314 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3315 "Calling driver EvtIoInternalDeviceControl for WDFREQUEST 0x%p",
3316 hRequest);
3317
3318 m_IoInternalDeviceControl.Invoke(
3319 GetHandle(),
3320 hRequest,
3321 pIrp->GetParameterIoctlOutputBufferLength(),
3322 pIrp->GetParameterIoctlInputBufferLength(),
3323 pIrp->GetParameterIoctlCode()
3324 );
3325 }
3326 else {
3327
3328 //
3329 // If we have an IoStart registered, call it
3330 //
3331 if (m_IoDefault.Method) {
3332
3333 DoTraceLevelMessage(
3334 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3335 "Calling driver EvtIoDefault for WDFREQUEST 0x%p", hRequest);
3336
3337
3338 //
3339 // If we don't allow zero length requests, we must dig in whether
3340 // its a read or a write
3341 //
3342 if (!m_AllowZeroLengthRequests) {
3343
3344 if (majorFunction == IRP_MJ_READ) {
3345
3346 if (pIrp->GetParameterReadLength() == 0) {
3347
3348 DoTraceLevelMessage(
3349 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3350 "Zero length WDFREQUEST 0x%p completed automatically "
3351 "by WDFQUEUE 0x%p", hRequest, GetObjectHandle());
3352
3353 pRequest->Complete(STATUS_SUCCESS);
3354 if (FxDriverGlobals->FxVerifierOn) {
3355 //
3356 // Release the reference taken in the call to SetCompletionState
3357 // at the top of the function.
3358 //
3359 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3360 }
3361 return;
3362 }
3363 }
3364 else if (majorFunction == IRP_MJ_WRITE) {
3365
3366 if (pIrp->GetParameterWriteLength() == 0) {
3367
3368 pRequest->Complete(STATUS_SUCCESS);
3369
3370 DoTraceLevelMessage(
3371 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3372 "Zero length WDFREQUEST 0x%p completed automatically "
3373 "by WDFQUEUE 0x%p", hRequest, GetObjectHandle());
3374
3375 if (FxDriverGlobals->FxVerifierOn) {
3376 //
3377 // Release the reference taken in the call to SetCompletionState
3378 // at the top of the function.
3379 //
3380 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3381 }
3382 return;
3383 }
3384 }
3385 }
3386
3387 pRequest->SetPresented();
3388
3389 m_IoDefault.Invoke(GetHandle(), hRequest);
3390 }
3391 else {
3392 Status = STATUS_INVALID_DEVICE_REQUEST;
3393
3394 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3395 "Driver has no event callback "
3396 "for %!WDF_REQUEST_TYPE!, completing WDFREQUEST 0x%p with "
3397 "%!STATUS!",
3398 majorFunction,
3399 pRequest,
3400 Status);
3401
3402 pRequest->Complete(Status);
3403
3404 if (FxDriverGlobals->FxVerifierOn) {
3405 //
3406 // Release our extra verifier reference now
3407 //
3408 // Release the reference taken in the call to SetCompletionState
3409 // at the top of the function.
3410 //
3411 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3412 }
3413
3414 return;
3415 }
3416 }
3417
3418 // ******************************
3419 // Request may now be a freed object unless verifier is on. Only touch
3420 // request if verifier is on.
3421 // ******************************
3422
3423 if (FxDriverGlobals->FxVerifierOn) {
3424
3425 //
3426 // If the request has been forwarded, don't clear this
3427 // since the new queue may already be dispatching in a new thread or DPC
3428 //
3429 if ((pRequest->GetVerifierFlags() & FXREQUEST_FLAG_FORWARDED) == 0x0) {
3430 pRequest->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_DISPATCH);
3431 }
3432
3433 //
3434 // Release our extra verifier reference now
3435 //
3436 // Release the reference taken in the call to SetCompletionState
3437
3438 // at the top of the function.
3439 //
3440 pRequest->RELEASE(FXREQUEST_STATE_TAG);
3441 }
3442
3443 // Driver accepted a request
3444 return;
3445 }
3446
3447
3448 //
3449 // Register a callback when the Queue has a request.
3450 //
3451 // Only valid for a manual Queue.
3452 //
3453 _Must_inspect_result_
3454 NTSTATUS
ReadyNotify(__in PFN_WDF_IO_QUEUE_STATE QueueReady,__in_opt WDFCONTEXT Context)3455 FxIoQueue::ReadyNotify(
3456 __in PFN_WDF_IO_QUEUE_STATE QueueReady,
3457 __in_opt WDFCONTEXT Context
3458 )
3459 {
3460 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
3461 KIRQL irql;
3462 NTSTATUS status;
3463
3464 // Only valid for a manually dispatched Queue
3465 if (m_Type != WdfIoQueueDispatchManual) {
3466 status = STATUS_INVALID_DEVICE_REQUEST;
3467 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3468 "WDFQUEUE 0x%p is "
3469 "not a Manual queue, ReadyNotify is only valid "
3470 "on a manual Queue, %!STATUS!",
3471 GetObjectHandle(), status);
3472 FxVerifierDbgBreakPoint(FxDriverGlobals);
3473 return status;
3474 }
3475
3476 Lock(&irql);
3477
3478 // If the queue is deleted, requests will not be serviced anymore
3479 if (m_Deleted) {
3480 Unlock(irql);
3481 return STATUS_DELETE_PENDING;
3482 }
3483
3484 if (QueueReady != NULL) {
3485
3486 //
3487 // Only one ReadyNotify registration per Queue is allowed
3488 //
3489 if (m_ReadyNotify.Method != NULL) {
3490 status = STATUS_INVALID_DEVICE_REQUEST;
3491 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3492 "WDFQUEUE 0x%p "
3493 "already has a ReadyNotify callback 0x%p"
3494 "registered, %!STATUS!",GetObjectHandle(),
3495 &m_ReadyNotify, status);
3496 FxVerifierDbgBreakPoint(FxDriverGlobals);
3497 Unlock(irql);
3498 return status;
3499 }
3500
3501 m_ReadyNotify.Method = QueueReady;
3502 m_ReadyNotifyContext = Context;
3503 }
3504 else {
3505
3506 //
3507 // A request to cancel ready notifications
3508 //
3509
3510 // If already cancelled, the driver is confused, notify it
3511 if (m_ReadyNotify.Method == NULL) {
3512 status = STATUS_INVALID_DEVICE_REQUEST;
3513 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3514 "WDFQUEUE 0x%p "
3515 "does not have a ReadyNotify to cancel, %!STATUS!",
3516 GetObjectHandle(), status);
3517 FxVerifierDbgBreakPoint(FxDriverGlobals);
3518 Unlock(irql);
3519 return status;
3520 }
3521
3522 //
3523 // The queue should be stopped from dispatching requests to
3524 // avoid missing state transistions between clear and set.
3525 //
3526 if(IsState(WdfIoQueueDispatchRequests)) {
3527 status = STATUS_INVALID_DEVICE_REQUEST;
3528 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3529 "WDFQUEUE 0x%p "
3530 "should be stopped before clearing ReadyNotify callback "
3531 "0x%p registered, %!STATUS!",GetObjectHandle(),
3532 &m_ReadyNotify, status);
3533 FxVerifierDbgBreakPoint(FxDriverGlobals);
3534 Unlock(irql);
3535 return status;
3536
3537 }
3538
3539 m_ReadyNotify.Method = NULL;
3540 m_ReadyNotifyContext = NULL;
3541 }
3542
3543 //
3544 // Check for ready notification since there may already be an event
3545 //
3546 DispatchEvents(irql);
3547
3548 return STATUS_SUCCESS;
3549 }
3550
3551 VOID
QueueStart()3552 FxIoQueue::QueueStart(
3553 )
3554 {
3555 KIRQL irql;
3556
3557 Lock(&irql);
3558
3559 SetState((FX_IO_QUEUE_SET_STATE)(FxIoQueueSetAcceptRequests | FxIoQueueSetDispatchRequests) );
3560
3561 //
3562 // We should set the flag to notify the driver on queue start in case
3563 // the driver stops the queue while the ReadyNotify callback is executing.
3564 // If that happens, the request will be left in the manual queue with
3565 // m_TransitionFromEmpty cleared.
3566 //
3567 if (m_Queue.GetRequestCount() > 0L) {
3568 m_TransitionFromEmpty = TRUE;
3569 m_ForceTransitionFromEmptyWhenAddingNewRequest = FALSE;
3570 }
3571
3572 //
3573 // We may have transitioned to a status that resumes
3574 // processing, so call dispatch function.
3575 //
3576
3577 DispatchEvents(irql);
3578
3579 return;
3580 }
3581
3582 _Must_inspect_result_
3583 NTSTATUS
QueueIdle(__in BOOLEAN CancelRequests,__in_opt PFN_WDF_IO_QUEUE_STATE IdleComplete,__in_opt WDFCONTEXT Context)3584 FxIoQueue::QueueIdle(
3585 __in BOOLEAN CancelRequests,
3586 __in_opt PFN_WDF_IO_QUEUE_STATE IdleComplete,
3587 __in_opt WDFCONTEXT Context
3588 )
3589
3590 /*++
3591
3592 Routine Description:
3593
3594 Idle (stop) the Queue.
3595
3596 If CancelRequests == TRUE,
3597 1) any requests in the Queue that have not been presented to the device driver are
3598 completed with STATUS_CANCELLED.
3599 2) any requests that the driver is operating on that are cancelable will have an
3600 I/O Cancel done on them.
3601 3) any forward progress queued IRPs are completed with STATUS_CANCELLED.
3602
3603 Arguments:
3604
3605 Returns:
3606
3607 --*/
3608
3609 {
3610 PFX_DRIVER_GLOBALS fxDriverGlobals = GetDriverGlobals();
3611 KIRQL irql;
3612 NTSTATUS status;
3613 LIST_ENTRY fwrIrpList = {0};
3614 FxRequest* request;
3615
3616
3617 Lock(&irql);
3618
3619 // If the queue is deleted, requests will not be serviced anymore
3620 if (m_Deleted) {
3621 status = STATUS_DELETE_PENDING;
3622 DoTraceLevelMessage(fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3623 "WDFQUEUE 0x%p is already deleted, %!STATUS!",
3624 GetObjectHandle(), status);
3625 Unlock(irql);
3626
3627 return status;
3628 }
3629
3630 //
3631 // If a IdleComplete callback is supplied, we must register it up
3632 // front since a transition empty could occur in another thread.
3633 //
3634 if (IdleComplete != NULL) {
3635
3636 //
3637 // Only one Idle or Purge Complete callback can be outstanding
3638 // at a time per Queue
3639 //
3640 if (m_IdleComplete.Method != NULL) {
3641 status = STATUS_INVALID_DEVICE_REQUEST;
3642 DoTraceLevelMessage(fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3643 "WDFQUEUE 0x%p already has a "
3644 "IdleComplete callback registered 0x%p, "
3645 "%!STATUS!", GetObjectHandle(),
3646 m_IdleComplete.Method,
3647 status);
3648 Unlock(irql);
3649
3650 return status;
3651 }
3652
3653 m_IdleComplete.Method = IdleComplete;
3654 m_IdleCompleteContext = Context;
3655 }
3656
3657 // Set Accept request and Clear dispatch requests
3658 SetState((FX_IO_QUEUE_SET_STATE)(FxIoQueueSetAcceptRequests | FxIoQueueClearDispatchRequests));
3659
3660 //
3661 // Get ready to cancel current queued requests. Note that we don't want to
3662 // prevent new requests from being queue, i.e., it is legal for an upper
3663 // driver can resend another request in its completion routine.
3664 //
3665 if (CancelRequests) {
3666 //
3667 // Driver wants to abort/complete all queued request and cancel or
3668 // wait for all requests the driver is currently handling. Thus we must
3669 // prevent the driver from requeuing stale requests.
3670 // The 'cancel driver requests' field gives us this ability.
3671 // It is set here, and cleared when:
3672 // (a) Driver doesn't own any more requests, or
3673 // (b) the driver calls WdfIoQueueStart again (dispatch gate is opened).
3674 // When set, the framework automatically deletes any request that the
3675 // driver requeues.
3676 //
3677 m_CancelDispatchedRequests = TRUE;
3678
3679 request = NULL; // Initial tag used by PeekRequest.
3680 #pragma warning(disable:4127)
3681 while (TRUE) {
3682 #pragma warning(default:4127)
3683 status = FxRequest::PeekRequest(&m_Queue, // in:queue
3684 request, // in:tag.
3685 NULL, // in:file_obj
3686 NULL, // out:parameters
3687 &request); // out:request.
3688 if (status != STATUS_SUCCESS) {
3689 ASSERT(status != STATUS_NOT_FOUND);
3690 break;
3691 }
3692
3693 //
3694 // Tag this request and release the extra ref that Peek() takes.
3695 //
3696 request->m_Canceled = TRUE;
3697
3698 #pragma prefast(suppress:__WARNING_PASSING_FUNCTION_UNEXPECTED_NULL, "This is the tag value used in the ADDREF of Peek()")
3699 request->RELEASE(NULL);
3700 }
3701
3702 //
3703 // Move forward progress IRPs to a temp list; we use this logic to
3704 // allow new IRPs to be pended to the original list.
3705 //
3706 if (IsForwardProgressQueue()) {
3707 InitializeListHead(&fwrIrpList);
3708 GetForwardProgressIrps(&fwrIrpList, NULL);
3709 }
3710 }
3711
3712 // Unlock queue lock
3713 Unlock(irql);
3714
3715 if (CancelRequests) {
3716 #pragma warning(disable:4127)
3717 while (TRUE) {
3718 #pragma warning(default:4127)
3719 //
3720 // Get the next FxRequest from the cancel safe queue
3721 //
3722 Lock(&irql);
3723 request = FxRequest::GetNextRequest(&m_Queue);
3724 if (request == NULL) {
3725 DoTraceLevelMessage(
3726 fxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3727 "All WDFQUEUE 0x%p requests cancelled",
3728 GetObjectHandle());
3729 Unlock(irql);
3730 break;
3731 }
3732
3733 // Irp is not cancellable now
3734
3735 //
3736 // Make sure to purged requests only if:
3737 // (a) the request was present when we started this operation.
3738 // (b) any following request that is marked as cancelled.
3739 //
3740 if (request->IsCancelled() == FALSE) {
3741 status = request->InsertHeadIrpQueue(&m_Queue, NULL);
3742 if (NT_SUCCESS(status)) {
3743 Unlock(irql);
3744 break;
3745 }
3746
3747 ASSERT(status == STATUS_CANCELLED);
3748 }
3749 DoTraceLevelMessage(
3750 fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIO,
3751 "Cancelling WDFREQUEST 0x%p, WDFQUEUE 0x%p",
3752 request->GetHandle(),GetObjectHandle());
3753
3754 //
3755 // We must add a reference since the CancelForQueue path
3756 // assumes we were on the FxIrpQueue with the extra reference
3757 //
3758 request->ADDREF(FXREQUEST_QUEUE_TAG);
3759
3760 //
3761 // Mark the request as cancelled, place it on the cancel list,
3762 // and schedule the cancel event to the driver
3763 //
3764 CancelForQueue(request, irql);
3765 }
3766
3767 //
3768 // Walk the driver cancelable list cancelling the requests.
3769 //
3770 #pragma warning(disable:4127)
3771 while (TRUE) {
3772 #pragma warning(default:4127)
3773 //
3774 // Get the next request of driver cancelable requests
3775 //
3776 Lock(&irql);
3777 request = FxRequest::GetNextRequest(&m_DriverCancelable);
3778 if (request == NULL) {
3779 DoTraceLevelMessage(
3780 fxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
3781 "All driver cancellable requests cancelled "
3782 " in WDFQUEUE 0x%p",
3783 GetObjectHandle());
3784 Unlock(irql);
3785 break;
3786 }
3787
3788 request->m_Canceled = TRUE;
3789
3790 Unlock(irql);
3791
3792 //
3793 // If the driver follows the pattern of removing cancel status
3794 // from the request before completion, then there is no race
3795 // with this routine since we will not be able to retrieve any
3796 // requests the driver has made non-cancellable in preparation
3797 // for completion.
3798 //
3799 request->ADDREF(FXREQUEST_QUEUE_TAG);
3800
3801 CancelForDriver(request);
3802
3803 // The request could have been completed and released by the driver
3804 }
3805
3806 //
3807 // Cleanup forward progress IRP list.
3808 //
3809 if (IsForwardProgressQueue()) {
3810 CancelIrps(&fwrIrpList);
3811 }
3812 }
3813
3814 //
3815 // Since we set that no new requests may be dispatched,
3816 // if both m_Queue.GetRequestCount(), m_DriverIoCount == 0, and
3817 // m_Dispatch == 0, right now the queue is completely idle.
3818 //
3819
3820 //
3821 // We check if our m_PurgeComplete callback is still set
3822 // since it may have been called by another thread when
3823 // we dropped the lock above
3824 //
3825 Lock(&irql);
3826 DispatchEvents(irql);
3827
3828 //
3829 // If the driver registered an IdleComplete callback, and it was
3830 // not idle in the above check, it will be called when the final
3831 // callback handler from the device driver returns.
3832 //
3833 return STATUS_SUCCESS;
3834 }
3835
3836 _Must_inspect_result_
3837 NTSTATUS
QueueIdleSynchronously(__in BOOLEAN CancelRequests)3838 FxIoQueue::QueueIdleSynchronously(
3839 __in BOOLEAN CancelRequests
3840 )
3841 /*++
3842
3843 Routine Description:
3844
3845 Idle the Queue and wait for the driver-owned requests to complete.
3846
3847 Arguments:
3848
3849 CancelRequests - If TRUE, functions tries to cancel outstanding requests.
3850
3851 Returns:
3852
3853 --*/
3854 {
3855 NTSTATUS status;
3856 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
3857 MxEvent* event = this->m_RequestWaitEventUm.GetSelfPointer();
3858 #else
3859 MxEvent eventOnStack;
3860 //
3861 // Note that initialize always succeeds in KM so return is not checked.
3862 //
3863 eventOnStack.Initialize(NotificationEvent, FALSE);
3864 MxEvent* event = eventOnStack.GetSelfPointer();
3865 #endif
3866
3867 status = QueueIdle(CancelRequests, _IdleComplete, event->GetSelfPointer());
3868
3869 if(NT_SUCCESS(status)) {
3870
3871 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
3872 "Waiting for %d requests to complete "
3873 "on WDFQUEUE 0x%p",
3874 m_DriverIoCount,
3875 GetObjectHandle());
3876
3877 Mx::MxEnterCriticalRegion();
3878
3879 GetDriverGlobals()->WaitForSignal(event->GetSelfPointer(),
3880 "waiting for queue to stop, WDFQUEUE", GetHandle(),
3881 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
3882 WaitSignalBreakUnderVerifier);
3883
3884
3885 Mx::MxLeaveCriticalRegion();
3886 }
3887
3888 return status;
3889
3890 }
3891
3892 _Must_inspect_result_
3893 NTSTATUS
QueuePurge(__in BOOLEAN CancelQueueRequests,__in BOOLEAN CancelDriverRequests,__in_opt PFN_WDF_IO_QUEUE_STATE PurgeComplete,__in_opt WDFCONTEXT Context)3894 FxIoQueue::QueuePurge(
3895 __in BOOLEAN CancelQueueRequests,
3896 __in BOOLEAN CancelDriverRequests,
3897 __in_opt PFN_WDF_IO_QUEUE_STATE PurgeComplete,
3898 __in_opt WDFCONTEXT Context
3899 )
3900 /*++
3901
3902 Routine Description:
3903
3904 Purge the Queue.
3905
3906 If CancelQueueRequests == TRUE, any requests in the
3907 Queue that have not been presented to the device driver are
3908 completed with STATUS_CANCELLED.
3909
3910 If CancelDriverRequests == TRUE, any requests that the
3911 driver is operating on that are cancelable will have an
3912 I/O Cancel done on them.
3913
3914 Arguments:
3915
3916 Returns:
3917
3918 --*/
3919 {
3920 FxRequest* pRequest;
3921 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
3922 KIRQL irql;
3923 NTSTATUS status;
3924
3925 Lock(&irql);
3926
3927 //
3928 // If the Queue is deleted, there can't be any requests
3929 // to purge, and the queue is no longer executing its
3930 // event dispatch loop, so we would stop responding if we
3931 // registered now.
3932 //
3933 // We could try and silently succeed this, but if we do, we
3934 // must invoke the PurgeComplete callback, and without our
3935 // queue state machine excuting, we can not ensure any
3936 // callback constraints are handled such as locking, queueing
3937 // to passive level, etc. So we just fail to indicate to the
3938 // driver we *will not* be invoking its PurgeComplete function.
3939 //
3940 if (m_Deleted) {
3941 status = STATUS_DELETE_PENDING;
3942 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3943 "WDFQUEUE 0x%p is already deleted %!STATUS!",
3944 GetObjectHandle(), status);
3945 Unlock(irql);
3946
3947 return status;
3948 }
3949
3950 //
3951 // If a PurgeComplete callback is supplied, we must register it up
3952 // front since a transition empty could occur in another thread.
3953 //
3954 if (PurgeComplete != NULL) {
3955
3956 //
3957 // Only one PurgeComplete callback can be outstanding
3958 // at a time per Queue
3959 //
3960 if (m_PurgeComplete.Method != NULL) {
3961 status = STATUS_INVALID_DEVICE_REQUEST;
3962 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
3963 "WDFQUEUE 0x%p already has a "
3964 "PurgeComplete callback registered 0x%p "
3965 "%!STATUS!", GetObjectHandle(),
3966 m_PurgeComplete.Method, status);
3967 Unlock(irql);
3968
3969 return status;
3970 }
3971
3972 m_PurgeComplete.Method = PurgeComplete;
3973 m_PurgeCompleteContext = Context;
3974 }
3975
3976 // Clear accept requests
3977 SetState(FxIoQueueClearAcceptRequests);
3978
3979 if (CancelQueueRequests && CancelDriverRequests &&
3980 FxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11)) {
3981 //
3982 // Driver wants to abort/complete all queued request and cancel or
3983 // wait for all requests the driver is currently handling. Thus we must
3984 // prevent the driver from requeuing stale requests.
3985 // This flag is set here, and cleared when:
3986 // (a) Driver doesn't own any more requests, or
3987 // (b) the driver calls WdfIoQueueStart again (dispatch gate is opened).
3988 // When set, the framework automatically deletes any request that the
3989 // driver requeues.
3990 // For compatibility we do this only for drivers v1.11 and above.
3991 //
3992 m_CancelDispatchedRequests = TRUE;
3993 }
3994
3995 // Unlock queue lock
3996 Unlock(irql);
3997
3998 if (CancelQueueRequests) {
3999 #pragma warning(disable:4127)
4000 while (TRUE) {
4001 #pragma warning(default:4127)
4002 //
4003 // Get the next FxRequest from the cancel safe queue
4004 //
4005 Lock(&irql);
4006 pRequest = FxRequest::GetNextRequest(&m_Queue);
4007 if (pRequest == NULL) {
4008 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
4009 "All WDFQUEUE 0x%p requests cancelled",
4010 GetObjectHandle());
4011 Unlock(irql);
4012 break;
4013 }
4014
4015 // Irp is not cancellable now
4016
4017 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIO,
4018 "Cancelling WDFREQUEST 0x%p, WDFQUEUE 0x%p",
4019 pRequest->GetHandle(),GetObjectHandle());
4020
4021 //
4022 // We must add a reference since the CancelForQueue path
4023 // assumes we were on the FxIrpQueue with the extra reference
4024 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
4025
4026 //
4027 // Mark the request as cancelled, place it on the cancel list,
4028 // and schedule the cancel event to the driver
4029 //
4030 CancelForQueue(pRequest, irql);
4031
4032 }
4033 }
4034
4035 if (CancelDriverRequests) {
4036
4037 //
4038 // Walk the driver cancelable list cancelling
4039 // the requests.
4040 //
4041 #pragma warning(disable:4127)
4042 while (TRUE) {
4043 #pragma warning(default:4127)
4044 //
4045 // Get the next request of driver cancelable requests
4046 //
4047 Lock(&irql);
4048 pRequest = FxRequest::GetNextRequest(&m_DriverCancelable);
4049 if (pRequest == NULL) {
4050 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
4051 "All driver cancellable requests cancelled "
4052 " in WDFQUEUE 0x%p",
4053 GetObjectHandle());
4054 Unlock(irql);
4055 break;
4056 }
4057
4058 pRequest->m_Canceled = TRUE;
4059
4060 Unlock(irql);
4061
4062 //
4063 // If the driver follows the pattern of removing cancel status
4064 // from the request before completion, then there is no race
4065 // with this routine since we will not be able to retrieve any
4066 // requests the driver has made non-cancellable in preparation
4067 // for completion.
4068 //
4069 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
4070
4071 CancelForDriver(pRequest);
4072
4073 // The request could have been completed and released by the driver
4074 }
4075 }
4076
4077 if (IsForwardProgressQueue()) {
4078 PurgeForwardProgressIrps(NULL);
4079 }
4080
4081 //
4082 // Since we set that no new requests may be enqueued,
4083 // if both m_Queue.GetRequestCount() and m_DriverIoCount == 0 right
4084 // now the queue is completely purged.
4085 //
4086
4087 //
4088 // We check if our m_PurgeComplete callback is still set
4089 // since it may have been called by another thread when
4090 // we dropped the lock above
4091 //
4092 Lock(&irql);
4093
4094 DispatchEvents(irql);
4095
4096 //
4097 // If the driver registered a PurgeComplete callback, and it was
4098 // not empty in the above check, it will be called when a
4099 // request complete from the device driver completes the
4100 // final request.
4101 //
4102 return STATUS_SUCCESS;
4103 }
4104
4105 _Must_inspect_result_
4106 NTSTATUS
QueuePurgeSynchronously()4107 FxIoQueue::QueuePurgeSynchronously(
4108 )
4109 /*++
4110
4111 Routine Description:
4112
4113 Purge the queue and wait for it to complete.
4114 When this call returns, there are no requests in the queue or device
4115 driver and the queue state is set to reject new requests.
4116
4117 --*/
4118 {
4119 NTSTATUS status;
4120
4121 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
4122 MxEvent* event = this->m_RequestWaitEventUm.GetSelfPointer();
4123 #else
4124 MxEvent eventOnStack;
4125 //
4126 // Note that initialize always succeeds in KM so return is not checked.
4127 //
4128 eventOnStack.Initialize(NotificationEvent, FALSE);
4129 MxEvent* event = eventOnStack.GetSelfPointer();
4130 #endif
4131
4132 status = QueuePurge(TRUE, TRUE, _PurgeComplete, event->GetSelfPointer());
4133
4134 if(NT_SUCCESS(status)) {
4135
4136 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
4137 "Waiting for %d requests to complete "
4138 "on WDFQUEUE 0x%p",
4139 (m_DriverIoCount + m_Queue.GetRequestCount()),
4140 GetObjectHandle());
4141
4142 Mx::MxEnterCriticalRegion();
4143
4144 GetDriverGlobals()->WaitForSignal(event->GetSelfPointer(),
4145 "waiting for queue to purge, WDFQUEUE", GetHandle(),
4146 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
4147 WaitSignalBreakUnderVerifier);
4148
4149 Mx::MxLeaveCriticalRegion();
4150 }
4151
4152 return status;
4153
4154 }
4155
4156 _Must_inspect_result_
4157 NTSTATUS
QueueDrain(__in_opt PFN_WDF_IO_QUEUE_STATE PurgeComplete,__in_opt WDFCONTEXT Context)4158 FxIoQueue::QueueDrain(
4159 __in_opt PFN_WDF_IO_QUEUE_STATE PurgeComplete,
4160 __in_opt WDFCONTEXT Context
4161 )
4162 {
4163 //
4164 // We drain the queue by calling QueuePurge with CancelQueueRequests
4165 // and CancelDriverRequests == FALSE. The Queue will reject new
4166 // requests, but allow the device driver to continue processing
4167 // requests currently on the Queue. The DrainComplete callback is
4168 // invoked when there are no requests in Queue or device driver.
4169 //
4170
4171 return QueuePurge(FALSE, FALSE, PurgeComplete, Context);
4172
4173 }
4174
4175 _Must_inspect_result_
4176 NTSTATUS
QueueDrainSynchronously()4177 FxIoQueue::QueueDrainSynchronously(
4178 )
4179 /*++
4180
4181 Routine Description:
4182
4183 Drain the queue and wait for it to complete.
4184 When this call returns, there are no requests in the queue or device
4185 driver and the queue state is set to reject new requests.
4186
4187 --*/
4188 {
4189 NTSTATUS status;
4190 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
4191 MxEvent* event = this->m_RequestWaitEventUm.GetSelfPointer();
4192 #else
4193 MxEvent eventOnStack;
4194 //
4195 // Note that initialize always succeeds in KM so return is not checked.
4196 //
4197 eventOnStack.Initialize(NotificationEvent, FALSE);
4198 MxEvent* event = eventOnStack.GetSelfPointer();
4199 #endif
4200
4201 status = QueueDrain(_PurgeComplete, event->GetSelfPointer());
4202
4203 if(NT_SUCCESS(status)) {
4204
4205 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
4206 "Waiting for %d requests to complete "
4207 "on WDFQUEUE 0x%p",
4208 (m_DriverIoCount + m_Queue.GetRequestCount()),
4209 GetObjectHandle());
4210
4211 Mx::MxEnterCriticalRegion();
4212
4213 GetDriverGlobals()->WaitForSignal(event->GetSelfPointer(),
4214 "waiting for queue to drain, WDFQUEUE", GetHandle(),
4215 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
4216 WaitSignalBreakUnderVerifier);
4217
4218 Mx::MxLeaveCriticalRegion();
4219 }
4220
4221 return status;
4222
4223 }
4224
4225
4226 VOID
GetRequestCount(__out_opt PULONG pQueuedRequests,__out_opt PULONG pDriverPendingRequests)4227 FxIoQueue::GetRequestCount(
4228 __out_opt PULONG pQueuedRequests,
4229 __out_opt PULONG pDriverPendingRequests
4230 )
4231 /*++
4232
4233 Routine Description:
4234
4235 Return the count of requests currently on the queue
4236 and owned by the driver.
4237
4238 Arguments:
4239
4240 Returns:
4241
4242 --*/
4243 {
4244 if (pQueuedRequests != NULL) {
4245 *pQueuedRequests = m_Queue.GetRequestCount();
4246 }
4247
4248 if (pDriverPendingRequests != NULL) {
4249 *pDriverPendingRequests = m_DriverIoCount;
4250 }
4251
4252 return;
4253 }
4254
4255 VOID
FlushByFileObject(__in MdFileObject FileObject)4256 FxIoQueue::FlushByFileObject(
4257 __in MdFileObject FileObject
4258 )
4259 /*++
4260
4261 Routine Description:
4262
4263 Scan the queue and cancel all the requests that have
4264 the same fileobject as the input argument.
4265
4266 This function is called when the IoPkg receives a
4267 IRP_MJ_CLEANUP requests.
4268
4269 Additional reference is already taken on the object by the caller
4270 to prevent the queue from being deleted.
4271
4272 Return Value:
4273
4274 --*/
4275 {
4276 FxRequest* pRequest = NULL;
4277 NTSTATUS status;
4278 KIRQL irql;
4279
4280 if (IsForwardProgressQueue()) {
4281 PurgeForwardProgressIrps(FileObject);
4282 }
4283
4284 Lock(&irql);
4285
4286 #pragma warning(disable:4127)
4287 while (TRUE) {
4288 #pragma warning(default:4127)
4289
4290 //
4291 // Get the next FxRequest from the cancel safe queue
4292 //
4293 status = FxRequest::GetNextRequest(&m_Queue, FileObject, NULL, &pRequest);
4294 if(status == STATUS_NO_MORE_ENTRIES) {
4295 break;
4296 }
4297 if(!NT_SUCCESS(status)) {
4298 ASSERTMSG("GetNextRequest failed\n", FALSE);
4299 break;
4300 }
4301
4302 //
4303 // We must add a reference since the CancelForQueue path
4304 // assumes we were on the FxIrpQueue with the extra reference
4305 //
4306 pRequest->ADDREF(FXREQUEST_QUEUE_TAG);
4307
4308 //
4309 // Mark the request as cancelled, place it on the cancel list,
4310 // and schedule the cancel event to the driver
4311 //
4312 CancelForQueue(pRequest, irql);
4313
4314 //
4315 // Reacquire the lock because CancelForQueue visits the dispatch-loop
4316 // and releases the lock.
4317 //
4318 Lock(&irql);
4319 }
4320
4321 DispatchEvents(irql);
4322
4323 return;
4324
4325 }
4326
4327 _Releases_lock_(this->m_SpinLock.m_Lock)
4328 VOID
CancelForQueue(__in FxRequest * pRequest,__in __drv_restoresIRQL KIRQL PreviousIrql)4329 FxIoQueue::CancelForQueue(
4330 __in FxRequest* pRequest,
4331 __in __drv_restoresIRQL KIRQL PreviousIrql
4332 )
4333 /*++
4334
4335 Routine Description:
4336
4337 This routine performs the actions when notified of a cancel
4338 on a request that has not been presented to the driver
4339
4340 Return Value:
4341
4342 NTSTATUS
4343
4344 --*/
4345 {
4346 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4347 FxRequestCompletionState oldState;
4348
4349 // This is not an error, but want to make sure cancel testing works
4350 if (FxDriverGlobals->FxVerifierOn) {
4351
4352 // Clear cancellable status, otherwise verifier in FxRequest::Complete will complain
4353 pRequest->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_CANCELABLE);
4354
4355 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIO,
4356 "WDFREQUEST 0x%p "
4357 "was cancelled while on WDFQUEUE 0x%p",
4358 pRequest->GetHandle(),GetObjectHandle());
4359 }
4360
4361 pRequest->m_Canceled = TRUE;
4362
4363 pRequest->MarkRemovedFromIrpQueue();
4364
4365 //
4366 // Drop the extra reference taken when it was added to the queue
4367 // because the request is now leaving the queue.
4368 //
4369 pRequest->RELEASE(FXREQUEST_QUEUE_TAG);
4370
4371 //
4372 // If the driver has registered m_CanceledOnQueue callback, and if
4373 // the request was ever presented to the driver then we need to
4374 // notify the driver
4375 //
4376 if(m_IoCanceledOnQueue.Method && pRequest->m_Presented) {
4377
4378 //
4379 // Set the state to indicate the request has come from a queue.
4380 //
4381 oldState = pRequest->SetCompletionState(FxRequestCompletionStateQueue);
4382 ASSERT(oldState == FxRequestCompletionStateNone);
4383 UNREFERENCED_PARAMETER(oldState);
4384
4385 // Insert it on the driver owned list
4386 InsertInDriverOwnedList(pRequest);
4387
4388 if (FxDriverGlobals->FxVerifierOn) {
4389 ASSERT((pRequest->GetVerifierFlags() & FXREQUEST_FLAG_DRIVER_OWNED) == 0);
4390 pRequest->SetVerifierFlags(FXREQUEST_FLAG_DRIVER_OWNED);
4391 }
4392
4393 //
4394 // Also insert the request in to m_CanceledOnQueueList so
4395 // that we can notify the driver when we visit the DispatchEvents
4396 //
4397 InsertTailList(&m_CanceledOnQueueList, pRequest->GetListEntry(FxListEntryQueueOwned));
4398
4399 //
4400 // Release the reference taken in the call to SetCompletionState.
4401 //
4402 pRequest->RELEASE(FXREQUEST_STATE_TAG);
4403 } else {
4404
4405 Unlock(PreviousIrql);
4406
4407 // Its gone from our list, so complete it cancelled
4408 pRequest->CompleteWithInformation(STATUS_CANCELLED, 0);
4409
4410 // Dereference the request objects final reference
4411 pRequest->RELEASE(FXREQUEST_COMPLETE_TAG);
4412
4413 Lock(&PreviousIrql);
4414 }
4415
4416 // This may have caused the queue to be emptied
4417 DispatchInternalEvents(PreviousIrql);
4418
4419 return;
4420 }
4421
4422 VOID
_IrpCancelForQueue(__in FxIrpQueue * IrpQueue,__in MdIrp Irp,__in PMdIoCsqIrpContext CsqContext,__in KIRQL Irql)4423 FxIoQueue::_IrpCancelForQueue(
4424 __in FxIrpQueue* IrpQueue,
4425 __in MdIrp Irp,
4426 __in PMdIoCsqIrpContext CsqContext,
4427 __in KIRQL Irql
4428 )
4429 /*++
4430
4431 Routine Description:
4432 This is our Cancel Safe Queue Callback from FxIrpQueue notifying us of an
4433 I/O cancellation on the main (pre driver) Queue.
4434
4435 Note this callback is called with the queue lock held.
4436
4437 Arguments:
4438 IrpQueue - Queue the request was on
4439
4440 Irp - the irp being cancelled
4441
4442 CsqContext - the context associated with the irp
4443
4444 Return Value:
4445 None
4446
4447 --*/
4448 {
4449 FxIoQueue* ioQueue;
4450 FxRequest* pRequest;
4451
4452 ioQueue = CONTAINING_RECORD(IrpQueue, FxIoQueue, m_Queue);
4453 pRequest = FxRequest::RetrieveFromCsqContext(CsqContext);
4454
4455 //
4456 // Must reference the queue since this could be the final
4457 // request on a deleting queue
4458 //
4459 ioQueue->ADDREF(Irp);
4460
4461 //
4462 // We cannot drop the lock here because we may have to insert the
4463 // request in the driver owned list if the driver has registered
4464 // for canceled-on-queue callback. If we drop the lock and if the request
4465 // happens to be last request, the delete will run thru and put
4466 // the state of the queue to deleted state and prevent further dispatching
4467 // of requests.
4468 //
4469 ioQueue->CancelForQueue(pRequest, Irql);
4470
4471 ioQueue->RELEASE(Irp);
4472 }
4473
4474 VOID
FX_VF_METHOD(FxIoQueue,VerifyValidateCompletedRequest)4475 FX_VF_METHOD(FxIoQueue, VerifyValidateCompletedRequest)(
4476 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
4477 _In_ FxRequest* Request
4478 )
4479 {
4480 UNREFERENCED_PARAMETER(FxDriverGlobals);
4481
4482 PAGED_CODE_LOCKED();
4483
4484 PLIST_ENTRY pEntry;
4485 KIRQL irql;
4486
4487 Request->Lock(&irql);
4488
4489 (VOID) Request->VerifyRequestIsDriverOwned(FxDriverGlobals);
4490 Request->ClearVerifierFlagsLocked(FXREQUEST_FLAG_DRIVER_OWNED);
4491
4492 Request->Unlock(irql);
4493
4494 // Driver no longer owns it once completed
4495
4496 // Request can't be on a cancel list
4497 pEntry = Request->GetListEntry(FxListEntryQueueOwned);
4498 if( !IsListEmpty(pEntry) ) {
4499 DoTraceLevelMessage(
4500 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
4501 "WDFREQUEST 0x%p is on a cancellation list for WDFQUEUE 0x%p",
4502 Request->GetHandle(), GetObjectHandle());
4503 FxVerifierDbgBreakPoint(GetDriverGlobals());
4504 }
4505 }
4506
4507 VOID
FX_VF_METHOD(FxIoQueue,VerifyCancelForDriver)4508 FX_VF_METHOD(FxIoQueue, VerifyCancelForDriver) (
4509 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
4510 _In_ FxRequest* Request
4511 )
4512 {
4513 PLIST_ENTRY pEntry;
4514
4515 PAGED_CODE_LOCKED();
4516
4517 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIO,
4518 "WDFREQUEST 0x%p "
4519 "was cancelled in driver for WDFQUEUE 0x%p",
4520 Request->GetHandle(), GetObjectHandle());
4521
4522 // Verifier code assures this is available to the cancel processing
4523 pEntry = Request->GetListEntry(FxListEntryQueueOwned);
4524 if (!IsListEmpty(pEntry)) {
4525 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
4526 "WDFREQUEST 0x%p is "
4527 "already on list, FxRequest::m_ListEntry is busy!, "
4528 "WDFQUEUE 0x%p",
4529 Request->GetHandle(), GetObjectHandle());
4530 FxVerifierDbgBreakPoint(FxDriverGlobals);
4531 }
4532 }
4533
4534 VOID
CancelForDriver(__in FxRequest * pRequest)4535 FxIoQueue::CancelForDriver(
4536 __in FxRequest* pRequest
4537 )
4538 /*++
4539
4540 Routine Description:
4541
4542 This is called when a driver-owned cancelable request is canceled.
4543 This routine will add the request to m_Canceled list so that the
4544 dispatcher and call the driver cancel-routine to notify the driver.
4545
4546 Queue lock is not held.
4547
4548 Arguments:
4549
4550 pRequest - is a driver owned cancelable request.
4551
4552 Return Value:
4553
4554 --*/
4555 {
4556 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4557 KIRQL irql;
4558
4559 // This is not an error, but want to make sure cancel testing works
4560 VerifyCancelForDriver(FxDriverGlobals, pRequest);
4561
4562 //
4563 // We are called with no locks held, but
4564 // can be in arbitrary thread context from
4565 // a cancel occuring from another driver within
4566 // a driver stack.
4567 //
4568
4569 //
4570 // The Csq has removed this request from the driver pending
4571 // queue, and it no longer has a cancel function if the
4572 // driver does not accept the cancel right now.
4573 //
4574 // When callside eventually goes to remove it from the queue
4575 // by CsqContext, the Csq's will return NULL.
4576 //
4577 // Irp and FxRequest is still valid until the driver calls
4578 // WdfRequestComplete either as a result of this cancel
4579 // callback, or at its leasure if it chooses to ignore it.
4580 //
4581 // The insert of FxRequest onto the FxIrpQueue took out a
4582 // reference, and when an IRP gets cancelled, we are responsible
4583 // for this final dereference after calling into the driver.
4584 //
4585
4586 //
4587 // Cancellations are dispatched as events to the device driver
4588 // using the standard DispatchEvents processing loop. In order
4589 // to support this, we must defer it by linking this request
4590 // into a list of cancelled requests.
4591 //
4592 // The requests will be removed from this list and cancel notified
4593 // to the device driver by the processing loop.
4594 //
4595
4596 pRequest->MarkRemovedFromIrpQueue();
4597
4598 //
4599 // Queue it on the cancelled list
4600 //
4601 Lock(&irql);
4602
4603 InsertTailList(&m_Cancelled, pRequest->GetListEntry(FxListEntryQueueOwned));
4604
4605 //
4606 // Visit the event dispatcher
4607 //
4608 DispatchInternalEvents(irql);
4609
4610 return;
4611 }
4612
4613 VOID
_IrpCancelForDriver(__in FxIrpQueue * IrpQueue,__in MdIrp Irp,__in PMdIoCsqIrpContext CsqContext,__in KIRQL Irql)4614 FxIoQueue::_IrpCancelForDriver(
4615 __in FxIrpQueue* IrpQueue,
4616 __in MdIrp Irp,
4617 __in PMdIoCsqIrpContext CsqContext,
4618 __in KIRQL Irql
4619 )
4620 /*++
4621
4622 Routine Description:
4623 This is our Cancel Safe Queue Callback from FxIrpQueue notifying us of an
4624 I/O cancellation on a driver owned request (driver queue)
4625
4626 Note this callback is called with the queue lock held.
4627
4628 Arguments:
4629 IrpQueue - Queue the request was on
4630
4631 Irp - the irp being cancelled
4632
4633 CsqContext - the context associated with the irp
4634
4635
4636 Return Value:
4637 None
4638
4639 --*/
4640 {
4641 FxIoQueue* ioQueue;
4642 FxRequest* pRequest;
4643
4644 ioQueue = CONTAINING_RECORD(IrpQueue, FxIoQueue, m_DriverCancelable);
4645 pRequest = FxRequest::RetrieveFromCsqContext(CsqContext);
4646
4647 pRequest->m_Canceled = TRUE;
4648
4649 //
4650 // Must reference the queue since this could be the final
4651 // request on a deleting queue.
4652 //
4653 ioQueue->ADDREF(Irp);
4654
4655 //
4656 // We can drop the lock because this request is a driver owned request and
4657 // it is tracked by m_DriverIoCount. As a result delete will be blocked
4658 // until the request is completed.
4659 //
4660 ioQueue->Unlock(Irql);
4661
4662 ioQueue->CancelForDriver(pRequest);
4663
4664 ioQueue->RELEASE(Irp);
4665 }
4666
__drv_requiresIRQL(DISPATCH_LEVEL)4667 __drv_requiresIRQL(DISPATCH_LEVEL)
4668 VOID
4669 FxIoQueue::ProcessIdleComplete(
4670 __out PKIRQL PreviousIrql
4671 )
4672 /*++
4673
4674 Routine Description:
4675
4676 Handle IdleComplete.
4677 Calls back the driver if conditions are met.
4678 Called with Queue lock held, but can drop and re-acquire
4679 it when delivering the event callback to the device driver.
4680
4681 Arguments:
4682 IrpQueue - Queue the request was on
4683
4684 Irp - the irp being cancelled
4685
4686 CsqContext - the context associated with the irp
4687
4688 Return Value:
4689 None
4690
4691 --*/
4692 {
4693 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4694 WDFCONTEXT ctx;
4695 FxIoQueueIoState callback;
4696
4697
4698 callback = m_IdleComplete;
4699 ctx = m_IdleCompleteContext;
4700
4701 m_IdleComplete.Method = NULL;
4702 m_IdleCompleteContext = NULL;
4703
4704 Unlock(*PreviousIrql);
4705
4706 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
4707 "WDFQUEUE 0x%p is idle, calling driver callback",
4708 GetHandle());
4709
4710 // Notify driver by callback
4711 if (callback.Method != NULL) {
4712 callback.Invoke(GetHandle(), ctx);
4713 }
4714
4715 Lock(PreviousIrql);
4716
4717 return;
4718 }
4719
__drv_requiresIRQL(DISPATCH_LEVEL)4720 __drv_requiresIRQL(DISPATCH_LEVEL)
4721 VOID
4722 FxIoQueue::ProcessPurgeComplete(
4723 __out PKIRQL PreviousIrql
4724 )
4725 /*++
4726
4727 Routine Description:
4728
4729
4730 Handle PurgeComplete.
4731
4732 Calls back the driver if conditions are met.
4733
4734 Called with Queue lock held, but can drop and re-acquire
4735 it when delivering the event callback to the device driver.
4736
4737
4738 Arguments:
4739
4740 Return Value:
4741 None
4742
4743 --*/
4744 {
4745 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4746 WDFCONTEXT ctx;
4747 FxIoQueueIoState callback;
4748
4749 callback = m_PurgeComplete;
4750 ctx = m_PurgeCompleteContext;
4751
4752 m_PurgeComplete.Method = NULL;
4753 m_PurgeCompleteContext = NULL;
4754
4755 Unlock(*PreviousIrql);
4756
4757 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
4758 "WDFQUEUE 0x%p is purged, calling driver callback",
4759 GetObjectHandle());
4760
4761 // Notify driver by callback
4762 if (callback.Method != NULL) {
4763 callback.Invoke(GetHandle(), ctx);
4764 }
4765
4766 Lock(PreviousIrql);
4767
4768 return;
4769 }
4770
__drv_requiresIRQL(DISPATCH_LEVEL)4771 __drv_requiresIRQL(DISPATCH_LEVEL)
4772 VOID
4773 FxIoQueue::ProcessReadyNotify(
4774 __out PKIRQL PreviousIrql
4775 )
4776 /*++
4777
4778 Routine Description:
4779
4780 Callback the driver for a Queue ready notify
4781 Called with the Queue lock held, and may release
4782 and re-acquire it in calling back the driver
4783
4784 Arguments:
4785
4786 Return Value:
4787 None
4788
4789 --*/
4790 {
4791 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4792 WDFCONTEXT ctx;
4793 FxIoQueueIoState callback;
4794
4795 //
4796 // A callback to the driver "consumes" the notification.
4797 // Since we drop the lock when we call the driver, there is
4798 // a chance for the queue to be stopped or powered-off before
4799 // the driver tries to retrieve the request. So make sure
4800 // to set this flag when you power-on or start the queue to
4801 // avoid abandoning the requests in the queue.
4802 //
4803 m_TransitionFromEmpty = FALSE;
4804
4805 //
4806 // Save a local copy since another thread could
4807 // cancel the ready notification out from under us
4808 // when we drop the lock
4809 //
4810 callback = m_ReadyNotify;
4811
4812 ctx = m_ReadyNotifyContext;
4813
4814 Unlock(*PreviousIrql);
4815
4816 if (callback.Method != NULL) {
4817 callback.Invoke(GetHandle(), ctx);
4818 }
4819 else {
4820 if (FxDriverGlobals->FxVerifierOn) {
4821 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
4822 "ReadyNotify notify method is NULL "
4823 "on WDFQUEUE 0x%p", GetObjectHandle());
4824 FxVerifierDbgBreakPoint(FxDriverGlobals);
4825 }
4826 }
4827
4828 Lock(PreviousIrql);
4829
4830 return;
4831 }
4832
__drv_requiresIRQL(DISPATCH_LEVEL)4833 __drv_requiresIRQL(DISPATCH_LEVEL)
4834 BOOLEAN
4835 FxIoQueue::ProcessCancelledRequests(
4836 __out PKIRQL PreviousIrql
4837 )
4838 /*++
4839
4840 Routine Description:
4841
4842 Process any cancelled requests
4843 Called with the Queue lock held
4844 Can drop and re-acquire the queue lock
4845 Returns with the Queue lock held
4846
4847 Arguments:
4848
4849 Return Value:
4850 None
4851
4852 --*/
4853 {
4854 PLIST_ENTRY pEntry;
4855 FxRequest* pRequest;
4856
4857 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4858
4859 if (IsPowerStateNotifyingDriver()) {
4860 //
4861 // We will not process cancelled request while the driver is being
4862 // notified to stop processing request to avoid double completion
4863 // of the request.
4864 //
4865 return FALSE;
4866 }
4867
4868 while (!IsListEmpty(&m_Cancelled)) {
4869 pEntry = m_Cancelled.Flink;
4870
4871 RemoveEntryList(pEntry);
4872
4873 // FxRequest ensures its not on any list on checked builds
4874 InitializeListHead(pEntry);
4875
4876 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryQueueOwned, pEntry);
4877
4878 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIO,
4879 "Calling CancelRoutine routine "
4880 "for WDFREQUEST 0x%p on WDFQUEUE 0x%p",
4881 pRequest->GetHandle(), GetObjectHandle());
4882
4883 if (FxDriverGlobals->FxVerifierOn) {
4884
4885 // Set cancelled status, otherwise verifier in FxRequest::Complete will complain
4886 pRequest->SetVerifierFlags(FXREQUEST_FLAG_CANCELLED);
4887 }
4888
4889 Unlock(*PreviousIrql);
4890
4891 if (FxDriverGlobals->FxVerifierOn) {
4892 ASSERT((pRequest->GetVerifierFlags() & FXREQUEST_FLAG_DRIVER_OWNED) != 0);
4893 }
4894
4895 //
4896 // Notify the driver of cancel desire
4897 //
4898 pRequest->m_CancelRoutine.InvokeCancel(
4899 m_IoCancelCallbackLockPtr,
4900 pRequest->GetHandle()
4901 );
4902
4903 //
4904 // Release the reference that FxRequest took out on itself
4905 // when it was placed onto the FxIrpQueue. It is now leaving
4906 // the FxIrpQueue due to cancel, and we own this final
4907 // release.
4908 //
4909 pRequest->RELEASE(FXREQUEST_QUEUE_TAG);
4910
4911 Lock(PreviousIrql);
4912 }
4913
4914 return TRUE;
4915 }
4916
__drv_requiresIRQL(DISPATCH_LEVEL)4917 __drv_requiresIRQL(DISPATCH_LEVEL)
4918 BOOLEAN
4919 FxIoQueue::ProcessCancelledRequestsOnQueue(
4920 __out PKIRQL PreviousIrql
4921 )
4922 /*++
4923
4924 Routine Description:
4925
4926 Process any cancelled requests
4927 Called with the Queue lock held
4928 Can drop and re-acquire the queue lock
4929 Returns with the Queue lock held
4930
4931 Arguments:
4932
4933 Return Value:
4934 None
4935
4936 --*/
4937 {
4938 PLIST_ENTRY pEntry;
4939 FxRequest* pRequest;
4940
4941 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4942
4943 if (IsPowerStateNotifyingDriver()) {
4944 //
4945 // We will not process cancelled request while the driver is being
4946 // notified to stop/resume processing request to avoid double
4947 // completion of the request.
4948 //
4949 return FALSE;
4950 }
4951
4952 while (!IsListEmpty(&m_CanceledOnQueueList)) {
4953 pEntry = m_CanceledOnQueueList.Flink;
4954
4955 RemoveEntryList(pEntry);
4956
4957 // FxRequest ensures its not on any list on checked builds
4958 InitializeListHead(pEntry);
4959
4960 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryQueueOwned, pEntry);
4961
4962 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIO,
4963 "Calling CanceledOnQueue routine "
4964 "for WDFREQUEST 0x%p on WDFQUEUE 0x%p",
4965 pRequest->GetHandle(), GetObjectHandle());
4966
4967 if (FxDriverGlobals->FxVerifierOn) {
4968
4969 // Set cancelled status, otherwise verifier in FxRequest::Complete will complain
4970 pRequest->SetVerifierFlags(FXREQUEST_FLAG_CANCELLED);
4971 }
4972
4973 Unlock(*PreviousIrql);
4974
4975 if (FxDriverGlobals->FxVerifierOn) {
4976 ASSERT((pRequest->GetVerifierFlags() & FXREQUEST_FLAG_DRIVER_OWNED) != 0);
4977 }
4978
4979 //
4980 // Notify the driver
4981 //
4982 m_IoCanceledOnQueue.Invoke(GetHandle(), pRequest->GetHandle());
4983
4984 Lock(PreviousIrql);
4985 }
4986
4987 return TRUE;
4988 }
4989
__drv_requiresIRQL(DISPATCH_LEVEL)4990 __drv_requiresIRQL(DISPATCH_LEVEL)
4991 BOOLEAN
4992 FxIoQueue::ProcessPowerEvents(
4993 __out PKIRQL PreviousIrql
4994 )
4995 /*++
4996
4997 Routine Description:
4998
4999 Processes the power state machine for I/O queues.
5000
5001 Called with Queue lock held, but can drop and re-acquire
5002 it when it has to deliver event callbacks to the device driver.
5003
5004 This can modify queue state as it transits the state machine, and
5005 is called from the main event processing loop DispatchEvents().
5006
5007 Handling "in-flight" I/O requests in the device driver due
5008 to a power state transition (stopping) is tricky, and
5009 involves a complex state machine.
5010
5011 The device driver must ensure that all in-flight I/O is stopped
5012 before it can release the thread calling into the driver to
5013 perform the power state transition, otherwise a system crash
5014 can result from accessing hardware after resources have been removed.
5015
5016 In WDF, this burden is placed on FxIoQueue, so that the device driver
5017 does not have to implement the more complex aspects of this code,
5018 but can rely on notifications from the queue serialized under the
5019 IRQL level and locking it has configured.
5020
5021
5022 Implementation of FxIoQueue Power state machine:
5023 ------------------------------------------
5024
5025 Since we must drop our lock to callback into the device
5026 driver for power notifications, the processing must occur
5027 as a state machine with three lists.
5028
5029 On entry to the power stop state, any "in-flight" I/O requests
5030 are recorded on the m_DriverOwned list using
5031 FxRequest::FxListEntryDriverOwned for linkage.
5032
5033 All of the requests on m_DriverOwned are moved to m_PowerNotify
5034 while holding the lock, with m_DriverOwned cleared. The state is changed
5035 to indicate that the driver is now being notified of requests.
5036
5037 While in the driver notification state, requests are taken from the
5038 m_PowerNotify list, and moved on to the m_PowerDriverNotified list while
5039 under the lock, and the request is notified to the device driver
5040 by the callback while dropping the lock and re-acquiring the lock.
5041
5042 As the driver acknowledges the power notification, it calls
5043 WdfRequestStopAcknowledge (FxIoQueue::StopAcknowledge) which moves the
5044 request from the m_PowerDriverNotified list back to the m_DriverOwned
5045 list.
5046
5047 The device driver could also complete requests, in which case they
5048 just dis-appear from the lists by the completion code doing
5049 a RemoveEntryList on FxRequest::FxListEntryDriverOwned.
5050
5051 This occurs until the m_PowerNotify list is empty, in which case
5052 the state is changed to driver notified.
5053
5054 While in the driver notified state, the queue event processing continues
5055 until the m_PowerDriverNotified list is empty, and when it is, the
5056 stopped state is set, and the event m_PowerIdle is set. This releases
5057 the thread waiting on the power state transition in which all of the
5058 device drivers "in-flight" I/O has been stopped and accounted for.
5059
5060 State Transitions:
5061 --------------
5062
5063 During Stop:
5064
5065 FxIoQueuePowerOn
5066 --> FxIoQueuePowerStartingTransition
5067 --> FxIoQueuePowerStopping
5068 --> FxIoQueuePowerStoppingNotifyingDriver
5069 --> FxIoQueuePowerStoppingDriverNotified
5070 --> FxIoQueuePowerOff
5071
5072 During Purge:
5073
5074 FxIoQueuePowerPurge
5075 --> FxIoQueuePowerPurgeNotifyingDriver
5076 --> FxIoQueuePowerPurgeDriverNotified
5077 --> FxIoQueuePowerOff
5078
5079
5080 During Resume:
5081
5082 FxIoQueuePowerOff
5083 --> FxIoQueuePowerRestarting
5084 --> FxIoQueuePowerRestartingNotifyingDriver
5085 --> FxIoQueuePowerRestartingDriverNotified
5086 --> FxIoQueuePowerOn
5087
5088 Arguments:
5089
5090 Return Value:
5091
5092 TRUE - Continue processing the event loop
5093 FALSE - Stop processing event loop
5094
5095 --*/
5096 {
5097 PLIST_ENTRY Entry;
5098 FxRequest* pRequest;
5099
5100 PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
5101
5102 switch(m_PowerState) {
5103
5104 case FxIoQueuePowerStartingTransition:
5105 if (m_Dispatching == 1) {
5106
5107 //
5108 // If we are the last routine actively dispatching callbacks to
5109 // the device driver under a Power stop, then we must set the
5110 // event specifying no more callbacks are active.
5111 //
5112 m_PowerIdle.Set();
5113 }
5114
5115 return FALSE;
5116
5117 case FxIoQueuePowerStopping:
5118 //
5119 // Set state to FxIoQueuePowerPurgeNotifyingDriver
5120 //
5121 m_PowerState = FxIoQueuePowerStoppingNotifyingDriver;
5122
5123 // This should be empty on entry to this state
5124 ASSERT(IsListEmpty(&this->m_PowerNotify));
5125
5126 if (!IsListEmpty(&m_DriverOwned)) {
5127
5128 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5129 "Power Stop: WDFQUEUE 0x%p is powering off "
5130 "with in-flight requests",
5131 GetObjectHandle());
5132
5133
5134 // Ensure the logic of m_DriverOwned is correct
5135 ASSERT(m_DriverOwned.Flink->Blink == &m_DriverOwned);
5136 ASSERT(m_DriverOwned.Blink->Flink == &m_DriverOwned);
5137
5138 //
5139 // Move all requests on m_DriverOwned to m_PowerNotify
5140 //
5141 m_PowerNotify.Flink = m_DriverOwned.Flink;
5142 m_PowerNotify.Blink = m_DriverOwned.Blink;
5143 m_PowerNotify.Flink->Blink = &m_PowerNotify;
5144 m_PowerNotify.Blink->Flink = &m_PowerNotify;
5145
5146 // This is now empty
5147 InitializeListHead(&m_DriverOwned);
5148 }
5149 else {
5150 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5151 "Power Stop: WDFQUEUE 0x%p is powering off without "
5152 "in-flight requests",GetObjectHandle());
5153 }
5154
5155 //
5156 // Return to main processing loop which will callback to
5157 // process notifications
5158 //
5159 return TRUE;
5160
5161 case FxIoQueuePowerPurge:
5162 //
5163 // Set state to FxIoQueuePowerPurgeNotifyingDriver
5164 //
5165 m_PowerState = FxIoQueuePowerPurgeNotifyingDriver;
5166
5167 // This should be empty on entry to this state
5168 ASSERT(IsListEmpty(&this->m_PowerNotify));
5169
5170 if (!IsListEmpty(&m_DriverOwned)) {
5171
5172 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5173 "Power Stop: WDFQUEUE 0x%p is purging with "
5174 "in-flight requests",GetObjectHandle());
5175
5176 // Ensure the logic of m_DriverOwned is correct
5177 ASSERT(m_DriverOwned.Flink->Blink == &m_DriverOwned);
5178 ASSERT(m_DriverOwned.Blink->Flink == &m_DriverOwned);
5179
5180 //
5181 // Move all requests on m_DriverOwned to m_PowerNotify
5182 //
5183 m_PowerNotify.Flink = m_DriverOwned.Flink;
5184 m_PowerNotify.Blink = m_DriverOwned.Blink;
5185 m_PowerNotify.Flink->Blink = &m_PowerNotify;
5186 m_PowerNotify.Blink->Flink = &m_PowerNotify;
5187
5188 // This is now empty
5189 InitializeListHead(&m_DriverOwned);
5190 }
5191 else {
5192 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5193 "Power purge: WDFQUEUE 0x%p is purging without "
5194 "in-flight requests", GetObjectHandle());
5195 }
5196
5197 //
5198 // Return to main processing loop which will callback to
5199 // process notifications
5200 //
5201 return TRUE;
5202
5203 case FxIoQueuePowerStoppingNotifyingDriver: {
5204
5205 FxIoQueueIoStop stopCallback;
5206
5207 //
5208 // If the list of requests to notify the driver about is
5209 // empty, change to the notified state.
5210 //
5211 if (IsListEmpty(&m_PowerNotify)) {
5212 m_PowerState = FxIoQueuePowerStoppingDriverNotified;
5213
5214 //
5215 // Return to main processing loop which will callback to
5216 // process the wait/signaling for the driver to acknowledge
5217 // all stops.
5218 //
5219 return TRUE;
5220 }
5221
5222 //
5223 // Notify each entry in m_PowerNotify into the driver
5224 //
5225
5226 // Remove from the notify list, place it on the driver notified list
5227 Entry = RemoveHeadList(&m_PowerNotify);
5228
5229 InsertTailList(&m_PowerDriverNotified, Entry);
5230
5231 // Retrieve the FxRequest
5232 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryDriverOwned, Entry);
5233
5234 stopCallback = m_IoStop;
5235
5236 //
5237 // Notify driver by callback.
5238 //
5239 // If no callback is registered, the power thread will in effect
5240 // wait until the driver completes or cancels all requests.
5241 //
5242 if (stopCallback.Method != NULL && pRequest->m_Canceled == FALSE) {
5243 ULONG ActionFlags = WdfRequestStopActionSuspend;
5244
5245 if(pRequest->IsInIrpQueue(&m_DriverCancelable)) {
5246 ActionFlags |= WdfRequestStopRequestCancelable;
5247 }
5248
5249 DoTraceLevelMessage(
5250 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5251 "Power Stop Notifying Driver, WDFQUEUE 0x%p, WDFREQUEST 0x%p",
5252 GetObjectHandle(), pRequest->GetObjectHandle());
5253
5254 // Driver could be calling RequestComplete as we attempt to stop
5255 pRequest->ADDREF(FXREQUEST_HOLD_TAG);
5256
5257 Unlock(*PreviousIrql);
5258
5259 if (FxDriverGlobals->FxVerifierOn) {
5260 pRequest->SetVerifierFlags(FXREQUEST_FLAG_DRIVER_IN_EVTIOSTOP_CONTEXT);
5261 }
5262
5263 stopCallback.Invoke(GetHandle(), pRequest->GetHandle(), ActionFlags);
5264
5265 pRequest->RELEASE(FXREQUEST_HOLD_TAG);
5266
5267 Lock(PreviousIrql);
5268 }
5269
5270 //
5271 // As they are acknowledged, they will move back to m_DriverOwned.
5272 //
5273 // If the driver completes them, they go away.
5274 //
5275
5276 // Return to main processing loop and continue processing notifications
5277 return TRUE;
5278 }
5279
5280 case FxIoQueuePowerPurgeNotifyingDriver: {
5281
5282 FxIoQueueIoStop stopCallback;
5283
5284 //
5285 // If the list of requests to notify the driver about is
5286 // empty, change to the notified state.
5287 //
5288 if (IsListEmpty(&m_PowerNotify)) {
5289 m_PowerState = FxIoQueuePowerPurgeDriverNotified;
5290
5291 //
5292 // Return to main processing loop which will callback to
5293 // process the wait/signaling for the driver to acknowledge
5294 // all stops.
5295 //
5296 return TRUE;
5297 }
5298
5299 //
5300 // Notify each entry in m_PowerNotify into the driver
5301 //
5302
5303 // Remove from the notify list, place it on the driver notified list
5304 Entry = RemoveHeadList(&m_PowerNotify);
5305
5306 InsertTailList(&m_PowerDriverNotified, Entry);
5307
5308 // Retrieve the FxRequest
5309 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryDriverOwned, Entry);
5310
5311 stopCallback = m_IoStop;
5312
5313 //
5314 // Make sure power stop state is cleared before invoking the stop callback.
5315 //
5316 pRequest->ClearPowerStopState();
5317
5318 //
5319 // Notify driver by callback.
5320 //
5321 // If no callback is registered, the power thread will in effect
5322 // wait until the driver completes or cancels all requests.
5323 //
5324 if (stopCallback.Method != NULL && pRequest->m_Canceled == FALSE) {
5325 ULONG ActionFlags = WdfRequestStopActionPurge;
5326
5327 if(pRequest->IsInIrpQueue(&m_DriverCancelable)) {
5328 ActionFlags |= WdfRequestStopRequestCancelable;
5329 }
5330
5331 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5332 "Power Purge Notifying Driver "
5333 "WDFQUEUE 0x%p, WDFREQUEST 0x%p",
5334 GetObjectHandle(),pRequest->GetHandle());
5335
5336 // Driver could be calling RequestComplete as we attempt to stop
5337 pRequest->ADDREF(FXREQUEST_HOLD_TAG);
5338
5339 Unlock(*PreviousIrql);
5340
5341 if (FxDriverGlobals->FxVerifierOn) {
5342 pRequest->SetVerifierFlags(FXREQUEST_FLAG_DRIVER_IN_EVTIOSTOP_CONTEXT);
5343 }
5344
5345 stopCallback.Invoke(GetHandle(), pRequest->GetHandle(), ActionFlags);
5346
5347 pRequest->RELEASE(FXREQUEST_HOLD_TAG);
5348
5349 Lock(PreviousIrql);
5350 }
5351
5352 //
5353 // As they are acknowledged, they will move back to m_DriverOwned.
5354 //
5355 // If the driver completes them, they go away.
5356 //
5357
5358 // Return to main processing loop and continue processing notifications
5359 return TRUE;
5360 }
5361
5362 case FxIoQueuePowerStoppingDriverNotified:
5363 case FxIoQueuePowerPurgeDriverNotified: {
5364
5365 PLIST_ENTRY thisEntry, nextEntry, listHead;
5366 LIST_ENTRY acknowledgedList;
5367 BOOLEAN continueProcessing = FALSE;
5368
5369 InitializeListHead(&acknowledgedList);
5370
5371 //
5372 // First move all the acknowledged requests to local list and then
5373 // process the local list. We have do that in two steps because
5374 // ProcessAcknowledgedRequests drops and reacquires the lock.
5375 //
5376 listHead = &m_PowerDriverNotified;
5377
5378 for (thisEntry = listHead->Flink;
5379 thisEntry != listHead;
5380 thisEntry = nextEntry) {
5381
5382 // Retrieve the FxRequest
5383 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryDriverOwned, thisEntry);
5384
5385 nextEntry = thisEntry->Flink;
5386
5387 if (pRequest->IsPowerStopAcknowledged()) {
5388 RemoveEntryList(thisEntry);
5389 InsertTailList(&acknowledgedList, thisEntry);
5390 }
5391 }
5392
5393 //
5394 // Process all the acknowledged request from the local list.
5395 //
5396 while (!IsListEmpty(&acknowledgedList))
5397 {
5398 thisEntry = RemoveHeadList(&acknowledgedList);
5399 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryDriverOwned, thisEntry);
5400 ProcessAcknowledgedRequests(pRequest, PreviousIrql);
5401
5402 //
5403 // When this function drops the lock, other threads may attempt
5404 // to dispatch but fail since we are currently dispatching.
5405 // We need to be sure to process any pending events that other
5406 // threads initiated but could not be dispatch. The acknowledged
5407 // list will eventually be cleared out, allowing exit paths from
5408 // this function to return control to the driver.
5409 //
5410 continueProcessing = TRUE;
5411 }
5412
5413 //
5414 // Check to see if there are any unacknowledged requests.
5415 //
5416 if (!IsListEmpty(&m_PowerDriverNotified)) {
5417
5418 //
5419 // If there are still entries on the list, we potentially return
5420 // FALSE to tell the main event dispatching loop to return to
5421 // the device driver, since we are awaiting response from
5422 // the driver while in this state.
5423 //
5424
5425 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5426 "Power Stop: Waiting for Driver to complete or "
5427 "acknowledge in-flight requests on WDFQUEUE 0x%p",
5428 GetObjectHandle());
5429
5430 return continueProcessing;
5431 }
5432
5433 //
5434 // Check to see if there are any requests in the middle of two-phase-completion.
5435 // If so, bail out and wait.
5436 //
5437 if (m_TwoPhaseCompletions != 0) {
5438
5439 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5440 "Power Stop: Waiting for Driver to complete or "
5441 "acknowledge in-flight requests on WDFQUEUE 0x%p",
5442 GetObjectHandle());
5443
5444 return continueProcessing;
5445 }
5446
5447 //
5448 // All the requests are acknowledged. We will signal the pnp thread waiting
5449 // in StopProcessingForPower to continue if we are the last one to visit
5450 // the dispatch event loop.
5451 //
5452 //
5453 if ( m_Dispatching == 1) {
5454
5455 //
5456 // If we are the last routine actively dispatching callbacks to
5457 // the device driver under a Power stop, then we must set the
5458 // event specifying no more callbacks are active.
5459 //
5460
5461 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5462 "Power Stop: WDFQUEUE 0x%p is now powered off with no "
5463 "in-flight requests",GetObjectHandle());
5464
5465 m_PowerState = FxIoQueuePowerOff;
5466
5467 m_PowerIdle.Set();
5468
5469 return TRUE;
5470 }
5471
5472 //
5473 // The driver has acknowledged all requests, and the
5474 // notification list is empty. But, there are still outstanding
5475 // dispatch calls into the driver (m_Dispatching != 1), so we potentially
5476 // return false here to hopefully unwind to the final dispatch routine,
5477 // which will set the power off state.
5478 //
5479
5480 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5481 "Power Stop: Driver has acknowledged all in-flight "
5482 "requests, but WDFQUEUE 0x%p has outstanding callbacks",
5483 GetObjectHandle());
5484
5485 return continueProcessing;
5486 }
5487 case FxIoQueuePowerRestarting:
5488
5489 //
5490 // Power is being resumed to the device. We notify the
5491 // device driver by an event callback for all driver
5492 // owned requests that it has idled due to a previous
5493 // power stop.
5494 //
5495 m_PowerState = FxIoQueuePowerRestartingNotifyingDriver;
5496
5497 // This should be empty on entry to this state
5498 ASSERT(IsListEmpty(&this->m_PowerNotify));
5499
5500 if (!IsListEmpty(&m_DriverOwned)) {
5501
5502 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5503 "Power Resume: Driver has power paused requests "
5504 "on WDFQUEUE 0x%p",GetObjectHandle());
5505
5506 // Ensure the logic of m_DriverOwned is correct
5507 ASSERT(m_DriverOwned.Flink->Blink == &m_DriverOwned);
5508 ASSERT(m_DriverOwned.Blink->Flink == &m_DriverOwned);
5509
5510 //
5511 // Move all requests on m_DriverOwned to m_PowerNotify
5512 //
5513 m_PowerNotify.Flink = m_DriverOwned.Flink;
5514 m_PowerNotify.Blink = m_DriverOwned.Blink;
5515 m_PowerNotify.Flink->Blink = &m_PowerNotify;
5516 m_PowerNotify.Blink->Flink = &m_PowerNotify;
5517
5518 // This is now empty
5519 InitializeListHead(&m_DriverOwned);
5520 }
5521 else {
5522 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5523 "Power Resume: Driver has no power paused requests "
5524 "on WDFQUEUE 0x%p", GetObjectHandle());
5525 }
5526
5527 //
5528 // Return to main processing loop which will callback to
5529 // process notifications
5530 //
5531 return TRUE;
5532
5533
5534 case FxIoQueuePowerRestartingNotifyingDriver: {
5535
5536 FxIoQueueIoResume resumeCallback;
5537
5538 //
5539 // If the list of requests to notify the driver about is
5540 // empty, change to the notified state.
5541 //
5542 if (IsListEmpty(&m_PowerNotify)) {
5543
5544 m_PowerState = FxIoQueuePowerRestartingDriverNotified;
5545
5546 //
5547 // Return to main processing loop which will callback to
5548 // process the next state
5549 //
5550 return TRUE;
5551 }
5552
5553 //
5554 // Notify each entry in m_PowerNotify into the driver, placing them
5555 // back on the m_DriverOwned list.
5556 //
5557
5558 // Remove from the notify list, place it on the driver owned list
5559 Entry = RemoveHeadList(&m_PowerNotify);
5560
5561 InsertTailList(&m_DriverOwned, Entry);
5562
5563 // Retrieve the FxRequest
5564 pRequest = FxRequest::_FromOwnerListEntry(FxListEntryDriverOwned, Entry);
5565
5566 resumeCallback = m_IoResume;
5567
5568 // Notify driver by callback
5569 if (resumeCallback.Method != NULL && pRequest->m_Canceled == FALSE) {
5570
5571 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5572 "Power Resume, Notifying Driver, WDFQUEUE 0x%p, "
5573 "WDFREQUEST 0x%p",
5574 GetObjectHandle(),
5575 pRequest->GetObjectHandle());
5576
5577 // Driver could be calling RequestComplete as we attempt to resume
5578 pRequest->ADDREF(FXREQUEST_HOLD_TAG);
5579
5580 Unlock(*PreviousIrql);
5581
5582 resumeCallback.Invoke(GetHandle(), pRequest->GetHandle());
5583
5584 pRequest->RELEASE(FXREQUEST_HOLD_TAG);
5585
5586 Lock(PreviousIrql);
5587 }
5588 else {
5589 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5590 "Power Resume: Driver has no callback for "
5591 "EvtIoResume registered on WDFQUEUE 0x%p",GetObjectHandle());
5592 }
5593
5594 // Return to main processing loop and continue processing notifications
5595 return TRUE;
5596 }
5597
5598 case FxIoQueuePowerRestartingDriverNotified:
5599
5600 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5601 "Power Resume: WDFQUEUE 0x%p is now powered on and "
5602 "I/O has resumed",GetObjectHandle());
5603
5604 // Power state has resumed
5605 m_PowerState = FxIoQueuePowerOn;
5606
5607 //
5608 // We will resume dispatching I/O after all the queues
5609 // are moved into PowerOn state.
5610 //
5611 return FALSE;
5612
5613
5614 default:
5615 // Called on invalid state
5616 ASSERT(FALSE);
5617 return FALSE;
5618 }
5619
5620 /* NOTREACHED*/
5621 }
5622
__drv_requiresIRQL(DISPATCH_LEVEL)5623 __drv_requiresIRQL(DISPATCH_LEVEL)
5624 VOID
5625 FxIoQueue::ProcessAcknowledgedRequests(
5626 __in FxRequest* Request,
5627 __out PKIRQL PreviousIrql
5628 )
5629 /*++
5630
5631 Routine Description:
5632
5633 Process requests that are acknowledged by the driver.
5634
5635 Called with the queue lock held. This function can drop
5636 and reacquire the lock if needed.
5637
5638 Return Value:
5639
5640 --*/
5641 {
5642 PLIST_ENTRY Entry;
5643 BOOLEAN requeue;
5644 PFX_DRIVER_GLOBALS pFxDriverGlobals;
5645
5646 pFxDriverGlobals = GetDriverGlobals();
5647
5648 ASSERT(Request->IsPowerStopAcknowledged());
5649
5650 requeue = Request->IsPowerStopAcknowledgedWithRequeue();
5651
5652 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
5653 "Acknowledging WDFREQUEST %p on WDFQUEUE %p %s requeue option",
5654 Request->GetObjectHandle(), GetObjectHandle(),
5655 (requeue ? "with" : "without"));
5656
5657 Request->ClearPowerStopState();
5658
5659 //
5660 // Remove the request from the m_PowerDriverNotified list and
5661 // place it back on the m_DriverOwned list.
5662 //
5663 // N.B. Our caller guarantees that we have already been removed, thus we
5664 // must not explicitly remove here.
5665 //
5666 Entry = Request->GetListEntry(FxListEntryDriverOwned);
5667
5668 InitializeListHead(Entry);
5669
5670 InsertTailList(&this->m_DriverOwned, Entry);
5671
5672 if (pFxDriverGlobals->FxVerifierOn) {
5673 //
5674 // As soon as we drop the lock below the request may get completed.
5675 // So take an addition reference so that we can safely clear the
5676 // flag.
5677 //
5678 Request->ADDREF(FXREQUEST_HOLD_TAG);
5679 }
5680
5681 Unlock(*PreviousIrql);
5682
5683 if (pFxDriverGlobals->FxVerifierOn) {
5684 Request->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_IN_EVTIOSTOP_CONTEXT);
5685 Request->RELEASE(FXREQUEST_HOLD_TAG);
5686 }
5687
5688 if (requeue) {
5689 FxRequestCompletionState oldState;
5690 NTSTATUS status;
5691
5692 if (pFxDriverGlobals->FxVerifierOn) {
5693 Request->ClearVerifierFlags(FXREQUEST_FLAG_DRIVER_OWNED |
5694 FXREQUEST_FLAG_DRIVER_DISPATCH);
5695 }
5696
5697 //
5698 // If the device driver requests it back on the queue, we will place it
5699 // on the front and it will be re-delivered in the normal EvtIoDefault, ... path.
5700 //
5701 // EvtIoResume *will not* be called on power resume for this request.
5702 //
5703
5704 //
5705 // The request has only one reference, held by the completion
5706 // callback function. We need to take another one before cancelling
5707 // this function, otherwise we will lose the request object
5708 //
5709 Request->ADDREF(FXREQUEST_STATE_TAG);
5710
5711 // Cancel the request complete callback (deletes a reference)
5712 oldState = Request->SetCompletionState(FxRequestCompletionStateNone);
5713 ASSERT(oldState == FxRequestCompletionStateQueue);
5714 UNREFERENCED_PARAMETER(oldState);
5715
5716 Lock(PreviousIrql);
5717
5718 //
5719 // We are going to place the request back on the queue
5720 //
5721
5722 // Driver is returning I/O
5723 RemoveFromDriverOwnedList(Request);
5724
5725 //
5726 // Check if we need to delete this request.
5727 //
5728 if (m_CancelDispatchedRequests) {
5729 //
5730 // Do not requeue this request.
5731 //
5732 status = STATUS_CANCELLED;
5733 }
5734 else {
5735 //
5736 // Place the request back at the head of the main queue
5737 // so as not to re-order requests
5738 //
5739 status = Request->InsertHeadIrpQueue(&m_Queue, NULL);
5740 }
5741
5742 if (!NT_SUCCESS(status)) {
5743
5744 // Request not placed in queue, cancel it
5745 ASSERT(status == STATUS_CANCELLED);
5746
5747 status = STATUS_SUCCESS;
5748
5749 //
5750 // We must add a reference since the CancelForQueue path
5751 // assumes we were on the FxIrpQueue with the extra reference
5752 //
5753 Request->ADDREF(FXREQUEST_QUEUE_TAG);
5754
5755 //
5756 // Mark the request as cancelled, place it on the cancel list,
5757 // and schedule the cancel event to the driver
5758 //
5759 CancelForQueue(Request, *PreviousIrql);
5760
5761 //
5762 // Reacquire the lock because CancelForQueue visits the dispatch-loop
5763 // and releases the lock.
5764 //
5765 Lock(PreviousIrql);
5766 }
5767 else {
5768 // Check if went from no requests to have requests
5769 CheckTransitionFromEmpty();
5770 }
5771
5772 } else {
5773 Lock(PreviousIrql);
5774 }
5775
5776 return;
5777 }
5778
5779 VOID
StartPowerTransitionOff()5780 FxIoQueue::StartPowerTransitionOff(
5781 )
5782 /*++
5783
5784 Routine Description:
5785
5786 Purpose of this routine is to put the queue in state that would
5787 prevent any new requests from being dispatched to the driver.
5788
5789 Arguments:
5790
5791 Return Value:
5792
5793 VOID
5794
5795 --*/
5796 {
5797 KIRQL irql;
5798 BOOLEAN result;
5799
5800 if(m_PowerManaged == FALSE) {
5801 return;
5802 }
5803
5804 Lock(&irql);
5805
5806 if (m_Deleted == FALSE) {
5807 ASSERT(m_PowerState == FxIoQueuePowerOn);
5808 }
5809
5810 m_PowerState = FxIoQueuePowerStartingTransition;
5811
5812 // We must wait on the current thread until the queue is actually idle
5813 m_PowerIdle.Clear();
5814
5815 //
5816 // Run the event dispatching loop before waiting on the event
5817 // in case this thread actually performs the transition
5818 //
5819 result = DispatchEvents(irql);
5820 if(result) {
5821 //
5822 // This is called from a kernel mode PNP thread, so we do not need
5823 // a KeEnterCriticalRegion()
5824 //
5825 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
5826 "Waiting for all threads to stop dispatching requests"
5827 " so that WDFQUEUE 0x%p can be powered off",
5828 GetObjectHandle());
5829
5830 GetDriverGlobals()->WaitForSignal(m_PowerIdle.GetSelfPointer(),
5831 "waiting for all threads to stop dispatching requests so "
5832 "that queue can be powered off, WDFQUEUE", GetHandle(),
5833 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
5834 WaitSignalBreakUnderVerifier);
5835 }
5836
5837 return;
5838 }
5839
5840 VOID
StopProcessingForPower(__in FxIoStopProcessingForPowerAction Action)5841 FxIoQueue::StopProcessingForPower(
5842 __in FxIoStopProcessingForPowerAction Action
5843 )
5844 /*++
5845
5846 Routine Description:
5847
5848 Stops automatic I/O processing due to a power event that requires I/O to stop.
5849
5850 This is called on a PASSIVE_LEVEL thread that can block until
5851 I/O has been stopped, or completed/cancelled.
5852
5853 Additional reference is already taken on the object by the caller
5854 to prevent the queue from being deleted.
5855
5856
5857 Arguments:
5858
5859 Action -
5860
5861 FxIoStopProcessingForPowerHold:
5862 the function returns when the driver has acknowledged that it has
5863 stopped all I/O processing, but may have outstanding "in-flight" requests
5864 that have not been completed.
5865
5866 FxIoStopProcessingForPowerPurgeManaged:
5867 the function returns when all requests from a power managed queue have
5868 been completed and/or cancelled., and there are no more in-flight requests.
5869
5870 FxIoStopProcessingForPowerPurgeNonManaged:
5871 the function returns when all requests from a non-power managed queue have
5872 been completed and/or cancelled., and there are no more in-flight requests.
5873
5874 Return Value:
5875
5876 NTSTATUS
5877
5878 --*/
5879 {
5880 KIRQL irql;
5881 BOOLEAN result;
5882
5883 switch (Action) {
5884 case FxIoStopProcessingForPowerPurgeNonManaged:
5885
5886 //
5887 // If power managed, leave it alone
5888 //
5889 if(m_PowerManaged == TRUE) {
5890 // Should be powered off by now.
5891 ASSERT(m_PowerState == FxIoQueuePowerOff);
5892 return;
5893 }
5894
5895 //
5896 // Queue is being shut down. This flag prevents the following:
5897 // (1) a race condition where a dispatch queue handler changes the
5898 // state of the queue to accept_requests while we are in the
5899 // middle of a power stopping (purge) operation
5900
5901 // (2) another thread calling Stop or Start on a queue that is in the
5902 // middle of a power stopping (purge) operation.
5903 //
5904 Lock(&irql);
5905 SetStateForShutdown();
5906 Unlock(irql);
5907
5908 QueuePurge(TRUE, TRUE, NULL, NULL);
5909
5910 Lock(&irql);
5911 //
5912 // Queue must be in PowerOn state.
5913 //
5914 ASSERT(m_PowerState == FxIoQueuePowerOn);
5915
5916 m_PowerState = FxIoQueuePowerPurge;
5917
5918 break;
5919
5920 case FxIoStopProcessingForPowerPurgeManaged:
5921
5922 //
5923 // If not power managed, leave it alone
5924 //
5925 if(m_PowerManaged == FALSE) {
5926 ASSERT(m_PowerState == FxIoQueuePowerOn);
5927 return;
5928 }
5929
5930 //
5931 // Queue is being shut down. This flag prevents the following:
5932 // (1) a race condition where a dispatch queue handler changes the
5933 // state of the queue to accept_requests while we are in the
5934 // middle of a power stopping (purge) operation
5935
5936 // (2) another thread calling Stop or Start on a queue that is in the
5937 // middle of a power stopping (purge) operation.
5938 //
5939 Lock(&irql);
5940 SetStateForShutdown();
5941 Unlock(irql);
5942
5943 QueuePurge(TRUE, TRUE, NULL, NULL);
5944
5945 Lock(&irql);
5946
5947 m_PowerState = FxIoQueuePowerPurge;
5948
5949 break;
5950
5951 case FxIoStopProcessingForPowerHold:
5952 //
5953 // If not power managed, leave it alone
5954 //
5955 if(m_PowerManaged == FALSE) {
5956 ASSERT(m_PowerState == FxIoQueuePowerOn);
5957 return;
5958 }
5959
5960 Lock(&irql);
5961
5962 m_PowerState = FxIoQueuePowerStopping;
5963
5964 break;
5965
5966 default:
5967 ASSERT(FALSE);
5968 return;
5969 }
5970
5971 // We must wait on the current thread until the queue is actually idle
5972 m_PowerIdle.Clear();
5973
5974 //
5975 // Run the event dispatching loop before waiting on the event
5976 // in case this thread actually performs the transition
5977 //
5978 result = DispatchEvents(irql);
5979 if(result) {
5980 //
5981 // This is called from a kernel mode PNP thread, so we do not need
5982 // a KeEnterCriticalRegion()
5983 //
5984 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
5985 "Waiting for all inflight requests to be acknowledged "
5986 " on WDFQUEUE 0x%p",
5987 GetObjectHandle());
5988
5989 GetDriverGlobals()->WaitForSignal(m_PowerIdle.GetSelfPointer(),
5990 "waiting for all inflight requests "
5991 "to be acknowledged on WDFQUEUE",
5992 GetHandle(),
5993 GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
5994 WaitSignalBreakUnderVerifier);
5995 }
5996
5997 return;
5998 }
5999
6000 VOID
StartPowerTransitionOn(VOID)6001 FxIoQueue::StartPowerTransitionOn(
6002 VOID
6003 )
6004 /*++
6005 Routine Description: Start dispatching I/Os
6006
6007 Arguments: VOID
6008
6009 Return Value: VOID
6010 --*/
6011 {
6012 KIRQL irql;
6013
6014 if(m_PowerManaged == FALSE) {
6015 ASSERT(m_PowerState == FxIoQueuePowerOn);
6016 return;
6017 }
6018
6019 Lock(&irql);
6020
6021 if (m_Deleted == FALSE) {
6022 ASSERT(m_PowerState == FxIoQueuePowerOn);
6023 }
6024
6025 //
6026 // If there are requests in the queue when we power up, we
6027 // should set m_TransitionFromEmpty to trigger event-ready notify
6028 // callback on the manual queue to kick start processing of requests.
6029 // If we don't set, there is a posibility for abandoning the requests in the
6030 // the queue if the queue is powered off between the time we call
6031 // ProcessReadNotify and the call to retrieve requests made by the driver
6032 // because the retrieve call will fail and the request will be left in the
6033 // queue with m_TransitionFromEmpty state cleared.
6034 //
6035 if (m_Queue.GetRequestCount() > 0L) {
6036 m_TransitionFromEmpty = TRUE;
6037 m_ForceTransitionFromEmptyWhenAddingNewRequest = FALSE;
6038 }
6039
6040 DispatchEvents(irql);
6041
6042 return;
6043 }
6044
6045 VOID
ResumeProcessingForPower(VOID)6046 FxIoQueue::ResumeProcessingForPower(
6047 VOID
6048 )
6049 /*++
6050
6051 Routine Description:
6052
6053 Resumes a PowerManaged queue for automatic I/O processing due to
6054 a power event that allows I/O to resume.
6055
6056 Does nothing if its a non-power managed queue.
6057
6058 Additional reference is already taken on the object by the caller
6059 to prevent the queue from being deleted.
6060
6061 Arguments:
6062
6063 Return Value:
6064
6065 NTSTATUS
6066
6067 --*/
6068 {
6069 KIRQL irql;
6070
6071 //
6072 // If not power managed, leave it alone
6073 //
6074 if (!m_PowerManaged) {
6075 ASSERT(m_PowerState == FxIoQueuePowerOn);
6076 return;
6077 }
6078
6079 Lock(&irql);
6080
6081 if (m_PowerState == FxIoQueuePowerOn) {
6082 Unlock(irql);
6083 return;
6084 }
6085
6086 ASSERT(m_PowerState == FxIoQueuePowerOff);
6087
6088 m_PowerState = FxIoQueuePowerRestarting;
6089
6090 //
6091 // We have transitioned to a status that resumes
6092 // processing, so call dispatch function.
6093 //
6094
6095 DispatchEvents(irql);
6096
6097 return;
6098 }
6099
6100 VOID
SetStateForShutdown(VOID)6101 FxIoQueue::SetStateForShutdown(
6102 VOID
6103 )
6104 /*++
6105
6106 Routine Description:
6107 The queue is shutting down. Disable WdfQueueStart/Stop from re-enabling
6108 the AcceptRequest bit.
6109
6110 Arguments:
6111 None
6112
6113 Return Value:
6114 None
6115
6116 --*/
6117 {
6118 //
6119 // No need to take a lock since caller is responsible for providing the
6120 // required synchronization.
6121 //
6122
6123 //
6124 // Do not allow request to be queued.
6125 //
6126 SetState((FX_IO_QUEUE_SET_STATE)(FxIoQueueSetShutdown | FxIoQueueClearAcceptRequests));
6127 }
6128
6129 VOID
ResetStateForRestart(VOID)6130 FxIoQueue::ResetStateForRestart(
6131 VOID
6132 )
6133 /*++
6134
6135 Routine Description:
6136 This is called on a device (PDO) which has been restarted from the removed
6137 state. It will reset purged queues so that it can accept new requests
6138 when ResumeProcessingForPower is called afterwards.
6139
6140 Additional reference is already taken on the object by the caller
6141 to prevent the queue from being deleted.
6142
6143
6144 Arguments:
6145 None
6146
6147 Return Value:
6148 None
6149
6150 --*/
6151 {
6152 KIRQL irql;
6153
6154 Lock(&irql);
6155
6156 //
6157 // For non power managed queues, let us reset the m_PowerState to On
6158 //
6159 if (!m_PowerManaged) {
6160 m_PowerState = FxIoQueuePowerOn;
6161 }
6162
6163 //
6164 // Allow requests to be queued.
6165 //
6166 SetState((FX_IO_QUEUE_SET_STATE)(FxIoQueueClearShutdown | FxIoQueueSetAcceptRequests));
6167
6168 //
6169 // No need to visit the DispatchEvents because the PowerState
6170 // is still off.
6171 //
6172 Unlock(irql);
6173
6174 return;
6175 }
6176
6177 BOOLEAN
IsIoEventHandlerRegistered(__in WDF_REQUEST_TYPE RequestType)6178 FxIoQueue::IsIoEventHandlerRegistered(
6179 __in WDF_REQUEST_TYPE RequestType
6180 )
6181 /*++
6182
6183 Routine Description:
6184 Given a request type, this function checks to see if the appropriate
6185 event handler is registered to receive dispatched requests.
6186
6187 Return Value:
6188
6189 TRUE - yes the queue is configured to dispatch requests of given RequestType
6190 FALSE - no, the queue cannot dispatch requests of given RequestType
6191
6192 --*/
6193 {
6194 if(m_Type == WdfIoQueueDispatchManual) {
6195 //
6196 // Manual queues wouldn't have any IoEvent callbacks registered.
6197 //
6198 return TRUE;
6199 }
6200
6201 //
6202 // Default handler is a catch all handler.
6203 //
6204 if(m_IoDefault.Method != NULL) {
6205 return TRUE;
6206 }
6207
6208 //
6209 // Default handle is not registered. So check to see if request specific
6210 // handler is registered.
6211 //
6212 switch(RequestType) {
6213 case WdfRequestTypeRead:
6214 if(m_IoRead.Method == NULL) {
6215 return FALSE;
6216 }
6217 break;
6218 case WdfRequestTypeWrite:
6219 if(m_IoWrite.Method == NULL) {
6220 return FALSE;
6221 }
6222 break;
6223 case WdfRequestTypeDeviceControl:
6224 if(m_IoDeviceControl.Method == NULL) {
6225 return FALSE;
6226 }
6227 break;
6228 case WdfRequestTypeDeviceControlInternal:
6229 if(m_IoInternalDeviceControl.Method == NULL) {
6230 return FALSE;
6231 }
6232 break;
6233 case WdfRequestTypeCreate: // Fall through. Must have default handler.
6234 default:
6235 return FALSE;
6236 }
6237
6238 return TRUE;
6239 }
6240
6241 VOID
_DeferredDispatchThreadThunk(__in PVOID Parameter)6242 FxIoQueue::_DeferredDispatchThreadThunk(
6243 __in PVOID Parameter
6244 )
6245 /*++
6246
6247 Routine Description:
6248 Thunk used when requests must be posted to a workitem.
6249
6250 --*/
6251 {
6252 FxIoQueue* pQueue = (FxIoQueue*)Parameter;
6253
6254 PFX_DRIVER_GLOBALS FxDriverGlobals = pQueue->GetDriverGlobals();
6255
6256 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
6257 "Dispatching requests from worker thread");
6258
6259 pQueue->DeferredDispatchRequestsFromWorkerThread();
6260
6261 return;
6262 }
6263
6264
6265 VOID
_DeferredDispatchDpcThunk(__in PKDPC Dpc,__in PVOID DeferredContext,__in PVOID SystemArgument1,__in PVOID SystemArgument2)6266 FxIoQueue::_DeferredDispatchDpcThunk(
6267 __in PKDPC Dpc,
6268 __in PVOID DeferredContext,
6269 __in PVOID SystemArgument1,
6270 __in PVOID SystemArgument2
6271 )
6272 /*++
6273
6274 Routine Description:
6275 Thunk used when requests must be posted to a DPC.
6276
6277 --*/
6278 {
6279 FxIoQueue* pQueue = (FxIoQueue*)DeferredContext;
6280
6281 PFX_DRIVER_GLOBALS FxDriverGlobals = pQueue->GetDriverGlobals();
6282
6283 UNREFERENCED_PARAMETER(Dpc);
6284 UNREFERENCED_PARAMETER(SystemArgument1);
6285 UNREFERENCED_PARAMETER(SystemArgument2);
6286
6287 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
6288 "Dispatching requests from DPC");
6289
6290 pQueue->DeferredDispatchRequestsFromDpc();
6291
6292 return;
6293 }
6294
6295 VOID
_PurgeComplete(__in WDFQUEUE Queue,__in WDFCONTEXT Context)6296 FxIoQueue::_PurgeComplete(
6297 __in WDFQUEUE Queue,
6298 __in WDFCONTEXT Context
6299 )
6300 /*++
6301
6302 Routine Description:
6303 Callback function when a queue purge completes
6304
6305 --*/
6306 {
6307 MxEvent* event = (MxEvent*)Context;
6308
6309 UNREFERENCED_PARAMETER(Queue);
6310
6311 event->SetWithIncrement(EVENT_INCREMENT);
6312
6313 return;
6314 }
6315
6316 VOID
_IdleComplete(__in WDFQUEUE Queue,__in WDFCONTEXT Context)6317 FxIoQueue::_IdleComplete(
6318 __in WDFQUEUE Queue,
6319 __in WDFCONTEXT Context
6320 )
6321 /*++
6322
6323 Routine Description:
6324 Callback function when a stop completes
6325
6326 --*/
6327 {
6328 MxEvent* event = (MxEvent*)Context;
6329
6330 UNREFERENCED_PARAMETER(Queue);
6331
6332 event->SetWithIncrement(EVENT_INCREMENT);
6333
6334 return;
6335 }
6336
6337 DECLSPEC_NORETURN
6338 VOID
FatalError(__in NTSTATUS Status)6339 FxIoQueue::FatalError(
6340 __in NTSTATUS Status
6341 )
6342 {
6343 WDF_QUEUE_FATAL_ERROR_DATA data;
6344
6345 RtlZeroMemory(&data, sizeof(data));
6346
6347 data.Queue = GetHandle();
6348 data.Request = NULL;
6349 data.Status = Status;
6350
6351 FxVerifierBugCheck(GetDriverGlobals(),
6352 WDF_QUEUE_FATAL_ERROR,
6353 (ULONG_PTR) &data);
6354 }
6355
6356
6357 _Must_inspect_result_
6358 NTSTATUS
AllocateReservedRequest(__deref_out FxRequest ** Request)6359 FxIoQueue::AllocateReservedRequest(
6360 __deref_out FxRequest** Request
6361 )
6362 /*++
6363
6364 Routine Description:
6365 Called by Fxpkgio::Dispatch to allocate a reserved request if one is
6366 avaialble.
6367
6368 --*/
6369
6370 {
6371 PFX_DRIVER_GLOBALS pFxDriverGlobals;
6372 NTSTATUS status;
6373 FxRequest* pRequest;
6374 PWDF_OBJECT_ATTRIBUTES reqAttribs;
6375
6376 pFxDriverGlobals = GetDriverGlobals();
6377 *Request = NULL;
6378 reqAttribs = NULL;
6379
6380 //
6381 // Get the right context for this request object.
6382 //
6383 if (GetCxDeviceInfo() != NULL) {
6384 reqAttribs = &GetCxDeviceInfo()->RequestAttributes;
6385 }
6386 else {
6387 reqAttribs = m_Device->GetRequestAttributes();
6388 }
6389
6390 //
6391 // FxRequest::_Create doesn't create a Request from the Device lookaside
6392 // hence we can't use that as the Request Context doesn't get associated
6393 // if the Request is not created from the lookaside.
6394 //
6395 status = FxRequest::_CreateForPackage(m_Device, reqAttribs, NULL, &pRequest);
6396 if (!NT_SUCCESS(status)) {
6397 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
6398 "Failure to allocate request %!STATUS!", status);
6399 return status;
6400 }
6401
6402 pRequest->SetReserved();
6403 pRequest->SetCurrentQueue(this);
6404
6405 //
6406 // This is used to return the request to the correct queue if it was
6407 // forwarded
6408 //
6409 pRequest->SetForwardProgressQueue(this);
6410 pRequest->SetCompleted(FALSE);
6411
6412 if (m_FwdProgContext->m_IoReservedResourcesAllocate.Method != NULL) {
6413
6414 pRequest->SetPresented();
6415
6416 status = m_FwdProgContext->m_IoReservedResourcesAllocate.Invoke(
6417 GetHandle(),
6418 pRequest->GetHandle());
6419 if (!NT_SUCCESS(status)) {
6420 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
6421 "Failure from m_IoReservedResourcesAllocate callback %!STATUS!",
6422 status);
6423 pRequest->FreeRequest();
6424 }
6425 }
6426
6427 if (NT_SUCCESS(status)) {
6428 *Request = pRequest;
6429 }
6430
6431 return status;
6432 }
6433
6434
6435 VOID
CancelIrps(__in PLIST_ENTRY IrpListHead)6436 FxIoQueue::CancelIrps(
6437 __in PLIST_ENTRY IrpListHead
6438 )
6439 /*++
6440
6441 Routine Description:
6442
6443 This function is called to purge (cancel) the specified list of IRPs.
6444 The IRP's Tail.Overlay.ListEntry field must be used to link these structs together.
6445
6446 --*/
6447 {
6448 MdIrp irp;
6449 FxIrp fxIrp;
6450 PLIST_ENTRY entry;
6451
6452 while(!IsListEmpty(IrpListHead)) {
6453
6454 entry = RemoveHeadList(IrpListHead);
6455
6456 irp = fxIrp.GetIrpFromListEntry(entry);
6457
6458 fxIrp.SetIrp(irp);
6459
6460 fxIrp.SetInformation(0);
6461 fxIrp.SetStatus(STATUS_CANCELLED);
6462 fxIrp.CompleteRequest(IO_NO_INCREMENT);
6463 }
6464 }
6465
6466 VOID
PurgeForwardProgressIrps(__in_opt MdFileObject FileObject)6467 FxIoQueue::PurgeForwardProgressIrps(
6468 __in_opt MdFileObject FileObject
6469 )
6470 /*++
6471
6472 Routine Description:
6473
6474 This function is called when the queue is purged.
6475
6476 --*/
6477 {
6478 LIST_ENTRY cleanupList;
6479
6480 InitializeListHead(&cleanupList);
6481 GetForwardProgressIrps(&cleanupList, FileObject);
6482 CancelIrps(&cleanupList);
6483 }
6484
6485 VOID
VerifierVerifyFwdProgListsLocked(VOID)6486 FxIoQueue::VerifierVerifyFwdProgListsLocked(
6487 VOID
6488 )
6489 /*++
6490
6491 Routine Description:
6492 Called from dispose to Free all the reserved requests.
6493
6494 --*/
6495 {
6496 ULONG countOfInUseRequests;
6497 ULONG countOfFreeRequests;
6498 PLIST_ENTRY thisEntry, nextEntry, listHead;
6499
6500 countOfInUseRequests = 0;
6501
6502 listHead = &m_FwdProgContext->m_ReservedRequestInUseList;
6503
6504 for(thisEntry = listHead->Flink;
6505 thisEntry != listHead;
6506 thisEntry = nextEntry)
6507 {
6508 nextEntry = thisEntry->Flink;
6509 countOfInUseRequests++;
6510 }
6511
6512 countOfFreeRequests = 0;
6513
6514 listHead = &m_FwdProgContext->m_ReservedRequestList;
6515
6516 for(thisEntry = listHead->Flink;
6517 thisEntry != listHead;
6518 thisEntry = nextEntry)
6519 {
6520 nextEntry = thisEntry->Flink;
6521 countOfFreeRequests++;
6522 }
6523
6524 ASSERT(countOfFreeRequests + countOfInUseRequests ==
6525 m_FwdProgContext->m_NumberOfReservedRequests);
6526 return;
6527 }
6528