From d7617bad96979cb1a5ecfbb56a05b97838bbbbb7 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Tue, 22 Dec 2015 13:57:14 +0000 Subject: MdeModulePkg ScsiDiskDxe: Add retry scheme for async SCSI I/O command Some SCSI devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data length of a SCSI I/O command is too large. This commit will repeatedly retry sending the SCSI command with a data length half of its previous value. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu Reviewed-by: Feng Tian git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19451 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c | 66 ++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 8 deletions(-) (limited to 'MdeModulePkg/Bus/Scsi') diff --git a/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c b/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c index 4725cf4..2d6d7e3 100644 --- a/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c +++ b/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c @@ -2715,15 +2715,40 @@ ScsiDiskAsyncReadSectors ( } if (EFI_ERROR (Status)) { // - // Free the SCSI_BLKIO2_REQUEST structure only when the first SCSI - // command fails. Otherwise, it will be freed in the callback function - // ScsiDiskNotify(). + // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data + // length of a SCSI I/O command is too large. + // In this case, we retry sending the SCSI command with a data length + // half of its previous value. // + if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) { + if ((MaxBlock > 1) && (SectorCount > 1)) { + MaxBlock = MIN (MaxBlock, SectorCount) >> 1; + continue; + } + } + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + // + // Free the SCSI_BLKIO2_REQUEST structure only when there is no other + // SCSI sub-task running. Otherwise, it will be freed in the callback + // function ScsiDiskNotify(). + // RemoveEntryList (&BlkIo2Req->Link); FreePool (BlkIo2Req); + + // + // It is safe to return error status to the caller, since there is no + // previous SCSI sub-task executing. + // + return EFI_DEVICE_ERROR; + } else { + // + // There are previous SCSI commands still running, EFI_SUCCESS should + // be returned to make sure that the caller does not free resources + // still using by these SCSI commands. + // + return EFI_SUCCESS; } - return EFI_DEVICE_ERROR; } // @@ -2878,15 +2903,40 @@ ScsiDiskAsyncWriteSectors ( } if (EFI_ERROR (Status)) { // - // Free the SCSI_BLKIO2_REQUEST structure only when the first SCSI - // command fails. Otherwise, it will be freed in the callback function - // ScsiDiskNotify(). + // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data + // length of a SCSI I/O command is too large. + // In this case, we retry sending the SCSI command with a data length + // half of its previous value. // + if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) { + if ((MaxBlock > 1) && (SectorCount > 1)) { + MaxBlock = MIN (MaxBlock, SectorCount) >> 1; + continue; + } + } + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + // + // Free the SCSI_BLKIO2_REQUEST structure only when there is no other + // SCSI sub-task running. Otherwise, it will be freed in the callback + // function ScsiDiskNotify(). + // RemoveEntryList (&BlkIo2Req->Link); FreePool (BlkIo2Req); + + // + // It is safe to return error status to the caller, since there is no + // previous SCSI sub-task executing. + // + return EFI_DEVICE_ERROR; + } else { + // + // There are previous SCSI commands still running, EFI_SUCCESS should + // be returned to make sure that the caller does not free resources + // still using by these SCSI commands. + // + return EFI_SUCCESS; } - return EFI_DEVICE_ERROR; } // -- cgit v1.1