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