in storage/class/classpnp/src/class.c [4415:6564]
_Out_opt_ _Deref_out_range_(0,100) ULONG *RetryInterval
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
PVOID senseBuffer = SrbGetSenseInfoBuffer(Srb);
BOOLEAN retry = TRUE;
BOOLEAN logError = FALSE;
BOOLEAN unhandledError = FALSE;
BOOLEAN incrementErrorCount = FALSE;
//
// NOTE: This flag must be used only for read/write requests that
// fail with a unexpected retryable error.
//
BOOLEAN logRetryableError = TRUE;
//
// Indicates if we should log this error in our internal log.
//
BOOLEAN logErrorInternal = TRUE;
ULONGLONG badSector = 0;
ULONG uniqueId = 0;
NTSTATUS logStatus;
ULONGLONG readSector;
ULONG index;
ULONG retryInterval = 0;
KIRQL oldIrql;
PCDB cdb = SrbGetCdb(Srb);
UCHAR cdbOpcode = 0;
ULONG cdbLength = SrbGetCdbLength(Srb);
#if DBG
BOOLEAN isReservationConflict = FALSE;
#endif
if (cdb) {
cdbOpcode = cdb->CDB6GENERIC.OperationCode;
}
*Status = STATUS_IO_DEVICE_ERROR;
logStatus = -1;
if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
//
// Log anything remotely incorrect about paging i/o
//
logError = TRUE;
uniqueId = 301;
logStatus = IO_WARNING_PAGING_FAILURE;
}
//
// Check that request sense buffer is valid.
//
NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
//
// must handle the SRB_STATUS_INTERNAL_ERROR case first,
// as it has all the flags set.
//
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: Internal Error code is %x\n",
SrbGetSystemStatus(Srb)));
retry = FALSE;
*Status = SrbGetSystemStatus(Srb);
} else if (SrbGetScsiStatus(Srb) == SCSISTAT_RESERVATION_CONFLICT) {
//
// Need to reserve STATUS_DEVICE_BUSY to convey reservation conflict
// for read/write requests as there are upper level components that
// have built-in assumptions that STATUS_DEVICE_BUSY implies reservation
// conflict.
//
*Status = STATUS_DEVICE_BUSY;
retry = FALSE;
logError = FALSE;
#if DBG
isReservationConflict = TRUE;
#endif
} else {
BOOLEAN validSense = FALSE;
if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && senseBuffer) {
UCHAR errorCode = 0;
UCHAR senseKey = 0;
UCHAR addlSenseCode = 0;
UCHAR addlSenseCodeQual = 0;
BOOLEAN isIncorrectLengthValid = FALSE;
BOOLEAN incorrectLength = FALSE;
BOOLEAN isInformationValid = FALSE;
ULONGLONG information = 0;
validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
SrbGetSenseInfoBufferLength(Srb),
SCSI_SENSE_OPTIONS_NONE,
&senseKey,
&addlSenseCode,
&addlSenseCodeQual);
if (!validSense && !IsSenseDataFormatValueValid(senseBuffer)) {
NT_ASSERT(FALSE);
validSense = ScsiGetFixedSenseKeyAndCodes(senseBuffer,
SrbGetSenseInfoBufferLength(Srb),
&senseKey,
&addlSenseCode,
&addlSenseCodeQual);
}
if (!validSense) {
goto __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer;
}
errorCode = ScsiGetSenseErrorCode(senseBuffer);
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Error code is %x\n", errorCode));
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Sense key is %x\n", senseKey));
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code is %x\n", addlSenseCode));
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code qualifier is %x\n", addlSenseCodeQual));
if (IsDescriptorSenseDataFormat(senseBuffer)) {
//
// Sense data in Descriptor format
//
PVOID startBuffer = NULL;
UCHAR startBufferLength = 0;
if (ScsiGetSenseDescriptor(senseBuffer,
SrbGetSenseInfoBufferLength(Srb),
&startBuffer,
&startBufferLength)) {
UCHAR outType;
PVOID outBuffer = NULL;
UCHAR outBufferLength = 0;
BOOLEAN foundBlockCommandType = FALSE;
BOOLEAN foundInformationType = FALSE;
UCHAR descriptorLength = 0;
UCHAR typeList[2] = {SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION,
SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND};
while ((!foundBlockCommandType || !foundInformationType) &&
ScsiGetNextSenseDescriptorByType(startBuffer,
startBufferLength,
typeList,
ARRAYSIZE(typeList),
&outType,
&outBuffer,
&outBufferLength)) {
descriptorLength = ScsiGetSenseDescriptorLength(outBuffer);
if (outBufferLength < descriptorLength) {
// Descriptor data is truncated.
// Complete searching descriptors. Exit the loop now.
break;
}
if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND) {
//
// Block Command type
//
if (!foundBlockCommandType) {
foundBlockCommandType = TRUE;
if (ScsiValidateBlockCommandSenseDescriptor(outBuffer, outBufferLength)) {
incorrectLength = ((PSCSI_SENSE_DESCRIPTOR_BLOCK_COMMAND)outBuffer)->IncorrectLength;
isIncorrectLengthValid = TRUE;
}
} else {
//
// A Block Command descriptor is already found earlier.
//
// T10 SPC specification only allows one descriptor for Block Command Descriptor type.
// Assert here to catch devices that violate this rule. Ignore this descriptor.
//
NT_ASSERT(FALSE);
}
} else if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
//
// Information type
//
if (!foundInformationType) {
foundInformationType = TRUE;
if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
isInformationValid = TRUE;
}
} else {
//
// A Information descriptor is already found earlier.
//
// T10 SPC specification only allows one descriptor for Information Descriptor type.
// Assert here to catch devices that violate this rule. Ignore this descriptor.
//
NT_ASSERT(FALSE);
}
} else {
//
// ScsiGetNextDescriptorByType should only return a type that is specified by us.
//
NT_ASSERT(FALSE);
break;
}
//
// Advance to start address of next descriptor
//
startBuffer = (PUCHAR)outBuffer + descriptorLength;
startBufferLength = outBufferLength - descriptorLength;
}
}
} else {
//
// Sense data in Fixed format
//
incorrectLength = ((PFIXED_SENSE_DATA)(senseBuffer))->IncorrectLength;
REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseBuffer)->Information));
isInformationValid = TRUE;
isIncorrectLengthValid = TRUE;
}
switch (senseKey) {
case SCSI_SENSE_NO_SENSE: {
//
// Check other indicators.
//
if (isIncorrectLengthValid && incorrectLength) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Incorrect length detected.\n"));
*Status = STATUS_INVALID_BLOCK_LENGTH ;
retry = FALSE;
} else {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"No specific sense key\n"));
*Status = STATUS_IO_DEVICE_ERROR;
retry = TRUE;
}
break;
} // end SCSI_SENSE_NO_SENSE
case SCSI_SENSE_RECOVERED_ERROR: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Recovered error\n"));
*Status = STATUS_SUCCESS;
retry = FALSE;
logError = TRUE;
uniqueId = 258;
switch(addlSenseCode) {
case SCSI_ADSENSE_TRACK_ERROR:
case SCSI_ADSENSE_SEEK_ERROR: {
logStatus = IO_ERR_SEEK_ERROR;
break;
}
case SCSI_ADSENSE_REC_DATA_NOECC:
case SCSI_ADSENSE_REC_DATA_ECC: {
logStatus = IO_RECOVERED_VIA_ECC;
break;
}
case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
*((PULONG)wmiEventData) = sizeof(UCHAR);
wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
//
// Don't log another eventlog if we have already logged once
// NOTE: this should have been interlocked, but the structure
// was publicly defined to use a BOOLEAN (char). Since
// media only reports these errors once per X minutes,
// the potential race condition is nearly non-existant.
// the worst case is duplicate log entries, so ignore.
//
logError = FALSE;
if (fdoExtension->FailurePredicted == 0) {
logError = TRUE;
}
fdoExtension->FailureReason = addlSenseCodeQual;
logStatus = IO_WRN_FAILURE_PREDICTED;
ClassNotifyFailurePredicted(fdoExtension,
(PUCHAR)wmiEventData,
sizeof(wmiEventData),
FALSE, // do not log error
4, // unique error value
SrbGetPathId(Srb),
SrbGetTargetId(Srb),
SrbGetLun(Srb));
fdoExtension->FailurePredicted = TRUE;
break;
}
default: {
logStatus = IO_ERR_CONTROLLER_ERROR;
break;
}
} // end switch(addlSenseCode)
if (isIncorrectLengthValid && incorrectLength) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Incorrect length detected.\n"));
*Status = STATUS_INVALID_BLOCK_LENGTH ;
}
break;
} // end SCSI_SENSE_RECOVERED_ERROR
case SCSI_SENSE_NOT_READY: {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device not ready\n"));
*Status = STATUS_DEVICE_NOT_READY;
switch (addlSenseCode) {
case SCSI_ADSENSE_LUN_NOT_READY: {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Lun not ready\n"));
retryInterval = NOT_READY_RETRY_INTERVAL;
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_BECOMING_READY: {
DEVICE_EVENT_BECOMING_READY notReady = {0};
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"In process of becoming ready\n"));
notReady.Version = 1;
notReady.Reason = 1;
notReady.Estimated100msToReady = retryInterval * 10;
ClassSendNotification(fdoExtension,
&GUID_IO_DEVICE_BECOMING_READY,
sizeof(DEVICE_EVENT_BECOMING_READY),
¬Ready);
break;
}
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Manual intervention required\n"));
*Status = STATUS_NO_MEDIA_IN_DEVICE;
retry = FALSE;
break;
}
case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Format in progress\n"));
retry = FALSE;
break;
}
case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
DEVICE_EVENT_BECOMING_READY notReady = {0};
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Operation In Progress\n"));
notReady.Version = 1;
notReady.Reason = 2;
notReady.Estimated100msToReady = retryInterval * 10;
ClassSendNotification(fdoExtension,
&GUID_IO_DEVICE_BECOMING_READY,
sizeof(DEVICE_EVENT_BECOMING_READY),
¬Ready);
break;
}
case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Long write in progress\n"));
//
// This has been seen as a transcient failure on some cdrom
// drives. The cdrom class driver is going to override this
// setting but has no way of dropping the retry interval
//
retry = FALSE;
retryInterval = 1;
break;
}
case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: {
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"The device (%p) is busy allocating space.\n",
Fdo));
//
// This indicates that a thinly-provisioned device has hit
// a temporary resource exhaustion and is busy allocating
// more space. We need to retry the request as the device
// will eventually be able to service it.
//
*Status = STATUS_RETRY;
retry = TRUE;
break;
}
case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"not ready, cause unknown\n"));
/*
Many non-WHQL certified drives (mostly CD-RW) return
this when they have no media instead of the obvious
choice of:
SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
These drives should not pass WHQL certification due
to this discrepency.
*/
retry = FALSE;
break;
} else {
//
// Treat this as init command required and fall through.
//
}
}
case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
default: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Initializing command required\n"));
retryInterval = 0; // go back to default
logRetryableError = FALSE;
//
// This sense code/additional sense code
// combination may indicate that the device
// needs to be started. Send an start unit if this
// is a disk device.
//
if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_LOW_PRIORITY)){
ClassSendStartUnit(Fdo);
}
break;
}
} // end switch (addlSenseCodeQual)
break;
}
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"No Media in device.\n"));
*Status = STATUS_NO_MEDIA_IN_DEVICE;
retry = FALSE;
//
// signal MCN that there isn't any media in the device
//
if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"No Media in a non-removable device %p\n",
Fdo));
}
if (addlSenseCodeQual == 0xCC){
/*
* The IMAPI filter returns this ASCQ value when it is burning CD-R media.
* We want to indicate that the media is not present to most applications;
* but RSM has to know that the media is still in the drive (i.e. the drive is not free).
*/
ClassSetMediaChangeState(fdoExtension, MediaUnavailable, FALSE);
}
else {
ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
}
break;
}
} // end switch (addlSenseCode)
break;
} // end SCSI_SENSE_NOT_READY
case SCSI_SENSE_MEDIUM_ERROR: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Medium Error (bad block)\n"));
*Status = STATUS_DEVICE_DATA_ERROR;
retry = FALSE;
logError = TRUE;
uniqueId = 256;
logStatus = IO_ERR_BAD_BLOCK;
//
// Check if this error is due to unknown format
//
if (addlSenseCode == SCSI_ADSENSE_INVALID_MEDIA) {
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_UNKNOWN_FORMAT: {
*Status = STATUS_UNRECOGNIZED_MEDIA;
//
// Log error only if this is a paging request
//
if (!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
logError = FALSE;
}
break;
}
case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
*Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
logError = FALSE;
break;
}
default: {
break;
}
} // end switch addlSenseCodeQual
} // end SCSI_ADSENSE_INVALID_MEDIA
break;
} // end SCSI_SENSE_MEDIUM_ERROR
case SCSI_SENSE_HARDWARE_ERROR: {
BOOLEAN logHardwareError = TRUE;
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Hardware error\n"));
if (fdoData->LegacyErrorHandling == FALSE) {
//
// Hardware errors indicate something has seriously gone
// wrong with the device and retries are very unlikely to
// succeed so fail this request back immediately.
//
retry = FALSE;
*Status = STATUS_DEVICE_HARDWARE_ERROR;
logError = FALSE;
} else {
//
// Revert to legacy behavior. That is, retry everything by default.
//
retry = TRUE;
*Status = STATUS_IO_DEVICE_ERROR;
logError = TRUE;
uniqueId = 257;
logStatus = IO_ERR_CONTROLLER_ERROR;
logHardwareError = FALSE;
//
// This indicates the possibility of a dropped FC packet.
//
if ((addlSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addlSenseCodeQual == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) ||
(addlSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addlSenseCodeQual == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) {
//
// Fail requests that report this error back to the application.
//
retry = FALSE;
//
// Log a more descriptive error and avoid a second
// error message (IO_ERR_CONTROLLER_ERROR) being logged.
//
logHardwareError = TRUE;
logError = FALSE;
}
}
//
// If CRC error was returned, retry after a slight delay.
//
if (addlSenseCode == SCSI_ADSENSE_LUN_COMMUNICATION &&
addlSenseCodeQual == SCSI_SESNEQ_COMM_CRC_ERROR) {
retry = TRUE;
retryInterval = 1;
logHardwareError = FALSE;
logError = FALSE;
}
//
// Hardware errors warrant a more descriptive error.
// Specifically, we need to ensure this disk is easily
// identifiable.
//
if (logHardwareError) {
UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
UCHAR senseBufferSize = 0;
if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
senseInfoBufferLength,
&senseBufferSize)) {
senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
} else {
//
// it's smaller than required to read the total number of
// valid bytes, so just use the SenseInfoBufferLength field.
//
senseBufferSize = senseInfoBufferLength;
}
ClasspQueueLogIOEventWithContextWorker(Fdo,
senseBufferSize,
senseBuffer,
SRB_STATUS(Srb->SrbStatus),
SrbGetScsiStatus(Srb),
(ULONG)IO_ERROR_IO_HARDWARE_ERROR,
cdbLength,
cdb,
NULL);
}
break;
} // end SCSI_SENSE_HARDWARE_ERROR
case SCSI_SENSE_ILLEGAL_REQUEST: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Illegal SCSI request\n"));
*Status = STATUS_INVALID_DEVICE_REQUEST;
retry = FALSE;
switch (addlSenseCode) {
case SCSI_ADSENSE_NO_SENSE: {
switch (addlSenseCodeQual) {
//
// 1. Duplicate List Identifier
//
case SCSI_SENSEQ_OPERATION_IS_IN_PROGRESS: {
//
// XCOPY, READ BUFFER and CHANGE ALIASES return this sense combination under
// certain conditions. Since these commands aren't sent down natively by the
// Windows OS, return the default error for them and only handle this sense
// combination for offload data transfer commands.
//
if (ClasspIsOffloadDataTransferCommand(cdb)) {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Duplicate List Identifier (command %x, parameter field offset 0x%016llx)\n",
Fdo,
cdbOpcode,
information));
NT_ASSERTMSG("Duplicate list identifier specified", FALSE);
//
// The host should ensure that it uses unique list id for each TokenOperation request.
//
*Status = STATUS_OPERATION_IN_PROGRESS;
}
break;
}
}
break;
}
case SCSI_ADSENSE_LUN_COMMUNICATION: {
switch (addlSenseCodeQual) {
//
// 1. Source/Destination pairing can't communicate with each other or the copy manager.
//
case SCSI_SENSEQ_UNREACHABLE_TARGET: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Source-Destination LUNs can't communicate (command %x)\n",
Fdo,
cdbOpcode));
*Status = STATUS_DEVICE_UNREACHABLE;
break;
}
}
break;
}
case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
switch (addlSenseCodeQual) {
//
// 1. Sum of logical block fields in all block device range descriptors is greater than number
// of logical blocks in the ROD minus block offset into ROD
//
case SCSI_SENSEQ_DATA_UNDERRUN: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Host specified a transfer length greater than what is represented by the token (considering the offset) [command %x]\n",
Fdo,
cdbOpcode));
NT_ASSERTMSG("Host specified blocks to write beyond what is represented by the token", FALSE);
*Status = STATUS_DATA_OVERRUN;
break;
}
}
break;
}
//
// 1. Parameter data truncation (e.g. last descriptor was not fully specified)
//
case SCSI_ADSENSE_PARAMETER_LIST_LENGTH: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Target truncated the block device range descriptors in the parameter list (command %x)\n",
Fdo,
cdbOpcode));
NT_ASSERTMSG("Parameter data truncation", FALSE);
*Status = STATUS_DATA_OVERRUN;
break;
}
case SCSI_ADSENSE_ILLEGAL_COMMAND: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Illegal command\n"));
break;
}
case SCSI_ADSENSE_ILLEGAL_BLOCK: {
LARGE_INTEGER logicalBlockAddr;
LARGE_INTEGER lastLBA;
ULONG numTransferBlocks = 0;
logicalBlockAddr.QuadPart = 0;
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Illegal block address\n"));
*Status = STATUS_NONEXISTENT_SECTOR;
if (Fdo->DeviceType == FILE_DEVICE_DISK) {
if (IS_SCSIOP_READWRITE(cdbOpcode) && cdb) {
if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
REVERSE_BYTES_QUAD(&logicalBlockAddr, &cdb->CDB16.LogicalBlock);
REVERSE_BYTES(&numTransferBlocks, &cdb->CDB16.TransferLength);
} else {
REVERSE_BYTES(&logicalBlockAddr.LowPart, &cdb->CDB10.LogicalBlockByte0);
REVERSE_BYTES_SHORT((PUSHORT)&numTransferBlocks, &cdb->CDB10.TransferBlocksMsb);
}
REVERSE_BYTES_QUAD(&lastLBA, &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
if ((logicalBlockAddr.QuadPart > lastLBA.QuadPart) ||
((logicalBlockAddr.QuadPart + numTransferBlocks - 1) > lastLBA.QuadPart)) {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Request beyond boundary. Last LBA: 0x%I64X Read LBA: 0x%I64X Length: 0x%X\n",
(__int64) lastLBA.QuadPart, (__int64) logicalBlockAddr.QuadPart, numTransferBlocks));
} else {
//
// Should only retry these if the request was
// truly within our expected size.
//
// Fujitsu IDE drives have been observed to
// return this error transiently for a legal LBA;
// manual retry in the debugger then works, so
// there is a good chance that a programmed retry
// will also work.
//
retry = TRUE;
retryInterval = 5;
}
} else if (ClasspIsOffloadDataTransferCommand(cdb)) {
//
// 1. Number of logical blocks of block device range descriptor exceeds capacity of the medium
//
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): LBA out of range (command %x, parameter field offset 0x%016llx)\n",
Fdo,
cdbOpcode,
information));
NT_ASSERTMSG("Number of blocks specified exceeds LUN capacity", FALSE);
}
}
break;
}
//
// 1. Generic error - cause not reportable
// 2. Insufficient resources to create ROD
// 3. Insufficient resources to create Token
// 4. Max number of tokens exceeded
// 5. Remote Token creation not supported
// 6. Token expired
// 7. Token unknown
// 8. Unsupported Token type
// 9. Token corrupt
// 10. Token revoked
// 11. Token cancelled
// 12. Remote Token usage not supported
//
case SCSI_ADSENSE_INVALID_TOKEN: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Invalid/Expired/Modified token specified (command %x, parameter field offset 0x%016llx)\n",
Fdo,
cdbOpcode,
information));
*Status = STATUS_INVALID_TOKEN;
break;
}
case SCSI_ADSENSE_INVALID_CDB: {
if (ClasspIsOffloadDataTransferCommand(cdb)) {
//
// 1. Mismatched I_T nexus and list identifier
//
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Incorrect I_T nexus likely used (command %x)\n",
Fdo,
cdbOpcode));
//
// The host should ensure that it sends TokenOperation and ReceiveTokenInformation for the same
// list Id using the same I_T nexus.
//
*Status = STATUS_INVALID_INITIATOR_TARGET_PATH;
} else {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Invalid CDB\n"));
//
// Note: the retry interval is not typically used.
// it is set here only because a ClassErrorHandler
// cannot set the retryInterval, and the error may
// require a few commands to be sent to clear whatever
// caused this condition (i.e. disk clears the write
// cache, requiring at least two commands)
//
// hopefully, this shortcoming can be changed for
// blackcomb.
//
retryInterval = 3;
}
break;
}
case SCSI_ADSENSE_INVALID_LUN: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Invalid LUN\n"));
*Status = STATUS_NO_SUCH_DEVICE;
break;
}
case SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST: {
switch (addlSenseCodeQual) {
//
// 1. Alignment violation (e.g. copy manager is unable to copy because destination offset is NOT aligned to LUN's granularity/alignment)
//
case SCSI_SENSEQ_INVALID_RELEASE_OF_PERSISTENT_RESERVATION: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Alignment violation for command %x.\n",
Fdo,
cdbOpcode));
NT_ASSERTMSG("Specified offset is not aligned to LUN's granularity", FALSE);
*Status = STATUS_INVALID_OFFSET_ALIGNMENT;
break;
}
//
// 1. Number of block device range descriptors is greater than maximum range descriptors
//
case SCSI_SENSEQ_TOO_MANY_SEGMENT_DESCRIPTORS: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Too many descriptors in parameter list for command %x (parameter field offset 0x%016llx)\n",
Fdo,
cdbOpcode,
information));
NT_ASSERTMSG("Too many descriptors specified", FALSE);
*Status = STATUS_TOO_MANY_SEGMENT_DESCRIPTORS;
break;
}
default: {
if (ClasspIsOffloadDataTransferCommand(cdb)) {
//
// 1. (Various) Invalid parameter length
// 2. Requested inactivity timeout is greater than maximum inactivity timeout
// 3. Same LBA is included in more than one block device range descriptor (overlapping LBAs)
// 4. Total number of logical blocks of all block range descriptors is greater than the maximum transfer size
// 5. Total number of logical blocks of all block range descriptors is greater than maximum token transfer size
// (e.g. WriteUsingToken descriptors specify a cumulative total block count that exceeds the PopulateToken that created the token)
// 6. Block offset into ROD specified an offset that is greater than or equal to the number of logical blocks in the ROD
// 7. Number of logical blocks in a block device range descriptor is greater than maximum transfer length in blocks
//
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Illegal field in parameter list for command %x (parameter field offset 0x%016llx) [AddSense %x, AddSenseQ %x]\n",
Fdo,
cdbOpcode,
information,
addlSenseCode,
addlSenseCodeQual));
NT_ASSERTMSG("Invalid field in parameter list", FALSE);
*Status = STATUS_INVALID_FIELD_IN_PARAMETER_LIST;
}
break;
}
}
break;
}
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Copy protection failure\n"));
*Status = STATUS_COPY_PROTECTION_FAILURE;
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Authentication failure\n"));
*Status = STATUS_CSS_AUTHENTICATION_FAILURE;
break;
case SCSI_SENSEQ_KEY_NOT_PRESENT:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Key not present\n"));
*Status = STATUS_CSS_KEY_NOT_PRESENT;
break;
case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Key not established\n"));
*Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
break;
case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Read of scrambled sector w/o "
"authentication\n"));
*Status = STATUS_CSS_SCRAMBLED_SECTOR;
break;
case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Media region does not logical unit "
"region\n"));
*Status = STATUS_CSS_REGION_MISMATCH;
break;
case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
"ClassInterpretSenseInfo: "
"Region set error -- region may "
"be permanent\n"));
*Status = STATUS_CSS_RESETS_EXHAUSTED;
break;
} // end switch of ASCQ for COPY_PROTECTION_FAILURE
break;
}
case SCSI_ADSENSE_MUSIC_AREA: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Music area\n"));
break;
}
case SCSI_ADSENSE_DATA_AREA: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Data area\n"));
break;
}
case SCSI_ADSENSE_VOLUME_OVERFLOW: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Volume overflow\n"));
break;
}
} // end switch (addlSenseCode)
break;
} // end SCSI_SENSE_ILLEGAL_REQUEST
case SCSI_SENSE_UNIT_ATTENTION: {
ULONG count;
//
// A media change may have occured so increment the change
// count for the physical device
//
count = InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
"Media change count for device %d incremented to %#lx\n",
fdoExtension->DeviceNumber, count));
switch (addlSenseCode) {
case SCSI_ADSENSE_MEDIUM_CHANGED: {
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
"Media changed\n"));
if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
"Media Changed on non-removable device %p\n",
Fdo));
}
ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
break;
}
case SCSI_ADSENSE_BUS_RESET: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Bus reset\n"));
break;
}
case SCSI_ADSENSE_PARAMETERS_CHANGED: {
logRetryableError = FALSE;
if (addlSenseCodeQual == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device capacity changed (e.g. thinly provisioned LUN). Retry the request.\n"));
ClassQueueCapacityChangedEventWorker(Fdo);
//
// Retry with 1 second delay as ClassQueueCapacityChangedEventWorker may trigger a couple of commands sent to disk.
//
retryInterval = 1;
retry = TRUE;
}
break;
}
case SCSI_ADSENSE_LB_PROVISIONING: {
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_SOFT_THRESHOLD_REACHED: {
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device (%p) has hit a soft threshold.\n",
Fdo));
//
// This indicates that a resource provisioned or thinly
// provisioned device has hit a soft threshold. Queue a
// worker thread to log a system event and then retry the
// original request.
//
ClassQueueThresholdEventWorker(Fdo);
break;
}
default: {
retry = FALSE;
break;
}
} // end switch (addlSenseCodeQual)
break;
}
case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: {
if (addlSenseCodeQual == SCSI_SENSEQ_MICROCODE_CHANGED) {
//
// Device firmware has been changed. Retry the request.
//
logRetryableError = TRUE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device firmware has been changed.\n"));
retryInterval = 1;
retry = TRUE;
} else {
//
// Device information has changed, we need to rescan the
// bus for changed information such as the capacity.
//
logRetryableError = FALSE;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device information changed. Invalidate the bus\n"));
if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) {
ClassQueueProvisioningTypeChangedEventWorker(Fdo);
}
if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED ||
addlSenseCodeQual == SCSI_SENSEQ_OPERATING_DEFINITION_CHANGED) {
//
// Since either the LB provisioning type changed, or the block/slab size
// changed, next time anyone trying to query the FunctionSupportInfo, we
// will requery the device.
//
InterlockedIncrement((volatile LONG *)&fdoExtension->FunctionSupportInfo->ChangeRequestCount);
}
IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
retryInterval = 5;
}
break;
}
case SCSI_ADSENSE_OPERATOR_REQUEST: {
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_MEDIUM_REMOVAL: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Ejection request received!\n"));
ClassSendEjectionNotification(fdoExtension);
break;
}
case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Operator selected write permit?! "
"(unsupported!)\n"));
break;
}
case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Operator selected write protect?! "
"(unsupported!)\n"));
break;
}
}
}
case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
*((PULONG)wmiEventData) = sizeof(UCHAR);
wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
//
// Don't log another eventlog if we have already logged once
// NOTE: this should have been interlocked, but the structure
// was publicly defined to use a BOOLEAN (char). Since
// media only reports these errors once per X minutes,
// the potential race condition is nearly non-existant.
// the worst case is duplicate log entries, so ignore.
//
logError = FALSE;
if (fdoExtension->FailurePredicted == 0) {
logError = TRUE;
}
fdoExtension->FailureReason = addlSenseCodeQual;
logStatus = IO_WRN_FAILURE_PREDICTED;
ClassNotifyFailurePredicted(fdoExtension,
(PUCHAR)wmiEventData,
sizeof(wmiEventData),
FALSE, // do not log error
4, // unique error value
SrbGetPathId(Srb),
SrbGetTargetId(Srb),
SrbGetLun(Srb));
fdoExtension->FailurePredicted = TRUE;
//
// Since this is a Unit Attention we need to make
// sure we retry this request.
//
retry = TRUE;
break;
}
default: {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Unit attention\n"));
break;
}
} // end switch (addlSenseCode)
if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
{
if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
{
//
// Set bit to indicate that media may have changed
// and volume needs verification.
//
SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
*Status = STATUS_VERIFY_REQUIRED;
retry = FALSE;
}
else {
*Status = STATUS_IO_DEVICE_ERROR;
}
}
else
{
*Status = STATUS_IO_DEVICE_ERROR;
}
break;
} // end SCSI_SENSE_UNIT_ATTENTION
case SCSI_SENSE_DATA_PROTECT: {
retry = FALSE;
if (addlSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
{
switch (addlSenseCodeQual) {
case SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT: {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Device's (%p) resources are exhausted.\n",
Fdo));
ClassQueueResourceExhaustionEventWorker(Fdo);
//
// This indicates that a thinly-provisioned device has
// hit a permanent resource exhaustion. We need to
// return this status code so that patmgr can take the
// disk offline.
//
*Status = STATUS_DISK_RESOURCES_EXHAUSTED;
break;
}
default:
{
break;
}
} // end switch addlSenseCodeQual
}
else
{
if (IS_SCSIOP_WRITE(cdbOpcode)) {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Media write protected\n"));
*Status = STATUS_MEDIA_WRITE_PROTECTED;
} else {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Access denied\n"));
*Status = STATUS_ACCESS_DENIED;
}
}
break;
} // end SCSI_SENSE_DATA_PROTECT
case SCSI_SENSE_BLANK_CHECK: {
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Media blank check\n"));
retry = FALSE;
*Status = STATUS_NO_DATA_DETECTED;
break;
} // end SCSI_SENSE_BLANK_CHECK
case SCSI_SENSE_COPY_ABORTED: {
switch (addlSenseCode) {
case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
switch (addlSenseCodeQual) {
//
// 1. Target truncated the data transfer.
//
case SCSI_SENSEQ_DATA_UNDERRUN: {
TracePrint((TRACE_LEVEL_WARNING,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Data transfer was truncated (command %x)\n",
Fdo,
cdbOpcode));
*Status = STATUS_SUCCESS;
retry = FALSE;
break;
}
}
break;
}
}
break;
}
case SCSI_SENSE_ABORTED_COMMAND: {
if (ClasspIsOffloadDataTransferCommand(cdb)) {
switch (addlSenseCode) {
case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
switch (addlSenseCodeQual) {
//
// 1. Target truncated the data transfer.
//
case SCSI_SENSEQ_DATA_UNDERRUN: {
TracePrint((TRACE_LEVEL_WARNING,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Target has truncated the data transfer (command %x)\n",
Fdo,
cdbOpcode));
*Status = STATUS_SUCCESS;
retry = FALSE;
break;
}
}
break;
}
case SCSI_ADSENSE_RESOURCE_FAILURE: {
switch (addlSenseCodeQual) {
//
// 1. Copy manager wasn't able to finish the operation because of insuffient resources
// (e.g. microsnapshot failure on read, no space on write, etc.)
//
case SCSI_SENSEQ_INSUFFICIENT_RESOURCES: {
TracePrint((TRACE_LEVEL_ERROR,
TRACE_FLAG_IOCTL,
"ClassInterpretSenseInfo (%p): Target has insufficient resources (command %x)\n",
Fdo,
cdb->CDB6GENERIC.OperationCode));
*Status = STATUS_INSUFFICIENT_RESOURCES;
retry = FALSE;
break;
}
}
break;
}
}
} else {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Command aborted\n"));
*Status = STATUS_IO_DEVICE_ERROR;
retryInterval = 1;
}
break;
} // end SCSI_SENSE_ABORTED_COMMAND
default: {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Unrecognized sense code\n"));
*Status = STATUS_IO_DEVICE_ERROR;
break;
}
} // end switch (senseKey)
//
// Try to determine bad sector information from sense data
//
if (((IS_SCSIOP_READWRITE(cdbOpcode)) ||
(cdbOpcode == SCSIOP_VERIFY) ||
(cdbOpcode == SCSIOP_VERIFY16)) && cdb) {
if (isInformationValid)
{
readSector = 0;
badSector = information;
if (cdbOpcode == SCSIOP_READ16 || cdbOpcode == SCSIOP_WRITE16 || cdbOpcode == SCSIOP_VERIFY16) {
REVERSE_BYTES_QUAD(&readSector, &(cdb->AsByte[2]));
} else {
REVERSE_BYTES(&readSector, &(cdb->AsByte[2]));
}
if (cdbOpcode == SCSIOP_READ || cdbOpcode == SCSIOP_WRITE || cdbOpcode == SCSIOP_VERIFY) {
REVERSE_BYTES_SHORT(&index, &(cdb->CDB10.TransferBlocksMsb));
} else if (cdbOpcode == SCSIOP_READ6 || cdbOpcode == SCSIOP_WRITE6) {
index = cdb->CDB6READWRITE.TransferBlocks;
} else if(cdbOpcode == SCSIOP_READ12 || cdbOpcode == SCSIOP_WRITE12) {
REVERSE_BYTES(&index, &(cdb->CDB12.TransferLength));
} else {
REVERSE_BYTES(&index, &(cdb->CDB16.TransferLength));
}
//
// Make sure the bad sector is within the read sectors.
//
if (!(badSector >= readSector && badSector < readSector + index)) {
badSector = readSector;
}
}
}
}
__ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer:
if (!validSense) {
//
// Request sense buffer not valid. No sense information
// to pinpoint the error. Return general request fail.
//
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
"Request sense info not valid. SrbStatus %2x\n",
SRB_STATUS(Srb->SrbStatus)));
retry = TRUE;
switch (SRB_STATUS(Srb->SrbStatus)) {
case SRB_STATUS_ABORTED: {
//
// Update the error count for the device.
//
incrementErrorCount = TRUE;
*Status = STATUS_IO_TIMEOUT;
retryInterval = 1;
retry = TRUE;
break;
}
case SRB_STATUS_ERROR: {
*Status = STATUS_IO_DEVICE_ERROR;
if (SrbGetScsiStatus(Srb) == SCSISTAT_GOOD) {
//
// This is some strange return code. Update the error
// count for the device.
//
incrementErrorCount = TRUE;
} else if (SrbGetScsiStatus(Srb) == SCSISTAT_BUSY) {
*Status = STATUS_DEVICE_NOT_READY;
logRetryableError = FALSE;
}
break;
}
case SRB_STATUS_INVALID_REQUEST: {
*Status = STATUS_INVALID_DEVICE_REQUEST;
retry = FALSE;
break;
}
case SRB_STATUS_INVALID_PATH_ID:
case SRB_STATUS_NO_DEVICE:
case SRB_STATUS_NO_HBA:
case SRB_STATUS_INVALID_LUN:
case SRB_STATUS_INVALID_TARGET_ID: {
*Status = STATUS_NO_SUCH_DEVICE;
retry = FALSE;
break;
}
case SRB_STATUS_SELECTION_TIMEOUT: {
logError = TRUE;
logStatus = IO_ERR_NOT_READY;
uniqueId = 260;
*Status = STATUS_DEVICE_NOT_CONNECTED;
retry = FALSE;
break;
}
case SRB_STATUS_TIMEOUT:
case SRB_STATUS_COMMAND_TIMEOUT: {
//
// Update the error count for the device.
//
incrementErrorCount = TRUE;
*Status = STATUS_IO_TIMEOUT;
break;
}
case SRB_STATUS_PARITY_ERROR:
case SRB_STATUS_UNEXPECTED_BUS_FREE:
//
// Update the error count for the device
// and fall through to below
//
incrementErrorCount = TRUE;
case SRB_STATUS_BUS_RESET: {
*Status = STATUS_IO_DEVICE_ERROR;
logRetryableError = FALSE;
break;
}
case SRB_STATUS_DATA_OVERRUN: {
*Status = STATUS_DATA_OVERRUN;
retry = FALSE;
//
// For some commands, we allocate a buffer that may be
// larger than necessary. In these cases, the SRB may be
// returned with SRB_STATUS_DATA_OVERRUN to indicate a
// buffer *underrun*. However, the command was still
// successful so we ensure STATUS_SUCCESS is returned.
// We will also prevent these errors from causing noise in
// the error logs.
//
if ((cdbOpcode == SCSIOP_MODE_SENSE && SrbGetDataTransferLength(Srb) <= cdb->MODE_SENSE.AllocationLength) ||
(cdbOpcode == SCSIOP_INQUIRY && SrbGetDataTransferLength(Srb) <= cdb->CDB6INQUIRY.AllocationLength)) {
*Status = STATUS_SUCCESS;
logErrorInternal = FALSE;
logError = FALSE;
} else if (cdbOpcode == SCSIOP_MODE_SENSE10) {
USHORT allocationLength;
REVERSE_BYTES_SHORT(&(cdb->MODE_SENSE10.AllocationLength), &allocationLength);
if (SrbGetDataTransferLength(Srb) <= allocationLength) {
*Status = STATUS_SUCCESS;
logErrorInternal = FALSE;
logError = FALSE;
}
} else if (ClasspIsReceiveTokenInformation(cdb)) {
ULONG allocationLength;
REVERSE_BYTES(&(cdb->RECEIVE_TOKEN_INFORMATION.AllocationLength), &allocationLength);
if (SrbGetDataTransferLength(Srb) <= allocationLength) {
*Status = STATUS_SUCCESS;
logErrorInternal = FALSE;
logError = FALSE;
}
}
break;
}
case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
//
// Update the error count for the device.
//
incrementErrorCount = TRUE;
*Status = STATUS_IO_DEVICE_ERROR;
//
// If there was phase sequence error then limit the number of
// retries.
//
if (RetryCount > 1 ) {
retry = FALSE;
}
break;
}
case SRB_STATUS_REQUEST_FLUSHED: {
//
// If the status needs verification bit is set. Then set
// the status to need verification and no retry; otherwise,
// just retry the request.
//
if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
*Status = STATUS_VERIFY_REQUIRED;
retry = FALSE;
} else {
*Status = STATUS_IO_DEVICE_ERROR;
logRetryableError = FALSE;
}
break;
}
default: {
logError = TRUE;
logStatus = IO_ERR_CONTROLLER_ERROR;
uniqueId = 259;
*Status = STATUS_IO_DEVICE_ERROR;
unhandledError = TRUE;
logRetryableError = FALSE;
break;
}
}
//
// NTRAID #183546 - if we support GESN subtype NOT_READY events, and
// we know from a previous poll when the device will be ready (ETA)
// we should delay the retry more appropriately than just guessing.
//
/*
if (fdoExtension->MediaChangeDetectionInfo &&
fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
) {
// check if Gesn.ReadyTime if greater than current tick count
// if so, delay that long (from 1 to 30 seconds max?)
// else, leave the guess of time alone.
}
*/
}
}
if (incrementErrorCount) {
//
// if any error count occurred, delay the retry of this io by
// at least one second, if caller supports it.
//
if (retryInterval == 0) {
retryInterval = 1;
}
ClasspPerfIncrementErrorCount(fdoExtension);
}
//
// If there is a class specific error handler call it.
//
if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
SCSI_REQUEST_BLOCK tempSrb = {0};
PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Srb;
//
// If class driver does not support extended SRB and this is
// an extended SRB, convert to legacy SRB and pass to class
// driver.
//
if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
srbPtr = &tempSrb;
}
fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
srbPtr,
Status,
&retry);
}
//
// If the caller wants to know the suggested retry interval tell them.
//
if (ARGUMENT_PRESENT(RetryInterval)) {
*RetryInterval = retryInterval;
}
//
// The RESERVE(6) / RELEASE(6) commands are optional. So
// if they aren't supported, try the 10-byte equivalents
//
cdb = SrbGetCdb(Srb);
if (cdb) {
cdbOpcode = cdb->CDB6GENERIC.OperationCode;
}
if ((cdbOpcode == SCSIOP_RESERVE_UNIT ||
cdbOpcode == SCSIOP_RELEASE_UNIT) && cdb)
{
if (*Status == STATUS_INVALID_DEVICE_REQUEST)
{
SrbSetCdbLength(Srb, 10);
cdb->CDB10.OperationCode = (cdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
retry = TRUE;
}
}
#if DBG
//
// Ensure that for read/write requests, only return STATUS_DEVICE_BUSY if
// reservation conflict.
//
if (IS_SCSIOP_READWRITE(cdbOpcode) && (*Status == STATUS_DEVICE_BUSY)) {
NT_ASSERT(isReservationConflict == TRUE);
}
#endif
/*
* LOG the error:
* If logErrorInternal is set, log the error in our internal log.
* If logError is set, also log the error in the system log.
*/
if (logErrorInternal || logError) {
ULONG totalSize;
ULONG senseBufferSize = 0;
IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
CLASS_ERROR_LOG_DATA staticErrLogData = {0};
SENSE_DATA convertedSenseBuffer = {0};
UCHAR convertedSenseBufferLength = 0;
BOOLEAN senseDataConverted = FALSE;
//
// Logic below assumes that IO_ERROR_LOG_PACKET + CLASS_ERROR_LOG_DATA
// is less than ERROR_LOG_MAXIMUM_SIZE which is not true for extended SRB.
// Given that classpnp currently does not use >16 byte CDB, we'll convert
// an extended SRB to SCSI_REQUEST_BLOCK instead of changing this code.
// More changes will need to be made when classpnp starts using >16 byte
// CDBs.
//
//
// Calculate the total size of the error log entry.
// add to totalSize in the order that they are used.
// the advantage to calculating all the sizes here is
// that we don't have to do a bunch of extraneous checks
// later on in this code path.
//
totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
+ sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
//
// also save any available extra sense data, up to the maximum errlog
// packet size . WMI should be used for real-time analysis.
// the event log should only be used for post-mortem debugging.
//
if ((TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) && senseBuffer) {
UCHAR validSenseBytes = 0;
UCHAR senseInfoBufferLength = 0;
senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
//
// If sense data is in Descriptor format, convert it to Fixed format
// for the private log.
//
if (IsDescriptorSenseDataFormat(senseBuffer)) {
convertedSenseBufferLength = sizeof(convertedSenseBuffer);
senseDataConverted = ScsiConvertToFixedSenseFormat(senseBuffer,
senseInfoBufferLength,
(PVOID)&convertedSenseBuffer,
convertedSenseBufferLength);
}
//
// For System Log, copy the maximum amount of available sense data
//
if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
senseInfoBufferLength,
&validSenseBytes)) {
//
// If it is able to determine number of valid bytes,
// copy the maximum amount of available
// sense data that can be saved into the the errlog.
//
//
// set to save the most sense buffer possible
//
senseBufferSize = max(validSenseBytes, sizeof(staticErrLogData.SenseData));
senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
} else {
//
// it's smaller than required to read the total number of
// valid bytes, so just use the SenseInfoBufferLength field.
//
senseBufferSize = senseInfoBufferLength;
}
/*
* Bump totalSize by the number of extra senseBuffer bytes
* (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
* Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
*/
if (senseBufferSize > sizeof(staticErrLogData.SenseData)){
totalSize += senseBufferSize-sizeof(staticErrLogData.SenseData);
if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
totalSize = ERROR_LOG_MAXIMUM_SIZE;
}
}
}
//
// If we've used up all of our retry attempts, set the final status to
// reflect the appropriate result.
//
// ISSUE: the test below should also check RetryCount to determine if we will actually retry,
// but there is no easy test because we'd have to consider the original retry count
// for the op; besides, InterpretTransferPacketError sometimes ignores the retry
// decision returned by this function. So just ErrorRetried to be true in the majority case.
//
if (retry){
staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
staticErrLogData.ErrorRetried = TRUE;
} else {
staticErrLogEntry.FinalStatus = *Status;
}
//
// Don't log generic IO_WARNING_PAGING_FAILURE message if either the
// I/O is retried, or it completed successfully.
//
if (logStatus == IO_WARNING_PAGING_FAILURE &&
(retry || NT_SUCCESS(*Status)) ) {
logError = FALSE;
}
if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
staticErrLogData.ErrorPaging = TRUE;
}
if (unhandledError) {
staticErrLogData.ErrorUnhandled = TRUE;
}
//
// Calculate the device offset if there is a geometry.
//
staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
if (logStatus == -1){
staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
} else {
staticErrLogEntry.ErrorCode = logStatus;
}
/*
* The dump data follows the IO_ERROR_LOG_PACKET
*/
staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
staticErrLogEntry.SequenceNumber = 0;
staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
staticErrLogEntry.IoControlCode = IoDeviceCode;
staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
staticErrLogEntry.UniqueErrorValue = uniqueId;
KeQueryTickCount(&staticErrLogData.TickCount);
staticErrLogData.PortNumber = (ULONG)-1;
/*
* Save the entire contents of the SRB.
*/
if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
ClasspConvertToScsiRequestBlock(&staticErrLogData.Srb, (PSTORAGE_REQUEST_BLOCK)Srb);
} else {
staticErrLogData.Srb = *(PSCSI_REQUEST_BLOCK)Srb;
}
/*
* For our private log, save just the default length of the SENSE_DATA.
*/
if ((senseBufferSize != 0) && senseBuffer) {
//
// If sense buffer is in Fixed format, put it in the private log
//
// If sense buffer is in Descriptor format, put it in the private log if conversion to Fixed format
// succeeded. Otherwise, do not put it in the private log.
//
// If sense buffer is in unknown format, the device or the driver probably does not populate
// the first byte of sense data, we probably still want to log error in this case assuming
// it's fixed format, so that its sense key, its additional sense code, and its additional sense code
// qualifier would be shown in the debugger extension output. By doing so, it minimizes any potential
// negative impacts to our ability to diagnose issue.
//
if (IsDescriptorSenseDataFormat(senseBuffer)) {
if (senseDataConverted) {
RtlCopyMemory(&staticErrLogData.SenseData, &convertedSenseBuffer, min(convertedSenseBufferLength, sizeof(staticErrLogData.SenseData)));
}
} else {
RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(staticErrLogData.SenseData)));
}
}
/*
* Save the error log in our context.
* We only save the default sense buffer length.
*/
if (logErrorInternal) {
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
fdoData->ErrorLogNextIndex++;
fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
}
/*
* Log an event if an IO is being retried for reasons that may indicate
* a transient/permanent problem with the I_T_L nexus. But log the event
* only once per retried IO.
*/
if (!IS_SCSIOP_READWRITE(cdbOpcode) ||
!retry ||
(RetryCount != 0)) {
logRetryableError = FALSE;
}
if (logRetryableError) {
logError = TRUE;
}
/*
* If logError is set, also save this log in the system's error log.
* But make sure we don't log TUR failures over and over
* (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
*/
if (logError)
{
//
// We do not want to log certain system events repetitively
//
cdb = SrbGetCdb(Srb);
if (cdb) {
switch (cdb->CDB10.OperationCode)
{
case SCSIOP_TEST_UNIT_READY:
{
if (fdoData->LoggedTURFailureSinceLastIO)
{
logError = FALSE;
}
else
{
fdoData->LoggedTURFailureSinceLastIO = TRUE;
}
break;
}
case SCSIOP_SYNCHRONIZE_CACHE:
{
if (fdoData->LoggedSYNCFailure)
{
logError = FALSE;
}
else
{
fdoData->LoggedSYNCFailure = TRUE;
}
break;
}
}
}
}
if (logError){
if (logRetryableError) {
NT_ASSERT(IS_SCSIOP_READWRITE(cdbOpcode));
//
// A large Disk TimeOutValue (like 60 seconds) results in giving a command a
// large window to complete in. However, if the target returns a retryable error
// just prior to the command timing out, and if multiple retries kick in, it may
// take a significantly long time for the request to complete back to the
// application, leading to a user perception of a hung system. So log an event
// for retried IO so that an admin can help explain the reason for this behavior.
//
ClasspQueueLogIOEventWithContextWorker(Fdo,
senseBufferSize,
senseBuffer,
SRB_STATUS(Srb->SrbStatus),
SrbGetScsiStatus(Srb),
(ULONG)IO_WARNING_IO_OPERATION_RETRIED,
cdbLength,
cdb,
NULL);
} else {
PIO_ERROR_LOG_PACKET errorLogEntry;
PCLASS_ERROR_LOG_DATA errlogData;
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
if (errorLogEntry){
errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
*errorLogEntry = staticErrLogEntry;
*errlogData = staticErrLogData;
/*
* For the system log, copy as much of the sense buffer as possible.
*/
if ((senseBufferSize != 0) && senseBuffer) {
RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
}
/*
* Write the error log packet to the system error logging thread.
* It will be freed by the kernel.
*/
IoWriteErrorLogEntry(errorLogEntry);
}
}
}
}
return retry;
} // end ClassInterpretSenseInfo()