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;