git: 94e28f979a84 - stable/15 - ufshci: run the controller fail path only once

From: Jaeyoon Choi <jaeyoon_at_FreeBSD.org>
Date: Tue, 06 Oct 2026 07:51:27 UTC
The branch stable/15 has been updated by jaeyoon:

URL: https://cgit.FreeBSD.org/src/commit/?id=94e28f979a84251f7108c10db7efc8ccdf424b2e

commit 94e28f979a84251f7108c10db7efc8ccdf424b2e
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-08-27 04:58:21 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-10-06 06:28:04 +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
    
    (cherry picked from commit 1258ae2bcb559854c966e425f4c5c48668d3cf7b)
---
 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 c738e246e96c..7b74faf95c53 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -423,7 +423,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)