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, ×AlreadyRetried) == 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