1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4 
5 Module Name:
6 
7     xferpkt.c
8 
9 Abstract:
10 
11     Packet routines for CLASSPNP
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "classp.h"
25 #include "debug.h"
26 
27 #ifdef DEBUG_USE_WPP
28 #include "xferpkt.tmh"
29 #endif
30 
31 #ifdef ALLOC_PRAGMA
32     #pragma alloc_text(PAGE, InitializeTransferPackets)
33     #pragma alloc_text(PAGE, DestroyAllTransferPackets)
34     #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
35     #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
36     #pragma alloc_text(PAGE, CleanupTransferPacketToWorkingSetSizeWorker)
37     #pragma alloc_text(PAGE, ClasspSetupPopulateTokenTransferPacket)
38 #endif
39 
40 /*
41  *  InitializeTransferPackets
42  *
43  *      Allocate/initialize TRANSFER_PACKETs and related resources.
44  */
InitializeTransferPackets(PDEVICE_OBJECT Fdo)45 NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo)
46 {
47     PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
48     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
49     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
50     PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
51     PSTORAGE_DEVICE_IO_CAPABILITY_DESCRIPTOR devIoCapabilityDesc = NULL;
52     STORAGE_PROPERTY_ID propertyId;
53     OSVERSIONINFOEXW osVersionInfo;
54     ULONG hwMaxPages;
55     ULONG arraySize;
56     ULONG index;
57     ULONG maxOutstandingIOPerLUN;
58     ULONG minWorkingSetTransferPackets;
59     ULONG maxWorkingSetTransferPackets;
60 
61     NTSTATUS status = STATUS_SUCCESS;
62 
63     PAGED_CODE();
64 
65     //
66     //  Precompute the maximum transfer length
67     //
68     NT_ASSERT(adapterDesc->MaximumTransferLength);
69 
70     hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;
71 
72     fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);
73     fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE);
74 
75     //
76     // Allocate per-node free packet lists
77     //
78     arraySize = KeQueryHighestNodeNumber() + 1;
79     fdoData->FreeTransferPacketsLists =
80         ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
81                               sizeof(PNL_SLIST_HEADER) * arraySize,
82                               CLASS_TAG_PRIVATE_DATA);
83 
84     if (fdoData->FreeTransferPacketsLists == NULL) {
85         status = STATUS_INSUFFICIENT_RESOURCES;
86         return status;
87     }
88 
89     for (index = 0; index < arraySize; index++) {
90         InitializeSListHead(&(fdoData->FreeTransferPacketsLists[index].SListHeader));
91         fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets = 0;
92         fdoData->FreeTransferPacketsLists[index].NumFreeTransferPackets = 0;
93     }
94 
95     InitializeListHead(&fdoData->AllTransferPacketsList);
96 
97     //
98     // Set the packet threshold numbers based on the Windows Client or Server SKU.
99     //
100 
101     osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
102     status = RtlGetVersion((POSVERSIONINFOW) &osVersionInfo);
103 
104     NT_ASSERT( NT_SUCCESS(status));
105 
106     //
107     // Retrieve info on IO capability supported by port drivers
108     //
109 
110     propertyId = StorageDeviceIoCapabilityProperty;
111     status = ClassGetDescriptor(fdoExt->CommonExtension.LowerDeviceObject,
112                                 &propertyId,
113                                 (PVOID *)&devIoCapabilityDesc);
114 
115     if (NT_SUCCESS(status) && (devIoCapabilityDesc != NULL)) {
116         maxOutstandingIOPerLUN = devIoCapabilityDesc->LunMaxIoCount;
117         FREE_POOL(devIoCapabilityDesc);
118 
119 #if DBG
120         fdoData->MaxOutstandingIOPerLUN = maxOutstandingIOPerLUN;
121 #endif
122 
123     } else {
124         maxOutstandingIOPerLUN = MAX_OUTSTANDING_IO_PER_LUN_DEFAULT;
125 
126 #if DBG
127         fdoData->MaxOutstandingIOPerLUN = 0;
128 #endif
129 
130     }
131 
132     //
133     // StorageDeviceIoCapabilityProperty support is optional so
134     // ignore any failures.
135     //
136 
137     status = STATUS_SUCCESS;
138 
139 
140     if ((osVersionInfo.wProductType != VER_NT_DOMAIN_CONTROLLER) &&
141         (osVersionInfo.wProductType != VER_NT_SERVER)) {
142 
143         // this is Client SKU
144 
145             minWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Client;
146 
147         // Note: the reason we use max here is to guarantee a reasonable large max number
148         // in the case where the port driver may return a very small supported outstanding
149         // IOs. For example, even EMMC drive only reports 1 outstanding IO supported, we
150         // may still want to set this value to be at least
151         // MAX_WORKINGSET_TRANSFER_PACKETS_Client.
152         maxWorkingSetTransferPackets = max(MAX_WORKINGSET_TRANSFER_PACKETS_Client,
153                                            2 * maxOutstandingIOPerLUN);
154 
155     } else {
156 
157         // this is Server SKU
158         // Note: the addition max here to make sure we set the min to be at least
159         // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound no matter what maxOutstandingIOPerLUN
160         // reported. We shouldn't set this value to be smaller than client system.
161         // In other words, the minWorkingSetTransferPackets for server will always between
162         // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound and MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound
163 
164         minWorkingSetTransferPackets =
165             max(MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound,
166                 min(MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound,
167                     maxOutstandingIOPerLUN));
168 
169         maxWorkingSetTransferPackets = max(MAX_WORKINGSET_TRANSFER_PACKETS_Server,
170                                            2 * maxOutstandingIOPerLUN);
171     }
172 
173 
174     fdoData->LocalMinWorkingSetTransferPackets = minWorkingSetTransferPackets;
175     fdoData->LocalMaxWorkingSetTransferPackets = maxWorkingSetTransferPackets;
176 
177     //
178     //  Allow class driver to override the settings
179     //
180     if (commonExt->DriverExtension->WorkingSet != NULL) {
181         PCLASS_WORKING_SET workingSet = commonExt->DriverExtension->WorkingSet;
182 
183         // override only if non-zero
184         if (workingSet->XferPacketsWorkingSetMinimum != 0)
185         {
186             fdoData->LocalMinWorkingSetTransferPackets = workingSet->XferPacketsWorkingSetMinimum;
187             // adjust maximum upwards if needed
188             if (fdoData->LocalMaxWorkingSetTransferPackets < fdoData->LocalMinWorkingSetTransferPackets)
189             {
190                 fdoData->LocalMaxWorkingSetTransferPackets = fdoData->LocalMinWorkingSetTransferPackets;
191             }
192         }
193         // override only if non-zero
194         if (workingSet->XferPacketsWorkingSetMaximum != 0)
195         {
196             fdoData->LocalMaxWorkingSetTransferPackets = workingSet->XferPacketsWorkingSetMaximum;
197             // adjust minimum downwards if needed
198             if (fdoData->LocalMinWorkingSetTransferPackets > fdoData->LocalMaxWorkingSetTransferPackets)
199             {
200                 fdoData->LocalMinWorkingSetTransferPackets = fdoData->LocalMaxWorkingSetTransferPackets;
201             }
202         }
203         // that's all the adjustments required/allowed
204     } // end working set size special code
205 
206     for (index = 0; index < arraySize; index++) {
207         while (fdoData->FreeTransferPacketsLists[index].NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){
208             PTRANSFER_PACKET pkt = NewTransferPacket(Fdo);
209             if (pkt) {
210                 InterlockedIncrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets));
211                 pkt->AllocateNode = index;
212                 EnqueueFreeTransferPacket(Fdo, pkt);
213             } else {
214                 status = STATUS_INSUFFICIENT_RESOURCES;
215                 break;
216             }
217         }
218         fdoData->FreeTransferPacketsLists[index].DbgPeakNumTransferPackets = fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets;
219     }
220 
221     //
222     //  Pre-initialize our SCSI_REQUEST_BLOCK template with all
223     //  the constant fields.  This will save a little time for each xfer.
224     //  NOTE: a CdbLength field of 10 may not always be appropriate
225     //
226 
227     if (NT_SUCCESS(status))  {
228         if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
229             ULONG ByteSize = 0;
230 
231     #if (NTDDI_VERSION >= NTDDI_WINBLUE)
232             if ((fdoExt->MiniportDescriptor != NULL) &&
233                 (fdoExt->MiniportDescriptor->Size >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_MINIPORT_DESCRIPTOR, ExtraIoInfoSupported)) &&
234                 (fdoExt->MiniportDescriptor->ExtraIoInfoSupported == TRUE)) {
235                 status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
236                                                     fdoExt->AdapterDescriptor->AddressType,
237                                                     DefaultStorageRequestBlockAllocateRoutine,
238                                                     &ByteSize,
239                                                     2,
240                                                     SrbExDataTypeScsiCdb16,
241                                                     SrbExDataTypeIoInfo
242                                                   );
243             } else {
244                 status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
245                                                     fdoExt->AdapterDescriptor->AddressType,
246                                                     DefaultStorageRequestBlockAllocateRoutine,
247                                                     &ByteSize,
248                                                     1,
249                                                     SrbExDataTypeScsiCdb16
250                                                   );
251             }
252     #else
253             status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
254                                                 fdoExt->AdapterDescriptor->AddressType,
255                                                 DefaultStorageRequestBlockAllocateRoutine,
256                                                 &ByteSize,
257                                                 1,
258                                                 SrbExDataTypeScsiCdb16
259                                                 );
260     #endif
261             if (NT_SUCCESS(status)) {
262                 ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
263             } else {
264                 NT_ASSERT(FALSE);
265             }
266         } else {
267             fdoData->SrbTemplate = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs');
268             if (fdoData->SrbTemplate == NULL) {
269                 status = STATUS_INSUFFICIENT_RESOURCES;
270             } else {
271                 RtlZeroMemory(fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK));
272                 fdoData->SrbTemplate->Length = sizeof(SCSI_REQUEST_BLOCK);
273                 fdoData->SrbTemplate->Function = SRB_FUNCTION_EXECUTE_SCSI;
274             }
275         }
276     }
277 
278     if (status == STATUS_SUCCESS) {
279         SrbSetRequestAttribute(fdoData->SrbTemplate, SRB_SIMPLE_TAG_REQUEST);
280         SrbSetSenseInfoBufferLength(fdoData->SrbTemplate, SENSE_BUFFER_SIZE_EX);
281         SrbSetCdbLength(fdoData->SrbTemplate, 10);
282     }
283 
284     return status;
285 }
286 
287 
DestroyAllTransferPackets(PDEVICE_OBJECT Fdo)288 VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo)
289 {
290     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
291     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
292     TRANSFER_PACKET *pkt;
293     ULONG index;
294     ULONG arraySize;
295 
296     PAGED_CODE();
297 
298     //
299     // fdoData->FreeTransferPacketsLists could be NULL if
300     // there was an error during start device.
301     //
302     if (fdoData->FreeTransferPacketsLists != NULL) {
303 
304         NT_ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList));
305 
306         arraySize = KeQueryHighestNodeNumber() + 1;
307         for (index = 0; index < arraySize; index++) {
308             pkt = DequeueFreeTransferPacketEx(Fdo, FALSE, index);
309             while (pkt) {
310                 DestroyTransferPacket(pkt);
311                 InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets));
312                 pkt = DequeueFreeTransferPacketEx(Fdo, FALSE, index);
313             }
314 
315             NT_ASSERT(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets == 0);
316         }
317     }
318 
319     FREE_POOL(fdoData->SrbTemplate);
320 }
321 
__drv_allocatesMem(Mem)322 __drv_allocatesMem(Mem)
323 #ifdef _MSC_VER
324 #pragma warning(suppress:28195) // This function may not allocate memory in some error cases.
325 #endif
326 PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo)
327 {
328     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
329     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
330     PTRANSFER_PACKET newPkt = NULL;
331     ULONG transferLength = (ULONG)-1;
332     NTSTATUS status = STATUS_SUCCESS;
333 
334     if (NT_SUCCESS(status)) {
335         status = RtlULongAdd(fdoData->HwMaxXferLen, PAGE_SIZE, &transferLength);
336         if (!NT_SUCCESS(status)) {
337 
338             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "Integer overflow in calculating transfer packet size."));
339             status = STATUS_INTEGER_OVERFLOW;
340         }
341     }
342 
343     /*
344      *  Allocate the actual packet.
345      */
346     if (NT_SUCCESS(status)) {
347         newPkt = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(TRANSFER_PACKET), 'pnPC');
348         if (newPkt == NULL) {
349             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate transfer packet."));
350             status = STATUS_INSUFFICIENT_RESOURCES;
351         } else {
352             RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET));
353             newPkt->AllocateNode = KeGetCurrentNodeNumber();
354             if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
355 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
356                 if ((fdoExt->MiniportDescriptor != NULL) &&
357                     (fdoExt->MiniportDescriptor->Size >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_MINIPORT_DESCRIPTOR, ExtraIoInfoSupported)) &&
358                     (fdoExt->MiniportDescriptor->ExtraIoInfoSupported == TRUE)) {
359                     status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
360                                         fdoExt->AdapterDescriptor->AddressType,
361                                         DefaultStorageRequestBlockAllocateRoutine,
362                                         NULL,
363                                         2,
364                                         SrbExDataTypeScsiCdb16,
365                                         SrbExDataTypeIoInfo
366                                         );
367                 } else {
368                     status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
369                                         fdoExt->AdapterDescriptor->AddressType,
370                                         DefaultStorageRequestBlockAllocateRoutine,
371                                         NULL,
372                                         1,
373                                         SrbExDataTypeScsiCdb16
374                                         );
375                 }
376 #else
377                 status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
378                                     fdoExt->AdapterDescriptor->AddressType,
379                                     DefaultStorageRequestBlockAllocateRoutine,
380                                     NULL,
381                                     1,
382                                     SrbExDataTypeScsiCdb16
383                                     );
384 #endif
385             } else {
386 #ifdef _MSC_VER
387 #pragma prefast(suppress:6014, "The allocated memory that Pkt->Srb points to will be freed in DestroyTransferPacket().")
388 #endif
389                 newPkt->Srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs');
390                 if (newPkt->Srb == NULL) {
391                     status = STATUS_INSUFFICIENT_RESOURCES;
392                 }
393 
394             }
395 
396             if (status != STATUS_SUCCESS)
397             {
398                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate SRB."));
399                 FREE_POOL(newPkt);
400             }
401         }
402     }
403 
404     /*
405      *  Allocate Irp for the packet.
406      */
407     if (NT_SUCCESS(status) && newPkt != NULL) {
408         newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE);
409         if (newPkt->Irp == NULL) {
410             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate IRP for transfer packet."));
411             status = STATUS_INSUFFICIENT_RESOURCES;
412         }
413     }
414 
415     /*
416      * Allocate a MDL.  Add one page to the length to insure an extra page
417      * entry is allocated if the buffer does not start on page boundaries.
418      */
419     if (NT_SUCCESS(status) && newPkt != NULL) {
420 
421         NT_ASSERT(transferLength != (ULONG)-1);
422 
423         newPkt->PartialMdl = IoAllocateMdl(NULL,
424                                            transferLength,
425                                            FALSE,
426                                            FALSE,
427                                            NULL);
428         if (newPkt->PartialMdl == NULL) {
429             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate MDL for transfer packet."));
430             status = STATUS_INSUFFICIENT_RESOURCES;
431         } else {
432             NT_ASSERT(newPkt->PartialMdl->Size >= (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(fdoData->HwMaxXferLen) * sizeof(PFN_NUMBER)));
433         }
434 
435     }
436 
437     /*
438      * Allocate per-packet retry history, if required
439      */
440     if (NT_SUCCESS(status) &&
441         (fdoData->InterpretSenseInfo != NULL) &&
442         (newPkt != NULL)
443         ) {
444         // attempt to allocate also the history
445         ULONG historyByteCount = 0;
446 
447         // SAL annotation and ClassInitializeEx() should both catch this case
448         NT_ASSERT(fdoData->InterpretSenseInfo->HistoryCount != 0);
449         _Analysis_assume_(fdoData->InterpretSenseInfo->HistoryCount != 0);
450 
451         historyByteCount = sizeof(SRB_HISTORY_ITEM) * fdoData->InterpretSenseInfo->HistoryCount;
452         historyByteCount += sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
453 
454         newPkt->RetryHistory = (PSRB_HISTORY)ExAllocatePoolWithTag(NonPagedPoolNx, historyByteCount, 'hrPC');
455 
456         if (newPkt->RetryHistory == NULL) {
457             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate MDL for transfer packet."));
458             status = STATUS_INSUFFICIENT_RESOURCES;
459         } else {
460             // call this routine directly once since it's the first initialization of
461             // the structure and the internal maximum count field is not yet setup.
462             HistoryInitializeRetryLogs(newPkt->RetryHistory, fdoData->InterpretSenseInfo->HistoryCount);
463         }
464     }
465 
466     /*
467      *  Enqueue the packet in our static AllTransferPacketsList
468      *  (just so we can find it during debugging if its stuck somewhere).
469      */
470     if (NT_SUCCESS(status) && newPkt != NULL)
471     {
472         KIRQL oldIrql;
473         newPkt->Fdo = Fdo;
474 #if DBG
475         newPkt->DbgPktId = InterlockedIncrement((volatile LONG *)&fdoData->DbgMaxPktId);
476 #endif
477         KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
478         InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry);
479         KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
480 
481     } else {
482         // free any resources acquired above (in reverse order)
483         if (newPkt != NULL) {
484             FREE_POOL(newPkt->RetryHistory);
485             if (newPkt->PartialMdl != NULL) { IoFreeMdl(newPkt->PartialMdl); }
486             if (newPkt->Irp        != NULL) { IoFreeIrp(newPkt->Irp);        }
487             if (newPkt->Srb        != NULL) { FREE_POOL(newPkt->Srb);        }
488             FREE_POOL(newPkt);
489         }
490     }
491 
492     return newPkt;
493 }
494 
495 
496 /*
497  *  DestroyTransferPacket
498  *
499  */
DestroyTransferPacket(_In_ __drv_freesMem (mem)PTRANSFER_PACKET Pkt)500 VOID DestroyTransferPacket(_In_ __drv_freesMem(mem) PTRANSFER_PACKET Pkt)
501 {
502     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
503     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
504     KIRQL oldIrql;
505 
506     NT_ASSERT(!Pkt->SlistEntry.Next);
507 //    NT_ASSERT(!Pkt->OriginalIrp);
508 
509     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
510 
511     /*
512      *  Delete the packet from our all-packets queue.
513      */
514     NT_ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry));
515     NT_ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList));
516     RemoveEntryList(&Pkt->AllPktsListEntry);
517     InitializeListHead(&Pkt->AllPktsListEntry);
518 
519     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
520 
521     IoFreeMdl(Pkt->PartialMdl);
522     IoFreeIrp(Pkt->Irp);
523     FREE_POOL(Pkt->RetryHistory);
524     FREE_POOL(Pkt->Srb);
525     FREE_POOL(Pkt);
526 }
527 
528 
EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo,__drv_aliasesMem PTRANSFER_PACKET Pkt)529 VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, __drv_aliasesMem PTRANSFER_PACKET Pkt)
530 {
531     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
532     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
533     ULONG allocateNode;
534     KIRQL oldIrql;
535 
536     NT_ASSERT(!Pkt->SlistEntry.Next);
537 
538     allocateNode = Pkt->AllocateNode;
539     InterlockedPushEntrySList(&(fdoData->FreeTransferPacketsLists[allocateNode].SListHeader), &Pkt->SlistEntry);
540     InterlockedIncrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets));
541 
542     /*
543      *  If the total number of packets is larger than LocalMinWorkingSetTransferPackets,
544      *  that means that we've been in stress.  If all those packets are now
545      *  free, then we are now out of stress and can free the extra packets.
546      *  Attempt to free down to LocalMaxWorkingSetTransferPackets immediately, and
547      *  down to LocalMinWorkingSetTransferPackets lazily (one at a time).
548      *  However, since we're at DPC, do this is a work item. If the device is removed
549      *  or we are unable to allocate the work item, do NOT free more than
550      *  MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE. Subsequent IO completions will end up freeing
551      *  up the rest, even if it is MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE at a time.
552      */
553     if (fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets >=
554         fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets) {
555 
556         /*
557          *  1.  Immediately snap down to our UPPER threshold.
558          */
559         if (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
560             fdoData->LocalMaxWorkingSetTransferPackets) {
561 
562             ULONG isRemoved;
563             PIO_WORKITEM workItem = NULL;
564 
565             workItem = IoAllocateWorkItem(Fdo);
566 
567             //
568             // Acquire a remove lock in order to make sure the device object and its
569             // private data structures will exist when the workitem fires.
570             // The remove lock will be released by the workitem (CleanupTransferPacketToWorkingSetSize).
571             //
572             isRemoved = ClassAcquireRemoveLock(Fdo, (PIRP)workItem);
573 
574             if (workItem && !isRemoved) {
575 
576                 TracePrint((TRACE_LEVEL_INFORMATION,
577                             TRACE_FLAG_GENERAL,
578                             "EnqueueFreeTransferPacket: Device (%p), queuing work item to clean up free transfer packets.\n",
579                             Fdo));
580 
581                 //
582                 // Queue a work item to trim down the total number of transfer packets to with the
583                 // working size.
584                 //
585                 IoQueueWorkItemEx(workItem, CleanupTransferPacketToWorkingSetSizeWorker, DelayedWorkQueue, (PVOID)(ULONG_PTR)allocateNode);
586 
587             } else {
588 
589                 if (workItem) {
590                     IoFreeWorkItem(workItem);
591                 }
592 
593                 if (isRemoved != REMOVE_COMPLETE) {
594                     ClassReleaseRemoveLock(Fdo, (PIRP)workItem);
595                 }
596 
597                 TracePrint((TRACE_LEVEL_ERROR,
598                             TRACE_FLAG_GENERAL,
599                             "EnqueueFreeTransferPacket: Device (%p), Failed to allocate memory for the work item.\n",
600                             Fdo));
601 
602                 CleanupTransferPacketToWorkingSetSize(Fdo, TRUE, allocateNode);
603             }
604         }
605 
606         /*
607          *  2.  Lazily work down to our LOWER threshold (by only freeing one packet at a time).
608          */
609         if (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
610             fdoData->LocalMinWorkingSetTransferPackets){
611             /*
612              *  Check the counter again with lock held.  This eliminates a race condition
613              *  while still allowing us to not grab the spinlock in the common codepath.
614              *
615              *  Note that the spinlock does not synchronize with threads dequeuing free
616              *  packets to send (DequeueFreeTransferPacket does that with a lightweight
617              *  interlocked exchange); the spinlock prevents multiple threads in this function
618              *  from deciding to free too many extra packets at once.
619              */
620             PTRANSFER_PACKET pktToDelete = NULL;
621 
622             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Exiting stress, lazily freeing one of %d/%d packets from node %d.",
623                 fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets,
624                 fdoData->LocalMinWorkingSetTransferPackets,
625                 allocateNode));
626 
627             KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
628             if ((fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets >=
629                 fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets) &&
630                 (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
631                 fdoData->LocalMinWorkingSetTransferPackets)){
632 
633                 pktToDelete = DequeueFreeTransferPacketEx(Fdo, FALSE, allocateNode);
634                 if (pktToDelete) {
635                     InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets));
636                 } else {
637                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW,
638                         "Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2). Node=%d",
639                         fdoData->LocalMinWorkingSetTransferPackets,
640                         Fdo,
641                         fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets,
642                         allocateNode));
643                 }
644             }
645             KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
646 
647             if (pktToDelete) {
648                 DestroyTransferPacket(pktToDelete);
649             }
650         }
651 
652     }
653 
654 }
655 
DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo,BOOLEAN AllocIfNeeded)656 PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded)
657 {
658     return DequeueFreeTransferPacketEx(Fdo, AllocIfNeeded, KeGetCurrentNodeNumber());
659 }
660 
DequeueFreeTransferPacketEx(_In_ PDEVICE_OBJECT Fdo,_In_ BOOLEAN AllocIfNeeded,_In_ ULONG Node)661 PTRANSFER_PACKET DequeueFreeTransferPacketEx(
662     _In_ PDEVICE_OBJECT Fdo,
663     _In_ BOOLEAN AllocIfNeeded,
664     _In_ ULONG Node)
665 {
666     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
667     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
668     PTRANSFER_PACKET pkt;
669     PSLIST_ENTRY slistEntry;
670 
671     slistEntry = InterlockedPopEntrySList(&(fdoData->FreeTransferPacketsLists[Node].SListHeader));
672 
673     if (slistEntry) {
674         slistEntry->Next = NULL;
675         pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
676         InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[Node].NumFreeTransferPackets));
677 
678         // when dequeuing the packet, also reset the history data
679         HISTORYINITIALIZERETRYLOGS(pkt);
680 
681     } else {
682         if (AllocIfNeeded) {
683             /*
684              *  We are in stress and have run out of lookaside packets.
685              *  In order to service the current transfer,
686              *  allocate an extra packet.
687              *  We will free it lazily when we are out of stress.
688              */
689             pkt = NewTransferPacket(Fdo);
690             if (pkt) {
691                 InterlockedIncrement((volatile LONG *)&fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
692                 fdoData->FreeTransferPacketsLists[Node].DbgPeakNumTransferPackets =
693                     max(fdoData->FreeTransferPacketsLists[Node].DbgPeakNumTransferPackets,
694                         fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
695             } else {
696                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "DequeueFreeTransferPacket: packet allocation failed"));
697             }
698         } else {
699             pkt = NULL;
700         }
701     }
702 
703     return pkt;
704 }
705 
706 
707 
708 /*
709  *  SetupReadWriteTransferPacket
710  *
711  *        This function is called once to set up the first attempt to send a packet.
712  *        It is not called before a retry, as SRB fields may be modified for the retry.
713  *
714  *      Set up the Srb of the TRANSFER_PACKET for the transfer.
715  *        The Irp is set up in SubmitTransferPacket because it must be reset
716  *        for each packet submission.
717  */
SetupReadWriteTransferPacket(PTRANSFER_PACKET Pkt,PVOID Buf,ULONG Len,LARGE_INTEGER DiskLocation,PIRP OriginalIrp)718 VOID SetupReadWriteTransferPacket(  PTRANSFER_PACKET Pkt,
719                                     PVOID Buf,
720                                     ULONG Len,
721                                     LARGE_INTEGER DiskLocation,
722                                     PIRP OriginalIrp)
723 {
724     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
725     PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
726     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
727     PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(OriginalIrp);
728     UCHAR majorFunc = origCurSp->MajorFunction;
729     LARGE_INTEGER logicalBlockAddr;
730     ULONG numTransferBlocks;
731     PCDB pCdb;
732     ULONG srbLength;
733     ULONG timeoutValue = fdoExt->TimeOutValue;
734 
735     logicalBlockAddr.QuadPart = Int64ShrlMod32(DiskLocation.QuadPart, fdoExt->SectorShift);
736     numTransferBlocks = Len >> fdoExt->SectorShift;
737 
738     /*
739      * This field is useful when debugging, since low-memory conditions are
740      * handled differently for CDROM (which is the only driver using StartIO)
741      */
742     Pkt->DriverUsesStartIO = (commonExtension->DriverExtension->InitData.ClassStartIo != NULL);
743 
744     /*
745      *  Slap the constant SRB fields in from our pre-initialized template.
746      *  We'll then only have to fill in the unique fields for this transfer.
747      *  Tell lower drivers to sort the SRBs by the logical block address
748      *  so that disk seeks are minimized.
749      */
750     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
751         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
752         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
753     } else {
754         srbLength = fdoData->SrbTemplate->Length;
755     }
756     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
757     SrbSetDataBuffer(Pkt->Srb, Buf);
758     SrbSetDataTransferLength(Pkt->Srb, Len);
759     SrbSetQueueSortKey(Pkt->Srb, logicalBlockAddr.LowPart);
760     if (logicalBlockAddr.QuadPart > 0xFFFFFFFF) {
761         //
762         // If the requested LBA is more than max ULONG set the
763         // QueueSortKey to the maximum value, so that these
764         // requests can be added towards the end of the queue.
765         //
766 
767         SrbSetQueueSortKey(Pkt->Srb, 0xFFFFFFFF);
768     }
769     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
770     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
771 
772 
773     SrbSetTimeOutValue(Pkt->Srb, timeoutValue);
774 
775     /*
776      *  Arrange values in CDB in big-endian format.
777      */
778     pCdb = SrbGetCdb(Pkt->Srb);
779     if (pCdb) {
780         if (TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB)) {
781             REVERSE_BYTES_QUAD(&pCdb->CDB16.LogicalBlock, &logicalBlockAddr);
782             REVERSE_BYTES(&pCdb->CDB16.TransferLength, &numTransferBlocks);
783             pCdb->CDB16.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ16 : SCSIOP_WRITE16;
784             SrbSetCdbLength(Pkt->Srb, 16);
785         } else {
786             pCdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3;
787             pCdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2;
788             pCdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1;
789             pCdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0;
790             pCdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
791             pCdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
792             pCdb->CDB10.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ : SCSIOP_WRITE;
793         }
794     }
795 
796     /*
797      *  Set SRB and IRP flags
798      */
799     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags);
800     if (TEST_FLAG(OriginalIrp->Flags, IRP_PAGING_IO) ||
801         TEST_FLAG(OriginalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)){
802         SrbSetSrbFlags(Pkt->Srb, SRB_CLASS_FLAGS_PAGING);
803     }
804     SrbSetSrbFlags(Pkt->Srb, (majorFunc==IRP_MJ_READ) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
805 
806     /*
807      *  Allow caching only if this is not a write-through request.
808      *  If write-through and caching is enabled on the device, force
809      *  media access.
810      *  Ignore SL_WRITE_THROUGH for reads; it's only set because the file handle was opened with WRITE_THROUGH.
811      */
812     if ((majorFunc == IRP_MJ_WRITE) && TEST_FLAG(origCurSp->Flags, SL_WRITE_THROUGH) && pCdb) {
813         pCdb->CDB10.ForceUnitAccess = fdoExt->CdbForceUnitAccess;
814     } else {
815         SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
816     }
817 
818     /*
819      *  Remember the buf and len in the SRB because miniports
820      *  can overwrite SRB.DataTransferLength and we may need it again
821      *  for the retry.
822      */
823     Pkt->BufPtrCopy = Buf;
824     Pkt->BufLenCopy = Len;
825     Pkt->TargetLocationCopy = DiskLocation;
826 
827     Pkt->OriginalIrp = OriginalIrp;
828     Pkt->NumRetries = fdoData->MaxNumberOfIoRetries;
829     Pkt->SyncEventPtr = NULL;
830     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE;
831 #if !defined(__REACTOS__) && NTDDI_VERSION >= NTDDI_WINBLUE
832     Pkt->NumIoTimeoutRetries = fdoData->MaxNumberOfIoRetries;
833     Pkt->NumThinProvisioningRetries = 0;
834 #endif
835 
836 
837     if (pCdb) {
838         DBGLOGFLUSHINFO(fdoData, TRUE, (BOOLEAN)(pCdb->CDB10.ForceUnitAccess), FALSE);
839     } else {
840         DBGLOGFLUSHINFO(fdoData, TRUE, FALSE, FALSE);
841     }
842 }
843 
844 
845 /*
846  *  SubmitTransferPacket
847  *
848  *        Set up the IRP for the TRANSFER_PACKET submission and send it down.
849  */
SubmitTransferPacket(PTRANSFER_PACKET Pkt)850 NTSTATUS SubmitTransferPacket(PTRANSFER_PACKET Pkt)
851 {
852     PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
853     PDEVICE_OBJECT nextDevObj = commonExtension->LowerDeviceObject;
854     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
855     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
856     BOOLEAN idleRequest = FALSE;
857     PIO_STACK_LOCATION nextSp;
858 
859     NT_ASSERT(Pkt->Irp->CurrentLocation == Pkt->Irp->StackCount+1);
860 
861     /*
862      *  Attach the SRB to the IRP.
863      *  The reused IRP's stack location has to be rewritten for each retry
864      *  call because IoCompleteRequest clears the stack locations.
865      */
866     IoReuseIrp(Pkt->Irp, STATUS_NOT_SUPPORTED);
867 
868 
869     nextSp = IoGetNextIrpStackLocation(Pkt->Irp);
870     nextSp->MajorFunction = IRP_MJ_SCSI;
871     nextSp->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
872 
873     SrbSetScsiStatus(Pkt->Srb, 0);
874     Pkt->Srb->SrbStatus = 0;
875     SrbSetSenseInfoBufferLength(Pkt->Srb, SENSE_BUFFER_SIZE_EX);
876 
877     if (Pkt->CompleteOriginalIrpWhenLastPacketCompletes) {
878         /*
879          *  Only dereference the "original IRP"'s stack location
880          *  if its a real client irp (as opposed to a static irp
881          *  we're using just for result status for one of the non-IO scsi commands).
882          *
883          *  For read/write, propagate the storage-specific IRP stack location flags
884          *  (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
885          */
886         PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
887         nextSp->Flags = origCurSp->Flags;
888     }
889 
890     //
891     // If the request is not split, we can use the original IRP MDL.  If the
892     // request needs to be split, we need to use a partial MDL.  The partial MDL
893     // is needed because more than one driver might be mapping the same MDL
894     // and this causes problems.
895     //
896     if (Pkt->UsePartialMdl == FALSE) {
897         Pkt->Irp->MdlAddress = Pkt->OriginalIrp->MdlAddress;
898     } else {
899         IoBuildPartialMdl(Pkt->OriginalIrp->MdlAddress, Pkt->PartialMdl, SrbGetDataBuffer(Pkt->Srb), SrbGetDataTransferLength(Pkt->Srb));
900         Pkt->Irp->MdlAddress = Pkt->PartialMdl;
901     }
902 
903 
904     DBGLOGSENDPACKET(Pkt);
905     HISTORYLOGSENDPACKET(Pkt);
906 
907     //
908     // Set the original irp here for SFIO.
909     //
910     ClasspSrbSetOriginalIrp(Pkt->Srb, (PVOID) (Pkt->OriginalIrp));
911 
912     //
913     // No need to lock for IdlePrioritySupported, since it will
914     // be modified only at initialization time.
915     //
916     if (fdoData->IdlePrioritySupported == TRUE) {
917         idleRequest = ClasspIsIdleRequest(Pkt->OriginalIrp);
918         if (idleRequest) {
919             InterlockedIncrement(&fdoData->ActiveIdleIoCount);
920         } else {
921             InterlockedIncrement(&fdoData->ActiveIoCount);
922         }
923     }
924 
925     IoSetCompletionRoutine(Pkt->Irp, TransferPktComplete, Pkt, TRUE, TRUE, TRUE);
926     return IoCallDriver(nextDevObj, Pkt->Irp);
927 }
928 
929 
930 NTSTATUS
931 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
TransferPktComplete(IN PDEVICE_OBJECT NullFdo,IN PIRP Irp,IN PVOID Context)932 TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context)
933 {
934     PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context;
935     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension;
936     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
937     BOOLEAN packetDone = FALSE;
938     BOOLEAN idleRequest = FALSE;
939     ULONG transferLength;
940     LARGE_INTEGER completionTime;
941     ULONGLONG lastIoCompletionTime;
942 
943     UNREFERENCED_PARAMETER(NullFdo);
944 
945     /*
946      *  Put all the assertions and spew in here so we don't have to look at them.
947      */
948     DBGLOGRETURNPACKET(pkt);
949     DBGCHECKRETURNEDPKT(pkt);
950     HISTORYLOGRETURNEDPACKET(pkt);
951 
952 
953     completionTime = ClasspGetCurrentTime();
954 
955     //
956     // Record the time at which the last IO completed while snapping the old
957     // value to be used later. This can occur on multiple threads and hence
958     // could be overwritten with an older value. This is OK because this value
959     // is maintained as a heuristic.
960     //
961 
962 #ifdef _WIN64
963 #ifndef __REACTOS__
964     lastIoCompletionTime = ReadULong64NoFence((volatile ULONG64*)&fdoData->LastIoCompletionTime.QuadPart);
965     WriteULong64NoFence((volatile ULONG64*)&fdoData->LastIoCompletionTime.QuadPart,
966                         completionTime.QuadPart);
967 #else
968     lastIoCompletionTime = *(volatile ULONG64*)&fdoData->LastIoCompletionTime.QuadPart;
969     *((volatile ULONG64*)&fdoData->LastIoCompletionTime.QuadPart) = completionTime.QuadPart;
970 #endif
971 #else
972     lastIoCompletionTime = InterlockedExchangeNoFence64((volatile LONG64*)&fdoData->LastIoCompletionTime.QuadPart,
973                                                         completionTime.QuadPart);
974 #endif
975 
976     if (fdoData->IdlePrioritySupported == TRUE) {
977         idleRequest = ClasspIsIdleRequest(pkt->OriginalIrp);
978         if (idleRequest) {
979             InterlockedDecrement(&fdoData->ActiveIdleIoCount);
980             NT_ASSERT(fdoData->ActiveIdleIoCount >= 0);
981         } else {
982             fdoData->LastNonIdleIoTime = completionTime;
983             InterlockedDecrement(&fdoData->ActiveIoCount);
984             NT_ASSERT(fdoData->ActiveIoCount >= 0);
985         }
986     }
987 
988     //
989     // If partial MDL was used, unmap the pages.  When the packet is retried, the
990     // MDL will be recreated.  If the packet is done, the MDL will be ready to be reused.
991     //
992     if (pkt->UsePartialMdl) {
993         MmPrepareMdlForReuse(pkt->PartialMdl);
994     }
995 
996     if (SRB_STATUS(pkt->Srb->SrbStatus) == SRB_STATUS_SUCCESS) {
997 
998         NT_ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
999 
1000         transferLength = SrbGetDataTransferLength(pkt->Srb);
1001 
1002         fdoData->LoggedTURFailureSinceLastIO = FALSE;
1003 
1004         /*
1005          *  The port driver should not have allocated a sense buffer
1006          *  if the SRB succeeded.
1007          */
1008         NT_ASSERT(!PORT_ALLOCATED_SENSE_EX(fdoExt, pkt->Srb));
1009 
1010         /*
1011          *  Add this packet's transferred length to the original IRP's.
1012          */
1013         InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
1014                               (LONG)transferLength);
1015 
1016 
1017         if ((pkt->InLowMemRetry) ||
1018             (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) {
1019             packetDone = StepLowMemRetry(pkt);
1020         } else {
1021             packetDone = TRUE;
1022         }
1023 
1024     }
1025     else {
1026         /*
1027          *  The packet failed.  We may retry it if possible.
1028          */
1029         BOOLEAN shouldRetry;
1030 
1031         /*
1032          *  Make sure IRP status matches SRB error status (since we propagate it).
1033          */
1034         if (NT_SUCCESS(Irp->IoStatus.Status)){
1035             Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
1036         }
1037 
1038         /*
1039          *  The packet failed.
1040          *  So when sending the packet down we either saw either an error or STATUS_PENDING,
1041          *  and so we returned STATUS_PENDING for the original IRP.
1042          *  So now we must mark the original irp pending to match that, _regardless_ of
1043          *  whether we actually switch threads here by retrying.
1044          *  (We also have to mark the irp pending if the lower driver marked the irp pending;
1045          *   that is dealt with farther down).
1046          */
1047         if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
1048             IoMarkIrpPending(pkt->OriginalIrp);
1049         }
1050 
1051         /*
1052          *  Interpret the SRB error (to a meaningful IRP status)
1053          *  and determine if we should retry this packet.
1054          *  This call looks at the returned SENSE info to figure out what to do.
1055          */
1056         shouldRetry = InterpretTransferPacketError(pkt);
1057 
1058         /*
1059          *  If the SRB queue is locked-up, release it.
1060          *  Do this after calling the error handler.
1061          */
1062         if (pkt->Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN){
1063             ClassReleaseQueue(pkt->Fdo);
1064         }
1065 
1066 
1067         if (NT_SUCCESS(Irp->IoStatus.Status)){
1068             /*
1069              *  The error was recovered above in the InterpretTransferPacketError() call.
1070              */
1071 
1072             NT_ASSERT(!shouldRetry);
1073 
1074             /*
1075              *  In the case of a recovered error,
1076              *  add the transfer length to the original Irp as we would in the success case.
1077              */
1078             InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
1079                                   (LONG)SrbGetDataTransferLength(pkt->Srb));
1080 
1081             if ((pkt->InLowMemRetry) ||
1082                 (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) {
1083                 packetDone = StepLowMemRetry(pkt);
1084             } else {
1085                 packetDone = TRUE;
1086             }
1087         } else {
1088             if (shouldRetry && (pkt->NumRetries > 0)){
1089                 packetDone = RetryTransferPacket(pkt);
1090             } else if (shouldRetry && (pkt->RetryHistory != NULL)){
1091                 // don't limit retries if class driver has custom interpretation routines
1092                 packetDone = RetryTransferPacket(pkt);
1093             } else {
1094                 packetDone = TRUE;
1095             }
1096         }
1097     }
1098 
1099     /*
1100      *  If the packet is completed, put it back in the free list.
1101      *  If it is the last packet servicing the original request, complete the original irp.
1102      */
1103     if (packetDone){
1104         LONG numPacketsRemaining;
1105         PIRP deferredIrp;
1106         PDEVICE_OBJECT Fdo = pkt->Fdo;
1107         UCHAR uniqueAddr = 0;
1108 
1109         /*
1110          *  In case a remove is pending, bump the lock count so we don't get freed
1111          *  right after we complete the original irp.
1112          */
1113         ClassAcquireRemoveLock(Fdo, (PVOID)&uniqueAddr);
1114 
1115 
1116         /*
1117          *  Sometimes the port driver can allocates a new 'sense' buffer
1118          *  to report transfer errors, e.g. when the default sense buffer
1119          *  is too small.  If so, it is up to us to free it.
1120          *  Now that we're done using the sense info, free it if appropriate.
1121          *  Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
1122          */
1123         if (PORT_ALLOCATED_SENSE_EX(fdoExt, pkt->Srb)) {
1124             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
1125             FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExt, pkt->Srb);
1126             SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
1127             SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
1128         } else {
1129             NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
1130             NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
1131         }
1132 
1133         RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
1134 
1135         /*
1136          *  Call IoSetMasterIrpStatus to set appropriate status
1137          *  for the Master IRP.
1138          */
1139         IoSetMasterIrpStatus(pkt->OriginalIrp, Irp->IoStatus.Status);
1140 
1141         if (!NT_SUCCESS(Irp->IoStatus.Status)){
1142             /*
1143              *  If the original I/O originated in user space (i.e. it is thread-queued),
1144              *  and the error is user-correctable (e.g. media is missing, for removable media),
1145              *  alert the user.
1146              *  Since this is only one of possibly several packets completing for the original IRP,
1147              *  we may do this more than once for a single request.  That's ok; this allows
1148              *  us to test each returned status with IoIsErrorUserInduced().
1149              */
1150             if (IoIsErrorUserInduced(Irp->IoStatus.Status) &&
1151                 pkt->CompleteOriginalIrpWhenLastPacketCompletes &&
1152                 pkt->OriginalIrp->Tail.Overlay.Thread){
1153 
1154                 IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, Fdo);
1155             }
1156         }
1157 
1158         /*
1159          *  We use a field in the original IRP to count
1160          *  down the transfer pieces as they complete.
1161          */
1162         numPacketsRemaining = InterlockedDecrement(
1163             (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]);
1164 
1165         if (numPacketsRemaining > 0){
1166             /*
1167              *  More transfer pieces remain for the original request.
1168              *  Wait for them to complete before completing the original irp.
1169              */
1170         } else {
1171 
1172             /*
1173              *  All the transfer pieces are done.
1174              *  Complete the original irp if appropriate.
1175              */
1176             NT_ASSERT(numPacketsRemaining == 0);
1177             if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
1178 
1179                 IO_PAGING_PRIORITY priority = (TEST_FLAG(pkt->OriginalIrp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(pkt->OriginalIrp) : IoPagingPriorityInvalid;
1180                 KIRQL oldIrql;
1181 
1182                 if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){
1183                     NT_ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information ==  IoGetCurrentIrpStackLocation(pkt->OriginalIrp)->Parameters.Read.Length);
1184                     ClasspPerfIncrementSuccessfulIo(fdoExt);
1185                 }
1186                 ClassReleaseRemoveLock(Fdo, pkt->OriginalIrp);
1187 
1188                 /*
1189                  *  We submitted all the downward irps, including this last one, on the thread
1190                  *  that the OriginalIrp came in on.  So the OriginalIrp is completing on a
1191                  *  different thread iff this last downward irp is completing on a different thread.
1192                  *  If BlkCache is loaded, for example, it will often complete
1193                  *  requests out of the cache on the same thread, therefore not marking the downward
1194                  *  irp pending and not requiring us to do so here.  If the downward request is completing
1195                  *  on the same thread, then by not marking the OriginalIrp pending we can save an APC
1196                  *  and get extra perf benefit out of BlkCache.
1197                  *  Note that if the packet ever cycled due to retry or LowMemRetry,
1198                  *  we set the pending bit in those codepaths.
1199                  */
1200                 if (pkt->Irp->PendingReturned){
1201                     IoMarkIrpPending(pkt->OriginalIrp);
1202                 }
1203 
1204 
1205                 ClassCompleteRequest(Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT);
1206 
1207                 //
1208                 // Drop the count only after completing the request, to give
1209                 // Mm some amount of time to issue its next critical request
1210                 //
1211 
1212                 if (priority == IoPagingPriorityHigh)
1213                 {
1214                     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
1215 
1216                     if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo)
1217                     {
1218                         fdoData->MaxInterleavedNormalIo = 0;
1219                     } else {
1220                         fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo;
1221                     }
1222 
1223                     fdoData->NumHighPriorityPagingIo--;
1224 
1225                     if (fdoData->NumHighPriorityPagingIo == 0)
1226                     {
1227                         LARGE_INTEGER period;
1228 
1229                         //
1230                         // Exiting throttle mode
1231                         //
1232 
1233                         KeQuerySystemTime(&fdoData->ThrottleStopTime);
1234 
1235                         period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart;
1236                         fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart);
1237                     }
1238 
1239                     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
1240                 }
1241 
1242                 if (idleRequest) {
1243                     ClasspCompleteIdleRequest(fdoExt);
1244                 }
1245 
1246                 /*
1247                  *  We may have been called by one of the class drivers (e.g. cdrom)
1248                  *  via the legacy API ClassSplitRequest.
1249                  *  This is the only case for which the packet engine is called for an FDO
1250                  *  with a StartIo routine; in that case, we have to call IoStartNextPacket
1251                  *  now that the original irp has been completed.
1252                  */
1253                 if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) {
1254                     if (TEST_FLAG(SrbGetSrbFlags(pkt->Srb), SRB_FLAGS_DONT_START_NEXT_PACKET)){
1255                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)"));
1256                     } else {
1257                         KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
1258                         IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable
1259                         KeLowerIrql(oldIrql);
1260                     }
1261                 }
1262             }
1263         }
1264 
1265         /*
1266          *  If the packet was synchronous, write the final result back to the issuer's status buffer
1267          *  and signal his event.
1268          */
1269         if (pkt->SyncEventPtr){
1270             KeSetEvent(pkt->SyncEventPtr, 0, FALSE);
1271             pkt->SyncEventPtr = NULL;
1272         }
1273 
1274         /*
1275          *  If the operation isn't a normal read/write, but needs to do more
1276          *  operation-specific processing, call the operation's continuation
1277          *  routine.  The operation may create and queue another transfer packet
1278          *  within this routine, but pkt is still freed after returning from the
1279          *  continuation routine.
1280          */
1281         if (pkt->ContinuationRoutine != NULL){
1282             pkt->ContinuationRoutine(pkt->ContinuationContext);
1283             pkt->ContinuationRoutine = NULL;
1284         }
1285 
1286         /*
1287          *  Free the completed packet.
1288          */
1289         pkt->UsePartialMdl = FALSE;
1290 //        pkt->OriginalIrp = NULL;
1291         pkt->InLowMemRetry = FALSE;
1292         EnqueueFreeTransferPacket(Fdo, pkt);
1293 
1294         /*
1295          *  Now that we have freed some resources,
1296          *  try again to send one of the previously deferred irps.
1297          */
1298         deferredIrp = DequeueDeferredClientIrp(Fdo);
1299         if (deferredIrp){
1300             ServiceTransferRequest(Fdo, deferredIrp, TRUE);
1301         }
1302 
1303         ClassReleaseRemoveLock(Fdo, (PVOID)&uniqueAddr);
1304     }
1305 
1306     return STATUS_MORE_PROCESSING_REQUIRED;
1307 }
1308 
1309 
1310 /*
1311  *  SetupEjectionTransferPacket
1312  *
1313  *      Set up a transferPacket for a synchronous Ejection Control transfer.
1314  */
SetupEjectionTransferPacket(TRANSFER_PACKET * Pkt,BOOLEAN PreventMediaRemoval,PKEVENT SyncEventPtr,PIRP OriginalIrp)1315 VOID SetupEjectionTransferPacket(   TRANSFER_PACKET *Pkt,
1316                                         BOOLEAN PreventMediaRemoval,
1317                                         PKEVENT SyncEventPtr,
1318                                         PIRP OriginalIrp)
1319 {
1320     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
1321     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1322     PCDB pCdb;
1323     ULONG srbLength;
1324 
1325     PAGED_CODE();
1326 
1327     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1328         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1329         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1330     } else {
1331         srbLength = fdoData->SrbTemplate->Length;
1332     }
1333     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1334 
1335     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1336     SrbSetCdbLength(Pkt->Srb, 6);
1337     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1338     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1339     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1340     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1341 
1342     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1343 
1344     pCdb = SrbGetCdb(Pkt->Srb);
1345     if (pCdb) {
1346         pCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
1347         pCdb->MEDIA_REMOVAL.Prevent = PreventMediaRemoval;
1348     }
1349 
1350     Pkt->BufPtrCopy = NULL;
1351     Pkt->BufLenCopy = 0;
1352 
1353     Pkt->OriginalIrp = OriginalIrp;
1354     Pkt->NumRetries = NUM_LOCKMEDIAREMOVAL_RETRIES;
1355     Pkt->SyncEventPtr = SyncEventPtr;
1356     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1357 }
1358 
1359 
1360 /*
1361  *  SetupModeSenseTransferPacket
1362  *
1363  *      Set up a transferPacket for a synchronous Mode Sense transfer.
1364  */
SetupModeSenseTransferPacket(TRANSFER_PACKET * Pkt,PKEVENT SyncEventPtr,PVOID ModeSenseBuffer,UCHAR ModeSenseBufferLen,UCHAR PageMode,UCHAR SubPage,PIRP OriginalIrp,UCHAR PageControl)1365 VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt,
1366                                   PKEVENT SyncEventPtr,
1367                                   PVOID ModeSenseBuffer,
1368                                   UCHAR ModeSenseBufferLen,
1369                                   UCHAR PageMode,
1370                                   UCHAR SubPage,
1371                                   PIRP OriginalIrp,
1372                                   UCHAR PageControl)
1373 
1374 {
1375     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
1376     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1377     PCDB pCdb;
1378     ULONG srbLength;
1379 
1380     PAGED_CODE();
1381 
1382     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1383         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1384         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1385     } else {
1386         srbLength = fdoData->SrbTemplate->Length;
1387     }
1388     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1389 
1390     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1391     SrbSetCdbLength(Pkt->Srb, 6);
1392     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1393     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1394     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1395     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1396     SrbSetDataBuffer(Pkt->Srb, ModeSenseBuffer);
1397     SrbSetDataTransferLength(Pkt->Srb, ModeSenseBufferLen);
1398 
1399 
1400     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1401 
1402     pCdb = SrbGetCdb(Pkt->Srb);
1403     if (pCdb) {
1404         pCdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
1405         pCdb->MODE_SENSE.PageCode = PageMode;
1406         pCdb->MODE_SENSE.SubPageCode = SubPage;
1407         pCdb->MODE_SENSE.Pc = PageControl;
1408         pCdb->MODE_SENSE.AllocationLength = (UCHAR)ModeSenseBufferLen;
1409     }
1410 
1411     Pkt->BufPtrCopy = ModeSenseBuffer;
1412     Pkt->BufLenCopy = ModeSenseBufferLen;
1413 
1414     Pkt->OriginalIrp = OriginalIrp;
1415     Pkt->NumRetries = NUM_MODESENSE_RETRIES;
1416     Pkt->SyncEventPtr = SyncEventPtr;
1417     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1418 }
1419 
1420 /*
1421  *  SetupModeSelectTransferPacket
1422  *
1423  *      Set up a transferPacket for a synchronous Mode Select transfer.
1424  */
SetupModeSelectTransferPacket(TRANSFER_PACKET * Pkt,PKEVENT SyncEventPtr,PVOID ModeSelectBuffer,UCHAR ModeSelectBufferLen,BOOLEAN SavePages,PIRP OriginalIrp)1425 VOID SetupModeSelectTransferPacket(TRANSFER_PACKET *Pkt,
1426                                   PKEVENT SyncEventPtr,
1427                                   PVOID ModeSelectBuffer,
1428                                   UCHAR ModeSelectBufferLen,
1429                                   BOOLEAN SavePages,
1430                                   PIRP OriginalIrp)
1431 
1432 {
1433     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
1434     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1435     PCDB pCdb;
1436     ULONG srbLength;
1437 
1438     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1439         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1440         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1441     } else {
1442         srbLength = fdoData->SrbTemplate->Length;
1443     }
1444     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1445 
1446     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1447     SrbSetCdbLength(Pkt->Srb, 6);
1448     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1449     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1450     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1451     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1452     SrbSetDataBuffer(Pkt->Srb, ModeSelectBuffer);
1453     SrbSetDataTransferLength(Pkt->Srb, ModeSelectBufferLen);
1454 
1455     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1456 
1457     pCdb = SrbGetCdb(Pkt->Srb);
1458     if (pCdb) {
1459         pCdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
1460         pCdb->MODE_SELECT.SPBit = SavePages;
1461         pCdb->MODE_SELECT.PFBit = 1;
1462         pCdb->MODE_SELECT.ParameterListLength = (UCHAR)ModeSelectBufferLen;
1463     }
1464 
1465     Pkt->BufPtrCopy = ModeSelectBuffer;
1466     Pkt->BufLenCopy = ModeSelectBufferLen;
1467 
1468     Pkt->OriginalIrp = OriginalIrp;
1469     Pkt->NumRetries = NUM_MODESELECT_RETRIES;
1470     Pkt->SyncEventPtr = SyncEventPtr;
1471     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1472 }
1473 
1474 
1475 /*
1476  *  SetupDriveCapacityTransferPacket
1477  *
1478  *      Set up a transferPacket for a synchronous Drive Capacity transfer.
1479  */
SetupDriveCapacityTransferPacket(TRANSFER_PACKET * Pkt,PVOID ReadCapacityBuffer,ULONG ReadCapacityBufferLen,PKEVENT SyncEventPtr,PIRP OriginalIrp,BOOLEAN Use16ByteCdb)1480 VOID SetupDriveCapacityTransferPacket(   TRANSFER_PACKET *Pkt,
1481                                         PVOID ReadCapacityBuffer,
1482                                         ULONG ReadCapacityBufferLen,
1483                                         PKEVENT SyncEventPtr,
1484                                         PIRP OriginalIrp,
1485                                         BOOLEAN Use16ByteCdb)
1486 {
1487     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
1488     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1489     PCDB pCdb;
1490     ULONG srbLength;
1491     ULONG timeoutValue = fdoExt->TimeOutValue;
1492 
1493     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1494         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1495         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1496     } else {
1497         srbLength = fdoData->SrbTemplate->Length;
1498     }
1499     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1500 
1501     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1502     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1503     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1504     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1505 
1506 
1507     SrbSetTimeOutValue(Pkt->Srb, timeoutValue);
1508     SrbSetDataBuffer(Pkt->Srb, ReadCapacityBuffer);
1509     SrbSetDataTransferLength(Pkt->Srb, ReadCapacityBufferLen);
1510 
1511     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1512 
1513     pCdb = SrbGetCdb(Pkt->Srb);
1514     if (pCdb) {
1515         if (Use16ByteCdb == TRUE) {
1516             NT_ASSERT(ReadCapacityBufferLen >= sizeof(READ_CAPACITY_DATA_EX));
1517             SrbSetCdbLength(Pkt->Srb, 16);
1518             pCdb->CDB16.OperationCode = SCSIOP_READ_CAPACITY16;
1519             REVERSE_BYTES(&pCdb->CDB16.TransferLength, &ReadCapacityBufferLen);
1520             pCdb->AsByte[1] = 0x10; // Service Action
1521         } else {
1522             SrbSetCdbLength(Pkt->Srb, 10);
1523             pCdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
1524         }
1525     }
1526 
1527     Pkt->BufPtrCopy = ReadCapacityBuffer;
1528     Pkt->BufLenCopy = ReadCapacityBufferLen;
1529 
1530     Pkt->OriginalIrp = OriginalIrp;
1531     Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES;
1532 #if !defined(__REACTOS__) && NTDDI_VERSION >= NTDDI_WINBLUE
1533     Pkt->NumIoTimeoutRetries = NUM_DRIVECAPACITY_RETRIES;
1534 #endif
1535 
1536     Pkt->SyncEventPtr = SyncEventPtr;
1537     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1538 }
1539 
1540 
1541 #if 0
1542     /*
1543      *  SetupSendStartUnitTransferPacket
1544      *
1545      *      Set up a transferPacket for a synchronous Send Start Unit transfer.
1546      */
1547     VOID SetupSendStartUnitTransferPacket(   TRANSFER_PACKET *Pkt,
1548                                                     PIRP OriginalIrp)
1549     {
1550         PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
1551         PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1552         PCDB pCdb;
1553 
1554         PAGED_CODE();
1555 
1556         RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
1557 
1558         /*
1559          *  Initialize the SRB.
1560          *  Use a very long timeout value to give the drive time to spin up.
1561          */
1562         Pkt->Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
1563         Pkt->Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
1564         Pkt->Srb->TimeOutValue = START_UNIT_TIMEOUT;
1565         Pkt->Srb->CdbLength = 6;
1566         Pkt->Srb->OriginalRequest = Pkt->Irp;
1567         Pkt->Srb->SenseInfoBuffer = &Pkt->SrbErrorSenseData;
1568         Pkt->Srb->SenseInfoBufferLength = sizeof(Pkt->SrbErrorSenseData);
1569         Pkt->Srb->Lun = 0;
1570 
1571         SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
1572         SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
1573         SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1574 
1575         pCdb = (PCDB)Pkt->Srb->Cdb;
1576         pCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
1577         pCdb->START_STOP.Start = 1;
1578         pCdb->START_STOP.Immediate = 0;
1579         pCdb->START_STOP.LogicalUnitNumber = 0;
1580 
1581         Pkt->OriginalIrp = OriginalIrp;
1582         Pkt->NumRetries = 0;
1583         Pkt->SyncEventPtr = NULL;
1584         Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1585     }
1586 #endif
1587 
1588 
1589 VOID
1590 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
CleanupTransferPacketToWorkingSetSizeWorker(_In_ PVOID Fdo,_In_opt_ PVOID Context,_In_ PIO_WORKITEM IoWorkItem)1591 CleanupTransferPacketToWorkingSetSizeWorker(
1592     _In_ PVOID Fdo,
1593     _In_opt_ PVOID Context,
1594     _In_ PIO_WORKITEM IoWorkItem
1595     )
1596 {
1597     ULONG node = (ULONG) (ULONG_PTR)Context;
1598 
1599     PAGED_CODE();
1600 
1601     CleanupTransferPacketToWorkingSetSize((PDEVICE_OBJECT)Fdo, FALSE, node);
1602 
1603     //
1604     // Release the remove lock acquired in EnqueueFreeTransferPacket
1605     //
1606     ClassReleaseRemoveLock((PDEVICE_OBJECT)Fdo, (PIRP)IoWorkItem);
1607 
1608     if (IoWorkItem != NULL) {
1609         IoFreeWorkItem(IoWorkItem);
1610     }
1611 }
1612 
1613 
1614 VOID
CleanupTransferPacketToWorkingSetSize(_In_ PDEVICE_OBJECT Fdo,_In_ BOOLEAN LimitNumPktToDelete,_In_ ULONG Node)1615 CleanupTransferPacketToWorkingSetSize(
1616     _In_ PDEVICE_OBJECT Fdo,
1617     _In_ BOOLEAN LimitNumPktToDelete,
1618     _In_ ULONG Node
1619     )
1620 
1621 /*
1622 Routine Description:
1623 
1624     This function frees the resources for the free transfer packets attempting
1625     to bring them down within the working set size.
1626 
1627 Arguments:
1628     Fdo: The FDO that represents the device whose transfer packet size needs to be trimmed.
1629     LimitNumPktToDelete: Flag to indicate if the number of packets freed in one call should be capped.
1630     Node: NUMA node transfer packet is associated with.
1631 
1632 --*/
1633 
1634 {
1635     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
1636     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
1637     KIRQL oldIrql;
1638     SINGLE_LIST_ENTRY pktList;
1639     PSINGLE_LIST_ENTRY slistEntry;
1640     PTRANSFER_PACKET pktToDelete;
1641     ULONG requiredNumPktToDelete = fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets -
1642                                    fdoData->LocalMaxWorkingSetTransferPackets;
1643 
1644     if (LimitNumPktToDelete) {
1645         requiredNumPktToDelete = MIN(requiredNumPktToDelete, MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE);
1646     }
1647 
1648     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "CleanupTransferPacketToWorkingSetSize (%p): Exiting stress, block freeing %d packets.", Fdo, requiredNumPktToDelete));
1649 
1650     /*
1651      *  Check the counter again with lock held.  This eliminates a race condition
1652      *  while still allowing us to not grab the spinlock in the common codepath.
1653      *
1654      *  Note that the spinlock does not synchronize with threads dequeuing free
1655      *  packets to send (DequeueFreeTransferPacket does that with a lightweight
1656      *  interlocked exchange); the spinlock prevents multiple threads in this function
1657      *  from deciding to free too many extra packets at once.
1658      */
1659     SimpleInitSlistHdr(&pktList);
1660     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
1661     while ((fdoData->FreeTransferPacketsLists[Node].NumFreeTransferPackets >= fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets) &&
1662            (fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets > fdoData->LocalMaxWorkingSetTransferPackets) &&
1663            (requiredNumPktToDelete--)){
1664 
1665         pktToDelete = DequeueFreeTransferPacketEx(Fdo, FALSE, Node);
1666         if (pktToDelete){
1667             SimplePushSlist(&pktList,
1668                             (PSINGLE_LIST_ENTRY)&pktToDelete->SlistEntry);
1669             InterlockedDecrement((volatile LONG *)&fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
1670         } else {
1671             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW,
1672                 "Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1). Node=%d",
1673                 fdoData->LocalMaxWorkingSetTransferPackets,
1674                 Fdo,
1675                 fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets,
1676                 Node));
1677             break;
1678         }
1679     }
1680     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
1681 
1682     slistEntry = SimplePopSlist(&pktList);
1683     while (slistEntry) {
1684         pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
1685         DestroyTransferPacket(pktToDelete);
1686         slistEntry = SimplePopSlist(&pktList);
1687     }
1688 
1689     return;
1690 }
1691 
1692 
1693 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)1694 _IRQL_requires_min_(PASSIVE_LEVEL)
1695 _IRQL_requires_same_
1696 VOID
1697 ClasspSetupPopulateTokenTransferPacket(
1698     _In_ __drv_aliasesMem POFFLOAD_READ_CONTEXT OffloadReadContext,
1699     _In_ PTRANSFER_PACKET Pkt,
1700     _In_ ULONG Length,
1701     _In_reads_bytes_(Length) PUCHAR PopulateTokenBuffer,
1702     _In_ PIRP OriginalIrp,
1703     _In_ ULONG ListIdentifier
1704     )
1705 
1706 /*++
1707 
1708 Routine description:
1709 
1710     This routine is called once to set up a packet for PopulateToken.
1711     It builds up the SRB by setting the appropriate fields.
1712 
1713 Arguments:
1714 
1715     Pkt - The transfer packet to be sent down to the lower driver
1716     SyncEventPtr - The event that gets signaled once the IRP contained in the packet completes
1717     Length - Length of the buffer being sent as part of the command
1718     PopulateTokenBuffer - The buffer that contains the LBA ranges information for the PopulateToken operation
1719     OriginalIrp - The Io request to be processed
1720     ListIdentifier - The identifier that will be used to correlate a subsequent command to retrieve the token
1721 
1722 Return Value:
1723 
1724     Nothing
1725 
1726 --*/
1727 
1728 {
1729     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
1730     PCLASS_PRIVATE_FDO_DATA fdoData;
1731     PCDB pCdb;
1732     ULONG srbLength;
1733 
1734     PAGED_CODE();
1735 
1736     TracePrint((TRACE_LEVEL_VERBOSE,
1737                 TRACE_FLAG_IOCTL,
1738                 "ClasspSetupPopulateTokenTransferPacket (%p): Entering function. Irp %p\n",
1739                 Pkt->Fdo,
1740                 OriginalIrp));
1741 
1742     fdoExt = Pkt->Fdo->DeviceExtension;
1743     fdoData = fdoExt->PrivateFdoData;
1744 
1745     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1746         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1747         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1748     } else {
1749         srbLength = fdoData->SrbTemplate->Length;
1750     }
1751     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1752 
1753     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1754     SrbSetCdbLength(Pkt->Srb, 16);
1755     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1756     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1757     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1758     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1759     SrbSetDataBuffer(Pkt->Srb, PopulateTokenBuffer);
1760     SrbSetDataTransferLength(Pkt->Srb, Length);
1761 
1762     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1763 
1764     pCdb = SrbGetCdb(Pkt->Srb);
1765     if (pCdb) {
1766         pCdb->TOKEN_OPERATION.OperationCode = SCSIOP_POPULATE_TOKEN;
1767         pCdb->TOKEN_OPERATION.ServiceAction = SERVICE_ACTION_POPULATE_TOKEN;
1768 
1769         REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ListIdentifier, &ListIdentifier);
1770         REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ParameterListLength, &Length);
1771     }
1772 
1773     Pkt->BufPtrCopy = PopulateTokenBuffer;
1774     Pkt->BufLenCopy = Length;
1775 
1776     Pkt->OriginalIrp = OriginalIrp;
1777     Pkt->NumRetries = NUM_POPULATE_TOKEN_RETRIES;
1778     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1779 
1780     Pkt->ContinuationRoutine = ClasspPopulateTokenTransferPacketDone;
1781     Pkt->ContinuationContext = OffloadReadContext;
1782 
1783     TracePrint((TRACE_LEVEL_VERBOSE,
1784                 TRACE_FLAG_IOCTL,
1785                 "ClasspSetupPopulateTokenTransferPacket (%p): Exiting function with Irp %p\n",
1786                 Pkt->Fdo,
1787                 OriginalIrp));
1788 
1789     return;
1790 }
1791 
1792 
1793 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)1794 _IRQL_requires_min_(PASSIVE_LEVEL)
1795 _IRQL_requires_same_
1796 VOID
1797 ClasspSetupReceivePopulateTokenInformationTransferPacket(
1798     _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
1799     _In_ PTRANSFER_PACKET Pkt,
1800     _In_ ULONG Length,
1801     _In_reads_bytes_(Length) PUCHAR ReceivePopulateTokenInformationBuffer,
1802     _In_ PIRP OriginalIrp,
1803     _In_ ULONG ListIdentifier
1804     )
1805 
1806 /*++
1807 
1808 Routine description:
1809 
1810     This routine is called once to set up a packet for read token retrieval.
1811     It builds up the SRB by setting the appropriate fields.
1812 
1813 Arguments:
1814 
1815     Pkt - The transfer packet to be sent down to the lower driver
1816     Length - Length of the buffer being sent as part of the command
1817     ReceivePopulateTokenInformationBuffer - The buffer into which the target will pass back the token
1818     OriginalIrp - The Io request to be processed
1819     ListIdentifier - The identifier that will be used to correlate this command with its corresponding previous populate token operation
1820 
1821 Return Value:
1822 
1823     Nothing
1824 
1825 --*/
1826 
1827 {
1828     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
1829     PCLASS_PRIVATE_FDO_DATA fdoData;
1830     PCDB pCdb;
1831     ULONG srbLength;
1832 
1833     TracePrint((TRACE_LEVEL_VERBOSE,
1834                 TRACE_FLAG_IOCTL,
1835                 "ClasspSetupReceivePopulateTokenInformationTransferPacket (%p): Entering function. Irp %p\n",
1836                 Pkt->Fdo,
1837                 OriginalIrp));
1838 
1839     fdoExt = Pkt->Fdo->DeviceExtension;
1840     fdoData = fdoExt->PrivateFdoData;
1841 
1842     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1843         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1844         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1845     } else {
1846         srbLength = fdoData->SrbTemplate->Length;
1847     }
1848     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1849 
1850     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1851     SrbSetCdbLength(Pkt->Srb, 16);
1852     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1853     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1854     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1855     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1856     SrbSetDataBuffer(Pkt->Srb, ReceivePopulateTokenInformationBuffer);
1857     SrbSetDataTransferLength(Pkt->Srb, Length);
1858 
1859     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1860 
1861     pCdb = SrbGetCdb(Pkt->Srb);
1862     if (pCdb) {
1863         pCdb->RECEIVE_TOKEN_INFORMATION.OperationCode = SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION;
1864         pCdb->RECEIVE_TOKEN_INFORMATION.ServiceAction = SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION;
1865 
1866         REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.ListIdentifier, &ListIdentifier);
1867         REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.AllocationLength, &Length);
1868     }
1869 
1870     Pkt->BufPtrCopy = ReceivePopulateTokenInformationBuffer;
1871     Pkt->BufLenCopy = Length;
1872 
1873     Pkt->OriginalIrp = OriginalIrp;
1874     Pkt->NumRetries = NUM_POPULATE_TOKEN_RETRIES;
1875     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1876 
1877     Pkt->ContinuationRoutine = ClasspReceivePopulateTokenInformationTransferPacketDone;
1878     Pkt->ContinuationContext = OffloadReadContext;
1879 
1880     TracePrint((TRACE_LEVEL_VERBOSE,
1881                 TRACE_FLAG_IOCTL,
1882                 "ClasspSetupReceivePopulateTokenInformationTransferPacket (%p): Exiting function with Irp %p\n",
1883                 Pkt->Fdo,
1884                 OriginalIrp));
1885 
1886     return;
1887 }
1888 
1889 
1890 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)1891 _IRQL_requires_min_(PASSIVE_LEVEL)
1892 _IRQL_requires_same_
1893 VOID
1894 ClasspSetupWriteUsingTokenTransferPacket(
1895     _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
1896     _In_ PTRANSFER_PACKET Pkt,
1897     _In_ ULONG Length,
1898     _In_reads_bytes_ (Length) PUCHAR WriteUsingTokenBuffer,
1899     _In_ PIRP OriginalIrp,
1900     _In_ ULONG ListIdentifier
1901     )
1902 
1903 /*++
1904 
1905 Routine description:
1906 
1907     This routine is called once to set up a packet for WriteUsingToken.
1908     It builds up the SRB by setting the appropriate fields. It is not called
1909     before a retry as the SRB fields may be modified for the retry.
1910 
1911     The IRP is set up in SubmitTransferPacket because it must be reset for
1912     each packet submission.
1913 
1914 Arguments:
1915 
1916     Pkt - The transfer packet to be sent down to the lower driver
1917     Length - Length of the buffer being sent as part of the command
1918     WriteUsingTokenBuffer - The buffer that contains the read token and the write LBA ranges information for the WriteUsingToken operation
1919     OriginalIrp - The Io request to be processed
1920     ListIdentifier - The identifier that will be used to correlate a subsequent command to retrieve extended results in case of command failure
1921 
1922 Return Value:
1923 
1924     Nothing
1925 
1926 --*/
1927 
1928 {
1929     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
1930     PCLASS_PRIVATE_FDO_DATA fdoData;
1931     PCDB pCdb;
1932     ULONG srbLength;
1933 
1934     TracePrint((TRACE_LEVEL_VERBOSE,
1935                 TRACE_FLAG_IOCTL,
1936                 "ClasspSetupWriteUsingTokenTransferPacket (%p): Entering function. Irp %p\n",
1937                 Pkt->Fdo,
1938                 OriginalIrp));
1939 
1940     fdoExt = Pkt->Fdo->DeviceExtension;
1941     fdoData = fdoExt->PrivateFdoData;
1942 
1943     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1944         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
1945         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
1946     } else {
1947         srbLength = fdoData->SrbTemplate->Length;
1948     }
1949     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
1950 
1951     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
1952     SrbSetCdbLength(Pkt->Srb, 16);
1953     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
1954     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
1955     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
1956     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
1957     SrbSetDataBuffer(Pkt->Srb, WriteUsingTokenBuffer);
1958     SrbSetDataTransferLength(Pkt->Srb, Length);
1959 
1960     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
1961 
1962     pCdb = SrbGetCdb(Pkt->Srb);
1963     if (pCdb) {
1964         pCdb->TOKEN_OPERATION.OperationCode = SCSIOP_WRITE_USING_TOKEN;
1965         pCdb->TOKEN_OPERATION.ServiceAction = SERVICE_ACTION_WRITE_USING_TOKEN;
1966 
1967         REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ParameterListLength, &Length);
1968         REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ListIdentifier, &ListIdentifier);
1969     }
1970 
1971     Pkt->BufPtrCopy = WriteUsingTokenBuffer;
1972     Pkt->BufLenCopy = Length;
1973 
1974     Pkt->OriginalIrp = OriginalIrp;
1975     Pkt->NumRetries = NUM_WRITE_USING_TOKEN_RETRIES;
1976     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
1977 
1978     Pkt->ContinuationRoutine = ClasspWriteUsingTokenTransferPacketDone;
1979     Pkt->ContinuationContext = OffloadWriteContext;
1980 
1981     TracePrint((TRACE_LEVEL_VERBOSE,
1982                 TRACE_FLAG_IOCTL,
1983                 "ClasspSetupWriteUsingTokenTransferPacket (%p): Exiting function with Irp %p\n",
1984                 Pkt->Fdo,
1985                 OriginalIrp));
1986 
1987     return;
1988 }
1989 
1990 
1991 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)1992 _IRQL_requires_min_(PASSIVE_LEVEL)
1993 _IRQL_requires_same_
1994 VOID
1995 ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
1996     _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
1997     _In_ PTRANSFER_PACKET Pkt,
1998     _In_ ULONG Length,
1999     _In_reads_bytes_ (Length) PUCHAR ReceiveWriteUsingTokenInformationBuffer,
2000     _In_ PIRP OriginalIrp,
2001     _In_ ULONG ListIdentifier
2002     )
2003 
2004 /*++
2005 
2006 Routine description:
2007 
2008     This routine is called once to set up a packet for extended results for
2009     WriteUsingToken operation. It builds up the SRB by setting the appropriate fields.
2010 
2011 Arguments:
2012 
2013     Pkt - The transfer packet to be sent down to the lower driver
2014     SyncEventPtr - The event that gets signaled once the IRP contained in the packet completes
2015     Length - Length of the buffer being sent as part of the command
2016     ReceiveWriteUsingTokenInformationBuffer - The buffer into which the target will pass back the extended results
2017     OriginalIrp - The Io request to be processed
2018     ListIdentifier - The identifier that will be used to correlate this command with its corresponding previous write using token operation
2019 
2020 Return Value:
2021 
2022     Nothing
2023 
2024 --*/
2025 
2026 {
2027     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
2028     PCLASS_PRIVATE_FDO_DATA fdoData;
2029     PCDB pCdb;
2030     ULONG srbLength;
2031 
2032     TracePrint((TRACE_LEVEL_VERBOSE,
2033                 TRACE_FLAG_IOCTL,
2034                 "ClasspSetupReceiveWriteUsingTokenInformationTransferPacket (%p): Entering function. Irp %p\n",
2035                 Pkt->Fdo,
2036                 OriginalIrp));
2037 
2038     fdoExt = Pkt->Fdo->DeviceExtension;
2039     fdoData = fdoExt->PrivateFdoData;
2040 
2041     if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
2042         srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
2043         NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
2044     } else {
2045         srbLength = fdoData->SrbTemplate->Length;
2046     }
2047     RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
2048 
2049     SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
2050     SrbSetCdbLength(Pkt->Srb, 16);
2051     SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
2052     SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
2053     SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
2054     SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
2055     SrbSetDataBuffer(Pkt->Srb, ReceiveWriteUsingTokenInformationBuffer);
2056     SrbSetDataTransferLength(Pkt->Srb, Length);
2057 
2058     SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
2059 
2060     pCdb = SrbGetCdb(Pkt->Srb);
2061     if (pCdb) {
2062         pCdb->RECEIVE_TOKEN_INFORMATION.OperationCode = SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION;
2063         pCdb->RECEIVE_TOKEN_INFORMATION.ServiceAction = SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION;
2064 
2065         REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.AllocationLength, &Length);
2066         REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.ListIdentifier, &ListIdentifier);
2067     }
2068 
2069     Pkt->BufPtrCopy = ReceiveWriteUsingTokenInformationBuffer;
2070     Pkt->BufLenCopy = Length;
2071 
2072     Pkt->OriginalIrp = OriginalIrp;
2073     Pkt->NumRetries = NUM_WRITE_USING_TOKEN_RETRIES;
2074     Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
2075 
2076     Pkt->ContinuationRoutine = (PCONTINUATION_ROUTINE) ClasspReceiveWriteUsingTokenInformationTransferPacketDone;
2077     Pkt->ContinuationContext = OffloadWriteContext;
2078 
2079     TracePrint((TRACE_LEVEL_VERBOSE,
2080                 TRACE_FLAG_IOCTL,
2081                 "ClasspSetupReceiveWriteUsingTokenInformationTransferPacket (%p): Exiting function with Irp %p\n",
2082                 Pkt->Fdo,
2083                 OriginalIrp));
2084 
2085     return;
2086 }
2087 
2088 
2089