If doorbell buffering is used, a missing update of the CQ event index on
a host doorbell write may lead to a situation where the interrupt
remains asserted, but the host does not issue another mmio write to the
doorbell as the previously written value is newer than the event index.
The interrupt is not deasserted, causing an interrupt storm on the host
side. Fix this by updating the event index if the doorbell write does
not deassert the interrupt.

Assume a situation where the interrupt is deasserted, so cq->head ==
cq->tail == x. A first CQE is posted, so cq->tail becomes x + 1 and the
interrupt is asserted as cq->tail != cq->head. The host is handling the
CQE and writes x + 1 to it its shadow doorbell. Before the host issues
an mmio doorbell write, another CQE is posted, so event index remains x,
cq->head becomes x + 1, cq->tail becomes x + 2 and the IRQ remains
asserted. Just after the CQE has been posted, the host checks if the
condition to issue an mmio doorbell write applies:
  new_shadow_db - event_idx <= old_shadow_db - new_shadow_db
with new_shadow_db == x + 1, event_idx == x and old_shadow_db == x.
So the host issues an mmio to write x + 1 to the doorbell. As cq->tail
is x + 2, the interrupt remains asserted. But now, after the host is
done with processing the second CQE, it won't issue another doorbell
mmio write as the above condition is not met due to the CQ event_idx
still being x. Thus, the interrupt remains pending.

Update the event index on the doorbell write if the interrupt is not
deasserted. This causes the above condition to be met. So another mmio
write will be issued by the host and the interrupt will be deasserted.

[1] NVMe Base Specification, Rev. 2.3, Annex B.5.2

Fixes: 3f7fe8de3d49 ("hw/nvme: Implement shadow doorbell buffer support")
Signed-off-by: Chris Gellermann <[email protected]>
---
 hw/nvme/ctrl.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 22e878c42e1..fc9bc676764 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -6936,6 +6936,11 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, 
int val)
             }
 
             nvme_irq_deassert(n, cq);
+        } else {
+            if (n->dbbuf_enabled) {
+                nvme_update_cq_eventidx(cq);
+                nvme_update_cq_head(cq);
+            }
         }
     } else {
         /* Submission queue doorbell write */
-- 
2.47.3


Reply via email to