git: c9827f1528a6 - main - ufshci: handle controller command submit failures

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

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

commit c9827f1528a69664f4031e1c511b2e7022601a7c
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-08-10 01:34:06 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-08-10 02:28:47 +0000

    ufshci: handle controller command submit failures
    
    Return submission errors from the controller command helpers and
    propagate them to polled callers before waiting for completion. Free
    requests that never enter a hardware queue so failure paths do not leak
    or panic after the poll timeout.
    
    Sponsored by:           Samsung Electronics
    Reviewed by:            imp (mentor)
    Differential Revision:  https://reviews.freebsd.org/D58655
---
 sys/dev/ufshci/ufshci_ctrlr.c     |  7 +++++-
 sys/dev/ufshci/ufshci_ctrlr_cmd.c | 29 ++++++++++++++++++-----
 sys/dev/ufshci/ufshci_dev.c       | 48 +++++++++++++++++++++++++++++----------
 sys/dev/ufshci/ufshci_private.h   |  6 ++---
 sys/dev/ufshci/ufshci_req_queue.c | 16 +++++++++----
 5 files changed, 79 insertions(+), 27 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index b271720efb52..cb5549f8e7c9 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -494,9 +494,14 @@ int
 ufshci_ctrlr_send_nop(struct ufshci_controller *ctrlr)
 {
 	struct ufshci_completion_poll_status status;
+	int error;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_nop(ctrlr, ufshci_completion_poll_cb, &status);
+	error = ufshci_ctrlr_cmd_send_nop(ctrlr, ufshci_completion_poll_cb,
+	    &status);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_ctrlr_send_nop failed!\n");
diff --git a/sys/dev/ufshci/ufshci_ctrlr_cmd.c b/sys/dev/ufshci/ufshci_ctrlr_cmd.c
index cac01894c062..2e85cfa4b5b0 100644
--- a/sys/dev/ufshci/ufshci_ctrlr_cmd.c
+++ b/sys/dev/ufshci/ufshci_ctrlr_cmd.c
@@ -7,15 +7,18 @@
 
 #include "ufshci_private.h"
 
-void
+int
 ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg, uint8_t function, uint8_t lun,
     uint8_t task_tag, uint8_t iid)
 {
 	struct ufshci_request *req;
 	struct ufshci_task_mgmt_request_upiu *upiu;
+	int error;
 
 	req = ufshci_allocate_request_vaddr(NULL, 0, M_NOWAIT, cb_fn, cb_arg);
+	if (req == NULL)
+		return (ENOMEM);
 
 	req->request_size = sizeof(struct ufshci_task_mgmt_request_upiu);
 	req->response_size = sizeof(struct ufshci_task_mgmt_response_upiu);
@@ -30,15 +33,20 @@ ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
 	upiu->input_param2 = task_tag;
 	upiu->input_param3 = iid;
 
-	ufshci_ctrlr_submit_task_mgmt_request(ctrlr, req);
+	error = ufshci_ctrlr_submit_task_mgmt_request(ctrlr, req);
+	if (error)
+		ufshci_free_request(req);
+
+	return (error);
 }
 
