xref: /reactos/drivers/storage/class/classpnp/retry.c (revision bf1b3cb1)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 2010
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 #ifdef DEBUG_USE_WPP
28 #include "retry.tmh"
29 #endif
30 
31 
32 /*
33  *  InterpretTransferPacketError
34  *
35  *      Interpret the SRB error into a meaningful IRP status.
36  *      ClassInterpretSenseInfo also may modify the SRB for the retry.
37  *
38  *      Return TRUE iff packet should be retried.
39  */
InterpretTransferPacketError(PTRANSFER_PACKET Pkt)40 BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
41 {
42     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
43     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
44     ULONG timesAlreadyRetried;
45     BOOLEAN shouldRetry = FALSE;
46     PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
47 
48     /*
49      *  Interpret the error using the returned sense info first.
50      */
51     Pkt->RetryIn100nsUnits = 0;
52 
53 
54     /*
55      *  Pre-calculate the number of times the IO has already been
56      *  retried, so that all InterpretSenseInfo routines get the right value.
57      */
58     if (ClasspTransferPacketGetNumberOfRetriesDone(Pkt, pCdb, &timesAlreadyRetried) == FALSE)
59     {
60         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
61     }
62 
63     if (fdoData->InterpretSenseInfo != NULL) {
64 
65         SCSI_REQUEST_BLOCK tempSrb = { 0 };
66         PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
67 
68         // SAL annotation and ClassInitializeEx() both validate this
69         NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
70 
71         //
72         // If class driver does not support extended SRB and this is
73         // an extended SRB, convert to legacy SRB and pass to class
74         // driver.
75         //
76         if ((Pkt->Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
77             ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
78             CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
79             ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Pkt->Srb);
80             srbPtr = &tempSrb;
81         }
82 
83         shouldRetry = fdoData->InterpretSenseInfo->Interpret(Pkt->Fdo,
84                                                              Pkt->OriginalIrp,
85                                                              srbPtr,
86                                                              IRP_MJ_SCSI,
87                                                              0,
88                                                              timesAlreadyRetried,
89                                                              Pkt->RetryHistory,
90                                                              &Pkt->Irp->IoStatus.Status,
91                                                              &Pkt->RetryIn100nsUnits);
92 
93 
94     } else {
95 
96         //
97         // In this case, fdoData->InterpretSenseInfo == NULL so we must do our
98         // own error code and sense info processing.
99         //
100 
101         PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
102         UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
103         BOOLEAN validSense = FALSE;
104         UCHAR senseKey = 0;
105         UCHAR additionalSenseCode = 0;
106         UCHAR additionalSenseQual = 0;
107 
108         NT_ASSERT(senseInfoBuffer);
109 
110         validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
111                                              senseInfoBufferLength,
112                                              SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
113                                              &senseKey,
114                                              &additionalSenseCode,
115                                              &additionalSenseQual);
116 
117         if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL) {
118 
119             ULONG retryIntervalSeconds = 0;
120             /*
121              *  This is an Ejection Control SRB.  Interpret its sense info specially.
122              */
123             shouldRetry = ClassInterpretSenseInfo(
124                 Pkt->Fdo,
125                 (PSCSI_REQUEST_BLOCK)Pkt->Srb,
126                 IRP_MJ_SCSI,
127                 0,
128                 timesAlreadyRetried,
129                 &Pkt->Irp->IoStatus.Status,
130                 &retryIntervalSeconds);
131 
132             if (shouldRetry) {
133                 /*
134                  *  If the device is not ready, wait at least 2 seconds before retrying.
135                  */
136                 BOOLEAN setRetryIntervalSeconds = FALSE;
137 
138                 if (validSense) {
139 
140                     if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
141                         (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
142                         setRetryIntervalSeconds = TRUE;
143                     }
144                 }
145 
146                 if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
147                     setRetryIntervalSeconds = TRUE;
148                 }
149 
150                 if (setRetryIntervalSeconds) {
151                     retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
152                 }
153             }
154 
155             if (shouldRetry)
156             {
157                 Pkt->RetryIn100nsUnits = retryIntervalSeconds;
158                 Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
159             }
160 
161         }
162         else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
163                  (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)) {
164 
165             ULONG retryIntervalSeconds = 0;
166             /*
167              *  This is an Mode Sense SRB.  Interpret its sense info specially.
168              */
169             shouldRetry = ClassInterpretSenseInfo(
170                 Pkt->Fdo,
171                 (PSCSI_REQUEST_BLOCK)Pkt->Srb,
172                 IRP_MJ_SCSI,
173                 0,
174                 timesAlreadyRetried,
175                 &Pkt->Irp->IoStatus.Status,
176                 &retryIntervalSeconds);
177             if (shouldRetry) {
178                 /*
179                  *  If the device is not ready, wait at least 2 seconds before retrying.
180                  */
181                 BOOLEAN setRetryIntervalSeconds = FALSE;
182 
183                 if (validSense) {
184                     if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
185                         (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
186                         setRetryIntervalSeconds = TRUE;
187                     }
188                 }
189 
190                 if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
191                     setRetryIntervalSeconds = TRUE;
192                 }
193 
194                 if (setRetryIntervalSeconds) {
195                     retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
196                 }
197             }
198 
199             /*
200              *  Some special cases for mode sense.
201              */
202             if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
203                 shouldRetry = TRUE;
204             }
205             else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
206                 /*
207                  *  This is a HACK.
208                  *  Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
209                  *  underrun (i.e. success, and the buffer is longer than needed).
210                  *  So treat this as a success.
211                  *  When the caller of this function sees that the status was changed to success,
212                  *  it will add the transferred length to the original irp.
213                  */
214                 Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
215                 shouldRetry = FALSE;
216             }
217 
218             if (shouldRetry)
219             {
220                 Pkt->RetryIn100nsUnits = retryIntervalSeconds;
221                 Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
222             }
223 
224         }
225         else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
226                  (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) {
227 
228             ULONG retryIntervalSeconds = 0;
229 
230             /*
231              *  This is a Drive Capacity SRB.  Interpret its sense info specially.
232              */
233             shouldRetry = ClassInterpretSenseInfo(
234                 Pkt->Fdo,
235                 (PSCSI_REQUEST_BLOCK)Pkt->Srb,
236                 IRP_MJ_SCSI,
237                 0,
238                 timesAlreadyRetried,
239                 &Pkt->Irp->IoStatus.Status,
240                 &retryIntervalSeconds);
241             if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
242                 shouldRetry = TRUE;
243             }
244 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
245             else if (ClasspSrbTimeOutStatus(Pkt->Srb)) {
246 
247                 Pkt->TimedOut = TRUE;
248 
249                 if (shouldRetry) {
250                     //
251                     // For requests that have timed-out we may only perform a limited
252                     // number of retries.  This is typically less than the general
253                     // number of retries allowed.
254                     //
255                     if (Pkt->NumIoTimeoutRetries == 0) {
256                         shouldRetry = FALSE;
257                     } else {
258                         Pkt->NumIoTimeoutRetries--;
259                         //
260                         // We expect to be able to retry if there are some general retries remaining.
261                         //
262                     }
263                 }
264             }
265 #endif
266 
267             if (shouldRetry)
268             {
269                 Pkt->RetryIn100nsUnits = retryIntervalSeconds;
270                 Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
271             }
272 
273         }
274         else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
275 
276             ULONG retryIntervalSeconds = 0;
277             /*
278              *  This is a Read/Write Data packet.
279              */
280             PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
281 
282             shouldRetry = ClassInterpretSenseInfo(Pkt->Fdo,
283                                                   (PSCSI_REQUEST_BLOCK)Pkt->Srb,
284                                                   origCurrentSp->MajorFunction,
285                                                   0,
286                                                   timesAlreadyRetried,
287                                                   &Pkt->Irp->IoStatus.Status,
288                                                   &retryIntervalSeconds);
289 
290             /*
291              *  Deal with some special cases.
292              */
293             if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
294                 /*
295                  *  We are in extreme low-memory stress.
296                  *  We will retry in smaller chunks.
297                  */
298                 shouldRetry = TRUE;
299             }
300             else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
301                      (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
302                 /*
303                  *  We are still verifying a (possibly) reloaded disk/cdrom.
304                  *  So retry the request.
305                  */
306                 Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
307                 shouldRetry = TRUE;
308 
309             }
310 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
311             else if (ClasspSrbTimeOutStatus(Pkt->Srb)) {
312 
313                 Pkt->TimedOut = TRUE;
314 
315                 if (shouldRetry) {
316                     //
317                     // For requests that have timed-out we may only perform a limited
318                     // number of retries.  This is typically less than the general
319                     // number of retries allowed.
320                     //
321                     if (Pkt->NumIoTimeoutRetries == 0) {
322                         shouldRetry = FALSE;
323                     } else {
324                         Pkt->NumIoTimeoutRetries--;
325                         //
326                         // We expect to be able to retry if there are some general retries remaining.
327                         //
328                     }
329                 }
330             }
331 #endif
332 
333             if (shouldRetry)
334             {
335                 Pkt->RetryIn100nsUnits = retryIntervalSeconds;
336                 Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
337             }
338 
339         } else if (ClasspIsOffloadDataTransferCommand(pCdb)) {
340 
341             ULONG retryIntervalSeconds = 0;
342 
343             Pkt->TransferCount = 0;
344 
345             shouldRetry = ClassInterpretSenseInfo(
346                 Pkt->Fdo,
347                 (PSCSI_REQUEST_BLOCK)Pkt->Srb,
348                 IRP_MJ_SCSI,
349                 0,
350                 timesAlreadyRetried,
351                 &Pkt->Irp->IoStatus.Status,
352                 &retryIntervalSeconds);
353 
354             if (shouldRetry) {
355 
356                 Pkt->RetryIn100nsUnits = retryIntervalSeconds;
357                 Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
358 
359             } else {
360 
361                 if (ClasspIsTokenOperation(pCdb)) {
362 
363                     BOOLEAN isInformationValid = FALSE;
364                     ULONGLONG information = 0;
365 
366                     if (validSense) {
367 
368                         //
369                         // If this is a data underrun condition (i.e. target truncated the offload data transfer),
370                         // the SenseData's Information field may have the TransferCount.
371                         //
372                         if ((senseKey == SCSI_SENSE_COPY_ABORTED || senseKey == SCSI_SENSE_ABORTED_COMMAND) &&
373                             (additionalSenseCode == SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR && additionalSenseQual == SCSI_SENSEQ_DATA_UNDERRUN)) {
374 
375                             //
376                             // Sense data in Descriptor format
377                             //
378                             if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
379 
380                                 PVOID startBuffer = NULL;
381                                 UCHAR startBufferLength = 0;
382 
383 
384                                 if (ScsiGetSenseDescriptor(senseInfoBuffer,
385                                                             SrbGetSenseInfoBufferLength(Pkt->Srb),
386                                                             &startBuffer,
387                                                             &startBufferLength)) {
388                                     UCHAR outType;
389                                     PVOID outBuffer = NULL;
390                                     UCHAR outBufferLength = 0;
391 
392                                     UCHAR typeList[1] = { SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION };
393 
394                                     if (ScsiGetNextSenseDescriptorByType(startBuffer,
395                                         startBufferLength,
396                                         typeList,
397                                         ARRAYSIZE(typeList),
398                                         &outType,
399                                         &outBuffer,
400                                         &outBufferLength)) {
401 
402                                         if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
403 
404                                             if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
405                                                 REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
406                                                 isInformationValid = TRUE;
407                                             }
408 
409                                         } else {
410 
411                                             //
412                                             // ScsiGetNextDescriptorByType should only return a type that is specified by us.
413                                             //
414                                             NT_ASSERT(FALSE);
415                                         }
416                                     }
417                                 }
418                             } else {
419 
420                                 //
421                                 // Sense data in Fixed format
422                                 //
423                                 REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseInfoBuffer)->Information));
424                                 isInformationValid = TRUE;
425                             }
426 
427                             if (isInformationValid) {
428                                 Pkt->TransferCount = information;
429                             }
430                         }
431                     }
432                 }
433             }
434 
435         }
436         else {
437             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
438         }
439     }
440 
441     return shouldRetry;
442 }
443 
444 
445 /*
446  *  RetryTransferPacket
447  *
448  *      Retry sending a TRANSFER_PACKET.
449  *
450  *      Return TRUE iff the packet is complete.
451  *          (if so the status in pkt->irp is the final status).
452  */
RetryTransferPacket(PTRANSFER_PACKET Pkt)453 BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
454 {
455     BOOLEAN packetDone;
456     BOOLEAN scaleDown = FALSE;
457     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Pkt->Fdo->DeviceExtension;
458     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
459     PCDB pCdb = SrbGetCdb(Pkt->Srb);
460 
461 #if !defined(__REACTOS__) && NTDDI_VERSION >= NTDDI_WINBLUE
462     if(ClasspIsThinProvisioningError((PSCSI_REQUEST_BLOCK)Pkt->Srb) &&
463         (pCdb != NULL) && IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
464 
465         if(Pkt->NumThinProvisioningRetries >= NUM_THIN_PROVISIONING_RETRIES) {
466             //We've already retried this the maximum times.  Bail out.
467             return TRUE;
468         }
469         Pkt->NumThinProvisioningRetries++;
470     }
471     else {
472         NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory);
473         Pkt->NumRetries--;
474     }
475 #else
476     NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory);
477     Pkt->NumRetries--;
478 #endif
479 
480     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb)));
481 
482     if (!fdoData->DisableThrottling) {
483 
484         //
485         // If this is the last retry, then turn off disconnect, sync transfer,
486         // and tagged queuing.  On all other retries, leave the original settings.
487         // Do not apply this for thin provisioning soft threshold errors, since
488         // they should succeed as soon as they're retried on the right IT nexus.
489         //
490         if ((Pkt->NumRetries == 0) && !ClasspIsThinProvisioningError((PSCSI_REQUEST_BLOCK)Pkt->Srb)) {
491             scaleDown = TRUE;
492         }
493 
494 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
495         //
496         // If this request previously timed-out and there are no more retries left
497         // for timed-out requests, then we should also apply the scale down.
498         //
499         if (Pkt->TimedOut && Pkt->NumIoTimeoutRetries == 0) {
500             scaleDown = TRUE;
501         }
502 #endif
503     }
504 
505 
506     if (scaleDown) {
507         /*
508          *  Tone down performance on the retry.
509          *  This increases the chance for success on the retry.
510          *  We've seen instances of drives that fail consistently but then start working
511          *  once this scale-down is applied.
512          */
513         SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_DISCONNECT);
514         SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
515         SrbClearSrbFlags(Pkt->Srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
516         SrbSetRequestTag(Pkt->Srb, SP_UNTAGGED);
517     }
518 
519     if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
520 
521         UCHAR cdbOpcode = 0;
522         BOOLEAN isReadWrite = FALSE;
523 
524         if (pCdb) {
525             cdbOpcode = pCdb->CDB10.OperationCode;
526             isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode);
527         }
528 
529         if ((Pkt->DriverUsesStartIO) &&
530             ( (cdbOpcode == SCSIOP_WRITE6 ) ||
531               (cdbOpcode == SCSIOP_WRITE  ) ||
532               (cdbOpcode == SCSIOP_WRITE12) ||
533               (cdbOpcode == SCSIOP_WRITE16) )) {
534 
535             /* don't retry writes in super-low-memory conditions if the
536              * driver must serialize against StartIO.  This is because
537              * some write methods used in such drivers cannot accept
538              * random-sized writes. (i.e CD-RW in packet writing mode)
539              * Reads, however, are always safe to split up.
540              */
541             SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH1);
542             packetDone = TRUE;
543         }
544         else if (Pkt->InLowMemRetry || !isReadWrite){
545             /*
546              *  This should never happen under normal circumstances.
547              *  The memory manager guarantees that at least four pages will
548              *  be available to allow forward progress in the port driver.
549              *  So a one-page transfer should never fail with insufficient resources.
550              *
551              *  However, it is possible to get in here with virtual storage
552              *  or thin provisioned storage for example.
553              *  A single sector write can trigger an allocation request and
554              *  presently a forward progress guarantee is not provided.
555              *  VHD also may have some limitations in forward progress guarantee.
556              *  And USB too might also fall into this category.
557              */
558             SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH2);
559             packetDone = TRUE;
560         }
561         else {
562             /*
563              *  We are in low-memory stress.
564              *  Start the low-memory retry state machine, which tries to
565              *  resend the packet in little one-page chunks.
566              */
567             SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH3);
568             InitLowMemRetry(Pkt,
569                             Pkt->BufPtrCopy,
570                             Pkt->BufLenCopy,
571                             Pkt->TargetLocationCopy);
572             StepLowMemRetry(Pkt);
573             packetDone = FALSE;
574         }
575     }
576     else {
577         /*
578          *  Retry the packet by simply resending it after a delay.
579          *  Put the packet back in the pending queue and
580          *  schedule a timer to retry the transfer.
581          *
582          *  Do not call SetupReadWriteTransferPacket again because:
583          *  (1)  The minidriver may have set some bits
584          *       in the SRB that it needs again and
585          *  (2)  doing so would reset numRetries.
586          *
587          *  BECAUSE we do not call SetupReadWriteTransferPacket again,
588          *  we have to reset a couple fields in the SRB that
589          *  some miniports overwrite when they fail an SRB.
590          */
591 
592         SrbSetDataBuffer(Pkt->Srb, Pkt->BufPtrCopy);
593         SrbSetDataTransferLength(Pkt->Srb, Pkt->BufLenCopy);
594 
595         TransferPacketQueueRetryDpc(Pkt);
596 
597         packetDone = FALSE;
598     }
599 
600     return packetDone;
601 }
602 
603 
TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt)604 VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt)
605 {
606     KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
607 
608     if (Pkt->RetryIn100nsUnits == 0){
609         KeInsertQueueDpc(&Pkt->RetryTimerDPC, NULL, NULL);
610     }
611     else {
612         LARGE_INTEGER timerPeriod;
613 
614         NT_ASSERT(Pkt->RetryIn100nsUnits < 100 * 1000 * 1000 * 10); // sanity check -- 100 seconds is normally too long
615         timerPeriod.QuadPart = -(Pkt->RetryIn100nsUnits);
616         KeInitializeTimer(&Pkt->RetryTimer);
617         KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
618     }
619 }
620 
621 
622 VOID
623 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
TransferPacketRetryTimerDpc(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)624 TransferPacketRetryTimerDpc(   IN PKDPC Dpc,
625                                     IN PVOID DeferredContext,
626                                     IN PVOID SystemArgument1,
627                                     IN PVOID SystemArgument2)
628 {
629     PTRANSFER_PACKET pkt;
630     PDEVICE_OBJECT fdo;
631     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
632 
633     _Analysis_assume_(DeferredContext != NULL);
634 
635     pkt = (PTRANSFER_PACKET)DeferredContext;
636 
637     fdo = pkt->Fdo;
638     fdoExtension = fdo->DeviceExtension;
639 
640     UNREFERENCED_PARAMETER(Dpc);
641     UNREFERENCED_PARAMETER(SystemArgument1);
642     UNREFERENCED_PARAMETER(SystemArgument2);
643 
644 
645     /*
646      *  Sometimes the port driver can allocates a new 'sense' buffer
647      *  to report transfer errors, e.g. when the default sense buffer
648      *  is too small.  If so, it is up to us to free it.
649      *  Now that we're done using the sense info, free it if appropriate.
650      *  Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
651      */
652     if (PORT_ALLOCATED_SENSE_EX(fdoExtension, pkt->Srb)) {
653         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
654         FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, pkt->Srb);
655         SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
656         SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
657     }
658     else {
659         NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
660         NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
661     }
662 
663     RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
664 
665     SubmitTransferPacket(pkt);
666 
667 }
668 
669 
InitLowMemRetry(PTRANSFER_PACKET Pkt,PVOID BufPtr,ULONG Len,LARGE_INTEGER TargetLocation)670 VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
671 {
672     NT_ASSERT(Len > 0);
673     NT_ASSERT(!Pkt->InLowMemRetry);
674 
675     if (Pkt->DriverUsesStartIO)
676     {
677         /*
678          * special case: StartIO-based writing must stay serialized for performance
679          * and proper operations (i.e. sequential writing mode).  If need more than
680          * one transfer to perform this operation, and it's a StartIO-based driver
681          * (such as CDROM), then just use a single packet and use the retry logic
682          * that's already built-in to the packet engine.  Note that low-mem retry
683          * cannot be used directly because some write methods do not work if the
684          * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
685          */
686         Pkt->InLowMemRetry = FALSE;
687     }
688     else
689     {
690         Pkt->InLowMemRetry = TRUE;
691     }
692     Pkt->LowMemRetry_remainingBufPtr = BufPtr;
693     Pkt->LowMemRetry_remainingBufLen = Len;
694     Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
695 }
696 
697 
698 /*
699  *  StepLowMemRetry
700  *
701  *      During extreme low-memory stress, this function retries
702  *      a packet in small one-page chunks, sent serially.
703  *
704  *      Returns TRUE iff the packet is done.
705  */
StepLowMemRetry(PTRANSFER_PACKET Pkt)706 BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
707 {
708     BOOLEAN packetDone;
709 
710     if (Pkt->LowMemRetry_remainingBufLen == 0){
711         packetDone = TRUE;
712     }
713     else {
714         ULONG thisChunkLen;
715         if (Pkt->DriverUsesStartIO)
716         {
717             /*
718              * Need the fdoData for the HwMaxXferLen
719              */
720             PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
721             PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
722 
723             /*
724              * Need the adapterDesc to limit transfers based on byte count
725              */
726             PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
727             PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
728 
729             ULONG hwMaxXferLen;
730 
731             /*
732              * special case: StartIO-based writing must stay serialized for performance
733              * and proper operations (i.e. sequential writing mode).  If need more than
734              * one transfer to perform this operation, and it's a StartIO-based driver
735              * (such as CDROM), then just use a single packet and use the retry logic
736              * that's already built-in to the packet engine.  Note that low-mem retry
737              * cannot be used directly because some write methods do not work if the
738              * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
739              */
740             NT_ASSERT(!Pkt->InLowMemRetry);
741 
742             /*
743              *  We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
744              *  If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
745              *  Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
746              *           transfer aligned 64K requests in one piece.
747              *  Also note:  make sure adding PAGE_SIZE back in doesn't wrap to zero.
748              */
749 
750             if (((ULONG_PTR)(Pkt->LowMemRetry_remainingBufPtr) & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
751                 hwMaxXferLen = fdoData->HwMaxXferLen;
752             }
753             else {
754                 NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
755                 hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
756             }
757             thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, hwMaxXferLen);
758         }
759         else {
760             /*
761              *  Make sure the little chunk we send is <= a page length
762              *  AND that it does not cross any page boundaries.
763              */
764             ULONG bytesToNextPageBoundary;
765             bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
766             thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
767             NT_ASSERT(Pkt->InLowMemRetry);
768         }
769 
770 
771         /*
772          *  Set up the transfer packet for the new little chunk.
773          *  This will reset numRetries so that we retry each chunk as required.
774          */
775         SetupReadWriteTransferPacket(Pkt,
776                                      Pkt->LowMemRetry_remainingBufPtr,
777                                      thisChunkLen,
778                                      Pkt->LowMemRetry_nextChunkTargetLocation,
779                                      Pkt->OriginalIrp);
780 
781         Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
782         Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
783         Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
784 
785         //
786         // When running in low-memory stress, always use a partial MDL.
787         // This allows lower drivers to potentially map a smaller buffer.
788         //
789         Pkt->UsePartialMdl = TRUE;
790 
791         TransferPacketQueueRetryDpc(Pkt);
792 
793         packetDone = FALSE;
794     }
795 
796     return packetDone;
797 }
798 
799