git: 1258ae2bcb55 - main - ufshci: run the controller fail path only once

From: Jaeyoon Choi <jaeyoon_at_FreeBSD.org>
Date: Thu, 27 Aug 2026 05:12:45 UTC
The branch main has been updated by jaeyoon:

URL: https://cgit.FreeBSD.org/src/commit/?id=1258ae2bcb559854c966e425f4c5c48668d3cf7b

commit 1258ae2bcb559854c966e425f4c5c48668d3cf7b
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-08-27 04:58:21 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-08-27 05:08:37 +0000

    ufshci: run the controller fail path only once
    
    Two threads could run ufshci_ctrlr_fail() at the same time.
    Each one walked the queues and completed the same trackers
    again, which caused a double free and a panic.
    
    Turn is_failed into an atomic gate, so only the first caller
    walks the queues. The reset task now returns early on a failed
    controller instead of re-enabling it.
    
    Reviewed by:            imp (mentor)
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D58944
---
 sys/dev/ufshci/ufshci_ctrlr.c   | 12 +++++++++++-
 sys/dev/ufshci/ufshci_private.h |  2 +-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index bb07ad5f76da..2f930d55a295 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -15,7 +15,13 @@
 static void
 ufshci_ctrlr_fail(struct ufshci_controller *ctrlr)
 {
-	ctrlr->is_failed = true;
+	/*
+	 * The attach thread and the reset task can both fail the
+	 * controller. A second queue walk would complete the same
+	 * trackers again.
+	 */
+	if (atomic_swap_32(&ctrlr->is_failed, 1) != 0)
+		return;
 
 	ufshci_req_queue_fail(ctrlr, &ctrlr->task_mgmt_req_queue);
 	ufshci_req_queue_fail(ctrlr, &ctrlr->transfer_req_queue);
@@ -312,6 +318,10 @@ ufshci_ctrlr_reset_task(void *arg, int pending)
 	struct ufshci_controller *ctrlr = arg;
 	int error;
 
+	/* A failed controller must not be re-enabled. */
+	if (ctrlr->is_failed)
+		return;
+
 	/* Release resources */
 	ufshci_utmr_req_queue_disable(ctrlr);
 	ufshci_utr_req_queue_disable(ctrlr);
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index d87651a46a2a..d65c1b126cd6 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -409,7 +409,7 @@ struct ufshci_controller {
 	uint32_t max_tx_lanes;
 	uint32_t max_rx_lanes;
 
-	bool is_failed;
+	uint32_t is_failed;
 };
 
 #define ufshci_mmio_offsetof(reg) offsetof(struct ufshci_registers, reg)