Extend the experimental fault injection property x-fault-inject-mode
with a new "ssu-timeout" mode to facilitate automated testing of host
driver power management and Start Stop Unit (SSU) recovery handling.

When x-fault-inject-mode="ssu-timeout" is set and x-fault-inject-tag
is armed (e.g., 0xfffffffe for any tag, or a matching tag), the
controller only intercepts SCSI START_STOP (0x1B) commands sent to
the UFS Device Well-Known LUN (WLUN) and holds them without queuing
them to the SCSI layer, while letting non-SSU commands complete
normally. This triggers the host driver's 10-second SSU command
timeout and subsequent power management recovery routines (e.g.,
during suspend-to-idle or freeze transitions), verifying host error
recovery and link restoration paths.

Signed-off-by: Stanley Jhu <[email protected]>
---
 hw/ufs/ufs.c | 24 +++++++++++++++++++++---
 hw/ufs/ufs.h |  4 ++++
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index 6d7bd16953..c54b31716e 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -884,6 +884,7 @@ static void ufs_fault_inject_sq_cleanup(UfsHc *u, 
UfsRequest *req)
 static bool ufs_fault_inject_exec_req(UfsRequest *req)
 {
     UfsHc *u = req->hc;
+    uint8_t op;
 
     if (likely(u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_NONE) ||
         u->held_req != NULL ||
@@ -891,9 +892,21 @@ static bool ufs_fault_inject_exec_req(UfsRequest *req)
         return false;
     }
 
-    if (u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_ANY) {
-        uint8_t op = req->req_upiu.sc.cdb[0];
+    op = req->req_upiu.sc.cdb[0];
+    if (u->fault_inject_mode == UFS_FAULT_INJECT_SSU_TIMEOUT) {
+        if (op == START_STOP &&
+            req->req_upiu.header.lun == UFS_UPIU_UFS_DEVICE_WLUN &&
+            (u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_ANY ||
+             req->req_upiu.header.task_tag == u->active_fault_inject_tag)) {
+            u->active_fault_inject_tag = req->req_upiu.header.task_tag;
+            u->held_req = req;
+            trace_ufs_fault_inject_hold_req(req->req_upiu.header.task_tag, 0);
+            return true;
+        }
+        return false;
+    }
 
+    if (u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_ANY) {
         if (op == READ_10 || op == WRITE_10) {
             /*
              * In 10-byte SCSI read/write commands (SBC-4), bytes 2..5
@@ -3233,6 +3246,8 @@ static bool ufs_fault_inject_parse_mode(const char *str,
         *mode = UFS_FAULT_INJECT_IN_TRANSITION;
     } else if (g_strcmp0(str, "timeout") == 0) {
         *mode = UFS_FAULT_INJECT_TIMEOUT;
+    } else if (g_strcmp0(str, "ssu-timeout") == 0) {
+        *mode = UFS_FAULT_INJECT_SSU_TIMEOUT;
     } else {
         error_setg(errp, "invalid x-fault-inject-mode: %s", str);
         return false;
@@ -3320,6 +3335,9 @@ static void ufs_prop_get_fault_inject_mode(Object *obj, 
Visitor *v,
     case UFS_FAULT_INJECT_TIMEOUT:
         str = "timeout";
         break;
+    case UFS_FAULT_INJECT_SSU_TIMEOUT:
+        str = "ssu-timeout";
+        break;
     case UFS_FAULT_INJECT_ABORT_SUCCESS:
     default:
         str = "abort-success";
@@ -3370,7 +3388,7 @@ static void ufs_prop_release_fault_inject_mode(Object 
*obj, const char *name,
 const PropertyInfo ufs_prop_fault_inject_mode = {
     .type = "str",
     .description = "Fault injection mode: abort-success, abort-failed, "
-                   "in-transition, timeout",
+                   "in-transition, timeout, ssu-timeout",
     .get = ufs_prop_get_fault_inject_mode,
     .set = ufs_prop_set_fault_inject_mode,
     .release = ufs_prop_release_fault_inject_mode,
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index 39213650bb..29332e6b1d 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -151,12 +151,16 @@ typedef struct UfsLu {
  * UFS_FAULT_INJECT_TIMEOUT:       TMR ABORT_TASK does not complete or clear
  *                                 doorbell, triggering hardware-level TMR
  *                                 timeout.
+ * UFS_FAULT_INJECT_SSU_TIMEOUT:   Intercepts SCSI START_STOP (0x1B) commands
+ *                                 targeting the UFS Device WLUN when
+ *                                 x-fault-inject-tag is armed.
  */
 typedef enum UfsFaultInjectMode {
     UFS_FAULT_INJECT_ABORT_SUCCESS = 0,
     UFS_FAULT_INJECT_ABORT_FAILED,
     UFS_FAULT_INJECT_IN_TRANSITION,
     UFS_FAULT_INJECT_TIMEOUT,
+    UFS_FAULT_INJECT_SSU_TIMEOUT,
 } UfsFaultInjectMode;
 
 /*
-- 
2.56.0.218.g1ec904b173-goog


Reply via email to