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