⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 retry.c

📁 This is the library for all storage drivers. It simplifies writing a storage driver by implementing
💻 C
字号:
/*++

Copyright (C) Microsoft Corporation, 1991 - 1999

Module Name:

    retry.c

Abstract:

    Packet retry routines for CLASSPNP

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#include "classp.h"
#include "debug.h"



/*
 *  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)
{
    BOOLEAN shouldRetry = FALSE;
    PCDB pCdb = (PCDB)Pkt->Srb.Cdb;

    /*
     *  Interpret the error using the returned sense info first.
     */
    Pkt->RetryIntervalSec = 0;
    if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){
        /*
         *  This is an Ejection Control SRB.  Interpret its sense info specially.
         */
        shouldRetry = ClassInterpretSenseInfo(
                            Pkt->Fdo,
                            &Pkt->Srb,
                            IRP_MJ_SCSI,
                            0,
                            NUM_LOCKMEDIAREMOVAL_RETRIES - Pkt->NumRetries,
                            &Pkt->Irp->IoStatus.Status,
                            &Pkt->RetryIntervalSec);
        if (shouldRetry){
            /*
             *  If the device is not ready, wait at least 2 seconds before retrying.
             */
            PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
            ASSERT(senseInfoBuffer);
            if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
                (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
                    (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){

                Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
            }
        }
    }
    else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
            (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){
        /*
         *  This is an Mode Sense SRB.  Interpret its sense info specially.
         */
        shouldRetry = ClassInterpretSenseInfo(
                            Pkt->Fdo,
                            &Pkt->Srb,
                            IRP_MJ_SCSI,
                            0,
                            NUM_MODESENSE_RETRIES - Pkt->NumRetries,
                            &Pkt->Irp->IoStatus.Status,
                            &Pkt->RetryIntervalSec);
        if (shouldRetry){
            /*
             *  If the device is not ready, wait at least 2 seconds before retrying.
             */
            PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
            ASSERT(senseInfoBuffer);
            if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
                (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
                    (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){

                Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 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;
        }
    }
    else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
             (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) {
        /*
         *  This is a Drive Capacity SRB.  Interpret its sense info specially.
         */
        shouldRetry = ClassInterpretSenseInfo(
                            Pkt->Fdo,
                            &Pkt->Srb,
                            IRP_MJ_SCSI,
                            0,
                            NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries,
                            &Pkt->Irp->IoStatus.Status,
                            &Pkt->RetryIntervalSec);
        if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
            shouldRetry = TRUE;
        }
    }
    else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {

        /*
         *  This is a Read/Write Data packet.
         */
        PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);

        shouldRetry = ClassInterpretSenseInfo(
                            Pkt->Fdo,
                            &Pkt->Srb,
                            origCurrentSp->MajorFunction,
                            0,
                            NUM_IO_RETRIES - Pkt->NumRetries,
                            &Pkt->Irp->IoStatus.Status,
                            &Pkt->RetryIntervalSec);
        /*
         *  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;
        }
    }
    else {
        DBGERR(("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;

    DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));

    ASSERT(Pkt->NumRetries > 0);
    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.
         */
        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 = IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode);

        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;
}


VOID TransferPacketRetryTimerDpc(   IN PKDPC Dpc,
                                    IN PVOID DeferredContext,
                                    IN PVOID SystemArgument1,
                                    IN PVOID SystemArgument2)
{
    PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
    SubmitTransferPacket(pkt);
}


VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
{
    ASSERT(Len > 0);
    ASSERT(!Pkt->InLowMemRetry);

    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;
        ULONG bytesToNextPageBoundary;

        /*
         *  Make sure the little chunk we send is <= a page length
         *  AND that it does not cross any page boundaries.
         */
        bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
        thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);

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

        SubmitTransferPacket(Pkt);
        packetDone = FALSE;
    }

    return packetDone;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -