hw/ufs: Fix mcq completion queue wraparound
Currently, ufs_mcq_process_cq() writes to the CQ without checking whether there is available space. This can cause CQ entries to be discarded and overwritten. The solution is to stop writing when CQ is full and exert backpressure on the affected SQs. This is similar to how NVMe CQs operate. Signed-off-by: Ilia Levi <ilia.levi@intel.com> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com> (cherry picked from commit f78762a3cc81ca9842907a5fc1b2280083ac51ba) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
This commit is contained in:
parent
88ef8726ff
commit
ad5f6ffcd0
2 changed files with 28 additions and 1 deletions
20
hw/ufs/ufs.c
20
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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue