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 and UFS_ABORT_TASK complete the held
  transfer request with success without executing the SCSI command or
  transferring data, and return UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL.
- "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 <[email protected]>
---
 hw/ufs/ufs.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 1 deletion(-)

diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index f356818375..6d7bd16953 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -796,6 +796,91 @@ static void ufs_fault_inject_hce_reset(UfsHc *u)
     }
 }
 
+/*
+ * In-transition mode simulates a held request completing during TMR
+ * execution without executing the SCSI command or transferring data.
+ */
+static void ufs_fault_inject_complete_held_req(UfsRequest *hreq)
+{
+    uint16_t data_seg_len = sizeof(hreq->rsp_upiu.sr.sense_data_len);
+
+    hreq->rsp_upiu.sr.sense_data_len = 0;
+    hreq->rsp_upiu.sr.residual_transfer_count = 0;
+    ufs_build_upiu_header(hreq, UFS_UPIU_TRANSACTION_RESPONSE, 0,
+                          UFS_COMMAND_RESULT_SUCCESS, 0, data_seg_len);
+    ufs_complete_req(hreq, UFS_REQUEST_SUCCESS);
+}
+
+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->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;
+            *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL;
+            ufs_fault_inject_complete_held_req(hreq);
+        } else {
+            *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED;
+        }
+        return true;
+    }
+
+    if (tm_func == UFS_ABORT_TASK) {
+        if (u->fault_inject_mode == UFS_FAULT_INJECT_ABORT_FAILED) {
+            *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_FAILED;
+        } else if (u->fault_inject_mode == UFS_FAULT_INJECT_TIMEOUT) {
+            *skip_completion = true;
+        } else if (u->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;
+            *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL;
+            ufs_fault_inject_complete_held_req(hreq);
+        } else {
+            /* UFS_FAULT_INJECT_ABORT_SUCCESS or default */
+            u->held_req = NULL;
+            if (ufs_mcq_req(hreq)) {
+                if (hreq->sreq != NULL) {
+                    SCSIRequest *sreq = hreq->sreq;
+                    hreq->sreq = NULL;
+                    scsi_req_cancel(sreq);
+                }
+                hreq->state = UFS_REQUEST_ERROR;
+            } else {
+                ufs_clear_req(hreq);
+                hreq->state = UFS_REQUEST_IDLE;
+                u->reg.utrldbr &= ~(1 << hreq->slot);
+            }
+            u->active_fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+            u->params.fault_inject_tag = UFS_FAULT_INJECT_TAG_NONE;
+            *tm_resp = UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL;
+        }
+        return true;
+    }
+
+    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;
@@ -968,6 +1053,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;
@@ -977,7 +1063,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) {
@@ -1245,6 +1336,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;
-- 
2.56.0.218.g1ec904b173-goog


Reply via email to