In UFSHCI 4.1 the MCQ completion queue entry carries the request tag in
the cqe.task_tag field (DW5), whereas 4.0 hosts derive it from the UTP
command descriptor base address. The device reports version 4.1 in the VER
register but left task_tag/lun zero in the completion path, so a
4.1-compliant host reads tag 0 for every completion and cannot match it to
the outstanding request.

Add the task_tag/lun/iid fields to UfsCqEntry per the UFSHCI 4.1 CQE
layout and populate them from the request UPIU header.

For example, the Linux ufshcd_mcq_get_tag() uses cqe.task_tag for
version >= 4.1, so without this SCSI commands hung (e.g. INQUIRY to the
device W-LUN) while device-management commands still completed.

Signed-off-by: Jeuk Kim <[email protected]>
---
 hw/ufs/ufs.c        | 6 ++++++
 include/block/ufs.h | 6 +++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index ee7eba6793..464fd465b3 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -520,6 +520,12 @@ static void ufs_mcq_process_cq(void *opaque)
         req->cqe.prdt_off = cpu_to_le16(prdt_off);
         req->cqe.status = status;
         req->cqe.error = 0;
+        /*
+         * From UFSHCI 4.1 the host derives the request tag from cqe.task_tag
+         * rather than decoding it from utp_addr.
+         */
+        req->cqe.task_tag = req->req_upiu.header.task_tag;
+        req->cqe.lun = req->req_upiu.header.lun;
 
         ret = ufs_addr_write(u, cq->addr + tail, &req->cqe, sizeof(req->cqe));
         if (ret) {
diff --git a/include/block/ufs.h b/include/block/ufs.h
index 66e8f35c0e..d19b3c65ef 100644
--- a/include/block/ufs.h
+++ b/include/block/ufs.h
@@ -1333,7 +1333,11 @@ typedef struct QEMU_PACKED UfsCqEntry {
     uint8_t status;
     uint8_t error;
     uint16_t rsvd1;
-    uint32_t rsvd2[3];
+    uint8_t task_tag;
+    uint8_t lun;
+    uint8_t iid_ext_iid;
+    uint8_t rsvd2;
+    uint32_t rsvd3[2];
 } UfsCqEntry;
 
 static inline void _ufs_check_size(void)
-- 
2.43.0


Reply via email to