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 */ 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 */ 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 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 */ 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 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 */ 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