3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Packet retry routines for CLASSPNP
27 * InterpretTransferPacketError
29 * Interpret the SRB error into a meaningful IRP status.
30 * ClassInterpretSenseInfo also may modify the SRB for the retry.
32 * Return TRUE iff packet should be retried.
34 BOOLEAN NTAPI
InterpretTransferPacketError(PTRANSFER_PACKET Pkt
)
36 BOOLEAN shouldRetry
= FALSE
;
37 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
40 * Interpret the error using the returned sense info first.
42 Pkt
->RetryIntervalSec
= 0;
43 if (pCdb
->MEDIA_REMOVAL
.OperationCode
== SCSIOP_MEDIUM_REMOVAL
){
45 * This is an Ejection Control SRB. Interpret its sense info specially.
47 shouldRetry
= ClassInterpretSenseInfo(
52 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
53 &Pkt
->Irp
->IoStatus
.Status
,
54 &Pkt
->RetryIntervalSec
);
57 * If the device is not ready, wait at least 2 seconds before retrying.
59 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
60 ASSERT(senseInfoBuffer
);
61 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
62 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
63 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
65 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
69 else if ((pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE
) ||
70 (pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE10
)){
72 * This is an Mode Sense SRB. Interpret its sense info specially.
74 shouldRetry
= ClassInterpretSenseInfo(
79 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
80 &Pkt
->Irp
->IoStatus
.Status
,
81 &Pkt
->RetryIntervalSec
);
84 * If the device is not ready, wait at least 2 seconds before retrying.
86 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
87 ASSERT(senseInfoBuffer
);
88 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
89 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
90 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
92 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
97 * Some special cases for mode sense.
99 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
102 else if (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_DATA_OVERRUN
){
105 * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
106 * underrun (i.e. success, and the buffer is longer than needed).
107 * So treat this as a success.
109 Pkt
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
110 InterlockedExchangeAdd((PLONG
)&Pkt
->OriginalIrp
->IoStatus
.Information
, (LONG
)Pkt
->Srb
.DataTransferLength
);
114 else if (pCdb
->CDB10
.OperationCode
== SCSIOP_READ_CAPACITY
){
116 * This is a Drive Capacity SRB. Interpret its sense info specially.
118 shouldRetry
= ClassInterpretSenseInfo(
123 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
124 &Pkt
->Irp
->IoStatus
.Status
,
125 &Pkt
->RetryIntervalSec
);
126 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
130 else if ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
131 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
)){
133 * This is a Read/Write Data packet.
135 PIO_STACK_LOCATION origCurrentSp
= IoGetCurrentIrpStackLocation(Pkt
->OriginalIrp
);
137 shouldRetry
= ClassInterpretSenseInfo(
140 origCurrentSp
->MajorFunction
,
142 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
143 &Pkt
->Irp
->IoStatus
.Status
,
144 &Pkt
->RetryIntervalSec
);
146 * Deal with some special cases.
148 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
150 * We are in extreme low-memory stress.
151 * We will retry in smaller chunks.
155 else if (TEST_FLAG(origCurrentSp
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
156 (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
)){
158 * We are still verifying a (possibly) reloaded disk/cdrom.
159 * So retry the request.
161 Pkt
->Irp
->IoStatus
.Status
= STATUS_IO_DEVICE_ERROR
;
166 DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG
)pCdb
->CDB10
.OperationCode
, Pkt
));
173 * RetryTransferPacket
175 * Retry sending a TRANSFER_PACKET.
177 * Return TRUE iff the packet is complete.
178 * (if so the status in pkt->irp is the final status).
180 BOOLEAN NTAPI
RetryTransferPacket(PTRANSFER_PACKET Pkt
)
184 DBGTRACE(ClassDebugTrace
, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt
, DBGGETSCSIOPSTR(&Pkt
->Srb
)));
186 ASSERT(Pkt
->NumRetries
> 0);
190 * Tone down performance on the retry.
191 * This increases the chance for success on the retry.
192 * We've seen instances of drives that fail consistently but then start working
193 * once this scale-down is applied.
195 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
196 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
197 CLEAR_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
198 Pkt
->Srb
.QueueTag
= SP_UNTAGGED
;
200 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
201 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
202 BOOLEAN isReadWrite
= ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
203 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
));
205 if (Pkt
->InLowMemRetry
|| !isReadWrite
){
207 * This should never happen.
208 * The memory manager guarantees that at least four pages will
209 * be available to allow forward progress in the port driver.
210 * So a one-page transfer should never fail with insufficient resources.
212 ASSERT(isReadWrite
&& !Pkt
->InLowMemRetry
);
217 * We are in low-memory stress.
218 * Start the low-memory retry state machine, which tries to
219 * resend the packet in little one-page chunks.
221 InitLowMemRetry( Pkt
,
224 Pkt
->TargetLocationCopy
);
225 StepLowMemRetry(Pkt
);
231 * Retry the packet by simply resending it after a delay.
232 * Put the packet back in the pending queue and
233 * schedule a timer to retry the transfer.
235 * Do not call SetupReadWriteTransferPacket again because:
236 * (1) The minidriver may have set some bits
237 * in the SRB that it needs again and
238 * (2) doing so would reset numRetries.
240 * BECAUSE we do not call SetupReadWriteTransferPacket again,
241 * we have to reset a couple fields in the SRB that
242 * some miniports overwrite when they fail an SRB.
245 Pkt
->Srb
.DataBuffer
= Pkt
->BufPtrCopy
;
246 Pkt
->Srb
.DataTransferLength
= Pkt
->BufLenCopy
;
248 if (Pkt
->RetryIntervalSec
== 0){
250 * Always delay by at least a little when retrying.
251 * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
253 LARGE_INTEGER timerPeriod
;
255 timerPeriod
.HighPart
= -1;
256 timerPeriod
.LowPart
= -(LONG
)((ULONG
)MINIMUM_RETRY_UNITS
*KeQueryTimeIncrement());
257 KeInitializeTimer(&Pkt
->RetryTimer
);
258 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
259 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
262 LARGE_INTEGER timerPeriod
;
264 ASSERT(Pkt
->RetryIntervalSec
< 100); // sanity check
265 timerPeriod
.HighPart
= -1;
266 timerPeriod
.LowPart
= Pkt
->RetryIntervalSec
*-10000000;
267 KeInitializeTimer(&Pkt
->RetryTimer
);
268 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
269 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
277 VOID NTAPI
TransferPacketRetryTimerDpc(IN PKDPC Dpc
,
278 IN PVOID DeferredContext
,
279 IN PVOID SystemArgument1
,
280 IN PVOID SystemArgument2
)
282 PTRANSFER_PACKET pkt
= (PTRANSFER_PACKET
)DeferredContext
;
283 SubmitTransferPacket(pkt
);
286 VOID NTAPI
InitLowMemRetry(PTRANSFER_PACKET Pkt
, PVOID BufPtr
, ULONG Len
, LARGE_INTEGER TargetLocation
)
289 ASSERT(!Pkt
->InLowMemRetry
);
290 Pkt
->InLowMemRetry
= TRUE
;
291 Pkt
->LowMemRetry_remainingBufPtr
= BufPtr
;
292 Pkt
->LowMemRetry_remainingBufLen
= Len
;
293 Pkt
->LowMemRetry_nextChunkTargetLocation
= TargetLocation
;
299 * During extreme low-memory stress, this function retries
300 * a packet in small one-page chunks, sent serially.
302 * Returns TRUE iff the packet is done.
304 BOOLEAN NTAPI
StepLowMemRetry(PTRANSFER_PACKET Pkt
)
308 if (Pkt
->LowMemRetry_remainingBufLen
== 0){
313 ULONG bytesToNextPageBoundary
;
316 * Make sure the little chunk we send is <= a page length
317 * AND that it does not cross any page boundaries.
319 bytesToNextPageBoundary
= PAGE_SIZE
-(ULONG
)((ULONG_PTR
)Pkt
->LowMemRetry_remainingBufPtr
%PAGE_SIZE
);
320 thisChunkLen
= MIN(Pkt
->LowMemRetry_remainingBufLen
, bytesToNextPageBoundary
);
323 * Set up the transfer packet for the new little chunk.
324 * This will reset numRetries so that we retry each chunk as required.
326 SetupReadWriteTransferPacket(Pkt
,
327 Pkt
->LowMemRetry_remainingBufPtr
,
329 Pkt
->LowMemRetry_nextChunkTargetLocation
,
332 Pkt
->LowMemRetry_remainingBufPtr
+= thisChunkLen
;
333 Pkt
->LowMemRetry_remainingBufLen
-= thisChunkLen
;
334 Pkt
->LowMemRetry_nextChunkTargetLocation
.QuadPart
+= thisChunkLen
;
336 SubmitTransferPacket(Pkt
);