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 <hao.a...@intel.com> --- MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c | 66 ++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 8 deletions(-) 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; } // -- 1.9.5.msysgit.0 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel