Exemplo n.º 1
0
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);

}
Exemplo n.º 2
0
Arquivo: retry.c Projeto: uri247/wdk80
/*
 *  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 (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL)
    {
        timesAlreadyRetried = NUM_LOCKMEDIAREMOVAL_RETRIES - Pkt->NumRetries;
    }
    else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
             (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10))
    {
        timesAlreadyRetried = NUM_MODESENSE_RETRIES - Pkt->NumRetries;
    }
    else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
             (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16))
    {
        timesAlreadyRetried = NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries;
    }
    else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode))
    {
        timesAlreadyRetried = NUM_IO_RETRIES - Pkt->NumRetries;
    }
    else if (pCdb->TOKEN_OPERATION.OperationCode == SCSIOP_POPULATE_TOKEN &&
             pCdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_POPULATE_TOKEN)
    {
        timesAlreadyRetried = NUM_POPULATE_TOKEN_RETRIES - Pkt->NumRetries;
    }
    else if (pCdb->TOKEN_OPERATION.OperationCode == SCSIOP_WRITE_USING_TOKEN &&
             pCdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN)
    {
        timesAlreadyRetried = NUM_WRITE_USING_TOKEN_RETRIES - Pkt->NumRetries;
    }
    else if (ClasspIsReceiveTokenInformation(pCdb))
    {
        timesAlreadyRetried = NUM_RECEIVE_TOKEN_INFORMATION_RETRIES - Pkt->NumRetries;
    }
    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));
        timesAlreadyRetried = 0;
    }


    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 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.
             */
            PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);

            UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
            BOOLEAN validSense = FALSE;
            UCHAR additionalSenseCode = 0;
            BOOLEAN setRetryIntervalSeconds = FALSE;

            validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
                                                 senseInfoBufferLength,
                                                 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
                                                 NULL,
                                                 &additionalSenseCode,
                                                 NULL);

            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.
             */
            PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
            UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
            BOOLEAN validSense = FALSE;
            UCHAR additionalSenseCode = 0;
            BOOLEAN setRetryIntervalSeconds = FALSE;

            NT_ASSERT(senseInfoBuffer);

            validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
                                                 senseInfoBufferLength,
                                                 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
                                                 NULL,
                                                 &additionalSenseCode,
                                                 NULL);

            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 (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)) {

                PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
                UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
                BOOLEAN validSense = FALSE;
                UCHAR senseKey = 0;
                UCHAR additionalSenseCode = 0;
                UCHAR additionalSenseQual = 0;
                BOOLEAN isInformationValid = FALSE;
                ULONGLONG information = 0;

                NT_ASSERT(senseInfoBuffer);

                validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
                                                     senseInfoBufferLength,
                                                     SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
                                                     &senseKey,
                                                     &additionalSenseCode,
                                                     &additionalSenseQual);

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