3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Packet retry routines for CLASSPNP
30 * InterpretTransferPacketError
32 * Interpret the SRB error into a meaningful IRP status.
33 * ClassInterpretSenseInfo also may modify the SRB for the retry.
35 * Return TRUE iff packet should be retried.
37 BOOLEAN
InterpretTransferPacketError(PTRANSFER_PACKET Pkt
)
39 BOOLEAN shouldRetry
= FALSE
;
40 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
43 * Interpret the error using the returned sense info first.
45 Pkt
->RetryIntervalSec
= 0;
46 if (pCdb
->MEDIA_REMOVAL
.OperationCode
== SCSIOP_MEDIUM_REMOVAL
){
48 * This is an Ejection Control SRB. Interpret its sense info specially.
50 shouldRetry
= ClassInterpretSenseInfo(
55 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
56 &Pkt
->Irp
->IoStatus
.Status
,
57 &Pkt
->RetryIntervalSec
);
60 * If the device is not ready, wait at least 2 seconds before retrying.
62 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
63 ASSERT(senseInfoBuffer
);
64 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
65 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
66 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
68 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
72 else if ((pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE
) ||
73 (pCdb
->MODE_SENSE
.OperationCode
== SCSIOP_MODE_SENSE10
)){
75 * This is an Mode Sense SRB. Interpret its sense info specially.
77 shouldRetry
= ClassInterpretSenseInfo(
82 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
83 &Pkt
->Irp
->IoStatus
.Status
,
84 &Pkt
->RetryIntervalSec
);
87 * If the device is not ready, wait at least 2 seconds before retrying.
89 PSENSE_DATA senseInfoBuffer
= Pkt
->Srb
.SenseInfoBuffer
;
90 ASSERT(senseInfoBuffer
);
91 if (((Pkt
->Irp
->IoStatus
.Status
== STATUS_DEVICE_NOT_READY
) &&
92 (senseInfoBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)) ||
93 (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)){
95 Pkt
->RetryIntervalSec
= MAX(Pkt
->RetryIntervalSec
, 2);
100 * Some special cases for mode sense.
102 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
105 else if (SRB_STATUS(Pkt
->Srb
.SrbStatus
) == SRB_STATUS_DATA_OVERRUN
){
108 * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
109 * underrun (i.e. success, and the buffer is longer than needed).
110 * So treat this as a success.
112 Pkt
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
113 InterlockedExchangeAdd((PLONG
)&Pkt
->OriginalIrp
->IoStatus
.Information
, (LONG
)Pkt
->Srb
.DataTransferLength
);
117 else if (pCdb
->CDB10
.OperationCode
== SCSIOP_READ_CAPACITY
){
119 * This is a Drive Capacity SRB. Interpret its sense info specially.
121 shouldRetry
= ClassInterpretSenseInfo(
126 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
127 &Pkt
->Irp
->IoStatus
.Status
,
128 &Pkt
->RetryIntervalSec
);
129 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
){
133 else if ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
134 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
)){
136 * This is a Read/Write Data packet.
138 PIO_STACK_LOCATION origCurrentSp
= IoGetCurrentIrpStackLocation(Pkt
->OriginalIrp
);
140 shouldRetry
= ClassInterpretSenseInfo(
143 origCurrentSp
->MajorFunction
,
145 MAXIMUM_RETRIES
- Pkt
->NumRetries
,
146 &Pkt
->Irp
->IoStatus
.Status
,
147 &Pkt
->RetryIntervalSec
);
149 * Deal with some special cases.
151 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
153 * We are in extreme low-memory stress.
154 * We will retry in smaller chunks.
158 else if (TEST_FLAG(origCurrentSp
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
159 (Pkt
->Irp
->IoStatus
.Status
== STATUS_VERIFY_REQUIRED
)){
161 * We are still verifying a (possibly) reloaded disk/cdrom.
162 * So retry the request.
164 Pkt
->Irp
->IoStatus
.Status
= STATUS_IO_DEVICE_ERROR
;
169 DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG
)pCdb
->CDB10
.OperationCode
, Pkt
));
177 * RetryTransferPacket
179 * Retry sending a TRANSFER_PACKET.
181 * Return TRUE iff the packet is complete.
182 * (if so the status in pkt->irp is the final status).
184 BOOLEAN
RetryTransferPacket(PTRANSFER_PACKET Pkt
)
188 DBGTRACE(ClassDebugTrace
, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt
, DBGGETSCSIOPSTR(&Pkt
->Srb
)));
190 ASSERT(Pkt
->NumRetries
> 0);
194 * Tone down performance on the retry.
195 * This increases the chance for success on the retry.
196 * We've seen instances of drives that fail consistently but then start working
197 * once this scale-down is applied.
199 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
200 SET_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
201 CLEAR_FLAG(Pkt
->Srb
.SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
202 Pkt
->Srb
.QueueTag
= SP_UNTAGGED
;
204 if (Pkt
->Irp
->IoStatus
.Status
== STATUS_INSUFFICIENT_RESOURCES
){
205 PCDB pCdb
= (PCDB
)Pkt
->Srb
.Cdb
;
206 BOOLEAN isReadWrite
= ((pCdb
->CDB10
.OperationCode
== SCSIOP_READ
) ||
207 (pCdb
->CDB10
.OperationCode
== SCSIOP_WRITE
));
209 if (Pkt
->InLowMemRetry
|| !isReadWrite
){
211 * This should never happen.
212 * The memory manager guarantees that at least four pages will
213 * be available to allow forward progress in the port driver.
214 * So a one-page transfer should never fail with insufficient resources.
216 ASSERT(isReadWrite
&& !Pkt
->InLowMemRetry
);
221 * We are in low-memory stress.
222 * Start the low-memory retry state machine, which tries to
223 * resend the packet in little one-page chunks.
225 InitLowMemRetry( Pkt
,
228 Pkt
->TargetLocationCopy
);
229 StepLowMemRetry(Pkt
);
235 * Retry the packet by simply resending it after a delay.
236 * Put the packet back in the pending queue and
237 * schedule a timer to retry the transfer.
239 * Do not call SetupReadWriteTransferPacket again because:
240 * (1) The minidriver may have set some bits
241 * in the SRB that it needs again and
242 * (2) doing so would reset numRetries.
244 * BECAUSE we do not call SetupReadWriteTransferPacket again,
245 * we have to reset a couple fields in the SRB that
246 * some miniports overwrite when they fail an SRB.
249 Pkt
->Srb
.DataBuffer
= Pkt
->BufPtrCopy
;
250 Pkt
->Srb
.DataTransferLength
= Pkt
->BufLenCopy
;
252 if (Pkt
->RetryIntervalSec
== 0){
254 * Always delay by at least a little when retrying.
255 * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
257 LARGE_INTEGER timerPeriod
;
259 timerPeriod
.HighPart
= -1;
260 timerPeriod
.LowPart
= -(LONG
)((ULONG
)MINIMUM_RETRY_UNITS
*KeQueryTimeIncrement());
261 KeInitializeTimer(&Pkt
->RetryTimer
);
262 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
263 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
266 LARGE_INTEGER timerPeriod
;
268 ASSERT(Pkt
->RetryIntervalSec
< 100); // sanity check
269 timerPeriod
.HighPart
= -1;
270 timerPeriod
.LowPart
= Pkt
->RetryIntervalSec
*-10000000;
271 KeInitializeTimer(&Pkt
->RetryTimer
);
272 KeInitializeDpc(&Pkt
->RetryTimerDPC
, TransferPacketRetryTimerDpc
, Pkt
);
273 KeSetTimer(&Pkt
->RetryTimer
, timerPeriod
, &Pkt
->RetryTimerDPC
);
282 VOID
TransferPacketRetryTimerDpc( IN PKDPC Dpc
,
283 IN PVOID DeferredContext
,
284 IN PVOID SystemArgument1
,
285 IN PVOID SystemArgument2
)
287 PTRANSFER_PACKET pkt
= (PTRANSFER_PACKET
)DeferredContext
;
288 SubmitTransferPacket(pkt
);
292 VOID
InitLowMemRetry(PTRANSFER_PACKET Pkt
, PVOID BufPtr
, ULONG Len
, LARGE_INTEGER TargetLocation
)
295 ASSERT(!Pkt
->InLowMemRetry
);
296 Pkt
->InLowMemRetry
= TRUE
;
297 Pkt
->LowMemRetry_remainingBufPtr
= BufPtr
;
298 Pkt
->LowMemRetry_remainingBufLen
= Len
;
299 Pkt
->LowMemRetry_nextChunkTargetLocation
= TargetLocation
;
306 * During extreme low-memory stress, this function retries
307 * a packet in small one-page chunks, sent serially.
309 * Returns TRUE iff the packet is done.
311 BOOLEAN
StepLowMemRetry(PTRANSFER_PACKET Pkt
)
315 if (Pkt
->LowMemRetry_remainingBufLen
== 0){
320 ULONG bytesToNextPageBoundary
;
323 * Make sure the little chunk we send is <= a page length
324 * AND that it does not cross any page boundaries.
326 bytesToNextPageBoundary
= PAGE_SIZE
-(ULONG
)((ULONG_PTR
)Pkt
->LowMemRetry_remainingBufPtr
%PAGE_SIZE
);
327 thisChunkLen
= MIN(Pkt
->LowMemRetry_remainingBufLen
, bytesToNextPageBoundary
);
330 * Set up the transfer packet for the new little chunk.
331 * This will reset numRetries so that we retry each chunk as required.
333 SetupReadWriteTransferPacket(Pkt
,
334 Pkt
->LowMemRetry_remainingBufPtr
,
336 Pkt
->LowMemRetry_nextChunkTargetLocation
,
339 Pkt
->LowMemRetry_remainingBufPtr
+= thisChunkLen
;
340 Pkt
->LowMemRetry_remainingBufLen
-= thisChunkLen
;
341 Pkt
->LowMemRetry_nextChunkTargetLocation
.QuadPart
+= thisChunkLen
;
343 SubmitTransferPacket(Pkt
);