3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Packet routines for CLASSPNP
27 #pragma alloc_text(PAGE, InitializeTransferPackets)
28 #pragma alloc_text(PAGE, DestroyAllTransferPackets)
29 #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
30 #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
34 ULONG MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
35 ULONG MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
39 * InitializeTransferPackets
41 * Allocate/initialize TRANSFER_PACKETs and related resources.
43 NTSTATUS NTAPI
InitializeTransferPackets(PDEVICE_OBJECT Fdo
)
45 PCOMMON_DEVICE_EXTENSION commonExt
= Fdo
->DeviceExtension
;
46 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
47 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
48 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc
= commonExt
->PartitionZeroExtension
->AdapterDescriptor
;
50 NTSTATUS status
= STATUS_SUCCESS
;
55 * Precompute the maximum transfer length
57 ASSERT(adapterDesc
->MaximumTransferLength
);
58 ASSERT(adapterDesc
->MaximumPhysicalPages
);
59 hwMaxPages
= adapterDesc
->MaximumPhysicalPages
? adapterDesc
->MaximumPhysicalPages
-1 : 0;
61 #if defined(_AMD64_SIMULATOR_)
64 // The simulator appears to have a problem with large transfers.
73 fdoData
->HwMaxXferLen
= MIN(adapterDesc
->MaximumTransferLength
, hwMaxPages
<< PAGE_SHIFT
);
74 fdoData
->HwMaxXferLen
= MAX(fdoData
->HwMaxXferLen
, PAGE_SIZE
);
76 fdoData
->NumTotalTransferPackets
= 0;
77 fdoData
->NumFreeTransferPackets
= 0;
78 InitializeSListHead(&fdoData
->FreeTransferPacketsList
);
79 InitializeListHead(&fdoData
->AllTransferPacketsList
);
80 InitializeListHead(&fdoData
->DeferredClientIrpList
);
83 * Set the packet threshold numbers based on the Windows SKU.
85 if (ExVerifySuite(Personal
)){
86 // this is Windows Personal
87 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
88 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
90 else if (ExVerifySuite(Enterprise
) || ExVerifySuite(DataCenter
)){
91 // this is Advanced Server or Datacenter
92 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise
;
93 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise
;
95 else if (ExVerifySuite(TerminalServer
)){
96 // this is standard Server or Pro with terminal server
97 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Server
;
98 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Server
;
101 // this is Professional without terminal server
102 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
103 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
106 while (fdoData
->NumFreeTransferPackets
< MIN_INITIAL_TRANSFER_PACKETS
){
107 PTRANSFER_PACKET pkt
= NewTransferPacket(Fdo
);
109 InterlockedIncrement((PLONG
)&fdoData
->NumTotalTransferPackets
);
110 EnqueueFreeTransferPacket(Fdo
, pkt
);
113 status
= STATUS_INSUFFICIENT_RESOURCES
;
117 fdoData
->DbgPeakNumTransferPackets
= fdoData
->NumTotalTransferPackets
;
120 * Pre-initialize our SCSI_REQUEST_BLOCK template with all
121 * the constant fields. This will save a little time for each xfer.
122 * NOTE: a CdbLength field of 10 may not always be appropriate
124 RtlZeroMemory(&fdoData
->SrbTemplate
, sizeof(SCSI_REQUEST_BLOCK
));
125 fdoData
->SrbTemplate
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
126 fdoData
->SrbTemplate
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
127 fdoData
->SrbTemplate
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
128 fdoData
->SrbTemplate
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
129 fdoData
->SrbTemplate
.CdbLength
= 10;
134 VOID NTAPI
DestroyAllTransferPackets(PDEVICE_OBJECT Fdo
)
136 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
137 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
138 TRANSFER_PACKET
*pkt
;
142 ASSERT(IsListEmpty(&fdoData
->DeferredClientIrpList
));
144 while ((pkt
= DequeueFreeTransferPacket(Fdo
, FALSE
))){
145 DestroyTransferPacket(pkt
);
146 InterlockedDecrement((PLONG
)&fdoData
->NumTotalTransferPackets
);
149 ASSERT(fdoData
->NumTotalTransferPackets
== 0);
152 PTRANSFER_PACKET NTAPI
NewTransferPacket(PDEVICE_OBJECT Fdo
)
154 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
155 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
156 PTRANSFER_PACKET newPkt
;
158 newPkt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(TRANSFER_PACKET
), 'pnPC');
160 RtlZeroMemory(newPkt
, sizeof(TRANSFER_PACKET
)); // just to be sure
163 * Allocate resources for the packet.
165 newPkt
->Irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
172 * Enqueue the packet in our static AllTransferPacketsList
173 * (just so we can find it during debugging if its stuck somewhere).
175 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
176 InsertTailList(&fdoData
->AllTransferPacketsList
, &newPkt
->AllPktsListEntry
);
177 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
189 * DestroyTransferPacket
192 VOID NTAPI
DestroyTransferPacket(PTRANSFER_PACKET Pkt
)
194 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
195 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
198 ASSERT(!Pkt
->SlistEntry
.Next
);
199 ASSERT(!Pkt
->OriginalIrp
);
201 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
204 * Delete the packet from our all-packets queue.
206 ASSERT(!IsListEmpty(&Pkt
->AllPktsListEntry
));
207 ASSERT(!IsListEmpty(&fdoData
->AllTransferPacketsList
));
208 RemoveEntryList(&Pkt
->AllPktsListEntry
);
209 InitializeListHead(&Pkt
->AllPktsListEntry
);
211 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
217 VOID NTAPI
EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo
, PTRANSFER_PACKET Pkt
)
219 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
220 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
224 ASSERT(!Pkt
->SlistEntry
.Next
);
226 InterlockedPushEntrySList(&fdoData
->FreeTransferPacketsList
, &Pkt
->SlistEntry
);
227 newNumPkts
= InterlockedIncrement((PLONG
)&fdoData
->NumFreeTransferPackets
);
228 ASSERT(newNumPkts
<= fdoData
->NumTotalTransferPackets
);
231 * If the total number of packets is larger than MinWorkingSetTransferPackets,
232 * that means that we've been in stress. If all those packets are now
233 * free, then we are now out of stress and can free the extra packets.
234 * Free down to MaxWorkingSetTransferPackets immediately, and
235 * down to MinWorkingSetTransferPackets lazily (one at a time).
237 if (fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
){
240 * 1. Immediately snap down to our UPPER threshold.
242 if (fdoData
->NumTotalTransferPackets
> MaxWorkingSetTransferPackets
){
243 SINGLE_LIST_ENTRY pktList
;
244 PSINGLE_LIST_ENTRY slistEntry
;
245 PTRANSFER_PACKET pktToDelete
;
247 DBGTRACE(ClassDebugTrace
, ("Exiting stress, block freeing (%d-%d) packets.", fdoData
->NumTotalTransferPackets
, MaxWorkingSetTransferPackets
));
250 * Check the counter again with lock held. This eliminates a race condition
251 * while still allowing us to not grab the spinlock in the common codepath.
253 * Note that the spinlock does not synchronize with threads dequeuing free
254 * packets to send (DequeueFreeTransferPacket does that with a lightweight
255 * interlocked exchange); the spinlock prevents multiple threads in this function
256 * from deciding to free too many extra packets at once.
258 SimpleInitSlistHdr(&pktList
);
259 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
260 while ((fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
) &&
261 (fdoData
->NumTotalTransferPackets
> MaxWorkingSetTransferPackets
)){
263 pktToDelete
= DequeueFreeTransferPacket(Fdo
, FALSE
);
265 SimplePushSlist(&pktList
, &pktToDelete
->SlistEntry
);
266 InterlockedDecrement((PLONG
)&fdoData
->NumTotalTransferPackets
);
269 DBGTRACE(ClassDebugTrace
, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets
, Fdo
, fdoData
->NumTotalTransferPackets
));
273 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
275 while ((slistEntry
= SimplePopSlist(&pktList
))){
276 pktToDelete
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
277 DestroyTransferPacket(pktToDelete
);
283 * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time).
285 if (fdoData
->NumTotalTransferPackets
> MinWorkingSetTransferPackets
){
287 * Check the counter again with lock held. This eliminates a race condition
288 * while still allowing us to not grab the spinlock in the common codepath.
290 * Note that the spinlock does not synchronize with threads dequeuing free
291 * packets to send (DequeueFreeTransferPacket does that with a lightweight
292 * interlocked exchange); the spinlock prevents multiple threads in this function
293 * from deciding to free too many extra packets at once.
295 PTRANSFER_PACKET pktToDelete
= NULL
;
297 DBGTRACE(ClassDebugTrace
, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData
->NumTotalTransferPackets
, MinWorkingSetTransferPackets
));
299 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
300 if ((fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
) &&
301 (fdoData
->NumTotalTransferPackets
> MinWorkingSetTransferPackets
)){
303 pktToDelete
= DequeueFreeTransferPacket(Fdo
, FALSE
);
305 InterlockedDecrement((PLONG
)&fdoData
->NumTotalTransferPackets
);
308 DBGTRACE(ClassDebugTrace
, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets
, Fdo
, fdoData
->NumTotalTransferPackets
));
311 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
314 DestroyTransferPacket(pktToDelete
);
322 PTRANSFER_PACKET NTAPI
DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo
, BOOLEAN AllocIfNeeded
)
324 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
325 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
326 PTRANSFER_PACKET pkt
;
327 PSINGLE_LIST_ENTRY slistEntry
;
330 slistEntry
= InterlockedPopEntrySList(&fdoData
->FreeTransferPacketsList
);
332 slistEntry
->Next
= NULL
;
333 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
334 ASSERT(fdoData
->NumFreeTransferPackets
> 0);
335 InterlockedDecrement((PLONG
)&fdoData
->NumFreeTransferPackets
);
340 * We are in stress and have run out of lookaside packets.
341 * In order to service the current transfer,
342 * allocate an extra packet.
343 * We will free it lazily when we are out of stress.
345 pkt
= NewTransferPacket(Fdo
);
347 InterlockedIncrement((PLONG
)&fdoData
->NumTotalTransferPackets
);
348 fdoData
->DbgPeakNumTransferPackets
= max(fdoData
->DbgPeakNumTransferPackets
, fdoData
->NumTotalTransferPackets
);
351 DBGWARN(("DequeueFreeTransferPacket: packet allocation failed"));
363 * SetupReadWriteTransferPacket
365 * This function is called once to set up the first attempt to send a packet.
366 * It is not called before a retry, as SRB fields may be modified for the retry.
368 * Set up the Srb of the TRANSFER_PACKET for the transfer.
369 * The Irp is set up in SubmitTransferPacket because it must be reset
370 * for each packet submission.
372 VOID NTAPI
SetupReadWriteTransferPacket(PTRANSFER_PACKET Pkt
,
375 LARGE_INTEGER DiskLocation
,
378 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
379 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
380 PIO_STACK_LOCATION origCurSp
= IoGetCurrentIrpStackLocation(OriginalIrp
);
381 UCHAR majorFunc
= origCurSp
->MajorFunction
;
382 ULONG logicalBlockAddr
;
383 ULONG numTransferBlocks
;
386 logicalBlockAddr
= (ULONG
)Int64ShrlMod32(DiskLocation
.QuadPart
, fdoExt
->SectorShift
);
387 numTransferBlocks
= Len
>> fdoExt
->SectorShift
;
390 * Slap the constant SRB fields in from our pre-initialized template.
391 * We'll then only have to fill in the unique fields for this transfer.
392 * Tell lower drivers to sort the SRBs by the logical block address
393 * so that disk seeks are minimized.
395 Pkt
->Srb
= fdoData
->SrbTemplate
; // copies _contents_ of SRB blocks
396 Pkt
->Srb
.DataBuffer
= Buf
;
397 Pkt
->Srb
.DataTransferLength
= Len
;
398 Pkt
->Srb
.QueueSortKey
= logicalBlockAddr
;
399 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
400 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
401 Pkt
->Srb
.TimeOutValue
= (Len
/0x10000) + ((Len
%0x10000) ? 1 : 0);
402 Pkt
->Srb
.TimeOutValue
*= fdoExt
->TimeOutValue
;
405 * Arrange values in CDB in big-endian format.
407 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
408 pCdb
->CDB10
.LogicalBlockByte0
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte3
;
409 pCdb
->CDB10
.LogicalBlockByte1
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte2
;
410 pCdb
->CDB10
.LogicalBlockByte2
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte1
;
411 pCdb
->CDB10
.LogicalBlockByte3
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte0
;
412 pCdb
->CDB10
.TransferBlocksMsb
= ((PFOUR_BYTE
)&numTransferBlocks
)->Byte1
;
413 pCdb
->CDB10
.TransferBlocksLsb
= ((PFOUR_BYTE
)&numTransferBlocks
)->Byte0
;
414 pCdb
->CDB10
.OperationCode
= (majorFunc
==IRP_MJ_READ
) ? SCSIOP_READ
: SCSIOP_WRITE
;
417 * Set SRB and IRP flags
419 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
420 if (TEST_FLAG(OriginalIrp
->Flags
, IRP_PAGING_IO
) ||
421 TEST_FLAG(OriginalIrp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
)){
422 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_CLASS_FLAGS_PAGING
);
424 SET_FLAG(Pkt
->Srb
.SrbFlags
, (majorFunc
==IRP_MJ_READ
) ? SRB_FLAGS_DATA_IN
: SRB_FLAGS_DATA_OUT
);
427 * Allow caching only if this is not a write-through request.
428 * If write-through and caching is enabled on the device, force
431 if (TEST_FLAG(origCurSp
->Flags
, SL_WRITE_THROUGH
)){
432 if (TEST_FLAG(fdoExt
->DeviceFlags
, DEV_WRITE_CACHE
)){
433 pCdb
->CDB10
.ForceUnitAccess
= TRUE
;
437 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_ADAPTER_CACHE_ENABLE
);
441 * Remember the buf and len in the SRB because miniports
442 * can overwrite SRB.DataTransferLength and we may need it again
445 Pkt
->BufPtrCopy
= Buf
;
446 Pkt
->BufLenCopy
= Len
;
447 Pkt
->TargetLocationCopy
= DiskLocation
;
449 Pkt
->OriginalIrp
= OriginalIrp
;
450 Pkt
->NumRetries
= MAXIMUM_RETRIES
;
451 Pkt
->SyncEventPtr
= NULL
;
452 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= TRUE
;
456 * SubmitTransferPacket
458 * Set up the IRP for the TRANSFER_PACKET submission and send it down.
460 VOID NTAPI
SubmitTransferPacket(PTRANSFER_PACKET Pkt
)
462 PCOMMON_DEVICE_EXTENSION commonExtension
= Pkt
->Fdo
->DeviceExtension
;
463 PDEVICE_OBJECT nextDevObj
= commonExtension
->LowerDeviceObject
;
464 PIO_STACK_LOCATION nextSp
= IoGetNextIrpStackLocation(Pkt
->Irp
);
466 ASSERT(Pkt
->Irp
->CurrentLocation
== Pkt
->Irp
->StackCount
+1);
469 * Attach the SRB to the IRP.
470 * The reused IRP's stack location has to be rewritten for each retry
471 * call because IoCompleteRequest clears the stack locations.
473 IoReuseIrp(Pkt
->Irp
, STATUS_NOT_SUPPORTED
);
474 nextSp
->MajorFunction
= IRP_MJ_SCSI
;
475 nextSp
->Parameters
.Scsi
.Srb
= &Pkt
->Srb
;
476 Pkt
->Srb
.ScsiStatus
= Pkt
->Srb
.SrbStatus
= 0;
477 if (Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
){
479 * Only dereference the "original IRP"'s stack location
480 * if its a real client irp (as opposed to a static irp
481 * we're using just for result status for one of the non-IO scsi commands).
483 * For read/write, propagate the storage-specific IRP stack location flags
484 * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
486 PIO_STACK_LOCATION origCurSp
= IoGetCurrentIrpStackLocation(Pkt
->OriginalIrp
);
487 nextSp
->Flags
= origCurSp
->Flags
;
491 * Write MDL address to new IRP. In the port driver the SRB DataBuffer
492 * field is used as the actual buffer pointer within the MDL,
493 * so the same MDL can be used for each partial transfer.
494 * This saves having to build a new MDL for each partial transfer.
496 Pkt
->Irp
->MdlAddress
= Pkt
->OriginalIrp
->MdlAddress
;
498 IoSetCompletionRoutine(Pkt
->Irp
, TransferPktComplete
, Pkt
, TRUE
, TRUE
, TRUE
);
499 IoCallDriver(nextDevObj
, Pkt
->Irp
);
502 NTSTATUS NTAPI
TransferPktComplete(IN PDEVICE_OBJECT NullFdo
, IN PIRP Irp
, IN PVOID Context
)
504 PTRANSFER_PACKET pkt
= (PTRANSFER_PACKET
)Context
;
505 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= pkt
->Fdo
->DeviceExtension
;
506 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
507 PIO_STACK_LOCATION origCurrentSp
= IoGetCurrentIrpStackLocation(pkt
->OriginalIrp
);
508 BOOLEAN packetDone
= FALSE
;
511 * Put all the assertions and spew in here so we don't have to look at them.
513 DBGCHECKRETURNEDPKT(pkt
);
515 if (SRB_STATUS(pkt
->Srb
.SrbStatus
) == SRB_STATUS_SUCCESS
){
517 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
520 * The port driver should not have allocated a sense buffer
521 * if the SRB succeeded.
523 ASSERT(!PORT_ALLOCATED_SENSE(fdoExt
, &pkt
->Srb
));
526 * Add this packet's transferred length to the original IRP's.
528 InterlockedExchangeAdd((PLONG
)&pkt
->OriginalIrp
->IoStatus
.Information
,
529 (LONG
)pkt
->Srb
.DataTransferLength
);
531 if (pkt
->InLowMemRetry
){
532 packetDone
= StepLowMemRetry(pkt
);
541 * The packet failed. We may retry it if possible.
546 * Make sure IRP status matches SRB error status (since we propagate it).
548 if (NT_SUCCESS(Irp
->IoStatus
.Status
)){
549 Irp
->IoStatus
.Status
= STATUS_UNSUCCESSFUL
;
553 * Interpret the SRB error (to a meaningful IRP status)
554 * and determine if we should retry this packet.
555 * This call looks at the returned SENSE info to figure out what to do.
557 shouldRetry
= InterpretTransferPacketError(pkt
);
560 * Sometimes the port driver can allocates a new 'sense' buffer
561 * to report transfer errors, e.g. when the default sense buffer
562 * is too small. If so, it is up to us to free it.
563 * Now that we're done interpreting the sense info, free it if appropriate.
565 if (PORT_ALLOCATED_SENSE(fdoExt
, &pkt
->Srb
)) {
566 DBGTRACE(ClassDebugSenseInfo
, ("Freeing port-allocated sense buffer for pkt %ph.", pkt
));
567 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt
, &pkt
->Srb
);
568 pkt
->Srb
.SenseInfoBuffer
= &pkt
->SrbErrorSenseData
;
569 pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
573 * If the SRB queue is locked-up, release it.
574 * Do this after calling the error handler.
576 if (pkt
->Srb
.SrbStatus
& SRB_STATUS_QUEUE_FROZEN
){
577 ClassReleaseQueue(pkt
->Fdo
);
580 if (shouldRetry
&& (pkt
->NumRetries
> 0)){
581 packetDone
= RetryTransferPacket(pkt
);
590 * If the packet is completed, put it back in the free list.
591 * If it is the last packet servicing the original request, complete the original irp.
594 LONG numPacketsRemaining
;
596 PDEVICE_OBJECT Fdo
= pkt
->Fdo
;
600 * In case a remove is pending, bump the lock count so we don't get freed
601 * right after we complete the original irp.
603 ClassAcquireRemoveLock(Fdo
, (PIRP
)&uniqueAddr
);
606 * The original IRP should get an error code
607 * if any one of the packets failed.
609 if (!NT_SUCCESS(Irp
->IoStatus
.Status
)){
610 pkt
->OriginalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
613 * If the original I/O originated in user space (i.e. it is thread-queued),
614 * and the error is user-correctable (e.g. media is missing, for removable media),
616 * Since this is only one of possibly several packets completing for the original IRP,
617 * we may do this more than once for a single request. That's ok; this allows
618 * us to test each returned status with IoIsErrorUserInduced().
620 if (IoIsErrorUserInduced(Irp
->IoStatus
.Status
) &&
621 pkt
->CompleteOriginalIrpWhenLastPacketCompletes
&&
622 pkt
->OriginalIrp
->Tail
.Overlay
.Thread
){
624 IoSetHardErrorOrVerifyDevice(pkt
->OriginalIrp
, pkt
->Fdo
);
629 * We use a field in the original IRP to count
630 * down the transfer pieces as they complete.
632 numPacketsRemaining
= InterlockedDecrement(
633 (PLONG
)&pkt
->OriginalIrp
->Tail
.Overlay
.DriverContext
[0]);
635 if (numPacketsRemaining
> 0){
637 * More transfer pieces remain for the original request.
638 * Wait for them to complete before completing the original irp.
644 * All the transfer pieces are done.
645 * Complete the original irp if appropriate.
647 ASSERT(numPacketsRemaining
== 0);
648 if (pkt
->CompleteOriginalIrpWhenLastPacketCompletes
){
649 if (NT_SUCCESS(pkt
->OriginalIrp
->IoStatus
.Status
)){
650 ASSERT((ULONG
)pkt
->OriginalIrp
->IoStatus
.Information
== origCurrentSp
->Parameters
.Read
.Length
);
651 ClasspPerfIncrementSuccessfulIo(fdoExt
);
653 ClassReleaseRemoveLock(pkt
->Fdo
, pkt
->OriginalIrp
);
655 ClassCompleteRequest(pkt
->Fdo
, pkt
->OriginalIrp
, IO_DISK_INCREMENT
);
658 * We may have been called by one of the class drivers (e.g. cdrom)
659 * via the legacy API ClassSplitRequest.
660 * This is the only case for which the packet engine is called for an FDO
661 * with a StartIo routine; in that case, we have to call IoStartNextPacket
662 * now that the original irp has been completed.
664 if (fdoExt
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
665 if (TEST_FLAG(pkt
->Srb
.SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
)){
666 DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)"));
670 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
671 IoStartNextPacket(pkt
->Fdo
, FALSE
);
672 KeLowerIrql(oldIrql
);
679 * If the packet was synchronous, write the final
680 * result back to the issuer's status buffer and
683 if (pkt
->SyncEventPtr
){
684 KeSetEvent(pkt
->SyncEventPtr
, 0, FALSE
);
685 pkt
->SyncEventPtr
= NULL
;
689 * Free the completed packet.
691 pkt
->OriginalIrp
= NULL
;
692 pkt
->InLowMemRetry
= FALSE
;
693 EnqueueFreeTransferPacket(pkt
->Fdo
, pkt
);
696 * Now that we have freed some resources,
697 * try again to send one of the previously deferred irps.
699 deferredIrp
= DequeueDeferredClientIrp(fdoData
);
701 DBGWARN(("... retrying deferred irp %xh.", deferredIrp
));
702 ServiceTransferRequest(pkt
->Fdo
, deferredIrp
);
705 ClassReleaseRemoveLock(Fdo
, (PIRP
)&uniqueAddr
);
708 return STATUS_MORE_PROCESSING_REQUIRED
;
712 * SetupEjectionTransferPacket
714 * Set up a transferPacket for a synchronous Ejection Control transfer.
716 VOID NTAPI
SetupEjectionTransferPacket( TRANSFER_PACKET
*Pkt
,
717 BOOLEAN PreventMediaRemoval
,
718 PKEVENT SyncEventPtr
,
721 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
726 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
728 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
729 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
730 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
731 Pkt
->Srb
.CdbLength
= 6;
732 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
733 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
734 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
735 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
737 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
738 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
739 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
741 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
742 pCdb
->MEDIA_REMOVAL
.OperationCode
= SCSIOP_MEDIUM_REMOVAL
;
743 pCdb
->MEDIA_REMOVAL
.Prevent
= PreventMediaRemoval
;
745 Pkt
->BufPtrCopy
= NULL
;
748 Pkt
->OriginalIrp
= OriginalIrp
;
749 Pkt
->NumRetries
= NUM_LOCKMEDIAREMOVAL_RETRIES
;
750 Pkt
->SyncEventPtr
= SyncEventPtr
;
751 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
755 * SetupModeSenseTransferPacket
757 * Set up a transferPacket for a synchronous Mode Sense transfer.
759 VOID NTAPI
SetupModeSenseTransferPacket(TRANSFER_PACKET
*Pkt
,
760 PKEVENT SyncEventPtr
,
761 PVOID ModeSenseBuffer
,
762 UCHAR ModeSenseBufferLen
,
766 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
771 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
773 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
774 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
775 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
776 Pkt
->Srb
.CdbLength
= 6;
777 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
778 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
779 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
780 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
781 Pkt
->Srb
.DataBuffer
= ModeSenseBuffer
;
782 Pkt
->Srb
.DataTransferLength
= ModeSenseBufferLen
;
784 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
785 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DATA_IN
);
786 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
787 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
789 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
790 pCdb
->MODE_SENSE
.OperationCode
= SCSIOP_MODE_SENSE
;
791 pCdb
->MODE_SENSE
.PageCode
= PageMode
;
792 pCdb
->MODE_SENSE
.AllocationLength
= (UCHAR
)ModeSenseBufferLen
;
794 Pkt
->BufPtrCopy
= ModeSenseBuffer
;
795 Pkt
->BufLenCopy
= ModeSenseBufferLen
;
797 Pkt
->OriginalIrp
= OriginalIrp
;
798 Pkt
->NumRetries
= NUM_MODESENSE_RETRIES
;
799 Pkt
->SyncEventPtr
= SyncEventPtr
;
800 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
804 * SetupDriveCapacityTransferPacket
806 * Set up a transferPacket for a synchronous Drive Capacity transfer.
808 VOID NTAPI
SetupDriveCapacityTransferPacket(TRANSFER_PACKET
*Pkt
,
809 PVOID ReadCapacityBuffer
,
810 ULONG ReadCapacityBufferLen
,
811 PKEVENT SyncEventPtr
,
814 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
817 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
819 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
820 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
821 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
822 Pkt
->Srb
.CdbLength
= 10;
823 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
824 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
825 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
826 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
827 Pkt
->Srb
.DataBuffer
= ReadCapacityBuffer
;
828 Pkt
->Srb
.DataTransferLength
= ReadCapacityBufferLen
;
830 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
831 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DATA_IN
);
832 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
833 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
835 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
836 pCdb
->CDB10
.OperationCode
= SCSIOP_READ_CAPACITY
;
838 Pkt
->BufPtrCopy
= ReadCapacityBuffer
;
839 Pkt
->BufLenCopy
= ReadCapacityBufferLen
;
841 Pkt
->OriginalIrp
= OriginalIrp
;
842 Pkt
->NumRetries
= NUM_DRIVECAPACITY_RETRIES
;
843 Pkt
->SyncEventPtr
= SyncEventPtr
;
844 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
849 * SetupSendStartUnitTransferPacket
851 * Set up a transferPacket for a synchronous Send Start Unit transfer.
853 VOID
SetupSendStartUnitTransferPacket( TRANSFER_PACKET
*Pkt
,
856 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
857 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
862 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
865 * Initialize the SRB.
866 * Use a very long timeout value to give the drive time to spin up.
868 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
869 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
870 Pkt
->Srb
.TimeOutValue
= START_UNIT_TIMEOUT
;
871 Pkt
->Srb
.CdbLength
= 6;
872 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
873 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
874 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
877 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
878 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_AUTOSENSE
);
879 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
881 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
882 pCdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
883 pCdb
->START_STOP
.Start
= 1;
884 pCdb
->START_STOP
.Immediate
= 0;
885 pCdb
->START_STOP
.LogicalUnitNumber
= 0;
887 Pkt
->OriginalIrp
= OriginalIrp
;
889 Pkt
->SyncEventPtr
= NULL
;
890 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;