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:
@@ -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;
|
||||
|
Reference in New Issue
Block a user