Commit 0c6b9e77fb for qemu.org
commit 0c6b9e77fbd1ecd3888c6a694349a3cec7ad6256
Author: Stanley Jhu <stanleyjhu@google.com>
Date: Fri Oct 23 19:38:25 2026 +0800
hw/ufs: Add ssu-timeout mode for power management recovery testing
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 <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 83d5fc2441..f8da7c9554 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -862,6 +862,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 ||
@@ -869,8 +870,21 @@ static bool ufs_fault_inject_exec_req(UfsRequest *req)
return false;
}
+ op = req->req_upiu.sc.cdb[0];
+ if (u->params.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) {
- uint8_t op = req->req_upiu.sc.cdb[0];
uint64_t lba = 0;
bool is_match = false;
@@ -3226,6 +3240,7 @@ const QEnumLookup UfsFaultInjectMode_lookup = {
[UFS_FAULT_INJECT_ABORT_FAILED] = "abort-failed",
[UFS_FAULT_INJECT_IN_TRANSITION] = "in-transition",
[UFS_FAULT_INJECT_TIMEOUT] = "timeout",
+ [UFS_FAULT_INJECT_SSU_TIMEOUT] = "ssu-timeout",
},
.size = UFS_FAULT_INJECT_MODE_MAX,
};
@@ -3295,7 +3310,7 @@ const PropertyInfo ufs_prop_fault_inject_tag = {
const PropertyInfo ufs_prop_fault_inject_mode = {
.type = "UfsFaultInjectMode",
.description = "Fault injection mode: abort-success, abort-failed, "
- "in-transition, timeout",
+ "in-transition, timeout, ssu-timeout",
.enum_table = &UfsFaultInjectMode_lookup,
.get = qdev_propinfo_get_enum,
.set = qdev_propinfo_set_enum,
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index 80bdaa1a2a..7ddc540546 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -110,12 +110,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,
UFS_FAULT_INJECT_MODE_MAX,
} UfsFaultInjectMode;