[CMAKE]
[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 #include "debug.h"
26
27
28
29 /*
30 * InterpretTransferPacketError
31 *
32 * Interpret the SRB error into a meaningful IRP status.
33 * ClassInterpretSenseInfo also may modify the SRB for the retry.
34 *
35 * Return TRUE iff packet should be retried.
36 */
37 BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
38 {
39 BOOLEAN shouldRetry = FALSE;
40 PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
41
42 /*
43 * Interpret the error using the returned sense info first.
44 */
45 Pkt->RetryIntervalSec = 0;
46 if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){
47 /*
48 * This is an Ejection Control SRB. Interpret its sense info specially.
49 */
50 shouldRetry = ClassInterpretSenseInfo(
51 Pkt->Fdo,
52 &Pkt->Srb,
53 IRP_MJ_SCSI,
54 0,
55 MAXIMUM_RETRIES - Pkt->NumRetries,
56 &Pkt->Irp->IoStatus.Status,
57 &Pkt->RetryIntervalSec);
58 if (shouldRetry){
59 /*
60 * If the device is not ready, wait at least 2 seconds before retrying.
61 */
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)){
67
68 Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
69 }
70 }
71 }
72 else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
73 (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){
74 /*
75 * This is an Mode Sense SRB. Interpret its sense info specially.
76 */
77 shouldRetry = ClassInterpretSenseInfo(
78 Pkt->Fdo,
79 &Pkt->Srb,
80 IRP_MJ_SCSI,
81 0,
82 MAXIMUM_RETRIES - Pkt->NumRetries,
83 &Pkt->Irp->IoStatus.Status,
84 &Pkt->RetryIntervalSec);
85 if (shouldRetry){
86 /*
87 * If the device is not ready, wait at least 2 seconds before retrying.
88 */
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)){
94
95 Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
96 }
97 }
98
99 /*
100 * Some special cases for mode sense.
101 */
102 if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
103 shouldRetry = TRUE;
104 }
105 else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN){
106 /*
107 * This is a HACK.
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.
111 */
112 Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
113 InterlockedExchangeAdd((PLONG)&Pkt->OriginalIrp->IoStatus.Information, (LONG)Pkt->Srb.DataTransferLength);
114 shouldRetry = FALSE;
115 }
116 }
117 else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){
118 /*
119 * This is a Drive Capacity SRB. Interpret its sense info specially.
120 */
121 shouldRetry = ClassInterpretSenseInfo(
122 Pkt->Fdo,
123 &Pkt->Srb,
124 IRP_MJ_SCSI,
125 0,
126 MAXIMUM_RETRIES - Pkt->NumRetries,
127 &Pkt->Irp->IoStatus.Status,
128 &Pkt->RetryIntervalSec);
129 if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
130 shouldRetry = TRUE;
131 }
132 }
133 else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
134 (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){
135 /*
136 * This is a Read/Write Data packet.
137 */
138 PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
139
140 shouldRetry = ClassInterpretSenseInfo(
141 Pkt->Fdo,
142 &Pkt->Srb,
143 origCurrentSp->MajorFunction,
144 0,
145 MAXIMUM_RETRIES - Pkt->NumRetries,
146 &Pkt->Irp->IoStatus.Status,
147 &Pkt->RetryIntervalSec);
148 /*
149 * Deal with some special cases.
150 */
151 if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
152 /*
153 * We are in extreme low-memory stress.
154 * We will retry in smaller chunks.
155 */
156 shouldRetry = TRUE;
157 }
158 else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
159 (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){
160 /*
161 * We are still verifying a (possibly) reloaded disk/cdrom.
162 * So retry the request.
163 */
164 Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
165 shouldRetry = TRUE;
166 }
167 }
168 else {
169 DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
170 }
171
172 return shouldRetry;
173 }
174
175
176 /*
177 * RetryTransferPacket
178 *
179 * Retry sending a TRANSFER_PACKET.
180 *
181 * Return TRUE iff the packet is complete.
182 * (if so the status in pkt->irp is the final status).
183 */
184 BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
185 {
186 BOOLEAN packetDone;
187
188 DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));
189
190 ASSERT(Pkt->NumRetries > 0);
191 Pkt->NumRetries--;
192
193 /*
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.
198 */
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;
203
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));
208
209 if (Pkt->InLowMemRetry || !isReadWrite){
210 /*
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.
215 */
216 ASSERT(isReadWrite && !Pkt->InLowMemRetry);
217 packetDone = TRUE;
218 }
219 else {
220 /*
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.
224 */
225 InitLowMemRetry( Pkt,
226 Pkt->BufPtrCopy,
227 Pkt->BufLenCopy,
228 Pkt->TargetLocationCopy);
229 StepLowMemRetry(Pkt);
230 packetDone = FALSE;
231 }
232 }
233 else {
234 /*
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.
238 *
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.
243 *
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.
247 */
248
249 Pkt->Srb.DataBuffer = Pkt->BufPtrCopy;
250 Pkt->Srb.DataTransferLength = Pkt->BufLenCopy;
251
252 if (Pkt->RetryIntervalSec == 0){
253 /*
254 * Always delay by at least a little when retrying.
255 * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
256 */
257 LARGE_INTEGER timerPeriod;
258
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);
264 }
265 else {
266 LARGE_INTEGER timerPeriod;
267
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);
274 }
275 packetDone = FALSE;
276 }
277
278 return packetDone;
279 }
280
281
282 VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
283 IN PVOID DeferredContext,
284 IN PVOID SystemArgument1,
285 IN PVOID SystemArgument2)
286 {
287 PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
288 SubmitTransferPacket(pkt);
289 }
290
291
292 VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
293 {
294 ASSERT(Len > 0);
295 ASSERT(!Pkt->InLowMemRetry);
296 Pkt->InLowMemRetry = TRUE;
297 Pkt->LowMemRetry_remainingBufPtr = BufPtr;
298 Pkt->LowMemRetry_remainingBufLen = Len;
299 Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
300 }
301
302
303 /*
304 * StepLowMemRetry
305 *
306 * During extreme low-memory stress, this function retries
307 * a packet in small one-page chunks, sent serially.
308 *
309 * Returns TRUE iff the packet is done.
310 */
311 BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
312 {
313 BOOLEAN packetDone;
314
315 if (Pkt->LowMemRetry_remainingBufLen == 0){
316 packetDone = TRUE;
317 }
318 else {
319 ULONG thisChunkLen;
320 ULONG bytesToNextPageBoundary;
321
322 /*
323 * Make sure the little chunk we send is <= a page length
324 * AND that it does not cross any page boundaries.
325 */
326 bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
327 thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
328
329 /*
330 * Set up the transfer packet for the new little chunk.
331 * This will reset numRetries so that we retry each chunk as required.
332 */
333 SetupReadWriteTransferPacket(Pkt,
334 Pkt->LowMemRetry_remainingBufPtr,
335 thisChunkLen,
336 Pkt->LowMemRetry_nextChunkTargetLocation,
337 Pkt->OriginalIrp);
338
339 Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
340 Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
341 Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
342
343 SubmitTransferPacket(Pkt);
344 packetDone = FALSE;
345 }
346
347 return packetDone;
348 }
349