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:
commit
fc10807db5
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue