From: Stanley Jhu <[email protected]> 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]> Reviewed-by: Jeuk Kim <[email protected]> Signed-off-by: Jeuk Kim <[email protected]> --- hw/ufs/ufs.c | 19 +++++++++++++++++-- hw/ufs/ufs.h | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 83d5fc24414..f8da7c9554c 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 80bdaa1a2aa..7ddc5405465 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; -- 2.43.0