-void
+int
 ufshci_ctrlr_cmd_send_nop(struct ufshci_controller *ctrlr, ufshci_cb_fn_t cb_fn,
     void *cb_arg)
 {
 	struct ufshci_request *req;
 	struct ufshci_nop_out_upiu *upiu;
+	int error;
 
 	req = ufshci_allocate_request_vaddr(NULL, 0, M_WAITOK, cb_fn, cb_arg);
 
@@ -50,15 +58,20 @@ ufshci_ctrlr_cmd_send_nop(struct ufshci_controller *ctrlr, ufshci_cb_fn_t cb_fn,
 	memset(upiu, 0, req->request_size);
 	upiu->header.trans_type = UFSHCI_UPIU_TRANSACTION_CODE_NOP_OUT;
 
-	ufshci_ctrlr_submit_transfer_request(ctrlr, req);
+	error = ufshci_ctrlr_submit_transfer_request(ctrlr, req);
+	if (error)
+		ufshci_free_request(req);
+
+	return (error);
 }
 
-void
+int
 ufshci_ctrlr_cmd_send_query_request(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg, struct ufshci_query_param param)
 {
 	struct ufshci_request *req;
 	struct ufshci_query_request_upiu *upiu;
+	int error;
 
 	req = ufshci_allocate_request_vaddr(NULL, 0, M_WAITOK, cb_fn, cb_arg);
 
@@ -77,5 +90,9 @@ ufshci_ctrlr_cmd_send_query_request(struct ufshci_controller *ctrlr,
 	upiu->value_64 = param.value;
 	upiu->length = param.desc_size;
 
-	ufshci_ctrlr_submit_transfer_request(ctrlr, req);
+	error = ufshci_ctrlr_submit_transfer_request(ctrlr, req);
+	if (error)
+		ufshci_free_request(req);
+
+	return (error);
 }
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index 0fedbca9a90e..c53d9b71ba2c 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -19,6 +19,7 @@ ufshci_dev_read_descriptor(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_READ_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_READ_DESCRIPTOR;
@@ -29,8 +30,11 @@ ufshci_dev_read_descriptor(struct ufshci_controller *ctrlr,
 	param.desc_size = desc_size;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr,
@@ -74,6 +78,7 @@ ufshci_dev_read_flag(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_READ_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_READ_FLAG;
@@ -83,8 +88,11 @@ ufshci_dev_read_flag(struct ufshci_controller *ctrlr,
 	param.value = 0;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_dev_read_flag failed!\n");
@@ -102,6 +110,7 @@ ufshci_dev_set_flag(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_WRITE_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_SET_FLAG;
@@ -111,8 +120,11 @@ ufshci_dev_set_flag(struct ufshci_controller *ctrlr,
 	param.value = 0;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_dev_set_flag failed!\n");
@@ -128,6 +140,7 @@ ufshci_dev_clear_flag(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_WRITE_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_CLEAR_FLAG;
@@ -137,8 +150,11 @@ ufshci_dev_clear_flag(struct ufshci_controller *ctrlr,
 	param.value = 0;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_dev_clear_flag failed!\n");
@@ -155,6 +171,7 @@ ufshci_dev_read_attribute(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_READ_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_READ_ATTRIBUTE;
@@ -164,8 +181,11 @@ ufshci_dev_read_attribute(struct ufshci_controller *ctrlr,
 	param.value = 0;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_dev_read_attribute failed!\n");
@@ -184,6 +204,7 @@ ufshci_dev_write_attribute(struct ufshci_controller *ctrlr,
 {
 	struct ufshci_completion_poll_status status;
 	struct ufshci_query_param param;
+	int error;
 
 	param.function = UFSHCI_QUERY_FUNC_STANDARD_WRITE_REQUEST;
 	param.opcode = UFSHCI_QUERY_OPCODE_WRITE_ATTRIBUTE;
@@ -193,8 +214,11 @@ ufshci_dev_write_attribute(struct ufshci_controller *ctrlr,
 	param.value = value;
 
 	status.done = 0;
-	ufshci_ctrlr_cmd_send_query_request(ctrlr, ufshci_completion_poll_cb,
-	    &status, param);
+	error = ufshci_ctrlr_cmd_send_query_request(ctrlr,
+	    ufshci_completion_poll_cb, &status, param);
+	if (error)
+		return (error);
+
 	ufshci_completion_poll(&status);
 	if (status.error) {
 		ufshci_printf(ctrlr, "ufshci_dev_write_attribute failed!\n");
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 8d18fab982d7..d4c4140f82ae 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -479,12 +479,12 @@ int ufshci_dev_link_state_transition(struct ufshci_controller *ctrlr,
     enum ufshci_uic_link_state target_state);
 
 /* Controller Command */
-void ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
+int ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg, uint8_t function, uint8_t lun,
     uint8_t task_tag, uint8_t iid);
-void ufshci_ctrlr_cmd_send_nop(struct ufshci_controller *ctrlr,
+int ufshci_ctrlr_cmd_send_nop(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg);
-void ufshci_ctrlr_cmd_send_query_request(struct ufshci_controller *ctrlr,
+int ufshci_ctrlr_cmd_send_query_request(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg, struct ufshci_query_param param);
 void ufshci_ctrlr_cmd_send_scsi_command(struct ufshci_controller *ctrlr,
     ufshci_cb_fn_t cb_fn, void *cb_arg, uint8_t *cmd_ptr, uint8_t cmd_len,
diff --git a/sys/dev/ufshci/ufshci_req_queue.c b/sys/dev/ufshci/ufshci_req_queue.c
index 395801274f0e..371b9c520979 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -632,11 +632,17 @@ ufshci_req_queue_timeout(void *arg)
 				ufshci_printf(ctrlr,
 				    "Recovery step 1: Timeout occurred. aborting the task(%d).\n",
 				    tr->req->request_upiu.header.task_tag);
-				ufshci_ctrlr_cmd_send_task_mgmt_request(ctrlr,
-				    ufshci_abort_complete, tr,
-				    UFSHCI_TASK_MGMT_FUNCTION_ABORT_TASK,
-				    tr->req->request_upiu.header.lun,
-				    tr->req->request_upiu.header.task_tag, 0);
+				if (ufshci_ctrlr_cmd_send_task_mgmt_request(ctrlr,
+					ufshci_abort_complete, tr,
+					UFSHCI_TASK_MGMT_FUNCTION_ABORT_TASK,
+					tr->req->request_upiu.header.lun,
+					tr->req->request_upiu.header.task_tag,
+					0) != 0) {
+					ufshci_req_queue_timeout_recovery(ctrlr,
+					    hwq);
+					idle = false;
+					break;
+				}
 			} else {
 				/* Recovery Step 2-5 */
 				ufshci_req_queue_timeout_recovery(ctrlr, hwq);