* Sync up to trunk head (r65353).
[reactos.git] / drivers / storage / classpnp / retry.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4
5 Module Name:
6
7 retry.c
8
9 Abstract:
10
11 Packet retry routines for CLASSPNP
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "classp.h"
25
26 /*
27 * InterpretTransferPacketError
28 *
29 * Interpret the SRB error into a meaningful IRP status.
30 * ClassInterpretSenseInfo also may modify the SRB for the retry.
31 *
32 * Return TRUE iff packet should be retried.
33 */
34 BOOLEAN NTAPI InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
35 {
36 BOOLEAN shouldRetry = FALSE;
37 PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
38
39 /*
40 * Interpret the error using the returned sense info first.
41 */
42 Pkt->RetryIntervalSec = 0;
43 if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){
44 /*
45 * This is an Ejection Control SRB. Interpret its sense info specially.
46 */
47 shouldRetry = ClassInterpretSenseInfo(
48 Pkt->Fdo,
49 &Pkt->Srb,
50 IRP_MJ_SCSI,
51 0,
52 MAXIMUM_RETRIES - Pkt->NumRetries,
53 &Pkt->Irp->IoStatus.Status,
54 &Pkt->RetryIntervalSec);
55 if (shouldRetry){
56 /*
57 * If the device is not ready, wait at least 2 seconds before retrying.
58 */
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)){
64
65 Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
66 }
67 }
68 }
69 else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
70 (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){
71 /*
72 * This is an Mode Sense SRB. Interpret its sense info specially.
73 */
74 shouldRetry = ClassInterpretSenseInfo(
75 Pkt->Fdo,
76 &Pkt->Srb,
77 IRP_MJ_SCSI,
78 0,
79 MAXIMUM_RETRIES - Pkt->NumRetries,
80 &Pkt->Irp->IoStatus.Status,
81 &Pkt->RetryIntervalSec);
82 if (shouldRetry){
83 /*
84 * If the device is not ready, wait at least 2 seconds before retrying.
85 */
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)){
91
92 Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
93 }
94 }
95
96 /*
97 * Some special cases for mode sense.
98 */
99 if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
100 shouldRetry = TRUE;
101 }
102 else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN){
103 /*
104 * This is a HACK.
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.
108 */
109 Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
110 InterlockedExchangeAdd((PLONG)&Pkt->OriginalIrp->IoStatus.Information, (LONG)Pkt->Srb.DataTransferLength);
111 shouldRetry = FALSE;
112 }
113 }
114 else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){
115 /*
116 * This is a Drive Capacity SRB. Interpret its sense info specially.
117 */
118 shouldRetry = ClassInterpretSenseInfo(
119 Pkt->Fdo,
120 &Pkt->Srb,
121 IRP_MJ_SCSI,
122 0,
123 MAXIMUM_RETRIES - Pkt->NumRetries,
124 &Pkt->Irp->IoStatus.Status,
125 &Pkt->RetryIntervalSec);
126 if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
127 shouldRetry = TRUE;
128 }
129 }
130 else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
131 (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){
132 /*
133 * This is a Read/Write Data packet.
134 */
135 PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
136
137 shouldRetry = ClassInterpretSenseInfo(
138 Pkt->Fdo,
139 &Pkt->Srb,
140 origCurrentSp->MajorFunction,
141 0,
142 MAXIMUM_RETRIES - Pkt->NumRetries,
143 &Pkt->Irp->IoStatus.Status,
144 &Pkt->RetryIntervalSec);
145 /*
146 * Deal with some special cases.
147 */
148 if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
149 /*
150 * We are in extreme low-memory stress.
151 * We will retry in smaller chunks.
152 */
153 shouldRetry = TRUE;
154 }
155 else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
156 (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){
157 /*
158 * We are still verifying a (possibly) reloaded disk/cdrom.
159 * So retry the request.
160 */
161 Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
162 shouldRetry = TRUE;
163 }
164 }
165 else {
166 DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
167 }
168
169 return shouldRetry;
170 }
171
172 /*
173 * RetryTransferPacket
174 *
175 * Retry sending a TRANSFER_PACKET.
176 *
177 * Return TRUE iff the packet is complete.
178 * (if so the status in pkt->irp is the final status).
179 */
180 BOOLEAN NTAPI RetryTransferPacket(PTRANSFER_PACKET Pkt)
181 {
182 BOOLEAN packetDone;
183
184 DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));
185
186 ASSERT(Pkt->NumRetries > 0);
187 Pkt->NumRetries--;
188
189 /*
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.
194 */
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;
199
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));
204
205 if (Pkt->InLowMemRetry || !isReadWrite){
206 /*
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.
211 */
212 ASSERT(isReadWrite && !Pkt->InLowMemRetry);
213 packetDone = TRUE;
214 }
215 else {
216 /*
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.
220 */
221 InitLowMemRetry( Pkt,
222 Pkt->BufPtrCopy,
223 Pkt->BufLenCopy,
224 Pkt->TargetLocationCopy);
225 StepLowMemRetry(Pkt);
226 packetDone = FALSE;
227 }
228 }
229 else {
230 /*
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.
234 *
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.
239 *
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.
243 */
244
245 Pkt->Srb.DataBuffer = Pkt->BufPtrCopy;
246 Pkt->Srb.DataTransferLength = Pkt->BufLenCopy;
247
248 if (Pkt->RetryIntervalSec == 0){
249 /*
250 * Always delay by at least a little when retrying.
251 * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
252 */
253 LARGE_INTEGER timerPeriod;
254
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);
260 }
261 else {
262 LARGE_INTEGER timerPeriod;
263
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);
270 }
271 packetDone = FALSE;
272 }
273
274 return packetDone;
275 }
276
277 VOID NTAPI TransferPacketRetryTimerDpc(IN PKDPC Dpc,
278 IN PVOID DeferredContext,
279 IN PVOID SystemArgument1,
280 IN PVOID SystemArgument2)
281 {
282 PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
283 SubmitTransferPacket(pkt);
284 }
285
286 VOID NTAPI InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
287 {
288 ASSERT(Len > 0);
289 ASSERT(!Pkt->InLowMemRetry);
290 Pkt->InLowMemRetry = TRUE;
291 Pkt->LowMemRetry_remainingBufPtr = BufPtr;
292 Pkt->LowMemRetry_remainingBufLen = Len;
293 Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
294 }
295
296 /*
297 * StepLowMemRetry
298 *
299 * During extreme low-memory stress, this function retries
300 * a packet in small one-page chunks, sent serially.
301 *
302 * Returns TRUE iff the packet is done.
303 */
304 BOOLEAN NTAPI StepLowMemRetry(PTRANSFER_PACKET Pkt)
305 {
306 BOOLEAN packetDone;
307
308 if (Pkt->LowMemRetry_remainingBufLen == 0){
309 packetDone = TRUE;
310 }
311 else {
312 ULONG thisChunkLen;
313 ULONG bytesToNextPageBoundary;
314
315 /*
316 * Make sure the little chunk we send is <= a page length
317 * AND that it does not cross any page boundaries.
318 */
319 bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
320 thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
321
322 /*
323 * Set up the transfer packet for the new little chunk.
324 * This will reset numRetries so that we retry each chunk as required.
325 */
326 SetupReadWriteTransferPacket(Pkt,
327 Pkt->LowMemRetry_remainingBufPtr,
328 thisChunkLen,
329 Pkt->LowMemRetry_nextChunkTargetLocation,
330 Pkt->OriginalIrp);
331
332 Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
333 Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
334 Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
335
336 SubmitTransferPacket(Pkt);
337 packetDone = FALSE;
338 }
339
340 return packetDone;
341 }