Re: [PATCH v7 1/3] vhost: invalidate vring access on IOTLB transitions
Jia Jia <[email protected]>
| Newsgroups | dev.linux.lists.virtualization,org.kernel.vger.kvm,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CAJwVaM+WZhw_GCfqcEkNpe8vsTmW_OSRpiQVBXR8cpuvWiTjmg@mail.gmail.com> |
>
> On Thu, Aug 20, 2026 at 04:03:30PM +0800, Jia Jia wrote:
> >When ACCESS_PLATFORM changes, the addresses cached in desc, avail, and
> >used change meaning with the address space. Clear the cached vring access
> >state when the device IOTLB is installed or removed so stale IOVAs cannot
> >be reused as direct userspace addresses.
> >
> >Keep device IOTLB initialization idempotent and apply the mode change even
> >when a virtqueue backend is attached. Drop the device-wide IOTLB first,
> >then clear each VQ state under its own mutex, and keep the old table alive
> >until every VQ has completed the handoff.
> >
> >A successful live mode change leaves the backend attached but invalidates
> >the cached vring addresses. Userspace must configure the vring addresses
> >for the new address mode before data processing can resume.
> >
> >Fixes: 6b1e6cc7855b ("vhost: new device IOTLB API")
> >Signed-off-by: Jia Jia <[email protected]>
> >---
> > drivers/vhost/vhost.c | 53 ++++++++++++++++++++++++++++++++++++++++++-
> > drivers/vhost/vhost.h | 1 +
> > 2 files changed, 53 insertions(+), 1 deletion(-)
> >
> >diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
> >index 14637cff0bd4..31fff9800045 100644
> >--- a/drivers/vhost/vhost.c
> >+++ b/drivers/vhost/vhost.c
> >@@ -344,6 +344,17 @@ static void __vhost_vq_meta_reset(struct vhost_virtqueue *vq)
> > vq->meta_iotlb[j] = NULL;
> > }
> >
> >+/* Caller must hold the virtqueue mutex. */
> >+static void vhost_vq_invalidate_access(struct vhost_virtqueue *vq)
> >+{
> >+ vq->desc = NULL;
> >+ vq->avail = NULL;
> >+ vq->used = NULL;
> >+ vq->log_used = false;
> >+ vq->log_addr = -1ull;
> >+ __vhost_vq_meta_reset(vq);
> >+}
> >+
> > static void vhost_vq_meta_reset(struct vhost_dev *d)
> > {
> > int i;
> >@@ -1918,6 +1929,9 @@ int vq_meta_prefetch(struct vhost_virtqueue *vq)
> > {
> > unsigned int num = vq->num;
> >
> >+ if (!vq->desc || !vq->avail || !vq->used)
> >+ return 0;
> >+
> > if (!vq->iotlb)
> > return 1;
> >
> >@@ -2287,11 +2301,48 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
> > }
> > EXPORT_SYMBOL_GPL(vhost_vring_ioctl);
> >
> >+/* Caller must hold the device mutex. */
> >+void vhost_clear_device_iotlb(struct vhost_dev *d)
>
> Is this the right patch where introduce this function?
>
> IMO should be introduced when we use it.
>
> About that I'm not sure if it is better to squash the other 2 patches
> with this one, otherwise will be this bisectable?
> I mean with just this patch applied (and without the other 2) who is
> going to free the old iotlb?
>
I'll rework the series so that the helper is introduced together with
the vhost-net and vhost-vsock callers, most likely by squashing the three
patches. I'm also reviewing the existing IOTLB replacement semantics
before preparing the next revision.
> >+{
> >+ struct vhost_iotlb *iotlb;
> >+ int i;
> >+
> >+ iotlb = d->iotlb;
> >+ if (!iotlb)
> >+ return;
> >+
> >+ /*
> >+ * Drop the device-wide view first. Each VQ then drops its
> >+ * per-VQ view and its cached ring access under its own mutex.
> >+ * Keep the old table alive until every VQ has completed this
> >+ * handoff, since a worker may still be using it while waiting
> >+ * for its VQ mutex.
> >+ */
> >+ d->iotlb = NULL;
> >+
> >+ for (i = 0; i < d->nvqs; ++i) {
> >+ struct vhost_virtqueue *vq = d->vqs[i];
> >+
> >+ mutex_lock(&vq->mutex);
> >+ vq->iotlb = NULL;
> >+ vhost_vq_invalidate_access(vq);
> >+ mutex_unlock(&vq->mutex);
> >+ }
> >+
> >+ vhost_clear_msg(d);
> >+ vhost_iotlb_free(iotlb);
> >+ wake_up_interruptible_poll(&d->wait, EPOLLIN | EPOLLRDNORM);
> >+}
> >+EXPORT_SYMBOL_GPL(vhost_clear_device_iotlb);
> >+
> > int vhost_init_device_iotlb(struct vhost_dev *d)
> > {
> > struct vhost_iotlb *niotlb, *oiotlb;
> > int i;
> >
> >+ if (d->iotlb)
> >+ return 0;
> >+
>
> IIUC after this patch `oiotlb` is always NULL, can we remove it?
>
> Thanks,
> Stefano
Thanks, I took another look. I don't think we should simply remove
oiotlb while keeping the early return, since the early return itself may
be too broad.
One case I need to examine more carefully is vhost stop/start. QEMU
unregisters the IOMMU listener while vhost is stopped. If an IOVA
mapping changes during that interval, preserving the existing kernel
IOTLB on the next VHOST_SET_FEATURES may retain an entry pointing to the
old HVA. A later lookup could hit that stale entry, so no IOTLB miss
would be reported and no replacement update would be triggered through
the miss path for that IOVA.
The original replacement path, including oiotlb, installs a new empty
table and safely keeps the old table alive until all VQs have switched.
I need to review this lifecycle more carefully before deciding whether
that replacement semantic can be removed. A logging-only feature update
may want to preserve the IOTLB, while a restart boundary may need a new
one, and d->iotlb != NULL alone does not distinguish those cases.
>
> > if (max_iotlb_entries <= 0)
> > return -EINVAL;
> >
> >@@ -2307,7 +2358,7 @@ int vhost_init_device_iotlb(struct vhost_dev *d)
> >
> > mutex_lock(&vq->mutex);
> > vq->iotlb = niotlb;
> >- __vhost_vq_meta_reset(vq);
> >+ vhost_vq_invalidate_access(vq);
> > mutex_unlock(&vq->mutex);
> > }
> >
> >diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
> >index 0192ade6e749..3c75e8089373 100644
> >--- a/drivers/vhost/vhost.h
> >+++ b/drivers/vhost/vhost.h
> >@@ -277,6 +277,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to,
> > int noblock);
> > ssize_t vhost_chr_write_iter(struct vhost_dev *dev,
> > struct iov_iter *from);
> >+void vhost_clear_device_iotlb(struct vhost_dev *d);
> > int vhost_init_device_iotlb(struct vhost_dev *d);
> >
> > void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
> >--
> >2.34.1
> >
>