Commit fc10807d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost

Pull virtio fixes from Michael Tsirkin:
 "Fixes all over the place.

  This includes a couple of tests that I would normally defer, but since
  they have already been helpful in catching some bugs, don't build for
  any users at all, and having them upstream makes life easier for
  everyone, I think it's ok even at this late stage"

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost:
  tools/virtio: Use tools/include/list.h instead of stubs
  tools/virtio: Reset index in virtio_test --reset.
  tools/virtio: Extract virtqueue initialization in vq_reset
  tools/virtio: Use __vring_new_virtqueue in virtio_test.c
  tools/virtio: Add --reset
  tools/virtio: Add --batch=random option
  tools/virtio: Add --batch option
  virtio-mem: add memory via add_memory_driver_managed()
  virtio-mem: silence a static checker warning
  vhost_vdpa: Fix potential underflow in vhost_vdpa_mmap()
  vdpa: fix typos in the comments for __vdpa_alloc_device()
parents fbb58011 cb91909e
...@@ -63,7 +63,7 @@ static void vdpa_release_dev(struct device *d) ...@@ -63,7 +63,7 @@ static void vdpa_release_dev(struct device *d)
* @config: the bus operations that is supported by this device * @config: the bus operations that is supported by this device
* @size: size of the parent structure that contains private data * @size: size of the parent structure that contains private data
* *
* Drvier should use vdap_alloc_device() wrapper macro instead of * Driver should use vdpa_alloc_device() wrapper macro instead of
* using this directly. * using this directly.
* *
* Returns an error when parent/config/dma_dev is not set or fail to get * Returns an error when parent/config/dma_dev is not set or fail to get
......
...@@ -263,9 +263,62 @@ static int vhost_test_set_features(struct vhost_test *n, u64 features) ...@@ -263,9 +263,62 @@ static int vhost_test_set_features(struct vhost_test *n, u64 features)
return 0; return 0;
} }
static long vhost_test_set_backend(struct vhost_test *n, unsigned index, int fd)
{
static void *backend;
const bool enable = fd != -1;
struct vhost_virtqueue *vq;
int r;
mutex_lock(&n->dev.mutex);
r = vhost_dev_check_owner(&n->dev);
if (r)
goto err;
if (index >= VHOST_TEST_VQ_MAX) {
r = -ENOBUFS;
goto err;
}
vq = &n->vqs[index];
mutex_lock(&vq->mutex);
/* Verify that ring has been setup correctly. */
if (!vhost_vq_access_ok(vq)) {
r = -EFAULT;
goto err_vq;
}
if (!enable) {
vhost_poll_stop(&vq->poll);
backend = vhost_vq_get_backend(vq);
vhost_vq_set_backend(vq, NULL);
} else {
vhost_vq_set_backend(vq, backend);
r = vhost_vq_init_access(vq);
if (r == 0)
r = vhost_poll_start(&vq->poll, vq->kick);
}
mutex_unlock(&vq->mutex);
if (enable) {
vhost_test_flush_vq(n, index);
}
mutex_unlock(&n->dev.mutex);
return 0;
err_vq:
mutex_unlock(&vq->mutex);
err:
mutex_unlock(&n->dev.mutex);
return r;
}
static long vhost_test_ioctl(struct file *f, unsigned int ioctl, static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg) unsigned long arg)
{ {
struct vhost_vring_file backend;
struct vhost_test *n = f->private_data; struct vhost_test *n = f->private_data;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
u64 __user *featurep = argp; u64 __user *featurep = argp;
...@@ -277,6 +330,10 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl, ...@@ -277,6 +330,10 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
if (copy_from_user(&test, argp, sizeof test)) if (copy_from_user(&test, argp, sizeof test))
return -EFAULT; return -EFAULT;
return vhost_test_run(n, test); return vhost_test_run(n, test);
case VHOST_TEST_SET_BACKEND:
if (copy_from_user(&backend, argp, sizeof backend))
return -EFAULT;
return vhost_test_set_backend(n, backend.index, backend.fd);
case VHOST_GET_FEATURES: case VHOST_GET_FEATURES:
features = VHOST_FEATURES; features = VHOST_FEATURES;
if (copy_to_user(featurep, &features, sizeof features)) if (copy_to_user(featurep, &features, sizeof features))
......
...@@ -4,5 +4,6 @@ ...@@ -4,5 +4,6 @@
/* Start a given test on the virtio null device. 0 stops all tests. */ /* Start a given test on the virtio null device. 0 stops all tests. */
#define VHOST_TEST_RUN _IOW(VHOST_VIRTIO, 0x31, int) #define VHOST_TEST_RUN _IOW(VHOST_VIRTIO, 0x31, int)
#define VHOST_TEST_SET_BACKEND _IOW(VHOST_VIRTIO, 0x32, int)
#endif #endif
...@@ -818,7 +818,7 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -818,7 +818,7 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config; const struct vdpa_config_ops *ops = vdpa->config;
struct vdpa_notification_area notify; struct vdpa_notification_area notify;
int index = vma->vm_pgoff; unsigned long index = vma->vm_pgoff;
if (vma->vm_end - vma->vm_start != PAGE_SIZE) if (vma->vm_end - vma->vm_start != PAGE_SIZE)
return -EINVAL; return -EINVAL;
......
...@@ -101,6 +101,11 @@ struct virtio_mem { ...@@ -101,6 +101,11 @@ struct virtio_mem {
/* The parent resource for all memory added via this device. */ /* The parent resource for all memory added via this device. */
struct resource *parent_resource; struct resource *parent_resource;
/*
* Copy of "System RAM (virtio_mem)" to be used for
* add_memory_driver_managed().
*/
const char *resource_name;
/* Summary of all memory block states. */ /* Summary of all memory block states. */
unsigned long nb_mb_state[VIRTIO_MEM_MB_STATE_COUNT]; unsigned long nb_mb_state[VIRTIO_MEM_MB_STATE_COUNT];
...@@ -414,8 +419,20 @@ static int virtio_mem_mb_add(struct virtio_mem *vm, unsigned long mb_id) ...@@ -414,8 +419,20 @@ static int virtio_mem_mb_add(struct virtio_mem *vm, unsigned long mb_id)
if (nid == NUMA_NO_NODE) if (nid == NUMA_NO_NODE)
nid = memory_add_physaddr_to_nid(addr); nid = memory_add_physaddr_to_nid(addr);
/*
* When force-unloading the driver and we still have memory added to
* Linux, the resource name has to stay.
*/
if (!vm->resource_name) {
vm->resource_name = kstrdup_const("System RAM (virtio_mem)",
GFP_KERNEL);
if (!vm->resource_name)
return -ENOMEM;
}
dev_dbg(&vm->vdev->dev, "adding memory block: %lu\n", mb_id); dev_dbg(&vm->vdev->dev, "adding memory block: %lu\n", mb_id);
return add_memory(nid, addr, memory_block_size_bytes()); return add_memory_driver_managed(nid, addr, memory_block_size_bytes(),
vm->resource_name);
} }
/* /*
...@@ -1192,7 +1209,7 @@ static int virtio_mem_mb_plug_any_sb(struct virtio_mem *vm, unsigned long mb_id, ...@@ -1192,7 +1209,7 @@ static int virtio_mem_mb_plug_any_sb(struct virtio_mem *vm, unsigned long mb_id,
VIRTIO_MEM_MB_STATE_OFFLINE); VIRTIO_MEM_MB_STATE_OFFLINE);
} }
return rc; return 0;
} }
/* /*
...@@ -1890,10 +1907,12 @@ static void virtio_mem_remove(struct virtio_device *vdev) ...@@ -1890,10 +1907,12 @@ static void virtio_mem_remove(struct virtio_device *vdev)
vm->nb_mb_state[VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL] || vm->nb_mb_state[VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL] ||
vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE] || vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE] ||
vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE_PARTIAL] || vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE_PARTIAL] ||
vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE_MOVABLE]) vm->nb_mb_state[VIRTIO_MEM_MB_STATE_ONLINE_MOVABLE]) {
dev_warn(&vdev->dev, "device still has system memory added\n"); dev_warn(&vdev->dev, "device still has system memory added\n");
else } else {
virtio_mem_delete_resource(vm); virtio_mem_delete_resource(vm);
kfree_const(vm->resource_name);
}
/* remove all tracking data - no locking needed */ /* remove all tracking data - no locking needed */
vfree(vm->mb_state); vfree(vm->mb_state);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/list.h>
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <errno.h> #include <errno.h>
...@@ -135,10 +136,4 @@ static inline void free_page(unsigned long addr) ...@@ -135,10 +136,4 @@ static inline void free_page(unsigned long addr)
(void) (&_min1 == &_min2); \ (void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; }) _min1 < _min2 ? _min1 : _min2; })
/* TODO: empty stubs for now. Broken but enough for virtio_ring.c */
#define list_add_tail(a, b) do {} while (0)
#define list_del(a) do {} while (0)
#define list_for_each_entry(a, b, c) while (0)
/* end of stubs */
#endif /* KERNEL_H */ #endif /* KERNEL_H */
...@@ -11,12 +11,11 @@ struct device { ...@@ -11,12 +11,11 @@ struct device {
struct virtio_device { struct virtio_device {
struct device dev; struct device dev;
u64 features; u64 features;
struct list_head vqs;
}; };
struct virtqueue { struct virtqueue {
/* TODO: commented as list macros are empty stubs for now. struct list_head list;
* Broken but enough for virtio_ring.c
* struct list_head list; */
void (*callback)(struct virtqueue *vq); void (*callback)(struct virtqueue *vq);
const char *name; const char *name;
struct virtio_device *vdev; struct virtio_device *vdev;
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE #define _GNU_SOURCE
#include <getopt.h> #include <getopt.h>
#include <limits.h>
#include <string.h> #include <string.h>
#include <poll.h> #include <poll.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
...@@ -18,6 +19,8 @@ ...@@ -18,6 +19,8 @@
#include <linux/virtio_ring.h> #include <linux/virtio_ring.h>
#include "../../drivers/vhost/test.h" #include "../../drivers/vhost/test.h"
#define RANDOM_BATCH -1
/* Unused */ /* Unused */
void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
...@@ -43,6 +46,10 @@ struct vdev_info { ...@@ -43,6 +46,10 @@ struct vdev_info {
struct vhost_memory *mem; struct vhost_memory *mem;
}; };
static const struct vhost_vring_file no_backend = { .fd = -1 },
backend = { .fd = 1 };
static const struct vhost_vring_state null_state = {};
bool vq_notify(struct virtqueue *vq) bool vq_notify(struct virtqueue *vq)
{ {
struct vq_info *info = vq->priv; struct vq_info *info = vq->priv;
...@@ -88,6 +95,19 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info) ...@@ -88,6 +95,19 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
assert(r >= 0); assert(r >= 0);
} }
static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
{
if (info->vq)
vring_del_virtqueue(info->vq);
memset(info->ring, 0, vring_size(num, 4096));
vring_init(&info->vring, num, info->ring, 4096);
info->vq = __vring_new_virtqueue(info->idx, info->vring, vdev, true,
false, vq_notify, vq_callback, "test");
assert(info->vq);
info->vq->priv = info;
}
static void vq_info_add(struct vdev_info *dev, int num) static void vq_info_add(struct vdev_info *dev, int num)
{ {
struct vq_info *info = &dev->vqs[dev->nvqs]; struct vq_info *info = &dev->vqs[dev->nvqs];
...@@ -97,14 +117,7 @@ static void vq_info_add(struct vdev_info *dev, int num) ...@@ -97,14 +117,7 @@ static void vq_info_add(struct vdev_info *dev, int num)
info->call = eventfd(0, EFD_NONBLOCK); info->call = eventfd(0, EFD_NONBLOCK);
r = posix_memalign(&info->ring, 4096, vring_size(num, 4096)); r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
assert(r >= 0); assert(r >= 0);
memset(info->ring, 0, vring_size(num, 4096)); vq_reset(info, num, &dev->vdev);
vring_init(&info->vring, num, info->ring, 4096);
info->vq = vring_new_virtqueue(info->idx,
info->vring.num, 4096, &dev->vdev,
true, false, info->ring,
vq_notify, vq_callback, "test");
assert(info->vq);
info->vq->priv = info;
vhost_vq_setup(dev, info); vhost_vq_setup(dev, info);
dev->fds[info->idx].fd = info->call; dev->fds[info->idx].fd = info->call;
dev->fds[info->idx].events = POLLIN; dev->fds[info->idx].events = POLLIN;
...@@ -116,6 +129,7 @@ static void vdev_info_init(struct vdev_info* dev, unsigned long long features) ...@@ -116,6 +129,7 @@ static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
int r; int r;
memset(dev, 0, sizeof *dev); memset(dev, 0, sizeof *dev);
dev->vdev.features = features; dev->vdev.features = features;
INIT_LIST_HEAD(&dev->vdev.vqs);
dev->buf_size = 1024; dev->buf_size = 1024;
dev->buf = malloc(dev->buf_size); dev->buf = malloc(dev->buf_size);
assert(dev->buf); assert(dev->buf);
...@@ -152,41 +166,93 @@ static void wait_for_interrupt(struct vdev_info *dev) ...@@ -152,41 +166,93 @@ static void wait_for_interrupt(struct vdev_info *dev)
} }
static void run_test(struct vdev_info *dev, struct vq_info *vq, static void run_test(struct vdev_info *dev, struct vq_info *vq,
bool delayed, int bufs) bool delayed, int batch, int reset_n, int bufs)
{ {
struct scatterlist sl; struct scatterlist sl;
long started = 0, completed = 0; long started = 0, completed = 0, next_reset = reset_n;
long completed_before; long completed_before, started_before;
int r, test = 1; int r, test = 1;
unsigned len; unsigned len;
long long spurious = 0; long long spurious = 0;
const bool random_batch = batch == RANDOM_BATCH;
r = ioctl(dev->control, VHOST_TEST_RUN, &test); r = ioctl(dev->control, VHOST_TEST_RUN, &test);
assert(r >= 0); assert(r >= 0);
if (!reset_n) {
next_reset = INT_MAX;
}
for (;;) { for (;;) {
virtqueue_disable_cb(vq->vq); virtqueue_disable_cb(vq->vq);
completed_before = completed; completed_before = completed;
started_before = started;
do { do {
if (started < bufs) { const bool reset = completed > next_reset;
if (random_batch)
batch = (random() % vq->vring.num) + 1;
while (started < bufs &&
(started - completed) < batch) {
sg_init_one(&sl, dev->buf, dev->buf_size); sg_init_one(&sl, dev->buf, dev->buf_size);
r = virtqueue_add_outbuf(vq->vq, &sl, 1, r = virtqueue_add_outbuf(vq->vq, &sl, 1,
dev->buf + started, dev->buf + started,
GFP_ATOMIC); GFP_ATOMIC);
if (likely(r == 0)) { if (unlikely(r != 0)) {
++started; if (r == -ENOSPC &&
if (unlikely(!virtqueue_kick(vq->vq))) started > started_before)
r = 0;
else
r = -1; r = -1;
break;
} }
} else
++started;
if (unlikely(!virtqueue_kick(vq->vq))) {
r = -1;
break;
}
}
if (started >= bufs)
r = -1; r = -1;
if (reset) {
r = ioctl(dev->control, VHOST_TEST_SET_BACKEND,
&no_backend);
assert(!r);
}
/* Flush out completed bufs if any */ /* Flush out completed bufs if any */
if (virtqueue_get_buf(vq->vq, &len)) { while (virtqueue_get_buf(vq->vq, &len)) {
++completed; ++completed;
r = 0; r = 0;
} }
if (reset) {
struct vhost_vring_state s = { .index = 0 };
vq_reset(vq, vq->vring.num, &dev->vdev);
r = ioctl(dev->control, VHOST_GET_VRING_BASE,
&s);
assert(!r);
s.num = 0;
r = ioctl(dev->control, VHOST_SET_VRING_BASE,
&null_state);
assert(!r);
r = ioctl(dev->control, VHOST_TEST_SET_BACKEND,
&backend);
assert(!r);
started = completed;
while (completed > next_reset)
next_reset += completed;
}
} while (r == 0); } while (r == 0);
if (completed == completed_before) if (completed == completed_before && started == started_before)
++spurious; ++spurious;
assert(completed <= bufs); assert(completed <= bufs);
assert(started <= bufs); assert(started <= bufs);
...@@ -203,7 +269,9 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, ...@@ -203,7 +269,9 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
test = 0; test = 0;
r = ioctl(dev->control, VHOST_TEST_RUN, &test); r = ioctl(dev->control, VHOST_TEST_RUN, &test);
assert(r >= 0); assert(r >= 0);
fprintf(stderr, "spurious wakeups: 0x%llx\n", spurious); fprintf(stderr,
"spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
spurious, started, completed);
} }
const char optstring[] = "h"; const char optstring[] = "h";
...@@ -244,6 +312,16 @@ const struct option longopts[] = { ...@@ -244,6 +312,16 @@ const struct option longopts[] = {
.name = "no-delayed-interrupt", .name = "no-delayed-interrupt",
.val = 'd', .val = 'd',
}, },
{
.name = "batch",
.val = 'b',
.has_arg = required_argument,
},
{
.name = "reset",
.val = 'r',
.has_arg = optional_argument,
},
{ {
} }
}; };
...@@ -255,6 +333,8 @@ static void help(void) ...@@ -255,6 +333,8 @@ static void help(void)
" [--no-event-idx]" " [--no-event-idx]"
" [--no-virtio-1]" " [--no-virtio-1]"
" [--delayed-interrupt]" " [--delayed-interrupt]"
" [--batch=random/N]"
" [--reset=N]"
"\n"); "\n");
} }
...@@ -263,6 +343,7 @@ int main(int argc, char **argv) ...@@ -263,6 +343,7 @@ int main(int argc, char **argv)
struct vdev_info dev; struct vdev_info dev;
unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
(1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1); (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
long batch = 1, reset = 0;
int o; int o;
bool delayed = false; bool delayed = false;
...@@ -289,6 +370,24 @@ int main(int argc, char **argv) ...@@ -289,6 +370,24 @@ int main(int argc, char **argv)
case 'D': case 'D':
delayed = true; delayed = true;
break; break;
case 'b':
if (0 == strcmp(optarg, "random")) {
batch = RANDOM_BATCH;
} else {
batch = strtol(optarg, NULL, 10);
assert(batch > 0);
assert(batch < (long)INT_MAX + 1);
}
break;
case 'r':
if (!optarg) {
reset = 1;
} else {
reset = strtol(optarg, NULL, 10);
assert(reset > 0);
assert(reset < (long)INT_MAX + 1);
}
break;
default: default:
assert(0); assert(0);
break; break;
...@@ -298,6 +397,6 @@ int main(int argc, char **argv) ...@@ -298,6 +397,6 @@ int main(int argc, char **argv)
done: done:
vdev_info_init(&dev, features); vdev_info_init(&dev, features);
vq_info_add(&dev, 256); vq_info_add(&dev, 256);
run_test(&dev, &dev.vqs[0], delayed, 0x100000); run_test(&dev, &dev.vqs[0], delayed, batch, reset, 0x100000);
return 0; return 0;
} }
...@@ -307,6 +307,7 @@ static int parallel_test(u64 features, ...@@ -307,6 +307,7 @@ static int parallel_test(u64 features,
close(to_host[0]); close(to_host[0]);
gvdev.vdev.features = features; gvdev.vdev.features = features;
INIT_LIST_HEAD(&gvdev.vdev.vqs);
gvdev.to_host_fd = to_host[1]; gvdev.to_host_fd = to_host[1];
gvdev.notifies = 0; gvdev.notifies = 0;
...@@ -453,6 +454,7 @@ int main(int argc, char *argv[]) ...@@ -453,6 +454,7 @@ int main(int argc, char *argv[])
getrange = getrange_iov; getrange = getrange_iov;
vdev.features = 0; vdev.features = 0;
INIT_LIST_HEAD(&vdev.vqs);
while (argv[1]) { while (argv[1]) {
if (strcmp(argv[1], "--indirect") == 0) if (strcmp(argv[1], "--indirect") == 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment