1 //
2 //    Copyright (C) Microsoft.  All rights reserved.
3 //
4 #include "../ioprivshared.hpp"
5 
6 
7 extern "C" {
8 #if defined(EVENT_TRACING)
9 #include "FxIoQueueKm.tmh"
10 #endif
11 }
12 
13 _Must_inspect_result_
14 NTSTATUS
15 FxIoQueue::QueueForwardProgressIrpLocked(
16     __in PIRP Irp
17     )
18 {
19     InsertTailList(&m_FwdProgContext->m_PendedIrpList,
20                    &Irp->Tail.Overlay.ListEntry);
21 
22     Irp->Tail.Overlay.DriverContext[FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY] =
23             m_FwdProgContext;
24 
25     IoSetCancelRoutine(Irp, _WdmCancelRoutineForReservedIrp);
26 
27     if (Irp->Cancel) {
28         if (IoSetCancelRoutine(Irp, NULL) != NULL){
29 
30             Irp->Tail.Overlay.DriverContext[FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY] = NULL;
31 
32             RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
33             InitializeListHead(&Irp->Tail.Overlay.ListEntry);
34 
35             return STATUS_CANCELLED;
36         }
37         else {
38             //
39             // CancelRoutine will complete the IRP
40             //
41             DO_NOTHING();
42         }
43     }
44 
45     IoMarkIrpPending(Irp);
46 
47     return STATUS_PENDING;
48 }
49 
50 
51 _Must_inspect_result_
52 PIRP
53 FxIoQueue::GetForwardProgressIrpLocked(
54     __in_opt PFILE_OBJECT FileObject
55     )
56 /*++
57 
58     Routine Description:
59         Remove an IRP from the pending irp list if it matches with the input
60         fileobject. If the fileobject value is NULL, return the first one from
61         the pending list.
62 
63 --*/
64 {
65     PIRP pIrp;
66     PLIST_ENTRY thisEntry, nextEntry, listHead;
67     PIO_STACK_LOCATION  pIrpStack;
68 
69     pIrp = NULL;
70     listHead = &m_FwdProgContext->m_PendedIrpList;
71 
72     for(thisEntry = listHead->Flink;
73         thisEntry != listHead;
74         thisEntry = nextEntry) {
75         nextEntry = thisEntry->Flink;
76 
77         pIrp = CONTAINING_RECORD(thisEntry, IRP, Tail.Overlay.ListEntry);
78         pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
79 
80         if (FileObject == NULL || FileObject == pIrpStack->FileObject) {
81 
82             RemoveEntryList(thisEntry);
83             InitializeListHead(thisEntry);
84 
85             if (NULL != IoSetCancelRoutine (pIrp, NULL)) {
86                 pIrp->Tail.Overlay.DriverContext[FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY] = NULL;
87                 break;
88             }
89             else {
90                 //
91                 // Irp is canceled and the cancel routines is waiting to run.
92                 // Continue to get the next the irp in the list.
93                 //
94                 DO_NOTHING();
95             }
96         }
97 
98         pIrp = NULL;
99     }
100 
101     return pIrp;
102 }
103 
104 VOID
105 FxIoQueue::FreeAllReservedRequests(
106     __in BOOLEAN Verify
107     )
108 /*++
109 
110 Routine Description:
111     Called from dispose to Free all the reserved requests.
112 
113     Verify -
114         TRUE - Make sure the number of request freed matches with the count of
115                request created.
116         FALSE - Called when we fail to allocate all the reserved requests
117                 during config at init time. So we don't verify because the
118                 count of request freed wouldn't match with the configured value.
119 --*/
120 {
121     ULONG count;
122     KIRQL oldIrql;
123 
124     count = 0;
125 
126     //
127     // Since forward progress request are allocated only for top level
128     // queues which cant be deleted so we dont need to add a reference on the
129     // queue for each reserved request since the Queue is guaranteed to be
130     // around when the request is being freed even if the Request was forwarded
131     // to another Queue.
132     //
133     m_FwdProgContext->m_PendedReserveLock.Acquire(&oldIrql);
134 
135     while (!IsListEmpty(&m_FwdProgContext->m_ReservedRequestList)) {
136         PLIST_ENTRY pEntry;
137         FxRequest * pRequest;
138 
139         pEntry = RemoveHeadList(&m_FwdProgContext->m_ReservedRequestList);
140         pRequest = FxRequest::_FromOwnerListEntry(FxListEntryForwardProgress,
141                                                   pEntry);
142         pRequest->FreeRequest();
143 
144         count++;
145     }
146 
147     if (Verify) {
148         ASSERT(count == m_FwdProgContext->m_NumberOfReservedRequests);
149     }
150 
151     ASSERT(IsListEmpty(&m_FwdProgContext->m_ReservedRequestInUseList));
152     ASSERT(IsListEmpty(&m_FwdProgContext->m_PendedIrpList));
153 
154     m_FwdProgContext->m_PendedReserveLock.Release(oldIrql);
155 
156     return;
157 }
158 
159 VOID
160 FxIoQueue::ReturnReservedRequest(
161     __in FxRequest *ReservedRequest
162     )
163 /*++
164 
165 Routine Description:
166     Reuse the ReservedRequest if there are pended IRPs otherwise
167     add it back to the reserve list.
168 
169 --*/
170 {
171     KIRQL   oldIrql;
172     PIRP    pIrp;
173     PFX_DRIVER_GLOBALS pFxDriverGlobals;
174 
175     pFxDriverGlobals = GetDriverGlobals();
176     pIrp= NULL;
177 
178     ASSERT(ReservedRequest->GetRefCnt() == 1);
179 
180     if (pFxDriverGlobals->FxVerifierOn) {
181         ReservedRequest->ClearVerifierFlags(
182             FXREQUEST_FLAG_RESERVED_REQUEST_ASSOCIATED_WITH_IRP);
183     }
184 
185     //
186     // Reuse the reserved request for dispatching the next pending IRP.
187     //
188     m_FwdProgContext->m_PendedReserveLock.Acquire(&oldIrql);
189 
190     pIrp = GetForwardProgressIrpLocked(NULL);
191 
192     m_FwdProgContext->m_PendedReserveLock.Release(oldIrql);
193 
194     ReservedRequest->ClearFieldsForReuse();
195 
196     if (pIrp != NULL) {
197         //
198         // Associate the reserved request with the Pended IRP
199         //
200         ReservedRequest->m_Irp.SetIrp(pIrp);
201         ReservedRequest->AssignMemoryBuffers(m_Device->GetIoType());
202 
203         if (pFxDriverGlobals->FxVerifierOn) {
204             ReservedRequest->SetVerifierFlags(
205                 FXREQUEST_FLAG_RESERVED_REQUEST_ASSOCIATED_WITH_IRP);
206         }
207 
208         //
209         // Ignore the return status because QueueRequest will complete the
210         // request on it own if it fails to queue request.
211         //
212         (VOID) QueueRequest(ReservedRequest);
213     }
214     else {
215         PutBackReservedRequest(ReservedRequest);
216     }
217 }
218 
219 VOID
220 FxIoQueue::GetForwardProgressIrps(
221     __in     PLIST_ENTRY    IrpListHead,
222     __in_opt MdFileObject   FileObject
223     )
224 /*++
225 
226 Routine Description:
227 
228     This function is called to retrieve the list of reserved queued IRPs.
229     The IRP's Tail.Overlay.ListEntry field is used to link these structs together.
230 
231 --*/
232 {
233     PIRP    irp;
234     KIRQL   irql;
235 
236     m_FwdProgContext->m_PendedReserveLock.Acquire(&irql);
237 
238     do {
239         irp = GetForwardProgressIrpLocked(FileObject);
240         if (irp != NULL) {
241             //
242             // Add it to the cleanupList. We will complete the IRP after
243             // releasing the lock.
244             //
245             InsertTailList(IrpListHead, &irp->Tail.Overlay.ListEntry);
246         }
247     } while (irp != NULL);
248 
249     m_FwdProgContext->m_PendedReserveLock.Release(irql);
250 }
251 
252 VOID
253 FxIoQueue::_WdmCancelRoutineForReservedIrp(
254     __inout struct _DEVICE_OBJECT * DeviceObject,
255     __in __drv_useCancelIRQL PIRP   Irp
256     )
257 /*++
258 
259 Routine Description:
260 
261     Cancel routine for forward progress irp.
262 
263 --*/
264 {
265     KIRQL irql;
266     PFXIO_FORWARD_PROGRESS_CONTEXT m_FwdPrgContext;
267 
268     UNREFERENCED_PARAMETER (DeviceObject);
269 
270     IoReleaseCancelSpinLock(Irp->CancelIrql);
271 
272     m_FwdPrgContext = (PFXIO_FORWARD_PROGRESS_CONTEXT)
273         Irp->Tail.Overlay.DriverContext[FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY];
274 
275     Irp->Tail.Overlay.DriverContext[FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY] = NULL;
276 
277     m_FwdPrgContext->m_PendedReserveLock.Acquire(&irql);
278 
279     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
280     InitializeListHead(&Irp->Tail.Overlay.ListEntry);
281 
282     m_FwdPrgContext->m_PendedReserveLock.Release(irql);
283 
284     Irp->IoStatus.Status = STATUS_CANCELLED;
285     Irp->IoStatus.Information = 0;
286     IoCompleteRequest(Irp, IO_NO_INCREMENT);
287 
288     return;
289 }
290 
291 VOID
292 FxIoQueue::FlushQueuedDpcs(
293     VOID
294     )
295 /*++
296 
297 Routine Description:
298 
299     This is the kernel mode routine to flush queued DPCs.
300 
301 Arguments:
302 
303 Return Value:
304 
305 --*/
306 {
307     KeFlushQueuedDpcs();
308 }
309 
310 
311 VOID
312 FxIoQueue::InsertQueueDpc(
313     VOID
314     )
315 /*++
316 
317 Routine Description:
318 
319     This is the kernel mode routine to insert a dpc.
320 
321 Arguments:
322 
323 Return Value:
324 
325 --*/
326 {
327     KeInsertQueueDpc(&m_Dpc, NULL, NULL);
328 }
329 
330 _Must_inspect_result_
331 NTSTATUS
332 FxIoQueue::GetReservedRequest(
333     __in MdIrp Irp,
334     __deref_out_opt FxRequest **ReservedRequest
335     )
336 /*++
337 
338 Routine Description:
339     Use the policy configured on the queue to decide whether to allocate a
340     reserved request.
341 
342 --*/
343 {
344     WDF_IO_FORWARD_PROGRESS_ACTION action;
345     FxRequest *pRequest;
346     KIRQL      oldIrql;
347     NTSTATUS status;
348     PFX_DRIVER_GLOBALS pFxDriverGlobals;
349 
350     pFxDriverGlobals = GetDriverGlobals();
351 
352     *ReservedRequest = NULL;
353     action = WdfIoForwardProgressActionInvalid;
354 
355     switch (m_FwdProgContext->m_Policy) {
356 
357     case WdfIoForwardProgressReservedPolicyPagingIO:
358         if (IsPagingIo(Irp)) {
359             action = WdfIoForwardProgressActionUseReservedRequest;
360         }
361         else {
362             action =  WdfIoForwardProgressActionFailRequest;
363         }
364 
365         break;
366     case WdfIoForwardProgressReservedPolicyAlwaysUseReservedRequest:
367 
368         action = WdfIoForwardProgressActionUseReservedRequest;
369         break;
370 
371     case WdfIoForwardProgressReservedPolicyUseExamine:
372 
373         //
374         // Call the driver to figure out what action to take.
375         //
376         if (m_FwdProgContext->m_IoExamineIrp.Method != NULL) {
377 
378             action = m_FwdProgContext->m_IoExamineIrp.Invoke(GetHandle(), Irp);
379 
380             //
381             // Make sure the use has returned a valid action
382             //
383             if((action < WdfIoForwardProgressActionFailRequest ||
384                 action > WdfIoForwardProgressActionUseReservedRequest)) {
385 
386                 status = STATUS_UNSUCCESSFUL;
387                 DoTraceLevelMessage(
388                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
389                     "EvtIoExamineIrp callback on WDFQUEUE %p returned an "
390                     "invalid action %d, %!STATUS!",
391                     GetHandle(), action, status);
392 
393                 FxVerifierDbgBreakPoint(pFxDriverGlobals);
394 
395                 return status;
396              }
397 
398         }
399         break;
400 
401     default:
402             ASSERTMSG("Invalid forward progress setting ", FALSE);
403             break;
404     }
405 
406     if (action == WdfIoForwardProgressActionFailRequest) {
407         status = STATUS_UNSUCCESSFUL;
408         DoTraceLevelMessage(
409             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
410             "Forward action on WDFQUEUE %p says that framework should fail "
411             "the Irp %p, %!STATUS!",
412             GetHandle(), Irp, status);
413 
414         return status;
415     }
416 
417     pRequest = NULL;
418 
419     m_FwdProgContext->m_PendedReserveLock.Acquire(&oldIrql);
420 
421     if (!IsListEmpty(&m_FwdProgContext->m_ReservedRequestList)) {
422         PLIST_ENTRY pEntry;
423 
424         pEntry = RemoveHeadList(&m_FwdProgContext->m_ReservedRequestList);
425 
426         pRequest = FxRequest::_FromOwnerListEntry(FxListEntryForwardProgress,
427                                                   pEntry);
428         ASSERT(pRequest != NULL);
429         ASSERT(pRequest->GetRefCnt() == 1);
430 
431         InsertTailList(&m_FwdProgContext->m_ReservedRequestInUseList,
432                     pRequest->GetListEntry(FxListEntryForwardProgress));
433 
434         pRequest->m_Irp.SetIrp(Irp);
435         pRequest->AssignMemoryBuffers(m_Device->GetIoType());
436 
437         if (pFxDriverGlobals->FxVerifierOn) {
438             pRequest->SetVerifierFlags(FXREQUEST_FLAG_RESERVED_REQUEST_ASSOCIATED_WITH_IRP);
439         }
440 
441         //
442         // if *ReservedRequest is non-null the caller needs to free the
443         // previous request it allocated.
444         //
445         *ReservedRequest = pRequest;
446 
447         status = STATUS_SUCCESS;
448     }
449     else {
450 
451         status = QueueForwardProgressIrpLocked(Irp);
452         ASSERT(*ReservedRequest == NULL);
453     }
454 
455     m_FwdProgContext->m_PendedReserveLock.Release(oldIrql);
456 
457     return status;
458 }
459 
460 _Must_inspect_result_
461 NTSTATUS
462 FxIoQueue::AssignForwardProgressPolicy(
463     __in PWDF_IO_QUEUE_FORWARD_PROGRESS_POLICY Policy
464     )
465 /*++
466 
467 Routine Description:
468     Configure the queue for forward Progress.
469 
470 --*/
471 {
472     NTSTATUS status;
473     FxRequest *pRequest;
474     ULONG index;
475     PFX_DRIVER_GLOBALS pFxDriverGlobals;
476 
477     pFxDriverGlobals = GetDriverGlobals();
478 
479     //
480     // If the queue is not a top level queue then return an error
481     //
482     status = STATUS_SUCCESS;
483 
484     //
485     // From v1.11 any queue can be top level (see WdfDeviceWdmDispatchIrpToIoQueue API).
486     //
487     if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1, 11) == FALSE) {
488         if (m_PkgIo->IsTopLevelQueue(this) == FALSE) {
489             status = STATUS_INVALID_PARAMETER;
490             DoTraceLevelMessage(
491                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
492                 "Setting Forward progress policy on non-top level queue %!STATUS!",
493                 status);
494 
495             return status;
496         }
497     }
498 
499     m_FwdProgContext = (PFXIO_FORWARD_PROGRESS_CONTEXT)
500                                 FxPoolAllocate(GetDriverGlobals(),
501                                     NonPagedPool,
502                                     sizeof(FXIO_FORWARD_PROGRESS_CONTEXT)
503                                     );
504     if (m_FwdProgContext == NULL) {
505         status = STATUS_INSUFFICIENT_RESOURCES;
506         DoTraceLevelMessage(
507             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
508             "Could not allocate memory for forward progress context %!STATUS!",
509             status);
510 
511         return status;
512     }
513 
514     *m_FwdProgContext = {};
515 
516     //
517     // Initialize the things which will not fail first
518     //
519     m_FwdProgContext->m_Policy = Policy->ForwardProgressReservedPolicy;
520 
521     m_FwdProgContext->m_NumberOfReservedRequests =
522             Policy->TotalForwardProgressRequests;
523 
524     m_FwdProgContext->m_IoReservedResourcesAllocate.Method  =
525             Policy->EvtIoAllocateResourcesForReservedRequest;
526 
527     m_FwdProgContext->m_IoResourcesAllocate.Method  =
528             Policy->EvtIoAllocateRequestResources;
529 
530     m_FwdProgContext->m_IoExamineIrp.Method  =
531             Policy->ForwardProgressReservePolicySettings.Policy.\
532                 ExaminePolicy.EvtIoWdmIrpForForwardProgress;
533 
534     InitializeListHead(&m_FwdProgContext->m_ReservedRequestList);
535     InitializeListHead(&m_FwdProgContext->m_ReservedRequestInUseList);
536     InitializeListHead(&m_FwdProgContext->m_PendedIrpList);
537 
538     m_FwdProgContext->m_PendedReserveLock.Initialize();
539 
540     for (index = 0;
541          index < m_FwdProgContext->m_NumberOfReservedRequests;
542          index++)
543     {
544         status = AllocateReservedRequest(&pRequest);
545         if (!NT_SUCCESS(status)) {
546             break;
547         }
548 
549         InsertTailList(&m_FwdProgContext->m_ReservedRequestList,
550                        pRequest->GetListEntry(FxListEntryForwardProgress));
551     }
552 
553     //
554     // Since we allow forward progress policy to be set even after AddDevice
555     // when the queue has already started dispatching, we need to make
556     // sure all checks which determine whether the queue is configured for
557     // forward progress succeed. Setting Forward progress on the queue as the
558     // last operation helps with that.
559     //
560     if (NT_SUCCESS(status)) {
561         m_SupportForwardProgress = TRUE;
562     }
563     else {
564         FreeAllReservedRequests(FALSE);
565 
566         m_FwdProgContext->m_PendedReserveLock.Uninitialize();
567 
568         FxPoolFree(m_FwdProgContext);
569         m_FwdProgContext = NULL;
570     }
571 
572     return status;
573 }
574