Commit bbd90ad70c for qemu.org
commit bbd90ad70cec41436f2a4d6da5a069519e7618ab
Author: Stanley Jhu <stanleyjhu@google.com>
Date: Fri Oct 23 19:38:24 2026 +0800
hw/ufs: Handle task management requests for fault-injected held requests
Wire up Task Management Request (TMR) and MCQ submission queue cleanup
handling for transfer requests held by x-fault-inject-tag according to
x-fault-inject-mode:
- "abort-success": UFS_QUERY_TASK returns
UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED. UFS_ABORT_TASK cancels the
underlying SCSI request and marks the MCQ request in error state for
SQRTC.ICU cleanup (or clears the legacy UTRL slot), then returns
UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL.
- "abort-failed": UFS_ABORT_TASK returns
UFS_UPIU_TASK_MANAGEMENT_FUNC_FAILED while keeping the request held.
- "in-transition": UFS_QUERY_TASK releases the held transfer request
and executes it through the normal SCSI command path, completing
with genuine device I/O and CQE generation, while returning
UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED per SAM-5 task set rules.
UFS_ABORT_TASK releases the held request to generic abort processing.
- "timeout": UFS_ABORT_TASK skips completion so the host driver hits
its TMR timeout path.
Also clear u->held_req and disarm active_fault_inject_tag when an MCQ
submission queue containing the held request is cleaned up via
ufs_mcq_sq_cleanup().
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index 1a5bc54eac..83d5fc2441 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -37,6 +37,7 @@
#define UFS_HID_PROGRESS_COMPLETE 100
static void ufs_exec_req(UfsRequest *req);
+static void ufs_fault_inject_execute_held_req(UfsRequest *hreq);
static void ufs_clear_req(UfsRequest *req);
static void ufs_mcq_process_cq(void *opaque);
@@ -797,6 +798,67 @@ static void ufs_fault_inject_hce_reset(UfsHc *u)
}
}
+static bool ufs_fault_inject_tmr(UfsHc *u, uint8_t tm_func, uint8_t lun,
+ uint32_t task_tag, uint8_t *tm_resp,
+ bool *skip_completion)
+{
+ UfsRequest *hreq = u->held_req;
+
+ *skip_completion = false;
+ if (!hreq || task_tag != u->active_fault_inject_tag ||
+ hreq->req_upiu.header.lun != lun) {
+ return false;
+ }
+
+ if (tm_func == UFS_QUERY_TASK) {
+ if (u->params.fault_inject_mode == UFS_FAULT_INJECT_IN_TRANSITION) {
+ u->held_req = NULL;
+ u->active_fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ u->params.fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ ufs_fault_inject_execute_held_req(hreq);
+ return false;
+ }
+ /*
+ * Generic QUERY TASK in ufs_process_tmr() will check
+ * ufs_find_req_by_tag() and return SUCCEEDED for the held request.
+ */
+ return false;
+ }
+
+ if (tm_func == UFS_ABORT_TASK) {
+ if (u->params.fault_inject_mode == UFS_FAULT_INJECT_ABORT_FAILED) {
+ *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_FAILED;
+ return true;
+ }
+ if (u->params.fault_inject_mode == UFS_FAULT_INJECT_TIMEOUT) {
+ *skip_completion = true;
+ return true;
+ }
+
+ /*
+ * UFS_FAULT_INJECT_ABORT_SUCCESS, UFS_FAULT_INJECT_IN_TRANSITION,
+ * or default:
+ * Release the fault-injection hold, then let generic ABORT TASK
+ * in ufs_process_tmr() handle cancellation.
+ */
+ u->held_req = NULL;
+ u->active_fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ u->params.fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ return false;
+ }
+
+ return false;
+}
+
+static void ufs_fault_inject_sq_cleanup(UfsHc *u, UfsRequest *req)
+{
+ if (u->held_req == req) {
+ u->held_req = NULL;
+ u->active_fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ u->params.fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+ }
+}
+
static bool ufs_fault_inject_exec_req(UfsRequest *req)
{
UfsHc *u = req->hc;
@@ -980,6 +1042,7 @@ static void ufs_process_tmr(UfsHc *u, uint32_t val)
UtpTaskReqDesc desc;
uint8_t tm_func, tm_resp, lun;
uint32_t task_tag;
+ bool skip_completion = false;
if (ufs_addr_read(u, desc_addr, &desc, sizeof(desc))) {
continue;
@@ -989,7 +1052,12 @@ static void ufs_process_tmr(UfsHc *u, uint32_t val)
lun = desc.upiu_req.req_header.lun;
task_tag = be32_to_cpu(desc.upiu_req.input_param2);
- if (tm_func == UFS_QUERY_TASK) {
+ if (ufs_fault_inject_tmr(u, tm_func, lun, task_tag, &tm_resp,
+ &skip_completion)) {
+ if (skip_completion) {
+ continue;
+ }
+ } else if (tm_func == UFS_QUERY_TASK) {
UfsRequest *req = ufs_find_req_by_tag(u, lun, task_tag);
if (req) {
@@ -1257,6 +1325,7 @@ static void ufs_mcq_sq_cleanup(UfsHc *u, uint8_t qid)
}
if (req) {
+ ufs_fault_inject_sq_cleanup(u, req);
if (req->sreq != NULL) {
SCSIRequest *sreq = req->sreq;
req->sreq = NULL;
@@ -2537,6 +2606,16 @@ static void ufs_exec_req(UfsRequest *req)
}
}
+static void ufs_fault_inject_execute_held_req(UfsRequest *hreq)
+{
+ UfsReqResult req_result;
+
+ req_result = ufs_exec_scsi_cmd(hreq);
+ if (req_result != UFS_REQUEST_NO_COMPLETE) {
+ ufs_complete_req(hreq, req_result);
+ }
+}
+
static void ufs_process_req(void *opaque)
{
UfsHc *u = opaque;