git: 5a53e2a6ba95 - main - ufshci: requeue requests that a reset aborted

From: Jaeyoon Choi <jaeyoon_at_FreeBSD.org>
Date: Tue, 03 Nov 2026 04:33:47 UTC
The branch main has been updated by jaeyoon:

URL: https://cgit.FreeBSD.org/src/commit/?id=5a53e2a6ba951ed90c05986efdd2bf995f7163e5

commit 5a53e2a6ba951ed90c05986efdd2bf995f7163e5
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-11-03 04:22:20 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-11-03 04:29:44 +0000

    ufshci: requeue requests that a reset aborted
    
    A request taken back by a reset was reported to CAM as an error, which
    used up a retry. A few resets in a row failed the I/O even though the
    device never saw it.
    
    Pass the overall command status to the completion callback and requeue
    aborted requests. The polled completion carries it too, so the
    passthrough ioctl reports it instead of a placeholder zero. The abort
    path now reports an invalid status instead of aborted, so a command
    that never finishes still uses up its retries.
    
    Reviewed by:            imp
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D61117
---
 sys/dev/ufshci/ufshci.c           |  1 +
 sys/dev/ufshci/ufshci.h           |  2 ++
 sys/dev/ufshci/ufshci_ioctl.c     |  3 +--
 sys/dev/ufshci/ufshci_ioctl.h     |  2 +-
 sys/dev/ufshci/ufshci_req_queue.c | 10 +++++++++-
 sys/dev/ufshci/ufshci_sim.c       | 10 +++++++++-
 6 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/sys/dev/ufshci/ufshci.c b/sys/dev/ufshci/ufshci.c
index 84a9629e74b0..44a5762d51c1 100644
--- a/sys/dev/ufshci/ufshci.c
+++ b/sys/dev/ufshci/ufshci.c
@@ -59,6 +59,7 @@ ufshci_completion_poll_cb(void *arg, const struct ufshci_completion *cpl,
 	 * or failed.
 	 */
 	memcpy(&status->cpl.response_upiu, &cpl->response_upiu, cpl->size);
+	status->cpl.ocs = cpl->ocs;
 	status->error = error;
 	atomic_store_rel_int(&status->done, 1);
 }
diff --git a/sys/dev/ufshci/ufshci.h b/sys/dev/ufshci/ufshci.h
index 3a225d92cb60..902e19cf6083 100644
--- a/sys/dev/ufshci/ufshci.h
+++ b/sys/dev/ufshci/ufshci.h
@@ -653,6 +653,8 @@ union ufshci_reponse_upiu {
 struct ufshci_completion {
 	union ufshci_reponse_upiu response_upiu;
 	size_t size;
+	/* Overall command status of the completed request. */
+	uint8_t ocs;
 };
 
 typedef void (*ufshci_cb_fn_t)(void *, const struct ufshci_completion *, bool);
diff --git a/sys/dev/ufshci/ufshci_ioctl.c b/sys/dev/ufshci/ufshci_ioctl.c
index ead404baea8e..4ea2dbc38ed2 100644
--- a/sys/dev/ufshci/ufshci_ioctl.c
+++ b/sys/dev/ufshci/ufshci_ioctl.c
@@ -176,8 +176,7 @@ ufshci_passthrough_cmd(struct ufshci_controller *ctrlr,
 	memcpy(&pt->resp_upiu, &status.cpl.response_upiu,
 	    min(sizeof(pt->resp_upiu), sizeof(status.cpl.response_upiu)));
 	pt->xfer_len = pt->len;
-	/* The completion does not carry the overall command status yet. */
-	pt->ocs = 0;
+	pt->ocs = status.cpl.ocs;
 
 	/*
 	 * A refused command still answered, so let the caller read the
diff --git a/sys/dev/ufshci/ufshci_ioctl.h b/sys/dev/ufshci/ufshci_ioctl.h
index c545c073fc0a..ae095062c391 100644
--- a/sys/dev/ufshci/ufshci_ioctl.h
+++ b/sys/dev/ufshci/ufshci_ioctl.h
@@ -39,7 +39,7 @@ struct ufshci_pt_command {
 	uint32_t flags;		      /* [in] UFSHCI_PT_FLAG_* */
 	uint32_t timeout_ms;	      /* [in] reserved, must be 0 */
 	uint32_t xfer_len;	      /* [out] bytes the request carried */
-	uint8_t ocs;		      /* [out] reserved, always 0 for now */
+	uint8_t ocs;		      /* [out] overall command status */
 	uint8_t reserved[7];
 };
 
diff --git a/sys/dev/ufshci/ufshci_req_queue.c b/sys/dev/ufshci/ufshci_req_queue.c
index d9cb3ba510bc..1c7ae5f54f48 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -240,6 +240,11 @@ ufshci_req_queue_complete_aborted_hwq(struct ufshci_hw_queue *hwq)
 
 		tr->slot_state = UFSHCI_SLOT_STATE_COMPLETING;
 		mtx_unlock(&hwq->qlock);
+		/*
+		 * TODO: The request that timed out and caused the reset is
+		 * requeued here as well, without using a retry, so a command
+		 * the device never answers repeats timeout and reset forever.
+		 */
 		ufshci_req_queue_manual_complete_tracker(tr,
 		    UFSHCI_DESC_ABORTED,
 		    UFSHCI_RESPONSE_CODE_GENERAL_FAILURE);
@@ -323,6 +328,8 @@ ufshci_req_queue_complete_tracker(struct ufshci_tracker *tr)
 		ocs = hwq->utrd[tr->slot_num].overall_command_status;
 	}
 
+	cpl.ocs = ocs;
+
 	error = ufshci_req_queue_response_is_error(req_queue, ocs,
 	    &cpl.response_upiu);
 
@@ -562,8 +569,9 @@ ufshci_abort_complete(void *arg, const struct ufshci_completion *status,
 		 */
 		ufshci_printf(tr->hwq->ctrlr,
 		    "abort task request failed, aborting task manually\n");
+		/* Not ABORTED: the SIM requeues those without a retry. */
 		ufshci_req_queue_manual_complete_tracker(tr,
-		    UFSHCI_DESC_ABORTED, UFSHCI_RESPONSE_CODE_GENERAL_FAILURE);
+		    UFSHCI_DESC_INVALID, UFSHCI_RESPONSE_CODE_GENERAL_FAILURE);
 
 		output_param1 = be32toh(
 		    status->response_upiu.task_mgmt_response_upiu.output_param1);
diff --git a/sys/dev/ufshci/ufshci_sim.c b/sys/dev/ufshci/ufshci_sim.c
index a6e160a99083..848fa753428e 100644
--- a/sys/dev/ufshci/ufshci_sim.c
+++ b/sys/dev/ufshci/ufshci_sim.c
@@ -46,7 +46,15 @@ ufshci_sim_scsiio_done(void *ccb_arg, const struct ufshci_completion *cpl,
 	    min(sense_data_len, sense_data_max_size));
 
 	ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
-	if (error) {
+	if (cpl->ocs == UFSHCI_DESC_ABORTED) {
+		/*
+		 * A reset took the request back, so requeue it without a
+		 * retry. A failed controller rejects the resubmit, so
+		 * this cannot loop.
+		 */
+		ccb->ccb_h.status = CAM_REQUEUE_REQ;
+		xpt_done(ccb);
+	} else if (error) {
 		printf("ufshci: SCSI command completion error, Status(0x%x)"
 		       " Key(0x%x), ASC(0x%x), ASCQ(0x%x)\n",
 		    cpl->response_upiu.cmd_response_upiu.header