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


Reply via email to