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:
Коммит
fc10807db5
|
@ -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)
|
||||||
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,
|
||||||
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 @@
|
||||||
|
|
||||||
/* 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)
|
||||||
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 {
|
||||||
|
|
||||||
/* 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)
|
||||||
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,
|
||||||
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)
|
||||||
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 @@
|
||||||
|
|
||||||
#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)
|
||||||
(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 {
|
||||||
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;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// 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 @@
|
||||||
#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 {
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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[] = {
|
||||||
.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)
|
||||||
" [--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)
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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[])
|
||||||
|
|
||||||
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)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче