3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Packet routines for CLASSPNP
28 #pragma alloc_text(PAGE, InitializeTransferPackets)
29 #pragma alloc_text(PAGE, DestroyAllTransferPackets)
30 #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
31 #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
35 ULONG MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
36 ULONG MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
40 * InitializeTransferPackets
42 * Allocate/initialize TRANSFER_PACKETs and related resources.
44 NTSTATUS
InitializeTransferPackets(PDEVICE_OBJECT Fdo
)
46 PCOMMON_DEVICE_EXTENSION commonExt
= Fdo
->DeviceExtension
;
47 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
48 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
49 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc
= commonExt
->PartitionZeroExtension
->AdapterDescriptor
;
51 NTSTATUS status
= STATUS_SUCCESS
;
56 * Precompute the maximum transfer length
58 ASSERT(adapterDesc
->MaximumTransferLength
);
59 ASSERT(adapterDesc
->MaximumPhysicalPages
);
60 hwMaxPages
= adapterDesc
->MaximumPhysicalPages
? adapterDesc
->MaximumPhysicalPages
-1 : 0;
62 #if defined(_AMD64_SIMULATOR_)
65 // The simulator appears to have a problem with large transfers.
74 fdoData
->HwMaxXferLen
= MIN(adapterDesc
->MaximumTransferLength
, hwMaxPages
<< PAGE_SHIFT
);
75 fdoData
->HwMaxXferLen
= MAX(fdoData
->HwMaxXferLen
, PAGE_SIZE
);
77 fdoData
->NumTotalTransferPackets
= 0;
78 fdoData
->NumFreeTransferPackets
= 0;
79 InitializeSListHead(&fdoData
->FreeTransferPacketsList
);
80 InitializeListHead(&fdoData
->AllTransferPacketsList
);
81 InitializeListHead(&fdoData
->DeferredClientIrpList
);
84 * Set the packet threshold numbers based on the Windows SKU.
86 if (ExVerifySuite(Personal
)){
87 // this is Windows Personal
88 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
89 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
91 else if (ExVerifySuite(Enterprise
) || ExVerifySuite(DataCenter
)){
92 // this is Advanced Server or Datacenter
93 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise
;
94 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise
;
96 else if (ExVerifySuite(TerminalServer
)){
97 // this is standard Server or Pro with terminal server
98 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Server
;
99 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Server
;
102 // this is Professional without terminal server
103 MinWorkingSetTransferPackets
= MIN_WORKINGSET_TRANSFER_PACKETS_Consumer
;
104 MaxWorkingSetTransferPackets
= MAX_WORKINGSET_TRANSFER_PACKETS_Consumer
;
107 while (fdoData
->NumFreeTransferPackets
< MIN_INITIAL_TRANSFER_PACKETS
){
108 PTRANSFER_PACKET pkt
= NewTransferPacket(Fdo
);
110 InterlockedIncrement(&fdoData
->NumTotalTransferPackets
);
111 EnqueueFreeTransferPacket(Fdo
, pkt
);
114 status
= STATUS_INSUFFICIENT_RESOURCES
;
118 fdoData
->DbgPeakNumTransferPackets
= fdoData
->NumTotalTransferPackets
;
121 * Pre-initialize our SCSI_REQUEST_BLOCK template with all
122 * the constant fields. This will save a little time for each xfer.
123 * NOTE: a CdbLength field of 10 may not always be appropriate
125 RtlZeroMemory(&fdoData
->SrbTemplate
, sizeof(SCSI_REQUEST_BLOCK
));
126 fdoData
->SrbTemplate
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
127 fdoData
->SrbTemplate
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
128 fdoData
->SrbTemplate
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
129 fdoData
->SrbTemplate
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
130 fdoData
->SrbTemplate
.CdbLength
= 10;
136 VOID
DestroyAllTransferPackets(PDEVICE_OBJECT Fdo
)
138 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
139 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
140 TRANSFER_PACKET
*pkt
;
144 ASSERT(IsListEmpty(&fdoData
->DeferredClientIrpList
));
146 while (pkt
= DequeueFreeTransferPacket(Fdo
, FALSE
)){
147 DestroyTransferPacket(pkt
);
148 InterlockedDecrement(&fdoData
->NumTotalTransferPackets
);
151 ASSERT(fdoData
->NumTotalTransferPackets
== 0);
155 PTRANSFER_PACKET
NewTransferPacket(PDEVICE_OBJECT Fdo
)
157 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
158 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
159 PTRANSFER_PACKET newPkt
;
161 newPkt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(TRANSFER_PACKET
), 'pnPC');
163 RtlZeroMemory(newPkt
, sizeof(TRANSFER_PACKET
)); // just to be sure
166 * Allocate resources for the packet.
168 newPkt
->Irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
175 * Enqueue the packet in our static AllTransferPacketsList
176 * (just so we can find it during debugging if its stuck somewhere).
178 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
179 InsertTailList(&fdoData
->AllTransferPacketsList
, &newPkt
->AllPktsListEntry
);
180 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
193 * DestroyTransferPacket
196 VOID
DestroyTransferPacket(PTRANSFER_PACKET Pkt
)
198 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
199 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
202 ASSERT(!Pkt
->SlistEntry
.Next
);
203 ASSERT(!Pkt
->OriginalIrp
);
205 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
208 * Delete the packet from our all-packets queue.
210 ASSERT(!IsListEmpty(&Pkt
->AllPktsListEntry
));
211 ASSERT(!IsListEmpty(&fdoData
->AllTransferPacketsList
));
212 RemoveEntryList(&Pkt
->AllPktsListEntry
);
213 InitializeListHead(&Pkt
->AllPktsListEntry
);
215 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
222 VOID
EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo
, PTRANSFER_PACKET Pkt
)
224 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
225 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
229 ASSERT(!Pkt
->SlistEntry
.Next
);
231 InterlockedPushEntrySList(&fdoData
->FreeTransferPacketsList
, &Pkt
->SlistEntry
);
232 newNumPkts
= InterlockedIncrement(&fdoData
->NumFreeTransferPackets
);
233 ASSERT(newNumPkts
<= fdoData
->NumTotalTransferPackets
);
236 * If the total number of packets is larger than MinWorkingSetTransferPackets,
237 * that means that we've been in stress. If all those packets are now
238 * free, then we are now out of stress and can free the extra packets.
239 * Free down to MaxWorkingSetTransferPackets immediately, and
240 * down to MinWorkingSetTransferPackets lazily (one at a time).
242 if (fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
){
245 * 1. Immediately snap down to our UPPER threshold.
247 if (fdoData
->NumTotalTransferPackets
> MaxWorkingSetTransferPackets
){
248 SINGLE_LIST_ENTRY pktList
;
249 PSINGLE_LIST_ENTRY slistEntry
;
250 PTRANSFER_PACKET pktToDelete
;
252 DBGTRACE(ClassDebugTrace
, ("Exiting stress, block freeing (%d-%d) packets.", fdoData
->NumTotalTransferPackets
, MaxWorkingSetTransferPackets
));
255 * Check the counter again with lock held. This eliminates a race condition
256 * while still allowing us to not grab the spinlock in the common codepath.
258 * Note that the spinlock does not synchronize with threads dequeuing free
259 * packets to send (DequeueFreeTransferPacket does that with a lightweight
260 * interlocked exchange); the spinlock prevents multiple threads in this function
261 * from deciding to free too many extra packets at once.
263 SimpleInitSlistHdr(&pktList
);
264 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
265 while ((fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
) &&
266 (fdoData
->NumTotalTransferPackets
> MaxWorkingSetTransferPackets
)){
268 pktToDelete
= DequeueFreeTransferPacket(Fdo
, FALSE
);
270 SimplePushSlist(&pktList
, &pktToDelete
->SlistEntry
);
271 InterlockedDecrement(&fdoData
->NumTotalTransferPackets
);
274 DBGTRACE(ClassDebugTrace
, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets
, Fdo
, fdoData
->NumTotalTransferPackets
));
278 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
280 while (slistEntry
= SimplePopSlist(&pktList
)){
281 pktToDelete
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
282 DestroyTransferPacket(pktToDelete
);
288 * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time).
290 if (fdoData
->NumTotalTransferPackets
> MinWorkingSetTransferPackets
){
292 * Check the counter again with lock held. This eliminates a race condition
293 * while still allowing us to not grab the spinlock in the common codepath.
295 * Note that the spinlock does not synchronize with threads dequeuing free
296 * packets to send (DequeueFreeTransferPacket does that with a lightweight
297 * interlocked exchange); the spinlock prevents multiple threads in this function
298 * from deciding to free too many extra packets at once.
300 PTRANSFER_PACKET pktToDelete
= NULL
;
302 DBGTRACE(ClassDebugTrace
, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData
->NumTotalTransferPackets
, MinWorkingSetTransferPackets
));
304 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
305 if ((fdoData
->NumFreeTransferPackets
>= fdoData
->NumTotalTransferPackets
) &&
306 (fdoData
->NumTotalTransferPackets
> MinWorkingSetTransferPackets
)){
308 pktToDelete
= DequeueFreeTransferPacket(Fdo
, FALSE
);
310 InterlockedDecrement(&fdoData
->NumTotalTransferPackets
);
313 DBGTRACE(ClassDebugTrace
, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets
, Fdo
, fdoData
->NumTotalTransferPackets
));
316 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
319 DestroyTransferPacket(pktToDelete
);
328 PTRANSFER_PACKET
DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo
, BOOLEAN AllocIfNeeded
)
330 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
331 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
332 PTRANSFER_PACKET pkt
;
333 PSINGLE_LIST_ENTRY slistEntry
;
336 slistEntry
= InterlockedPopEntrySList(&fdoData
->FreeTransferPacketsList
);
338 slistEntry
->Next
= NULL
;
339 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
340 ASSERT(fdoData
->NumFreeTransferPackets
> 0);
341 InterlockedDecrement(&fdoData
->NumFreeTransferPackets
);
346 * We are in stress and have run out of lookaside packets.
347 * In order to service the current transfer,
348 * allocate an extra packet.
349 * We will free it lazily when we are out of stress.
351 pkt
= NewTransferPacket(Fdo
);
353 InterlockedIncrement(&fdoData
->NumTotalTransferPackets
);
354 fdoData
->DbgPeakNumTransferPackets
= max(fdoData
->DbgPeakNumTransferPackets
, fdoData
->NumTotalTransferPackets
);
357 DBGWARN(("DequeueFreeTransferPacket: packet allocation failed"));
371 * SetupReadWriteTransferPacket
373 * This function is called once to set up the first attempt to send a packet.
374 * It is not called before a retry, as SRB fields may be modified for the retry.
376 * Set up the Srb of the TRANSFER_PACKET for the transfer.
377 * The Irp is set up in SubmitTransferPacket because it must be reset
378 * for each packet submission.
380 VOID
SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt
,
383 LARGE_INTEGER DiskLocation
,
386 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
387 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
388 PIO_STACK_LOCATION origCurSp
= IoGetCurrentIrpStackLocation(OriginalIrp
);
389 UCHAR majorFunc
= origCurSp
->MajorFunction
;
390 ULONG logicalBlockAddr
;
391 ULONG numTransferBlocks
;
394 logicalBlockAddr
= (ULONG
)Int64ShrlMod32(DiskLocation
.QuadPart
, fdoExt
->SectorShift
);
395 numTransferBlocks
= Len
>> fdoExt
->SectorShift
;
398 * Slap the constant SRB fields in from our pre-initialized template.
399 * We'll then only have to fill in the unique fields for this transfer.
400 * Tell lower drivers to sort the SRBs by the logical block address
401 * so that disk seeks are minimized.
403 Pkt
->Srb
= fdoData
->SrbTemplate
; // copies _contents_ of SRB blocks
404 Pkt
->Srb
.DataBuffer
= Buf
;
405 Pkt
->Srb
.DataTransferLength
= Len
;
406 Pkt
->Srb
.QueueSortKey
= logicalBlockAddr
;
407 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
408 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
409 Pkt
->Srb
.TimeOutValue
= (Len
/0x10000) + ((Len
%0x10000) ? 1 : 0);
410 Pkt
->Srb
.TimeOutValue
*= fdoExt
->TimeOutValue
;
413 * Arrange values in CDB in big-endian format.
415 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
416 pCdb
->CDB10
.LogicalBlockByte0
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte3
;
417 pCdb
->CDB10
.LogicalBlockByte1
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte2
;
418 pCdb
->CDB10
.LogicalBlockByte2
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte1
;
419 pCdb
->CDB10
.LogicalBlockByte3
= ((PFOUR_BYTE
)&logicalBlockAddr
)->Byte0
;
420 pCdb
->CDB10
.TransferBlocksMsb
= ((PFOUR_BYTE
)&numTransferBlocks
)->Byte1
;
421 pCdb
->CDB10
.TransferBlocksLsb
= ((PFOUR_BYTE
)&numTransferBlocks
)->Byte0
;
422 pCdb
->CDB10
.OperationCode
= (majorFunc
==IRP_MJ_READ
) ? SCSIOP_READ
: SCSIOP_WRITE
;
425 * Set SRB and IRP flags
427 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
428 if (TEST_FLAG(OriginalIrp
->Flags
, IRP_PAGING_IO
) ||
429 TEST_FLAG(OriginalIrp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
)){
430 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_CLASS_FLAGS_PAGING
);
432 SET_FLAG(Pkt
->Srb
.SrbFlags
, (majorFunc
==IRP_MJ_READ
) ? SRB_FLAGS_DATA_IN
: SRB_FLAGS_DATA_OUT
);
435 * Allow caching only if this is not a write-through request.
436 * If write-through and caching is enabled on the device, force
439 if (TEST_FLAG(origCurSp
->Flags
, SL_WRITE_THROUGH
)){
440 if (TEST_FLAG(fdoExt
->DeviceFlags
, DEV_WRITE_CACHE
)){
441 pCdb
->CDB10
.ForceUnitAccess
= TRUE
;
445 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_ADAPTER_CACHE_ENABLE
);
449 * Remember the buf and len in the SRB because miniports
450 * can overwrite SRB.DataTransferLength and we may need it again
453 Pkt
->BufPtrCopy
= Buf
;
454 Pkt
->BufLenCopy
= Len
;
455 Pkt
->TargetLocationCopy
= DiskLocation
;
457 Pkt
->OriginalIrp
= OriginalIrp
;
458 Pkt
->NumRetries
= MAXIMUM_RETRIES
;
459 Pkt
->SyncEventPtr
= NULL
;
460 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= TRUE
;
465 * SubmitTransferPacket
467 * Set up the IRP for the TRANSFER_PACKET submission and send it down.
469 VOID
SubmitTransferPacket(PTRANSFER_PACKET Pkt
)
471 PCOMMON_DEVICE_EXTENSION commonExtension
= Pkt
->Fdo
->DeviceExtension
;
472 PDEVICE_OBJECT nextDevObj
= commonExtension
->LowerDeviceObject
;
473 PIO_STACK_LOCATION nextSp
= IoGetNextIrpStackLocation(Pkt
->Irp
);
475 ASSERT(Pkt
->Irp
->CurrentLocation
== Pkt
->Irp
->StackCount
+1);
478 * Attach the SRB to the IRP.
479 * The reused IRP's stack location has to be rewritten for each retry
480 * call because IoCompleteRequest clears the stack locations.
482 IoReuseIrp(Pkt
->Irp
, STATUS_NOT_SUPPORTED
);
483 nextSp
->MajorFunction
= IRP_MJ_SCSI
;
484 nextSp
->Parameters
.Scsi
.Srb
= &Pkt
->Srb
;
485 Pkt
->Srb
.ScsiStatus
= Pkt
->Srb
.SrbStatus
= 0;
486 if (Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
){
488 * Only dereference the "original IRP"'s stack location
489 * if its a real client irp (as opposed to a static irp
490 * we're using just for result status for one of the non-IO scsi commands).
492 * For read/write, propagate the storage-specific IRP stack location flags
493 * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
495 PIO_STACK_LOCATION origCurSp
= IoGetCurrentIrpStackLocation(Pkt
->OriginalIrp
);
496 nextSp
->Flags
= origCurSp
->Flags
;
500 * Write MDL address to new IRP. In the port driver the SRB DataBuffer
501 * field is used as the actual buffer pointer within the MDL,
502 * so the same MDL can be used for each partial transfer.
503 * This saves having to build a new MDL for each partial transfer.
505 Pkt
->Irp
->MdlAddress
= Pkt
->OriginalIrp
->MdlAddress
;
507 IoSetCompletionRoutine(Pkt
->Irp
, TransferPktComplete
, Pkt
, TRUE
, TRUE
, TRUE
);
508 IoCallDriver(nextDevObj
, Pkt
->Irp
);
512 NTSTATUS
TransferPktComplete(IN PDEVICE_OBJECT NullFdo
, IN PIRP Irp
, IN PVOID Context
)
514 PTRANSFER_PACKET pkt
= (PTRANSFER_PACKET
)Context
;
515 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= pkt
->Fdo
->DeviceExtension
;
516 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
517 PIO_STACK_LOCATION origCurrentSp
= IoGetCurrentIrpStackLocation(pkt
->OriginalIrp
);
518 BOOLEAN packetDone
= FALSE
;
521 * Put all the assertions and spew in here so we don't have to look at them.
523 DBGCHECKRETURNEDPKT(pkt
);
525 if (SRB_STATUS(pkt
->Srb
.SrbStatus
) == SRB_STATUS_SUCCESS
){
527 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
530 * The port driver should not have allocated a sense buffer
531 * if the SRB succeeded.
533 ASSERT(!PORT_ALLOCATED_SENSE(fdoExt
, &pkt
->Srb
));
536 * Add this packet's transferred length to the original IRP's.
538 InterlockedExchangeAdd((PLONG
)&pkt
->OriginalIrp
->IoStatus
.Information
,
539 (LONG
)pkt
->Srb
.DataTransferLength
);
541 if (pkt
->InLowMemRetry
){
542 packetDone
= StepLowMemRetry(pkt
);
551 * The packet failed. We may retry it if possible.
556 * Make sure IRP status matches SRB error status (since we propagate it).
558 if (NT_SUCCESS(Irp
->IoStatus
.Status
)){
559 Irp
->IoStatus
.Status
= STATUS_UNSUCCESSFUL
;
563 * Interpret the SRB error (to a meaningful IRP status)
564 * and determine if we should retry this packet.
565 * This call looks at the returned SENSE info to figure out what to do.
567 shouldRetry
= InterpretTransferPacketError(pkt
);
570 * Sometimes the port driver can allocates a new 'sense' buffer
571 * to report transfer errors, e.g. when the default sense buffer
572 * is too small. If so, it is up to us to free it.
573 * Now that we're done interpreting the sense info, free it if appropriate.
575 if (PORT_ALLOCATED_SENSE(fdoExt
, &pkt
->Srb
)) {
576 DBGTRACE(ClassDebugSenseInfo
, ("Freeing port-allocated sense buffer for pkt %ph.", pkt
));
577 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt
, &pkt
->Srb
);
578 pkt
->Srb
.SenseInfoBuffer
= &pkt
->SrbErrorSenseData
;
579 pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
583 * If the SRB queue is locked-up, release it.
584 * Do this after calling the error handler.
586 if (pkt
->Srb
.SrbStatus
& SRB_STATUS_QUEUE_FROZEN
){
587 ClassReleaseQueue(pkt
->Fdo
);
590 if (shouldRetry
&& (pkt
->NumRetries
> 0)){
591 packetDone
= RetryTransferPacket(pkt
);
600 * If the packet is completed, put it back in the free list.
601 * If it is the last packet servicing the original request, complete the original irp.
604 LONG numPacketsRemaining
;
606 PDEVICE_OBJECT Fdo
= pkt
->Fdo
;
610 * In case a remove is pending, bump the lock count so we don't get freed
611 * right after we complete the original irp.
613 ClassAcquireRemoveLock(Fdo
, (PIRP
)&uniqueAddr
);
616 * The original IRP should get an error code
617 * if any one of the packets failed.
619 if (!NT_SUCCESS(Irp
->IoStatus
.Status
)){
620 pkt
->OriginalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
623 * If the original I/O originated in user space (i.e. it is thread-queued),
624 * and the error is user-correctable (e.g. media is missing, for removable media),
626 * Since this is only one of possibly several packets completing for the original IRP,
627 * we may do this more than once for a single request. That's ok; this allows
628 * us to test each returned status with IoIsErrorUserInduced().
630 if (IoIsErrorUserInduced(Irp
->IoStatus
.Status
) &&
631 pkt
->CompleteOriginalIrpWhenLastPacketCompletes
&&
632 pkt
->OriginalIrp
->Tail
.Overlay
.Thread
){
634 IoSetHardErrorOrVerifyDevice(pkt
->OriginalIrp
, pkt
->Fdo
);
639 * We use a field in the original IRP to count
640 * down the transfer pieces as they complete.
642 numPacketsRemaining
= InterlockedDecrement(
643 (PLONG
)&pkt
->OriginalIrp
->Tail
.Overlay
.DriverContext
[0]);
645 if (numPacketsRemaining
> 0){
647 * More transfer pieces remain for the original request.
648 * Wait for them to complete before completing the original irp.
654 * All the transfer pieces are done.
655 * Complete the original irp if appropriate.
657 ASSERT(numPacketsRemaining
== 0);
658 if (pkt
->CompleteOriginalIrpWhenLastPacketCompletes
){
659 if (NT_SUCCESS(pkt
->OriginalIrp
->IoStatus
.Status
)){
660 ASSERT((ULONG
)pkt
->OriginalIrp
->IoStatus
.Information
== origCurrentSp
->Parameters
.Read
.Length
);
661 ClasspPerfIncrementSuccessfulIo(fdoExt
);
663 ClassReleaseRemoveLock(pkt
->Fdo
, pkt
->OriginalIrp
);
665 ClassCompleteRequest(pkt
->Fdo
, pkt
->OriginalIrp
, IO_DISK_INCREMENT
);
668 * We may have been called by one of the class drivers (e.g. cdrom)
669 * via the legacy API ClassSplitRequest.
670 * This is the only case for which the packet engine is called for an FDO
671 * with a StartIo routine; in that case, we have to call IoStartNextPacket
672 * now that the original irp has been completed.
674 if (fdoExt
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
675 if (TEST_FLAG(pkt
->Srb
.SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
)){
676 DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)"));
680 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
681 IoStartNextPacket(pkt
->Fdo
, FALSE
);
682 KeLowerIrql(oldIrql
);
689 * If the packet was synchronous, write the final
690 * result back to the issuer's status buffer and
693 if (pkt
->SyncEventPtr
){
694 KeSetEvent(pkt
->SyncEventPtr
, 0, FALSE
);
695 pkt
->SyncEventPtr
= NULL
;
699 * Free the completed packet.
701 pkt
->OriginalIrp
= NULL
;
702 pkt
->InLowMemRetry
= FALSE
;
703 EnqueueFreeTransferPacket(pkt
->Fdo
, pkt
);
706 * Now that we have freed some resources,
707 * try again to send one of the previously deferred irps.
709 deferredIrp
= DequeueDeferredClientIrp(fdoData
);
711 DBGWARN(("... retrying deferred irp %xh.", deferredIrp
));
712 ServiceTransferRequest(pkt
->Fdo
, deferredIrp
);
715 ClassReleaseRemoveLock(Fdo
, (PIRP
)&uniqueAddr
);
718 return STATUS_MORE_PROCESSING_REQUIRED
;
723 * SetupEjectionTransferPacket
725 * Set up a transferPacket for a synchronous Ejection Control transfer.
727 VOID
SetupEjectionTransferPacket( TRANSFER_PACKET
*Pkt
,
728 BOOLEAN PreventMediaRemoval
,
729 PKEVENT SyncEventPtr
,
732 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
733 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
738 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
740 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
741 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
742 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
743 Pkt
->Srb
.CdbLength
= 6;
744 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
745 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
746 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
747 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
749 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
750 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
751 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
753 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
754 pCdb
->MEDIA_REMOVAL
.OperationCode
= SCSIOP_MEDIUM_REMOVAL
;
755 pCdb
->MEDIA_REMOVAL
.Prevent
= PreventMediaRemoval
;
757 Pkt
->BufPtrCopy
= NULL
;
760 Pkt
->OriginalIrp
= OriginalIrp
;
761 Pkt
->NumRetries
= NUM_LOCKMEDIAREMOVAL_RETRIES
;
762 Pkt
->SyncEventPtr
= SyncEventPtr
;
763 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
768 * SetupModeSenseTransferPacket
770 * Set up a transferPacket for a synchronous Mode Sense transfer.
772 VOID
SetupModeSenseTransferPacket( TRANSFER_PACKET
*Pkt
,
773 PKEVENT SyncEventPtr
,
774 PVOID ModeSenseBuffer
,
775 UCHAR ModeSenseBufferLen
,
779 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
780 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
785 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
787 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
788 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
789 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
790 Pkt
->Srb
.CdbLength
= 6;
791 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
792 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
793 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
794 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
795 Pkt
->Srb
.DataBuffer
= ModeSenseBuffer
;
796 Pkt
->Srb
.DataTransferLength
= ModeSenseBufferLen
;
798 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
799 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DATA_IN
);
800 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
801 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
803 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
804 pCdb
->MODE_SENSE
.OperationCode
= SCSIOP_MODE_SENSE
;
805 pCdb
->MODE_SENSE
.PageCode
= PageMode
;
806 pCdb
->MODE_SENSE
.AllocationLength
= (UCHAR
)ModeSenseBufferLen
;
808 Pkt
->BufPtrCopy
= ModeSenseBuffer
;
809 Pkt
->BufLenCopy
= ModeSenseBufferLen
;
811 Pkt
->OriginalIrp
= OriginalIrp
;
812 Pkt
->NumRetries
= NUM_MODESENSE_RETRIES
;
813 Pkt
->SyncEventPtr
= SyncEventPtr
;
814 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
819 * SetupDriveCapacityTransferPacket
821 * Set up a transferPacket for a synchronous Drive Capacity transfer.
823 VOID
SetupDriveCapacityTransferPacket( TRANSFER_PACKET
*Pkt
,
824 PVOID ReadCapacityBuffer
,
825 ULONG ReadCapacityBufferLen
,
826 PKEVENT SyncEventPtr
,
829 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
830 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
833 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
835 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
836 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
837 Pkt
->Srb
.QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
838 Pkt
->Srb
.CdbLength
= 10;
839 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
840 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
841 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
842 Pkt
->Srb
.TimeOutValue
= fdoExt
->TimeOutValue
;
843 Pkt
->Srb
.DataBuffer
= ReadCapacityBuffer
;
844 Pkt
->Srb
.DataTransferLength
= ReadCapacityBufferLen
;
846 Pkt
->Srb
.SrbFlags
= fdoExt
->SrbFlags
;
847 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DATA_IN
);
848 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
849 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
851 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
852 pCdb
->CDB10
.OperationCode
= SCSIOP_READ_CAPACITY
;
854 Pkt
->BufPtrCopy
= ReadCapacityBuffer
;
855 Pkt
->BufLenCopy
= ReadCapacityBufferLen
;
857 Pkt
->OriginalIrp
= OriginalIrp
;
858 Pkt
->NumRetries
= NUM_DRIVECAPACITY_RETRIES
;
859 Pkt
->SyncEventPtr
= SyncEventPtr
;
860 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;
866 * SetupSendStartUnitTransferPacket
868 * Set up a transferPacket for a synchronous Send Start Unit transfer.
870 VOID
SetupSendStartUnitTransferPacket( TRANSFER_PACKET
*Pkt
,
873 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Pkt
->Fdo
->DeviceExtension
;
874 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
879 RtlZeroMemory(&Pkt
->Srb
, sizeof(SCSI_REQUEST_BLOCK
));
882 * Initialize the SRB.
883 * Use a very long timeout value to give the drive time to spin up.
885 Pkt
->Srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
886 Pkt
->Srb
.Function
= SRB_FUNCTION_EXECUTE_SCSI
;
887 Pkt
->Srb
.TimeOutValue
= START_UNIT_TIMEOUT
;
888 Pkt
->Srb
.CdbLength
= 6;
889 Pkt
->Srb
.OriginalRequest
= Pkt
->Irp
;
890 Pkt
->Srb
.SenseInfoBuffer
= &Pkt
->SrbErrorSenseData
;
891 Pkt
->Srb
.SenseInfoBufferLength
= sizeof(SENSE_DATA
);
894 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
895 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_AUTOSENSE
);
896 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
898 pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
899 pCdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
900 pCdb
->START_STOP
.Start
= 1;
901 pCdb
->START_STOP
.Immediate
= 0;
902 pCdb
->START_STOP
.LogicalUnitNumber
= 0;
904 Pkt
->OriginalIrp
= OriginalIrp
;
906 Pkt
->SyncEventPtr
= NULL
;
907 Pkt
->CompleteOriginalIrpWhenLastPacketCompletes
= FALSE
;