]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/vhost/vhost.c
Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[mirror_ubuntu-zesty-kernel.git] / drivers / vhost / vhost.c
index c6f2d89c0e97cd4c184e615be6e3d86aca722a0d..d6432603880c1343ea2451eba6df1973e6d61822 100644 (file)
@@ -49,7 +49,7 @@ enum {
 
 INTERVAL_TREE_DEFINE(struct vhost_umem_node,
                     rb, __u64, __subtree_last,
-                    START, LAST, , vhost_umem_interval_tree);
+                    START, LAST, static inline, vhost_umem_interval_tree);
 
 #ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
 static void vhost_disable_cross_endian(struct vhost_virtqueue *vq)
@@ -261,8 +261,8 @@ void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work)
        if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) {
                /* We can only add the work to the list after we're
                 * sure it was not in the list.
+                * test_and_set_bit() implies a memory barrier.
                 */
-               smp_mb();
                llist_add(&work->node, &dev->work_list);
                wake_up_process(dev->worker);
        }
@@ -290,6 +290,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
        vq->avail = NULL;
        vq->used = NULL;
        vq->last_avail_idx = 0;
+       vq->last_used_event = 0;
        vq->avail_idx = 0;
        vq->last_used_idx = 0;
        vq->signalled_used = 0;
@@ -719,7 +720,7 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_umem *umem,
 static int translate_desc(struct vhost_virtqueue *vq, u64 addr, u32 len,
                          struct iovec iov[], int iov_size, int access);
 
-static int vhost_copy_to_user(struct vhost_virtqueue *vq, void *to,
+static int vhost_copy_to_user(struct vhost_virtqueue *vq, void __user *to,
                              const void *from, unsigned size)
 {
        int ret;
@@ -749,7 +750,7 @@ out:
 }
 
 static int vhost_copy_from_user(struct vhost_virtqueue *vq, void *to,
-                               void *from, unsigned size)
+                               void __user *from, unsigned size)
 {
        int ret;
 
@@ -783,7 +784,7 @@ out:
 }
 
 static void __user *__vhost_get_user(struct vhost_virtqueue *vq,
-                                    void *addr, unsigned size)
+                                    void __user *addr, unsigned size)
 {
        int ret;
 
@@ -934,8 +935,8 @@ static int umem_access_ok(u64 uaddr, u64 size, int access)
        return 0;
 }
 
-int vhost_process_iotlb_msg(struct vhost_dev *dev,
-                           struct vhost_iotlb_msg *msg)
+static int vhost_process_iotlb_msg(struct vhost_dev *dev,
+                                  struct vhost_iotlb_msg *msg)
 {
        int ret = 0;
 
@@ -1324,7 +1325,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
                        r = -EINVAL;
                        break;
                }
-               vq->last_avail_idx = s.num;
+               vq->last_avail_idx = vq->last_used_event = s.num;
                /* Forget the cached index value. */
                vq->avail_idx = vq->last_avail_idx;
                break;
@@ -1862,8 +1863,7 @@ static int get_indirect(struct vhost_virtqueue *vq,
                               i, count);
                        return -EINVAL;
                }
-               if (unlikely(copy_from_iter(&desc, sizeof(desc), &from) !=
-                            sizeof(desc))) {
+               if (unlikely(!copy_from_iter_full(&desc, sizeof(desc), &from))) {
                        vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
                               i, (size_t)vhost64_to_cpu(vq, indirect->addr) + i * sizeof desc);
                        return -EINVAL;
@@ -2159,10 +2159,6 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
        __u16 old, new;
        __virtio16 event;
        bool v;
-       /* Flush out used index updates. This is paired
-        * with the barrier that the Guest executes when enabling
-        * interrupts. */
-       smp_mb();
 
        if (vhost_has_feature(vq, VIRTIO_F_NOTIFY_ON_EMPTY) &&
            unlikely(vq->avail_idx == vq->last_avail_idx))
@@ -2170,6 +2166,10 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
 
        if (!vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX)) {
                __virtio16 flags;
+               /* Flush out used index updates. This is paired
+                * with the barrier that the Guest executes when enabling
+                * interrupts. */
+               smp_mb();
                if (vhost_get_user(vq, flags, &vq->avail->flags)) {
                        vq_err(vq, "Failed to get flags");
                        return true;
@@ -2184,11 +2184,26 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
        if (unlikely(!v))
                return true;
 
+       /* We're sure if the following conditions are met, there's no
+        * need to notify guest:
+        * 1) cached used event is ahead of new
+        * 2) old to new updating does not cross cached used event. */
+       if (vring_need_event(vq->last_used_event, new + vq->num, new) &&
+           !vring_need_event(vq->last_used_event, new, old))
+               return false;
+
+       /* Flush out used index updates. This is paired
+        * with the barrier that the Guest executes when enabling
+        * interrupts. */
+       smp_mb();
+
        if (vhost_get_user(vq, event, vhost_used_event(vq))) {
                vq_err(vq, "Failed to get used event idx");
                return true;
        }
-       return vring_need_event(vhost16_to_cpu(vq, event), new, old);
+       vq->last_used_event = vhost16_to_cpu(vq, event);
+
+       return vring_need_event(vq->last_used_event, new, old);
 }
 
 /* This actually signals the guest, using eventfd. */