Hi.
On 10/1/26 22:44, Qing Li wrote:
A single large request (up to 32 MiB, 128 PRDT entries) can stall on
some controllers, and a stuck request hung U-Boot forever because the
UTP completion timeout was only checked once the link had left
SLOWAUTO mode.
- reduce the maximum request size to 4 MiB (16 PRDT entries)
- always enforce the UTP completion timeout
- latch io_failed after a timeout or controller error so further
requests fail immediately instead of reusing broken descriptors
Could you split this change into multiple commits so it's easier
which changes are for with fix ?
Thanks,
Neil
Signed-off-by: Qing Li <[email protected]>
---
drivers/ufs/ufs-uclass.c | 30 ++++++++++++++++++++----------
drivers/ufs/ufs.h | 2 ++
2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 6ef01f8e4aa..7a8d3e9de53 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -60,7 +60,7 @@
#define MAX_PRDT_ENTRY 262144
/* maximum bytes per request */
-#define UFS_MAX_BYTES (128 * 256 * 1024)
+#define UFS_MAX_BYTES (4 * 1024 * 1024)
static inline bool ufshcd_is_hba_active(struct ufs_hba *hba);
static inline void ufshcd_hba_stop(struct ufs_hba *hba);
@@ -915,6 +915,9 @@ static int ufshcd_send_command(struct ufs_hba *hba,
unsigned int task_tag)
u32 intr_status;
u32 enabled_intr_status;
+ if (hba->io_failed)
+ return -EIO;
+
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* Make sure doorbell reg is updated before reading interrupt status */
@@ -926,16 +929,15 @@ static int ufshcd_send_command(struct ufs_hba *hba,
unsigned int task_tag)
enabled_intr_status = intr_status & hba->intr_mask;
ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
- if (hba->max_pwr_info.info.pwr_rx != SLOWAUTO_MODE &&
- hba->max_pwr_info.info.pwr_tx != SLOWAUTO_MODE) {
- if (get_timer(start) > QUERY_REQ_TIMEOUT) {
- dev_err(hba->dev,
- "Timedout waiting for UTP response\n");
- return -ETIMEDOUT;
- }
+ if (get_timer(start) > QUERY_REQ_TIMEOUT) {
+ hba->io_failed = true;
+ dev_err(hba->dev,
+ "Timedout waiting for UTP response\n");
+ return -ETIMEDOUT;
}
if (enabled_intr_status & UFSHCD_ERROR_MASK) {
+ hba->io_failed = true;
dev_err(hba->dev, "Error in status:%08x\n",
enabled_intr_status);
@@ -1030,6 +1032,9 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, enum dev_cmd_type cmd_type,
int err;
int resp;
+ if (hba->io_failed)
+ return -EIO;
+
err = ufshcd_comp_devman_upiu(hba, cmd_type);
if (err)
return err;
@@ -1661,16 +1666,21 @@ static int ufs_scsi_exec(struct udevice *scsi_dev,
struct scsi_cmd *pccb)
{
struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent);
u32 upiu_flags;
- int ocs, result = 0;
+ int err, ocs, result = 0;
u8 scsi_status;
+ if (hba->io_failed)
+ return -EIO;
+
ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, pccb->dma_dir);
ufshcd_prepare_utp_scsi_cmd_upiu(hba, pccb, upiu_flags);
prepare_prdt_table(hba, pccb);
ufshcd_cache_flush(pccb->pdata, pccb->datalen);
- ufshcd_send_command(hba, TASK_TAG);
+ err = ufshcd_send_command(hba, TASK_TAG);
+ if (err)
+ return err;
ufshcd_cache_invalidate(pccb->pdata, pccb->datalen);
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 0f6c93fbce7..c35f395ea7a 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -719,6 +719,8 @@ struct ufs_hba {
struct ufs_pwr_mode_info max_pwr_info;
struct ufs_dev_cmd dev_cmd;
+ /* Do not reuse UTP descriptors after a request failed to quiesce. */
+ bool io_failed;
};
static inline int ufshcd_ops_init(struct ufs_hba *hba)