MdeModulePkg/ScsiDisk: Using back-off algorithm to dynamically adjust transfer length in a single SCSI/ATAPI transfer to reach best device compatibility.

Besides this, the patch also fixed:
1) Wrong return value in SenseDataLength field of packet field of EFI_EXT_SCSI_PASS_THRU protocol, it should reflect real sense data length we got.
2) Wrong logic in ScsiDiskRequestSenseKeys that the logic makes SenseData pointer unaligned compared with BlockIo.Media.IoAlign field.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Tian, Feng <feng.tian@intel.com>
Reviewed-by: Zeng, Star <star.zeng@intel.com>
Reviewed-by: Fu, Siyuan <siyuan.fu@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15491 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Tian, Feng
2014-04-30 03:36:14 +00:00
committed by erictian
parent 56dea5e177
commit 73a9e82214
4 changed files with 284 additions and 214 deletions

View File

@@ -1279,40 +1279,46 @@ DetectMediaParsingSenseKeys (
ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
*Action = ACTION_NO_ACTION;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));
return EFI_SUCCESS;
}
if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
ScsiDiskDevice->BlkIo.Media->MediaId++;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));
return EFI_SUCCESS;
}
if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {
*Action = ACTION_RETRY_COMMAND_LATER;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));
return EFI_SUCCESS;
}
if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
*Action = ACTION_NO_ACTION;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));
*Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
return EFI_DEVICE_ERROR;
}
if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
*Action = ACTION_NO_ACTION;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n"));
*Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
return EFI_DEVICE_ERROR;
}
if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
if (RetryLater) {
*Action = ACTION_RETRY_COMMAND_LATER;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));
return EFI_SUCCESS;
}
*Action = ACTION_NO_ACTION;
return EFI_DEVICE_ERROR;
}
*Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
DEBUG ((EFI_D_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));
return EFI_SUCCESS;
}
@@ -1618,9 +1624,14 @@ ScsiDiskRequestSenseKeys (
*NumberOfSenseKeys = 0;
*SenseDataArray = ScsiDiskDevice->SenseData;
PtrSenseData = ScsiDiskDevice->SenseData;
Status = EFI_SUCCESS;
PtrSenseData = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA));
if (PtrSenseData == NULL) {
return EFI_DEVICE_ERROR;
}
for (SenseReq = TRUE; SenseReq;) {
ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));
Status = ScsiRequestSenseCommand (
ScsiDiskDevice->ScsiIo,
SCSI_DISK_TIMEOUT,
@@ -1651,12 +1662,15 @@ ScsiDiskRequestSenseKeys (
if (EFI_ERROR (FallStatus)) {
if (*NumberOfSenseKeys != 0) {
*NeedRetry = FALSE;
return EFI_SUCCESS;
Status = EFI_SUCCESS;
goto EXIT;
} else {
return EFI_DEVICE_ERROR;
Status = EFI_DEVICE_ERROR;
goto EXIT;
}
}
CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength);
(*NumberOfSenseKeys) += 1;
//
@@ -1667,9 +1681,11 @@ ScsiDiskRequestSenseKeys (
(*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {
SenseReq = FALSE;
}
PtrSenseData += 1;
}
return EFI_SUCCESS;
EXIT:
FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));
return Status;
}
@@ -1780,11 +1796,6 @@ ScsiDiskReadSectors (
UINT8 Index;
UINT8 MaxRetry;
BOOLEAN NeedRetry;
EFI_SCSI_SENSE_DATA *SenseData;
UINTN NumberOfSenseKeys;
SenseData = NULL;
NumberOfSenseKeys = 0;
Status = EFI_SUCCESS;
@@ -1855,8 +1866,6 @@ ScsiDiskReadSectors (
Status = ScsiDiskRead10 (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys,
Timeout,
PtrBuffer,
&ByteCount,
@@ -1867,8 +1876,6 @@ ScsiDiskReadSectors (
Status = ScsiDiskRead16 (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys,
Timeout,
PtrBuffer,
&ByteCount,
@@ -1934,11 +1941,6 @@ ScsiDiskWriteSectors (
UINT8 Index;
UINT8 MaxRetry;
BOOLEAN NeedRetry;
EFI_SCSI_SENSE_DATA *SenseData;
UINTN NumberOfSenseKeys;
SenseData = NULL;
NumberOfSenseKeys = 0;
Status = EFI_SUCCESS;
@@ -2008,8 +2010,6 @@ ScsiDiskWriteSectors (
Status = ScsiDiskWrite10 (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys,
Timeout,
PtrBuffer,
&ByteCount,
@@ -2020,8 +2020,6 @@ ScsiDiskWriteSectors (
Status = ScsiDiskWrite16 (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys,
Timeout,
PtrBuffer,
&ByteCount,
@@ -2060,13 +2058,11 @@ ScsiDiskWriteSectors (
@param ScsiDiskDevice The pointer of ScsiDiskDevice
@param NeedRetry The pointer of flag indicates if needs retry if error happens
@param SenseDataArray NOT used yet in this function
@param NumberOfSenseKeys The number of sense key
@param Timeout The time to complete the command
@param DataBuffer The buffer to fill with the read out data
@param DataLength The length of buffer
@param StartLba The start logic block address
@param SectorSize The size of sector
@param SectorCount The number of blocks to read
@return EFI_STATUS is returned by calling ScsiRead10Command().
**/
@@ -2074,13 +2070,11 @@ EFI_STATUS
ScsiDiskRead10 (
IN SCSI_DISK_DEV *ScsiDiskDevice,
OUT BOOLEAN *NeedRetry,
OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
OUT UINTN *NumberOfSenseKeys,
IN UINT64 Timeout,
OUT UINT8 *DataBuffer,
IN OUT UINT32 *DataLength,
IN UINT32 StartLba,
IN UINT32 SectorSize
IN UINT32 SectorCount
)
{
UINT8 SenseDataLength;
@@ -2090,8 +2084,16 @@ ScsiDiskRead10 (
UINT8 TargetStatus;
UINTN Action;
//
// Implement a backoff algorithem to resolve some compatibility issues that
// some SCSI targets or ATAPI devices couldn't correctly response reading/writing
// big data in a single operation.
// This algorithem will at first try to execute original request. If the request fails
// with media error sense data or else, it will reduce the transfer length to half and
// try again till the operation succeeds or fails with one sector transfer length.
//
BackOff:
*NeedRetry = FALSE;
*NumberOfSenseKeys = 0;
Action = ACTION_NO_ACTION;
SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
ReturnStatus = ScsiRead10Command (
@@ -2104,7 +2106,7 @@ ScsiDiskRead10 (
DataBuffer,
DataLength,
StartLba,
SectorSize
SectorCount
);
if (ReturnStatus == EFI_NOT_READY) {
@@ -2145,14 +2147,26 @@ ScsiDiskRead10 (
return EFI_DEVICE_ERROR;
}
if (TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
DEBUG ((EFI_D_VERBOSE, "ScsiDiskRead10: Check Condition happened!\n"));
if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
DEBUG ((EFI_D_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));
Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
if (EFI_ERROR (Status)) {
return Status;
} else if (Action == ACTION_RETRY_COMMAND_LATER) {
if (Action == ACTION_RETRY_COMMAND_LATER) {
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
} else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
if (SectorCount <= 1) {
//
// Jump out if the operation still fails with one sector transfer length.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
//
// Try again with half length if the sense data shows we need to retry.
//
SectorCount >>= 1;
*DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
goto BackOff;
} else {
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
@@ -2168,13 +2182,11 @@ ScsiDiskRead10 (
@param ScsiDiskDevice The pointer of ScsiDiskDevice
@param NeedRetry The pointer of flag indicates if needs retry if error happens
@param SenseDataArray NOT used yet in this function
@param NumberOfSenseKeys The number of sense key
@param Timeout The time to complete the command
@param DataBuffer The buffer to fill with the read out data
@param DataLength The length of buffer
@param StartLba The start logic block address
@param SectorSize The size of sector
@param SectorCount The number of blocks to write
@return EFI_STATUS is returned by calling ScsiWrite10Command().
@@ -2183,13 +2195,11 @@ EFI_STATUS
ScsiDiskWrite10 (
IN SCSI_DISK_DEV *ScsiDiskDevice,
OUT BOOLEAN *NeedRetry,
OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
OUT UINTN *NumberOfSenseKeys,
IN UINT64 Timeout,
IN UINT8 *DataBuffer,
IN OUT UINT32 *DataLength,
IN UINT32 StartLba,
IN UINT32 SectorSize
IN UINT32 SectorCount
)
{
EFI_STATUS Status;
@@ -2199,8 +2209,16 @@ ScsiDiskWrite10 (
UINT8 TargetStatus;
UINTN Action;
//
// Implement a backoff algorithem to resolve some compatibility issues that
// some SCSI targets or ATAPI devices couldn't correctly response reading/writing
// big data in a single operation.
// This algorithem will at first try to execute original request. If the request fails
// with media error sense data or else, it will reduce the transfer length to half and
// try again till the operation succeeds or fails with one sector transfer length.
//
BackOff:
*NeedRetry = FALSE;
*NumberOfSenseKeys = 0;
Action = ACTION_NO_ACTION;
SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
ReturnStatus = ScsiWrite10Command (
@@ -2213,7 +2231,7 @@ ScsiDiskWrite10 (
DataBuffer,
DataLength,
StartLba,
SectorSize
SectorCount
);
if (ReturnStatus == EFI_NOT_READY) {
*NeedRetry = TRUE;
@@ -2253,14 +2271,26 @@ ScsiDiskWrite10 (
return EFI_DEVICE_ERROR;
}
if (TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
DEBUG ((EFI_D_VERBOSE, "ScsiDiskWrite10: Check Condition happened!\n"));
if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
DEBUG ((EFI_D_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));
Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
if (EFI_ERROR (Status)) {
return Status;
} else if (Action == ACTION_RETRY_COMMAND_LATER) {
if (Action == ACTION_RETRY_COMMAND_LATER) {
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
} else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
if (SectorCount <= 1) {
//
// Jump out if the operation still fails with one sector transfer length.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
//
// Try again with half length if the sense data shows we need to retry.
//
SectorCount >>= 1;
*DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
goto BackOff;
} else {
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
@@ -2276,27 +2306,23 @@ ScsiDiskWrite10 (
@param ScsiDiskDevice The pointer of ScsiDiskDevice
@param NeedRetry The pointer of flag indicates if needs retry if error happens
@param SenseDataArray NOT used yet in this function
@param NumberOfSenseKeys The number of sense key
@param Timeout The time to complete the command
@param DataBuffer The buffer to fill with the read out data
@param DataLength The length of buffer
@param StartLba The start logic block address
@param SectorSize The size of sector
@param SectorCount The number of blocks to read
@return EFI_STATUS is returned by calling ScsiRead10Command().
@return EFI_STATUS is returned by calling ScsiRead16Command().
**/
EFI_STATUS
ScsiDiskRead16 (
IN SCSI_DISK_DEV *ScsiDiskDevice,
OUT BOOLEAN *NeedRetry,
OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
OUT UINTN *NumberOfSenseKeys,
IN UINT64 Timeout,
OUT UINT8 *DataBuffer,
IN OUT UINT32 *DataLength,
IN UINT64 StartLba,
IN UINT32 SectorSize
IN UINT32 SectorCount
)
{
UINT8 SenseDataLength;
@@ -2306,8 +2332,16 @@ ScsiDiskRead16 (
UINT8 TargetStatus;
UINTN Action;
//
// Implement a backoff algorithem to resolve some compatibility issues that
// some SCSI targets or ATAPI devices couldn't correctly response reading/writing
// big data in a single operation.
// This algorithem will at first try to execute original request. If the request fails
// with media error sense data or else, it will reduce the transfer length to half and
// try again till the operation succeeds or fails with one sector transfer length.
//
BackOff:
*NeedRetry = FALSE;
*NumberOfSenseKeys = 0;
Action = ACTION_NO_ACTION;
SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
ReturnStatus = ScsiRead16Command (
@@ -2320,7 +2354,7 @@ ScsiDiskRead16 (
DataBuffer,
DataLength,
StartLba,
SectorSize
SectorCount
);
if (ReturnStatus == EFI_NOT_READY) {
*NeedRetry = TRUE;
@@ -2360,14 +2394,26 @@ ScsiDiskRead16 (
return EFI_DEVICE_ERROR;
}
if (TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
DEBUG ((EFI_D_VERBOSE, "ScsiDiskRead16: Check Condition happened!\n"));
if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
DEBUG ((EFI_D_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));
Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
if (EFI_ERROR (Status)) {
return Status;
} else if (Action == ACTION_RETRY_COMMAND_LATER) {
if (Action == ACTION_RETRY_COMMAND_LATER) {
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
} else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
if (SectorCount <= 1) {
//
// Jump out if the operation still fails with one sector transfer length.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
//
// Try again with half length if the sense data shows we need to retry.
//
SectorCount >>= 1;
*DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
goto BackOff;
} else {
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
@@ -2383,28 +2429,24 @@ ScsiDiskRead16 (
@param ScsiDiskDevice The pointer of ScsiDiskDevice
@param NeedRetry The pointer of flag indicates if needs retry if error happens
@param SenseDataArray NOT used yet in this function
@param NumberOfSenseKeys The number of sense key
@param Timeout The time to complete the command
@param DataBuffer The buffer to fill with the read out data
@param DataLength The length of buffer
@param StartLba The start logic block address
@param SectorSize The size of sector
@param SectorCount The number of blocks to write
@return EFI_STATUS is returned by calling ScsiWrite10Command().
@return EFI_STATUS is returned by calling ScsiWrite16Command().
**/
EFI_STATUS
ScsiDiskWrite16 (
IN SCSI_DISK_DEV *ScsiDiskDevice,
OUT BOOLEAN *NeedRetry,
OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
OUT UINTN *NumberOfSenseKeys,
IN UINT64 Timeout,
IN UINT8 *DataBuffer,
IN OUT UINT32 *DataLength,
IN UINT64 StartLba,
IN UINT32 SectorSize
IN UINT32 SectorCount
)
{
EFI_STATUS Status;
@@ -2414,8 +2456,16 @@ ScsiDiskWrite16 (
UINT8 TargetStatus;
UINTN Action;
//
// Implement a backoff algorithem to resolve some compatibility issues that
// some SCSI targets or ATAPI devices couldn't correctly response reading/writing
// big data in a single operation.
// This algorithem will at first try to execute original request. If the request fails
// with media error sense data or else, it will reduce the transfer length to half and
// try again till the operation succeeds or fails with one sector transfer length.
//
BackOff:
*NeedRetry = FALSE;
*NumberOfSenseKeys = 0;
Action = ACTION_NO_ACTION;
SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
ReturnStatus = ScsiWrite16Command (
@@ -2428,7 +2478,7 @@ ScsiDiskWrite16 (
DataBuffer,
DataLength,
StartLba,
SectorSize
SectorCount
);
if (ReturnStatus == EFI_NOT_READY) {
*NeedRetry = TRUE;
@@ -2468,14 +2518,26 @@ ScsiDiskWrite16 (
return EFI_DEVICE_ERROR;
}
if (TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
DEBUG ((EFI_D_VERBOSE, "ScsiDiskWrite16: Check Condition happened!\n"));
if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
DEBUG ((EFI_D_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));
Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
if (EFI_ERROR (Status)) {
return Status;
} else if (Action == ACTION_RETRY_COMMAND_LATER) {
if (Action == ACTION_RETRY_COMMAND_LATER) {
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
} else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
if (SectorCount <= 1) {
//
// Jump out if the operation still fails with one sector transfer length.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
//
// Try again with half length if the sense data shows we need to retry.
//
SectorCount >>= 1;
*DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
goto BackOff;
} else {
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;