summaryrefslogtreecommitdiff
path: root/storage/class/classpnp/src/retry.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /storage/class/classpnp/src/retry.c
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'storage/class/classpnp/src/retry.c')
-rw-r--r--storage/class/classpnp/src/retry.c758
1 files changed, 758 insertions, 0 deletions
diff --git a/storage/class/classpnp/src/retry.c b/storage/class/classpnp/src/retry.c
new file mode 100644
index 00000000..90d70ce3
--- /dev/null
+++ b/storage/class/classpnp/src/retry.c
@@ -0,0 +1,758 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ retry.c
+
+Abstract:
+
+ Packet retry routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "retry.tmh"
+#endif
+
+
+/*
+ * InterpretTransferPacketError
+ *
+ * Interpret the SRB error into a meaningful IRP status.
+ * ClassInterpretSenseInfo also may modify the SRB for the retry.
+ *
+ * Return TRUE iff packet should be retried.
+ */
+BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ ULONG timesAlreadyRetried;
+ BOOLEAN shouldRetry = FALSE;
+ PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
+
+ /*
+ * Interpret the error using the returned sense info first.
+ */
+ Pkt->RetryIn100nsUnits = 0;
+
+
+ /*
+ * Pre-calculate the number of times the IO has already been
+ * retried, so that all InterpretSenseInfo routines get the right value.
+ */
+ if (ClasspTransferPacketGetNumberOfRetriesDone(Pkt, pCdb, &timesAlreadyRetried) == FALSE)
+ {
+ 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));
+ }
+
+ if (fdoData->InterpretSenseInfo != NULL) {
+
+ SCSI_REQUEST_BLOCK tempSrb = { 0 };
+ PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
+
+ // SAL annotation and ClassInitializeEx() both validate this
+ NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
+
+ //
+ // If class driver does not support extended SRB and this is
+ // an extended SRB, convert to legacy SRB and pass to class
+ // driver.
+ //
+ if ((Pkt->Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
+ CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
+ ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Pkt->Srb);
+ srbPtr = &tempSrb;
+ }
+
+ shouldRetry = fdoData->InterpretSenseInfo->Interpret(Pkt->Fdo,
+ Pkt->OriginalIrp,
+ srbPtr,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ Pkt->RetryHistory,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIn100nsUnits);
+
+
+ } else {
+
+ //
+ // In this case, fdoData->InterpretSenseInfo == NULL so we must do our
+ // own error code and sense info processing.
+ //
+
+ PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
+ UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
+ BOOLEAN validSense = FALSE;
+ UCHAR senseKey = 0;
+ UCHAR additionalSenseCode = 0;
+ UCHAR additionalSenseQual = 0;
+
+ NT_ASSERT(senseInfoBuffer);
+
+ validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
+ senseInfoBufferLength,
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &additionalSenseCode,
+ &additionalSenseQual);
+
+ if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is an Ejection Control SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ if (shouldRetry) {
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ BOOLEAN setRetryIntervalSeconds = FALSE;
+
+ if (validSense) {
+
+ if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+ }
+
+ if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+
+ if (setRetryIntervalSeconds) {
+ retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
+ }
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
+ (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is an Mode Sense SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+ if (shouldRetry) {
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ BOOLEAN setRetryIntervalSeconds = FALSE;
+
+ if (validSense) {
+ if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+ }
+
+ if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+
+ if (setRetryIntervalSeconds) {
+ retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
+ }
+ }
+
+ /*
+ * Some special cases for mode sense.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
+ shouldRetry = TRUE;
+ }
+ else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ /*
+ * This is a HACK.
+ * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
+ * underrun (i.e. success, and the buffer is longer than needed).
+ * So treat this as a success.
+ * When the caller of this function sees that the status was changed to success,
+ * it will add the transferred length to the original irp.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
+ shouldRetry = FALSE;
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
+ (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) {
+
+ ULONG retryIntervalSeconds = 0;
+
+ /*
+ * This is a Drive Capacity SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
+ shouldRetry = TRUE;
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is a Read/Write Data packet.
+ */
+ PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
+
+ shouldRetry = ClassInterpretSenseInfo(Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ origCurrentSp->MajorFunction,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ /*
+ * Deal with some special cases.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
+ /*
+ * We are in extreme low-memory stress.
+ * We will retry in smaller chunks.
+ */
+ shouldRetry = TRUE;
+ }
+ else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
+ /*
+ * We are still verifying a (possibly) reloaded disk/cdrom.
+ * So retry the request.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+ shouldRetry = TRUE;
+
+ }
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ else if (SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_BUS_RESET ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_TIMEOUT ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_COMMAND_TIMEOUT ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_ABORTED) {
+
+ Pkt->TimedOut = TRUE;
+
+ if (shouldRetry) {
+ //
+ // For requests that have timed-out we may only perform a limited
+ // number of retries. This is typically less than the general
+ // number of retries allowed.
+ //
+ if (Pkt->NumIoTimeoutRetries == 0) {
+ shouldRetry = FALSE;
+ } else {
+ Pkt->NumIoTimeoutRetries--;
+ //
+ // We expect to be able to retry if there are some general retries remaining.
+ //
+ NT_ASSERT(Pkt->NumRetries > 0);
+ }
+ }
+ }
+#endif
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ } else if (ClasspIsOffloadDataTransferCommand(pCdb)) {
+
+ ULONG retryIntervalSeconds = 0;
+
+ Pkt->TransferCount = 0;
+
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ if (shouldRetry) {
+
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+
+ } else {
+
+ if (ClasspIsTokenOperation(pCdb)) {
+
+ BOOLEAN isInformationValid = FALSE;
+ ULONGLONG information = 0;
+
+ if (validSense) {
+
+ //
+ // If this is a data underrun condition (i.e. target truncated the offload data transfer),
+ // the SenseData's Information field may have the TransferCount.
+ //
+ if ((senseKey == SCSI_SENSE_COPY_ABORTED || senseKey == SCSI_SENSE_ABORTED_COMMAND) &&
+ (additionalSenseCode == SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR && additionalSenseQual == SCSI_SENSEQ_DATA_UNDERRUN)) {
+
+ //
+ // Sense data in Descriptor format
+ //
+ if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
+
+ PVOID startBuffer = NULL;
+ UCHAR startBufferLength = 0;
+
+
+ if (ScsiGetSenseDescriptor(senseInfoBuffer,
+ SrbGetSenseInfoBufferLength(Pkt->Srb),
+ &startBuffer,
+ &startBufferLength)) {
+ UCHAR outType;
+ PVOID outBuffer = NULL;
+ UCHAR outBufferLength = 0;
+
+ UCHAR typeList[1] = { SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION };
+
+ if (ScsiGetNextSenseDescriptorByType(startBuffer,
+ startBufferLength,
+ typeList,
+ ARRAYSIZE(typeList),
+ &outType,
+ &outBuffer,
+ &outBufferLength)) {
+
+ if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
+
+ if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
+ REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
+ isInformationValid = TRUE;
+ }
+
+ } else {
+
+ //
+ // ScsiGetNextDescriptorByType should only return a type that is specified by us.
+ //
+ NT_ASSERT(FALSE);
+ }
+ }
+ }
+ } else {
+
+ //
+ // Sense data in Fixed format
+ //
+ REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseInfoBuffer)->Information));
+ isInformationValid = TRUE;
+ }
+
+ if (isInformationValid) {
+ Pkt->TransferCount = information;
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else {
+ 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));
+ }
+ }
+
+ return shouldRetry;
+}
+
+
+/*
+ * 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;
+ BOOLEAN scaleDown = FALSE;
+
+ 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 (Pkt->NumRetries == 0) {
+ scaleDown = TRUE;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ //
+ // If this request previously timed-out and there are no more retries left
+ // for timed-out requests then we should also apply the scale down.
+ //
+ if (Pkt->TimedOut &&
+ Pkt->NumIoTimeoutRetries == 0) {
+ scaleDown = TRUE;
+ }
+#endif
+
+ if (scaleDown) {
+ /*
+ * 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;
+}
+
+
+VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt)
+{
+ KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
+
+ if (Pkt->RetryIn100nsUnits == 0){
+ KeInsertQueueDpc(&Pkt->RetryTimerDPC, NULL, NULL);
+ }
+ else {
+ LARGE_INTEGER timerPeriod;
+
+ NT_ASSERT(Pkt->RetryIn100nsUnits < 100 * 1000 * 1000 * 10); // sanity check -- 100 seconds is normally too long
+ timerPeriod.QuadPart = -(Pkt->RetryIn100nsUnits);
+ KeInitializeTimer(&Pkt->RetryTimer);
+ KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
+ }
+}
+
+
+VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ PTRANSFER_PACKET pkt;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+
+ _Analysis_assume_(DeferredContext != NULL);
+
+ pkt = (PTRANSFER_PACKET)DeferredContext;
+
+ fdo = pkt->Fdo;
+ fdoExtension = fdo->DeviceExtension;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+
+ /*
+ * Sometimes the port driver can allocates a new 'sense' buffer
+ * to report transfer errors, e.g. when the default sense buffer
+ * is too small. If so, it is up to us to free it.
+ * Now that we're done using the sense info, free it if appropriate.
+ * Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
+ */
+ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, pkt->Srb)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, pkt->Srb);
+ SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
+ }
+ else {
+ NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
+ NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
+ }
+
+ RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
+
+ SubmitTransferPacket(pkt);
+
+}
+
+
+VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
+{
+ NT_ASSERT(Len > 0);
+ NT_ASSERT(!Pkt->InLowMemRetry);
+
+ if (Pkt->DriverUsesStartIO)
+ {
+ /*
+ * special case: StartIO-based writing must stay serialized for performance
+ * and proper operations (i.e. sequential writing mode). If need more than
+ * one transfer to perform this operation, and it's a StartIO-based driver
+ * (such as CDROM), then just use a single packet and use the retry logic
+ * that's already built-in to the packet engine. Note that low-mem retry
+ * cannot be used directly because some write methods do not work if the
+ * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
+ */
+ Pkt->InLowMemRetry = FALSE;
+ }
+ else
+ {
+ Pkt->InLowMemRetry = TRUE;
+ }
+ Pkt->LowMemRetry_remainingBufPtr = BufPtr;
+ Pkt->LowMemRetry_remainingBufLen = Len;
+ Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
+}
+
+
+/*
+ * StepLowMemRetry
+ *
+ * During extreme low-memory stress, this function retries
+ * a packet in small one-page chunks, sent serially.
+ *
+ * Returns TRUE iff the packet is done.
+ */
+BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN packetDone;
+
+ if (Pkt->LowMemRetry_remainingBufLen == 0){
+ packetDone = TRUE;
+ }
+ else {
+ ULONG thisChunkLen;
+ if (Pkt->DriverUsesStartIO)
+ {
+ /*
+ * Need the fdoData for the HwMaxXferLen
+ */
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+
+ /*
+ * Need the adapterDesc to limit transfers based on byte count
+ */
+ PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
+
+ ULONG hwMaxXferLen;
+
+ /*
+ * special case: StartIO-based writing must stay serialized for performance
+ * and proper operations (i.e. sequential writing mode). If need more than
+ * one transfer to perform this operation, and it's a StartIO-based driver
+ * (such as CDROM), then just use a single packet and use the retry logic
+ * that's already built-in to the packet engine. Note that low-mem retry
+ * cannot be used directly because some write methods do not work if the
+ * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
+ */
+ NT_ASSERT(!Pkt->InLowMemRetry);
+
+ /*
+ * We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
+ * If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
+ * Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
+ * transfer aligned 64K requests in one piece.
+ * Also note: make sure adding PAGE_SIZE back in doesn't wrap to zero.
+ */
+
+ if (((ULONG_PTR)(Pkt->LowMemRetry_remainingBufPtr) & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
+ hwMaxXferLen = fdoData->HwMaxXferLen;
+ }
+ else {
+ NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
+ hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
+ }
+ thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, hwMaxXferLen);
+ }
+ else {
+ /*
+ * Make sure the little chunk we send is <= a page length
+ * AND that it does not cross any page boundaries.
+ */
+ ULONG bytesToNextPageBoundary;
+ bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
+ thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
+ NT_ASSERT(Pkt->InLowMemRetry);
+ }
+
+
+ /*
+ * Set up the transfer packet for the new little chunk.
+ * This will reset numRetries so that we retry each chunk as required.
+ */
+ SetupReadWriteTransferPacket(Pkt,
+ Pkt->LowMemRetry_remainingBufPtr,
+ thisChunkLen,
+ Pkt->LowMemRetry_nextChunkTargetLocation,
+ Pkt->OriginalIrp);
+
+ Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
+ Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
+ Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
+
+ //
+ // When running in low-memory stress, always use a partial MDL.
+ // This allows lower drivers to potentially map a smaller buffer.
+ //
+ Pkt->UsePartialMdl = TRUE;
+
+ TransferPacketQueueRetryDpc(Pkt);
+
+ packetDone = FALSE;
+ }
+
+ return packetDone;
+}
+