git: 24c2ead33b44 - main - ufshci: tolerate partially constructed queues in SDB teardown

From: Jaeyoon Choi <jaeyoon_at_FreeBSD.org>
Date: Mon, 10 Aug 2026 02:31:23 UTC
The branch main has been updated by jaeyoon:

URL: https://cgit.FreeBSD.org/src/commit/?id=24c2ead33b44ca5db2605b462c935236f3a73443

commit 24c2ead33b44ca5db2605b462c935236f3a73443
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-08-10 01:42:16 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-08-10 02:28:50 +0000

    ufshci: tolerate partially constructed queues in SDB teardown
    
    When attach fails, ufshci_req_sdb_destroy() runs on a partially
    constructed queue, and it runs twice: once from the construct error
    path and once from the controller destructor.
    
    Make that safe: NULL-check each resource before freeing it and clear
    the pointer afterwards, so a second call finds nothing to do. The
    construct error label no longer frees the command descriptors itself,
    which fixes a double free of ucd_bus_addr. Also destroy the payload
    DMA tag, which was previously leaked. Drop the mtx_initialized()
    checks: the locks are always set up before any failure path can reach
    the destroy.
    
    Attach can also fail before the queues were constructed at all. The
    destructor would then call a NULL qops.destroy pointer, so skip the
    destroy when the queue was never set up.
    
    Sponsored by:           Samsung Electronics
    Reviewed by:            imp (mentor)
    Differential Revision:  https://reviews.freebsd.org/D58660
---
 sys/dev/ufshci/ufshci_req_queue.c |  8 +++++++
 sys/dev/ufshci/ufshci_req_sdb.c   | 44 +++++++++++++++++++++++----------------
 2 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_req_queue.c b/sys/dev/ufshci/ufshci_req_queue.c
index 371b9c520979..d8d86070c18c 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -72,6 +72,10 @@ ufshci_utmr_req_queue_construct(struct ufshci_controller *ctrlr)
 void
 ufshci_utmr_req_queue_destroy(struct ufshci_controller *ctrlr)
 {
+	/* Attach may fail before the queue ops are set up. */
+	if (ctrlr->task_mgmt_req_queue.qops.destroy == NULL)
+		return;
+
 	ctrlr->task_mgmt_req_queue.qops.destroy(ctrlr,
 	    &ctrlr->task_mgmt_req_queue);
 }
@@ -114,6 +118,10 @@ ufshci_utr_req_queue_construct(struct ufshci_controller *ctrlr)
 void
 ufshci_utr_req_queue_destroy(struct ufshci_controller *ctrlr)
 {
+	/* Attach may fail before the queue ops are set up. */
+	if (ctrlr->transfer_req_queue.qops.destroy == NULL)
+		return;
+
 	ctrlr->transfer_req_queue.qops.destroy(ctrlr,
 	    &ctrlr->transfer_req_queue);
 }
diff --git a/sys/dev/ufshci/ufshci_req_sdb.c b/sys/dev/ufshci/ufshci_req_sdb.c
index cc99b645331c..c7bf033ddc37 100644
--- a/sys/dev/ufshci/ufshci_req_sdb.c
+++ b/sys/dev/ufshci/ufshci_req_sdb.c
@@ -22,10 +22,16 @@ ufshci_req_sdb_cmd_desc_destroy(struct ufshci_req_queue *req_queue)
 	struct ufshci_tracker *tr;
 	int i;
 
-	for (i = 0; i < req_queue->num_trackers; i++) {
-		tr = hwq->act_tr[i];
-		bus_dmamap_destroy(req_queue->dma_tag_payload,
-		    tr->payload_dma_map);
+	if (req_queue->dma_tag_payload != NULL) {
+		for (i = 0; i < req_queue->num_trackers; i++) {
+			tr = hwq->act_tr[i];
+			if (tr->payload_dma_map != NULL)
+				bus_dmamap_destroy(req_queue->dma_tag_payload,
+				    tr->payload_dma_map);
+		}
+
+		bus_dma_tag_destroy(req_queue->dma_tag_payload);
+		req_queue->dma_tag_payload = NULL;
 	}
 
 	if (req_queue->ucd) {
@@ -42,6 +48,7 @@ ufshci_req_sdb_cmd_desc_destroy(struct ufshci_req_queue *req_queue)
 	}
 
 	free(req_queue->hwq->ucd_bus_addr, M_UFSHCI);
+	req_queue->hwq->ucd_bus_addr = NULL;
 }
 
 static void
@@ -145,7 +152,6 @@ ufshci_req_sdb_cmd_desc_construct(struct ufshci_req_queue *req_queue,
 
 	return (0);
 out:
-	ufshci_req_sdb_cmd_desc_destroy(req_queue);
 	return (ENOMEM);
 }
 
@@ -297,24 +303,27 @@ void
 ufshci_req_sdb_destroy(struct ufshci_controller *ctrlr,
     struct ufshci_req_queue *req_queue)
 {
-	struct ufshci_hw_queue *hwq = &req_queue->hwq[UFSHCI_SDB_Q];
-	struct ufshci_tracker *tr;
+	struct ufshci_hw_queue *hwq;
 	int i;
 
+	if (req_queue->hwq == NULL)
+		return;
+
+	hwq = &req_queue->hwq[UFSHCI_SDB_Q];
+
 	mtx_lock(&hwq->recovery_lock);
 	hwq->timer_armed = false;
 	mtx_unlock(&hwq->recovery_lock);
 	callout_drain(&hwq->timer);
 
-	if (!req_queue->is_task_mgmt)
-		ufshci_req_sdb_cmd_desc_destroy(&ctrlr->transfer_req_queue);
+	if (hwq->act_tr != NULL) {
+		if (!req_queue->is_task_mgmt)
+			ufshci_req_sdb_cmd_desc_destroy(
+			    &ctrlr->transfer_req_queue);
 
-	for (i = 0; i < req_queue->num_trackers; i++) {
-		tr = hwq->act_tr[i];
-		free(tr, M_UFSHCI);
-	}
+		for (i = 0; i < req_queue->num_trackers; i++)
+			free(hwq->act_tr[i], M_UFSHCI);
 
-	if (hwq->act_tr) {
 		free(hwq->act_tr, M_UFSHCI);
 		hwq->act_tr = NULL;
 	}
@@ -331,12 +340,11 @@ ufshci_req_sdb_destroy(struct ufshci_controller *ctrlr,
 		hwq->dma_tag_queue = NULL;
 	}
 
-	if (mtx_initialized(&hwq->recovery_lock))
-		mtx_destroy(&hwq->recovery_lock);
-	if (mtx_initialized(&hwq->qlock))
-		mtx_destroy(&hwq->qlock);
+	mtx_destroy(&hwq->recovery_lock);
+	mtx_destroy(&hwq->qlock);
 
 	free(req_queue->hwq, M_UFSHCI);
+	req_queue->hwq = NULL;
 }
 
 struct ufshci_hw_queue *