git: 3dbdcf937cf4 - main - ufshci: reclaim in-flight requests across a reset in SDB mode

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

URL: https://cgit.FreeBSD.org/src/commit/?id=3dbdcf937cf4689144666791c4113143b6dbc931

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

    ufshci: reclaim in-flight requests across a reset in SDB mode
    
    A reset wipes the controller's request list, so a request that was in
    flight never completes. Its slot stayed scheduled, the watchdog timed
    it out again, and the next reset went round the same way. CAM never
    got the request back.
    
    Claim in-flight requests when the queue is disabled and return them as
    aborted once it is back. If the reset fails first, the failure path
    completes them instead.
    
    Reviewed by:            imp
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D61116
---
 sys/dev/ufshci/ufshci_private.h   |  1 +
 sys/dev/ufshci/ufshci_req_queue.c | 36 ++++++++++++++++++++++++++++++++++--
 sys/dev/ufshci/ufshci_req_sdb.c   | 19 +++++++++++++++++++
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index d38e7c66ff46..1a2179a30299 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -513,6 +513,7 @@ int ufshci_utmr_req_queue_enable(struct ufshci_controller *ctrlr);
 void ufshci_utr_req_queue_disable(struct ufshci_controller *ctrlr);
 int ufshci_utr_req_queue_enable(struct ufshci_controller *ctrlr);
 void ufshci_req_queue_stop_watchdog(struct ufshci_req_queue *req_queue);
+void ufshci_req_queue_complete_aborted_hwq(struct ufshci_hw_queue *hwq);
 void ufshci_req_queue_fail(struct ufshci_controller *ctrlr,
     struct ufshci_req_queue *req_queue);
 int ufshci_req_queue_submit_request(struct ufshci_req_queue *req_queue,
diff --git a/sys/dev/ufshci/ufshci_req_queue.c b/sys/dev/ufshci/ufshci_req_queue.c
index c5a874018d0b..d9cb3ba510bc 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -220,6 +220,35 @@ ufshci_req_queue_manual_complete_tracker(struct ufshci_tracker *tr, uint8_t ocs,
 	ufshci_req_queue_complete_tracker(tr);
 }
 
+/*
+ * Complete every tracker the reset claimed. The claim keeps the
+ * completion scan away, so no tracker completes twice.
+ */
+void
+ufshci_req_queue_complete_aborted_hwq(struct ufshci_hw_queue *hwq)
+{
+	struct ufshci_tracker *tr;
+	uint32_t i;
+
+	mtx_lock(&hwq->qlock);
+
+	for (i = 0; i < hwq->num_trackers; i++) {
+		tr = hwq->act_tr[i];
+
+		if (tr->slot_state != UFSHCI_SLOT_STATE_NEED_ERROR_HANDLING)
+			continue;
+
+		tr->slot_state = UFSHCI_SLOT_STATE_COMPLETING;
+		mtx_unlock(&hwq->qlock);
+		ufshci_req_queue_manual_complete_tracker(tr,
+		    UFSHCI_DESC_ABORTED,
+		    UFSHCI_RESPONSE_CODE_GENERAL_FAILURE);
+		mtx_lock(&hwq->qlock);
+	}
+
+	mtx_unlock(&hwq->qlock);
+}
+
 void
 ufshci_req_queue_fail(struct ufshci_controller *ctrlr,
     struct ufshci_req_queue *req_queue)
@@ -240,9 +269,12 @@ ufshci_req_queue_fail(struct ufshci_controller *ctrlr,
 		 * A slot in UFSHCI_SLOT_STATE_RESERVED is visible here
 		 * only while its submit thread is failing a PRDT setup.
 		 * That thread completes the request, so leave the slot
-		 * alone.
+		 * alone. A reset that failed before it enabled the queue
+		 * again leaves its claimed slots behind, and nothing
+		 * else completes those.
 		 */
-		if (tr->slot_state != UFSHCI_SLOT_STATE_SCHEDULED)
+		if (tr->slot_state != UFSHCI_SLOT_STATE_SCHEDULED &&
+		    tr->slot_state != UFSHCI_SLOT_STATE_NEED_ERROR_HANDLING)
 			continue;
 
 		/*
diff --git a/sys/dev/ufshci/ufshci_req_sdb.c b/sys/dev/ufshci/ufshci_req_sdb.c
index ed062c95bca6..34b12c0c0136 100644
--- a/sys/dev/ufshci/ufshci_req_sdb.c
+++ b/sys/dev/ufshci/ufshci_req_sdb.c
@@ -382,6 +382,20 @@ ufshci_req_sdb_disable(struct ufshci_controller *ctrlr,
 	hwq->recovery_state = RECOVERY_WAITING;
 	TAILQ_FOREACH_SAFE(tr, &hwq->outstanding_tr, tailq, tr_temp) {
 		tr->deadline = SBT_MAX;
+
+		/*
+		 * A failed controller never enables the queue again, so
+		 * the failure path owns the requests.
+		 */
+		if (ctrlr->is_failed)
+			continue;
+
+		/*
+		 * The reset wipes the request list, so nothing completes
+		 * an unclaimed slot.
+		 */
+		if (tr->slot_state == UFSHCI_SLOT_STATE_SCHEDULED)
+			tr->slot_state = UFSHCI_SLOT_STATE_NEED_ERROR_HANDLING;
 	}
 
 	mtx_unlock(&hwq->qlock);
@@ -485,6 +499,11 @@ ufshci_req_sdb_enable(struct ufshci_controller *ctrlr,
 out:
 	mtx_unlock(&hwq->qlock);
 	mtx_unlock(&hwq->recovery_lock);
+
+	/* The queue is back, so an aborted request can retry right away. */
+	if (error == 0)
+		ufshci_req_queue_complete_aborted_hwq(hwq);
+
 	return (error);
 }