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
