The vduse_vdpa_set_vq_ready can be called in the lifetime of the device well after initial setup, and the device can read it afterwards.
Ensure that reads and writes to vq->ready are SMP safe so that the caller can trust that virtqueue kicks and calls behave as expected immediately after the operation returns. Signed-off-by: Eugenio Pérez <[email protected]> --- drivers/vdpa/vdpa_user/vduse_dev.c | 34 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 73d1d517dc6c..a4963aaf9332 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -460,6 +460,24 @@ static __poll_t vduse_dev_poll(struct file *file, poll_table *wait) return mask; } +static bool vduse_vq_get_ready(const struct vduse_virtqueue *vq) +{ + /* + * Paired with vduse_vq_set_ready smp_store, as the driver may modify + * it while the VDUSE instance is reading it. + */ + return smp_load_acquire(&vq->ready); +} + +static void vduse_vq_set_ready(struct vduse_virtqueue *vq, bool ready) +{ + /* + * Paired with vduse_vq_get_ready smp_load, as the driver may modify + * it while the VDUSE instance is reading it. + */ + smp_store_release(&vq->ready, ready); +} + static void vduse_dev_reset(struct vduse_dev *dev) { int i; @@ -486,7 +504,7 @@ static void vduse_dev_reset(struct vduse_dev *dev) for (i = 0; i < dev->vq_num; i++) { struct vduse_virtqueue *vq = dev->vqs[i]; - vq->ready = false; + vduse_vq_set_ready(vq, false); vq->desc_addr = 0; vq->driver_addr = 0; vq->device_addr = 0; @@ -529,7 +547,7 @@ static int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx, static void vduse_vq_kick(struct vduse_virtqueue *vq) { spin_lock(&vq->kick_lock); - if (!vq->ready) + if (!vduse_vq_get_ready(vq)) goto unlock; if (vq->kickfd) @@ -598,7 +616,7 @@ static void vduse_vdpa_set_vq_ready(struct vdpa_device *vdpa, struct vduse_dev *dev = vdpa_to_vduse(vdpa); struct vduse_virtqueue *vq = dev->vqs[idx]; - vq->ready = ready; + vduse_vq_set_ready(vq, ready); } static bool vduse_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 idx) @@ -606,7 +624,7 @@ static bool vduse_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 idx) struct vduse_dev *dev = vdpa_to_vduse(vdpa); struct vduse_virtqueue *vq = dev->vqs[idx]; - return vq->ready; + return vduse_vq_get_ready(vq); } static int vduse_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 idx, @@ -1097,7 +1115,7 @@ static int vduse_kickfd_setup(struct vduse_dev *dev, if (vq->kickfd) eventfd_ctx_put(vq->kickfd); vq->kickfd = ctx; - if (vq->ready && vq->kicked && vq->kickfd) { + if (vduse_vq_get_ready(vq) && vq->kicked && vq->kickfd) { eventfd_signal(vq->kickfd); vq->kicked = false; } @@ -1133,7 +1151,7 @@ static void vduse_vq_irq_inject(struct work_struct *work) struct vduse_virtqueue, inject); spin_lock_bh(&vq->irq_lock); - if (vq->ready && vq->cb.callback) + if (vduse_vq_get_ready(vq) && vq->cb.callback) vq->cb.callback(vq->cb.private); spin_unlock_bh(&vq->irq_lock); } @@ -1146,7 +1164,7 @@ static bool vduse_vq_signal_irqfd(struct vduse_virtqueue *vq) return false; spin_lock_irq(&vq->irq_lock); - if (vq->ready && vq->cb.trigger) { + if (vduse_vq_get_ready(vq) && vq->cb.trigger) { eventfd_signal(vq->cb.trigger); signal = true; } @@ -1500,7 +1518,7 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd, vq_info.split.avail_index = vq->state.split.avail_index; - vq_info.ready = vq->ready; + vq_info.ready = vduse_vq_get_ready(vq); ret = -EFAULT; if (copy_to_user(argp, &vq_info, sizeof(vq_info))) -- 2.52.0

