[PATCH rdma-next 03/15] RDMA/vmw_pvrdma: Pin QP and SRQ rings writable to match device DMA write access
From: Yishai Hadas <yishaih@nvidia.com>
Date: 2026-09-08 15:30:27
Subsystem:
infiniband subsystem, the rest, vmware pvrdma driver · Maintainers:
Jason Gunthorpe, Leon Romanovsky, Linus Torvalds, Bryan Tan, Vishnu Dasa
pvrdma_create_qp() and pvrdma_create_srq() pin their ring buffers with
access=0. PVRDMA embeds a pvrdma_ring_state header directly inside these
buffers:
qp->sq.ring = qp->pdir.pages[0];
qp->rq.ring = is_srq ? NULL : &qp->sq.ring[1];
and the hypervisor writes cons_head into that header -- a field the
driver never writes itself outside of QP reset. With access=0,
ib_access_writable() is false, so ib_umem_get_va() pins the pages
without FOLL_WRITE and does not mark them dirty on unpin.
Without FOLL_WRITE, pin_user_pages_fast() can hand back a page this
process does not exclusively own (e.g. the shared zero page for an
untouched anonymous mapping) instead of forcing a private copy. The
hypervisor is then free to write cons_head into that shared physical
page, corrupting memory visible to every other mapper of it. Missing the
dirty mark on unpin also risks the hypervisor's writes being silently
discarded on reclaim.
This is not gated by any IOMMU permission check: PVRDMA is a fully
emulated device, not a real PCIe device sitting behind a guest-facing
IOMMU, so the DMA mapping direction has no enforcement effect here. The
hypervisor already owns the guest's entire physical address space and
writes to it directly, without going through this process's page tables
or any virtual-address permission check -- the CPU's page protection
bits only gate CPU-issued load/store instructions via
virtual-to-physical translation, not a hypervisor writing to guest RAM
it already controls. FOLL_WRITE is a one-time decision made at pin time
about which physical page GUP hands back for this VA; once that page is
chosen (or the wrong one is chosen, as happens today), nothing stops a
later write to it.
PVRDMA's SRQ ring does not currently wire up its ring_state field (no
post_srq_recv is implemented), so nothing depends on it being written by
the hypervisor today. Pin it writable anyway, both for symmetry with the
QP rings this same driver embeds ring state in, and because the umem
pinning here has no way to know if that will still be true tomorrow.
Pass IB_ACCESS_LOCAL_WRITE for all three rings so they are pinned
consistently with how the hypervisor actually uses them.
Fixes: 29c8d9eba550 ("IB: Add vmw_pvrdma driver")
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
---
drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c | 6 ++++--
drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
index e939cd5ce40b..53cc49f2b8c9 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c@@ -270,7 +270,8 @@ int pvrdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, /* set qp->sq.wqe_cnt, shift, buf_size.. */ qp->rumem = ib_umem_get_va(ibqp->device, ucmd.rbuf_addr, - ucmd.rbuf_size, 0); + ucmd.rbuf_size, + IB_ACCESS_LOCAL_WRITE); if (IS_ERR(qp->rumem)) { ret = PTR_ERR(qp->rumem); goto err_qp;
@@ -282,7 +283,8 @@ int pvrdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, } qp->sumem = ib_umem_get_va(ibqp->device, ucmd.sbuf_addr, - ucmd.sbuf_size, 0); + ucmd.sbuf_size, + IB_ACCESS_LOCAL_WRITE); if (IS_ERR(qp->sumem)) { if (!is_srq) ib_umem_release(qp->rumem);
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c
index 345ec486a223..3252c2eb405a 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c@@ -146,7 +146,8 @@ int pvrdma_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr, if (ret) goto err_srq; - srq->umem = ib_umem_get_va(ibsrq->device, ucmd.buf_addr, ucmd.buf_size, 0); + srq->umem = ib_umem_get_va(ibsrq->device, ucmd.buf_addr, ucmd.buf_size, + IB_ACCESS_LOCAL_WRITE); if (IS_ERR(srq->umem)) { ret = PTR_ERR(srq->umem); goto err_srq;
--
2.18.1