virtio: fixes, tests

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.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAl7w3hcPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRpJ6UH/jVFVzILp3M5eVCewNsS2/j5HvRHxKb4DTwh
 eoNTYMSLMy5UnwOdNZ3RjmNHxc8hS04fQsHZpI6mAIPSLWdswjmRwQPN03/fvP9M
 nVdy3SxyU2ZzHbX/hRi3eTwhOkOY4RcyvkOjw49LLokIX6/DzsAO5ql3NAZW9vbL
 LqZQKzAb7BeltH5RWbPPdF2UCZKia/x9QYHIXh0gJZj5YaAzlvJywNDQcxuiIODb
 ZmHZZBaqZ66n99hWnSKfHtqX/fqRDqrdyameIVaSwgewho2AMj9V1FvvVMJWm669
 PjQuHDf03TBBDNP9UwJRjdko7qA6aqX5Tef9aGMyBy2LJUD12A8=
 =Z/dQ
 -----END PGP SIGNATURE-----

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()
This commit is contained in:
Linus Torvalds 2020-06-24 14:26:28 -07:00
commit fc10807db5
9 changed files with 207 additions and 35 deletions

View File

@ -63,7 +63,7 @@ static void vdpa_release_dev(struct device *d)
* @config: the bus operations that is supported by this device
* @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.
*
* Returns an error when parent/config/dma_dev is not set or fail to get

View File

@ -263,9 +263,62 @@ static int vhost_test_set_features(struct vhost_test *n, u64 features)
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,
unsigned long arg)
{
struct vhost_vring_file backend;
struct vhost_test *n = f->private_data;
void __user *argp = (void __user *)arg;
u64 __user *featurep = argp;
@ -277,6 +330,10 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
if (copy_from_user(&test, argp, sizeof test))
return -EFAULT;
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:
features = VHOST_FEATURES;
if (copy_to_user(featurep, &features, sizeof features))

View File

@ -4,5 +4,6 @@
/* 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_SET_BACKEND _IOW(VHOST_VIRTIO, 0x32, int)
#endif

View File

@ -818,7 +818,7 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
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)
return -EINVAL;

View File

@ -101,6 +101,11 @@ struct virtio_mem {
/* The parent resource for all memory added via this device. */
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. */
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)
if (nid == NUMA_NO_NODE)
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);
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,
VIRTIO_MEM_MB_STATE_OFFLINE);
}
return rc;
return 0;
}
/*
@ -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_ONLINE] ||
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");
else
} else {
virtio_mem_delete_resource(vm);
kfree_const(vm->resource_name);
}
/* remove all tracking data - no locking needed */
vfree(vm->mb_state);

View File

@ -11,6 +11,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/bug.h>
#include <errno.h>
@ -135,10 +136,4 @@ static inline void free_page(unsigned long addr)
(void) (&_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 */

View File

@ -11,12 +11,11 @@ struct device {
struct virtio_device {
struct device dev;
u64 features;
struct list_head vqs;
};
struct virtqueue {
/* TODO: commented as list macros are empty stubs for now.
* Broken but enough for virtio_ring.c
* struct list_head list; */
struct list_head list;
void (*callback)(struct virtqueue *vq);
const char *name;
struct virtio_device *vdev;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <getopt.h>
#include <limits.h>
#include <string.h>
#include <poll.h>
#include <sys/eventfd.h>
@ -18,6 +19,8 @@
#include <linux/virtio_ring.h>
#include "../../drivers/vhost/test.h"
#define RANDOM_BATCH -1
/* Unused */
void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
@ -43,6 +46,10 @@ struct vdev_info {
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)
{
struct vq_info *info = vq->priv;
@ -88,6 +95,19 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
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)
{
struct vq_info *info = &dev->vqs[dev->nvqs];
@ -97,14 +117,7 @@ static void vq_info_add(struct vdev_info *dev, int num)
info->call = eventfd(0, EFD_NONBLOCK);
r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
assert(r >= 0);
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.num, 4096, &dev->vdev,
true, false, info->ring,
vq_notify, vq_callback, "test");
assert(info->vq);
info->vq->priv = info;
vq_reset(info, num, &dev->vdev);
vhost_vq_setup(dev, info);
dev->fds[info->idx].fd = info->call;
dev->fds[info->idx].events = POLLIN;
@ -116,6 +129,7 @@ static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
int r;
memset(dev, 0, sizeof *dev);
dev->vdev.features = features;
INIT_LIST_HEAD(&dev->vdev.vqs);
dev->buf_size = 1024;
dev->buf = malloc(dev->buf_size);
assert(dev->buf);
@ -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,
bool delayed, int bufs)
bool delayed, int batch, int reset_n, int bufs)
{
struct scatterlist sl;
long started = 0, completed = 0;
long completed_before;
long started = 0, completed = 0, next_reset = reset_n;
long completed_before, started_before;
int r, test = 1;
unsigned len;
long long spurious = 0;
const bool random_batch = batch == RANDOM_BATCH;
r = ioctl(dev->control, VHOST_TEST_RUN, &test);
assert(r >= 0);
if (!reset_n) {
next_reset = INT_MAX;
}
for (;;) {
virtqueue_disable_cb(vq->vq);
completed_before = completed;
started_before = started;
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);
r = virtqueue_add_outbuf(vq->vq, &sl, 1,
dev->buf + started,
GFP_ATOMIC);
if (likely(r == 0)) {
++started;
if (unlikely(!virtqueue_kick(vq->vq)))
if (unlikely(r != 0)) {
if (r == -ENOSPC &&
started > started_before)
r = 0;
else
r = -1;
break;
}
} else
++started;
if (unlikely(!virtqueue_kick(vq->vq))) {
r = -1;
break;
}
}
if (started >= bufs)
r = -1;
if (reset) {
r = ioctl(dev->control, VHOST_TEST_SET_BACKEND,
&no_backend);
assert(!r);
}
/* Flush out completed bufs if any */
if (virtqueue_get_buf(vq->vq, &len)) {
while (virtqueue_get_buf(vq->vq, &len)) {
++completed;
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);
if (completed == completed_before)
if (completed == completed_before && started == started_before)
++spurious;
assert(completed <= bufs);
assert(started <= bufs);
@ -203,7 +269,9 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
test = 0;
r = ioctl(dev->control, VHOST_TEST_RUN, &test);
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";
@ -244,6 +312,16 @@ const struct option longopts[] = {
.name = "no-delayed-interrupt",
.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)
" [--no-event-idx]"
" [--no-virtio-1]"
" [--delayed-interrupt]"
" [--batch=random/N]"
" [--reset=N]"
"\n");
}
@ -263,6 +343,7 @@ int main(int argc, char **argv)
struct vdev_info dev;
unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
(1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
long batch = 1, reset = 0;
int o;
bool delayed = false;
@ -289,6 +370,24 @@ int main(int argc, char **argv)
case 'D':
delayed = true;
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:
assert(0);
break;
@ -298,6 +397,6 @@ int main(int argc, char **argv)
done:
vdev_info_init(&dev, features);
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;
}

View File

@ -307,6 +307,7 @@ static int parallel_test(u64 features,
close(to_host[0]);
gvdev.vdev.features = features;
INIT_LIST_HEAD(&gvdev.vdev.vqs);
gvdev.to_host_fd = to_host[1];
gvdev.notifies = 0;
@ -453,6 +454,7 @@ int main(int argc, char *argv[])
getrange = getrange_iov;
vdev.features = 0;
INIT_LIST_HEAD(&vdev.vqs);
while (argv[1]) {
if (strcmp(argv[1], "--indirect") == 0)