diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 128e2b1ea8..8fdc0854eb 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -447,6 +447,10 @@ static void ufs_mcq_process_cq(void *opaque) QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) { + if (ufs_mcq_cq_full(u, cq->cqid)) { + break; + } + ufs_dma_write_rsp_upiu(req); /* UTRD/CQE are LE; round-trip through host to keep BE correct. */ @@ -478,6 +482,12 @@ static void ufs_mcq_process_cq(void *opaque) tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe)); ufs_mcq_update_cq_tail(u, cq->cqid, tail); + if (QTAILQ_EMPTY(&req->sq->req_list) && + !ufs_mcq_sq_empty(u, req->sq->sqid)) { + /* Dequeueing from SQ was blocked due to lack of free requests */ + qemu_bh_schedule(req->sq->bh); + } + ufs_clear_req(req); QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry); } @@ -787,10 +797,18 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, } opr->sq.tp = data; break; - case offsetof(UfsMcqOpReg, cq.hp): + case offsetof(UfsMcqOpReg, cq.hp): { + UfsCq *cq = u->cq[qid]; + + if (ufs_mcq_cq_full(u, qid) && !QTAILQ_EMPTY(&cq->req_list)) { + /* Enqueueing to CQ was blocked because it was full */ + qemu_bh_schedule(cq->bh); + } + opr->cq.hp = data; ufs_mcq_update_cq_head(u, qid, data); break; + } case offsetof(UfsMcqOpReg, cq_int.is): opr->cq_int.is &= ~data; break; diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index 3799d97f30..13d964c5ae 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -200,6 +200,15 @@ static inline bool ufs_mcq_cq_empty(UfsHc *u, uint32_t qid) return ufs_mcq_cq_tail(u, qid) == ufs_mcq_cq_head(u, qid); } +static inline bool ufs_mcq_cq_full(UfsHc *u, uint32_t qid) +{ + uint32_t tail = ufs_mcq_cq_tail(u, qid); + uint16_t cq_size = u->cq[qid]->size; + + tail = (tail + sizeof(UfsCqEntry)) % (sizeof(UfsCqEntry) * cq_size); + return tail == ufs_mcq_cq_head(u, qid); +} + #define TYPE_UFS "ufs" #define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)