3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Packet retry routines for CLASSPNP
28 * InterpretTransferPacketError
30 * Interpret the SRB error into a meaningful IRP status.
31 * ClassInterpretSenseInfo also may modify the SRB for the retry.
33 * Return TRUE iff packet should be retried.
35 BOOLEAN NTAPI
InterpretTransferPacketError(PTRANSFER_PACKET Pkt
)
37 BOOLEAN shouldRetry
= FALSE
;
38 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
41 * Interpret the error using the returned sense info first.
43 Pkt
->RetryIntervalSec
= 0;
44 if (pCdb
->MEDIA_REMOVAL
.OperationCode
== SCSIOP_MEDIUM_REMOVAL
){
46 * This is an Ejection Control SRB. Interpret its sense info specially.
48 shouldRetry
= ClassInterpretSenseInfo(
53 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
54 &Pkt
->Irp
->IoStatus
.Status
,
55 &Pkt
->RetryIntervalSec
);
58 * If the device is not ready, wait at least 2 seconds before retrying.
60 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
61 ASSERT(senseInfoBuffer
);
62 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
63 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
64 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
66 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
70 else if ((pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE
) ||
71 (pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE10
)){
73 * This is an Mode Sense SRB. Interpret its sense info specially.
75 shouldRetry
= ClassInterpretSenseInfo(
80 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
81 &Pkt
->Irp
->IoStatus
.Status
,
82 &Pkt
->RetryIntervalSec
);
85 * If the device is not ready, wait at least 2 seconds before retrying.
87 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
88 ASSERT(senseInfoBuffer
);
89 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
90 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
91 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
93 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
98 * Some special cases for mode sense.
100 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
103 else if (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_DATA_OVERRUN
){
106 * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
107 * underrun (i.e. success, and the buffer is longer than needed).
108 * So treat this as a success.
110 Pkt
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
111 InterlockedExchangeAdd((PLONG
)&Pkt
->OriginalIrp
->IoStatus
.Information
, (LONG
)Pkt
->Srb
.DataTransferLength
);
115 else if (pCdb
->CDB10
.OperationCode
== SCSIOP_READ_CAPACITY
){
117 * This is a Drive Capacity SRB. Interpret its sense info specially.
119 shouldRetry
= ClassInterpretSenseInfo(
124 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
125 &Pkt
->Irp
->IoStatus
.Status
,
126 &Pkt
->RetryIntervalSec
);
127 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
131 else if ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
132 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
)){
134 * This is a Read/Write Data packet.
136 PIO_STACK_LOCATION origCurrentSp
= IoGetCurrentIrpStackLocation(Pkt
->OriginalIrp
);
138 shouldRetry
= ClassInterpretSenseInfo(
141 origCurrentSp
->MajorFunction
,
143 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
144 &Pkt
->Irp
->IoStatus
.Status
,
145 &Pkt
->RetryIntervalSec
);
147 * Deal with some special cases.
149 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
151 * We are in extreme low-memory stress.
152 * We will retry in smaller chunks.
156 else if (TEST_FLAG(origCurrentSp
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
157 (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
)){
159 * We are still verifying a (possibly) reloaded disk/cdrom.
160 * So retry the request.
162 Pkt
->Irp
->IoStatus
.Status
= STATUS_IO_DEVICE_ERROR
;
167 DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG
)pCdb
->CDB10
.OperationCode
, Pkt
));
174 * RetryTransferPacket
176 * Retry sending a TRANSFER_PACKET.
178 * Return TRUE iff the packet is complete.
179 * (if so the status in pkt->irp is the final status).
181 BOOLEAN NTAPI
RetryTransferPacket(PTRANSFER_PACKET Pkt
)
185 DBGTRACE(ClassDebugTrace
, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt
, DBGGETSCSIOPSTR(&Pkt
->Srb
)));
187 ASSERT(Pkt
->NumRetries
> 0);
191 * Tone down performance on the retry.
192 * This increases the chance for success on the retry.
193 * We've seen instances of drives that fail consistently but then start working
194 * once this scale-down is applied.
196 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
197 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
198 CLEAR_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
199 Pkt
->Srb
.QueueTag
= SP_UNTAGGED
;
201 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
202 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
203 BOOLEAN isReadWrite
= ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
204 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
));
206 if (Pkt
->InLowMemRetry
|| !isReadWrite
){
208 * This should never happen.
209 * The memory manager guarantees that at least four pages will
210 * be available to allow forward progress in the port driver.
211 * So a one-page transfer should never fail with insufficient resources.
213 ASSERT(isReadWrite
&& !Pkt
->InLowMemRetry
);
218 * We are in low-memory stress.
219 * Start the low-memory retry state machine, which tries to
220 * resend the packet in little one-page chunks.
222 InitLowMemRetry( Pkt
,
225 Pkt
->TargetLocationCopy
);
226 StepLowMemRetry(Pkt
);
232 * Retry the packet by simply resending it after a delay.
233 * Put the packet back in the pending queue and
234 * schedule a timer to retry the transfer.
236 * Do not call SetupReadWriteTransferPacket again because:
237 * (1) The minidriver may have set some bits
238 * in the SRB that it needs again and
239 * (2) doing so would reset numRetries.
241 * BECAUSE we do not call SetupReadWriteTransferPacket again,
242 * we have to reset a couple fields in the SRB that
243 * some miniports overwrite when they fail an SRB.
246 Pkt
->Srb
.DataBuffer
= Pkt
->BufPtrCopy
;
247 Pkt
->Srb
.DataTransferLength
= Pkt
->BufLenCopy
;
249 if (Pkt
->RetryIntervalSec
== 0){
251 * Always delay by at least a little when retrying.
252 * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
254 LARGE_INTEGER timerPeriod
;
256 timerPeriod
.HighPart
= -1;
257 timerPeriod
.LowPart
= -(LONG
)((ULONG
)MINIMUM_RETRY_UNITS
*KeQueryTimeIncrement());
258 KeInitializeTimer(&Pkt
->RetryTimer
);
259 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
260 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
263 LARGE_INTEGER timerPeriod
;
265 ASSERT(Pkt
->RetryIntervalSec
< 100); // sanity check
266 timerPeriod
.HighPart
= -1;
267 timerPeriod
.LowPart
= Pkt
->RetryIntervalSec
*-10000000;
268 KeInitializeTimer(&Pkt
->RetryTimer
);
269 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
270 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
278 VOID NTAPI
TransferPacketRetryTimerDpc(IN PKDPC Dpc
,
279 IN PVOID DeferredContext
,
280 IN PVOID SystemArgument1
,
281 IN PVOID SystemArgument2
)
283 PTRANSFER_PACKET pkt
= (PTRANSFER_PACKET
)DeferredContext
;
284 SubmitTransferPacket(pkt
);
287 VOID NTAPI
InitLowMemRetry(PTRANSFER_PACKET Pkt
, PVOID BufPtr
, ULONG Len
, LARGE_INTEGER TargetLocation
)
290 ASSERT(!Pkt
->InLowMemRetry
);
291 Pkt
->InLowMemRetry
= TRUE
;
292 Pkt
->LowMemRetry_remainingBufPtr
= BufPtr
;
293 Pkt
->LowMemRetry_remainingBufLen
= Len
;
294 Pkt
->LowMemRetry_nextChunkTargetLocation
= TargetLocation
;
300 * During extreme low-memory stress, this function retries
301 * a packet in small one-page chunks, sent serially.
303 * Returns TRUE iff the packet is done.
305 BOOLEAN NTAPI
StepLowMemRetry(PTRANSFER_PACKET Pkt
)
309 if (Pkt
->LowMemRetry_remainingBufLen
== 0){
314 ULONG bytesToNextPageBoundary
;
317 * Make sure the little chunk we send is <= a page length
318 * AND that it does not cross any page boundaries.
320 bytesToNextPageBoundary
= PAGE_SIZE
-(ULONG
)((ULONG_PTR
)Pkt
->LowMemRetry_remainingBufPtr
%PAGE_SIZE
);
321 thisChunkLen
= MIN(Pkt
->LowMemRetry_remainingBufLen
, bytesToNextPageBoundary
);
324 * Set up the transfer packet for the new little chunk.
325 * This will reset numRetries so that we retry each chunk as required.
327 SetupReadWriteTransferPacket(Pkt
,
328 Pkt
->LowMemRetry_remainingBufPtr
,
330 Pkt
->LowMemRetry_nextChunkTargetLocation
,
333 Pkt
->LowMemRetry_remainingBufPtr
+= thisChunkLen
;
334 Pkt
->LowMemRetry_remainingBufLen
-= thisChunkLen
;
335 Pkt
->LowMemRetry_nextChunkTargetLocation
.QuadPart
+= thisChunkLen
;
337 SubmitTransferPacket(Pkt
);