/* * RetryTransferPacket * * Retry sending a TRANSFER_PACKET. * * Return TRUE iff the packet is complete. * (if so the status in pkt->irp is the final status). */ BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt) { BOOLEAN packetDone; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb))); NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory); Pkt->NumRetries--; // // If this is the last retry, then turn off disconnect, sync transfer, // and tagged queuing. On all other retries, leave the original settings. // if ( 0 == Pkt->NumRetries ) { /* * Tone down performance on the retry. * This increases the chance for success on the retry. * We've seen instances of drives that fail consistently but then start working * once this scale-down is applied. */ SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_DISCONNECT); SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); SrbClearSrbFlags(Pkt->Srb, SRB_FLAGS_QUEUE_ACTION_ENABLE); SrbSetRequestTag(Pkt->Srb, SP_UNTAGGED); } if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) { PCDB pCdb = SrbGetCdb(Pkt->Srb); UCHAR cdbOpcode = 0; BOOLEAN isReadWrite = FALSE; PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; if (pCdb) { cdbOpcode = pCdb->CDB10.OperationCode; isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode); } if ((Pkt->DriverUsesStartIO) && ( (cdbOpcode == SCSIOP_WRITE6 ) || (cdbOpcode == SCSIOP_WRITE ) || (cdbOpcode == SCSIOP_WRITE12) || (cdbOpcode == SCSIOP_WRITE16) )) { /* don't retry writes in super-low-memory conditions if the * driver must serialize against StartIO. This is because * some write methods used in such drivers cannot accept * random-sized writes. (i.e CD-RW in packet writing mode) * Reads, however, are always safe to split up. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH1); packetDone = TRUE; } else if (Pkt->InLowMemRetry || !isReadWrite){ /* * This should never happen under normal circumstances. * The memory manager guarantees that at least four pages will * be available to allow forward progress in the port driver. * So a one-page transfer should never fail with insufficient resources. * * However, it is possible to get in here with virtual storage * or thin provisioned storage for example. * A single sector write can trigger an allocation request and * presently a forward progress guarantee is not provided. * VHD also may have some limitations in forward progress guarantee. * And USB too might also fall into this category. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH2); packetDone = TRUE; } else { /* * We are in low-memory stress. * Start the low-memory retry state machine, which tries to * resend the packet in little one-page chunks. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH3); InitLowMemRetry(Pkt, Pkt->BufPtrCopy, Pkt->BufLenCopy, Pkt->TargetLocationCopy); StepLowMemRetry(Pkt); packetDone = FALSE; } } else { /* * Retry the packet by simply resending it after a delay. * Put the packet back in the pending queue and * schedule a timer to retry the transfer. * * Do not call SetupReadWriteTransferPacket again because: * (1) The minidriver may have set some bits * in the SRB that it needs again and * (2) doing so would reset numRetries. * * BECAUSE we do not call SetupReadWriteTransferPacket again, * we have to reset a couple fields in the SRB that * some miniports overwrite when they fail an SRB. */ SrbSetDataBuffer(Pkt->Srb, Pkt->BufPtrCopy); SrbSetDataTransferLength(Pkt->Srb, Pkt->BufLenCopy); TransferPacketQueueRetryDpc(Pkt); packetDone = FALSE; } return packetDone; }
/* * RetryTransferPacket * * Retry sending a TRANSFER_PACKET. * * Return TRUE iff the packet is complete. * (if so the status in pkt->irp is the final status). */ BOOLEAN NTAPI RetryTransferPacket(PTRANSFER_PACKET Pkt) { BOOLEAN packetDone; DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb))); ASSERT(Pkt->NumRetries > 0); Pkt->NumRetries--; /* * Tone down performance on the retry. * This increases the chance for success on the retry. * We've seen instances of drives that fail consistently but then start working * once this scale-down is applied. */ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); CLEAR_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); Pkt->Srb.QueueTag = SP_UNTAGGED; if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){ PCDB pCdb = (PCDB)Pkt->Srb.Cdb; BOOLEAN isReadWrite = ((pCdb->CDB10.OperationCode == SCSIOP_READ) || (pCdb->CDB10.OperationCode == SCSIOP_WRITE)); if (Pkt->InLowMemRetry || !isReadWrite){ /* * This should never happen. * The memory manager guarantees that at least four pages will * be available to allow forward progress in the port driver. * So a one-page transfer should never fail with insufficient resources. */ ASSERT(isReadWrite && !Pkt->InLowMemRetry); packetDone = TRUE; } else { /* * We are in low-memory stress. * Start the low-memory retry state machine, which tries to * resend the packet in little one-page chunks. */ InitLowMemRetry( Pkt, Pkt->BufPtrCopy, Pkt->BufLenCopy, Pkt->TargetLocationCopy); StepLowMemRetry(Pkt); packetDone = FALSE; } } else { /* * Retry the packet by simply resending it after a delay. * Put the packet back in the pending queue and * schedule a timer to retry the transfer. * * Do not call SetupReadWriteTransferPacket again because: * (1) The minidriver may have set some bits * in the SRB that it needs again and * (2) doing so would reset numRetries. * * BECAUSE we do not call SetupReadWriteTransferPacket again, * we have to reset a couple fields in the SRB that * some miniports overwrite when they fail an SRB. */ Pkt->Srb.DataBuffer = Pkt->BufPtrCopy; Pkt->Srb.DataTransferLength = Pkt->BufLenCopy; if (Pkt->RetryIntervalSec == 0){ /* * Always delay by at least a little when retrying. * Some problems (e.g. CRC errors) are not recoverable without a slight delay. */ LARGE_INTEGER timerPeriod; timerPeriod.HighPart = -1; timerPeriod.LowPart = -(LONG)((ULONG)MINIMUM_RETRY_UNITS*KeQueryTimeIncrement()); KeInitializeTimer(&Pkt->RetryTimer); KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt); KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC); } else { LARGE_INTEGER timerPeriod; ASSERT(Pkt->RetryIntervalSec < 100); // sanity check timerPeriod.HighPart = -1; timerPeriod.LowPart = Pkt->RetryIntervalSec*-10000000; KeInitializeTimer(&Pkt->RetryTimer); KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt); KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC); } packetDone = FALSE; } return packetDone; }