xref: /reactos/drivers/storage/class/classpnp/retry.c (revision f7cab5a1)
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 }
342