Exemplo n.º 1
0
Arquivo: retry.c Projeto: uri247/wdk80
/*
 *  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;
}
Exemplo n.º 2
0
/*
 *  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;
}