Re: [PATCH 4/6] tools/virtio: add vring_test.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 01/17/2013 06:29 PM, Rusty Russell wrote:
> This is mainly to test the drivers/vhost/vringh.c code, but it also
> uses the drivers/virtio/virtio_ring.c code for the guest side.

vringh_test.c does not compile here:
(This series on top of 9a9284153d965a57edc7162a8e57c14c97f3a935)

$ cd tools/virtio
$ make
cc -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign
-fno-strict-overflow  -MMD    vringh_test.c   -o vringh_test
In file included from ./linux/vringh.h:1:0,
                 from ./../../drivers/vhost/vringh.c:6,
                 from vringh_test.c:7:
./linux/../../../include/linux/vringh.h:27:28: fatal error:
uapi/linux/uio.h: No such file or directory
compilation terminated.
make: *** [vringh_test] Error 1


> Usage for testing the basic implementation:
> 
> 	./vringh_test
> 	# Test with indirect descriptors
> 	./vringh_test --indirect
> 	# Test with indirect descriptors and event indexex
> 	./vringh_test --indirect --eventidx
> 
> You can run a parallel stress test by adding --parallel to any of the
> above options.
> 
> eg ./vringh_test --parallel:
> 	Using CPUS 0 and 3
> 	Guest: notified 10107974, pinged 107970
> 	Host: notified 108158, pinged 3172148
> 	Time: R=17.659 U=6.640 S=6.640
> 
> ./vringh_test --eventidx --parallel:
> 	Using CPUS 0 and 3
> 	Guest: notified 156357, pinged 156251
> 	Host: notified 156251, pinged 78179
> 	Time: R=4.518 U=3.536 S=3.536
> 
> Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
> ---
>  tools/virtio/Makefile      |    4 +-
>  tools/virtio/vringh_test.c |  591 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 593 insertions(+), 2 deletions(-)
>  create mode 100644 tools/virtio/vringh_test.c
> 
> diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile
> index d1d442e..b928c3e 100644
> --- a/tools/virtio/Makefile
> +++ b/tools/virtio/Makefile
> @@ -1,5 +1,5 @@
>  all: test mod
> -test: virtio_test
> +test: virtio_test vringh_test
>  virtio_test: virtio_ring.o virtio_test.o
>  CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow  -MMD
>  vpath %.c ../../drivers/virtio
> @@ -7,6 +7,6 @@ mod:
>  	${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test
>  .PHONY: all test mod clean
>  clean:
> -	${RM} *.o vhost_test/*.o vhost_test/.*.cmd \
> +	${RM} *.o vringh_test virtio_test vhost_test/*.o vhost_test/.*.cmd \
>                vhost_test/Module.symvers vhost_test/modules.order *.d
>  -include *.d
> diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c
> new file mode 100644
> index 0000000..f3868f4
> --- /dev/null
> +++ b/tools/virtio/vringh_test.c
> @@ -0,0 +1,591 @@
> +/* Simple test of virtio code, entirely in userpsace. */
> +#define _GNU_SOURCE
> +#include <sched.h>
> +#include <err.h>
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <../../drivers/vhost/vringh.c>
> +#include <../../drivers/virtio/virtio_ring.c>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <sys/wait.h>
> +#include <fcntl.h>
> +
> +#define USER_MEM (1024*1024)
> +void *__user_addr_min, *__user_addr_max;
> +void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
> +static u64 user_addr_offset;
> +
> +#define RINGSIZE 256
> +#define ALIGN 4096
> +
> +static void never_notify_host(struct virtqueue *vq)
> +{
> +	abort();
> +}
> +
> +static void never_callback_guest(struct virtqueue *vq)
> +{
> +	abort();
> +}
> +
> +static inline bool getrange_iov(u64 addr, struct vringh_range *r)
> +{
> +	r->start = (u64)(unsigned long)__user_addr_min - user_addr_offset;
> +	r->end_incl = (u64)(unsigned long)__user_addr_max - 1 - user_addr_offset;
> +	r->offset = user_addr_offset;
> +	return true;
> +}
> +
> +struct guest_virtio_device {
> +	struct virtio_device vdev;
> +	int to_host_fd;
> +	unsigned long notifies;
> +};
> +
> +static void parallel_notify_host(struct virtqueue *vq)
> +{
> +	struct guest_virtio_device *gvdev;
> +
> +	gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev);
> +	write(gvdev->to_host_fd, "", 1);
> +	gvdev->notifies++;
> +}
> +
> +#define NUM_XFERS (10000000)
> +
> +/* We aim for two "distant" cpus. */
> +static void find_cpus(unsigned int *first, unsigned int *last)
> +{
> +	unsigned int i;
> +
> +	*first = -1U;
> +	*last = 0;
> +	for (i = 0; i < 4096; i++) {
> +		cpu_set_t set;
> +		CPU_ZERO(&set);
> +		CPU_SET(i, &set);
> +		if (sched_setaffinity(getpid(), sizeof(set), &set) == 0) {
> +			if (i < *first)
> +				*first = i;
> +			if (i > *last)
> +				*last = i;
> +		}
> +	}
> +}
> +
> +static int parallel_test(unsigned long features)
> +{
> +	void *host_map, *guest_map;
> +	int fd, mapsize, to_guest[2], to_host[2];
> +	unsigned long xfers = 0, notifies = 0, receives = 0;
> +	unsigned int first_cpu, last_cpu;
> +	cpu_set_t cpu_set;
> +
> +	/* Create real file to mmap. */
> +	fd = open("/tmp/vringh_test-file", O_RDWR|O_CREAT|O_TRUNC, 0600);
> +	if (fd < 0)
> +		err(1, "Opening /tmp/vringh_test-file");
> +
> +	/* Extra room at the end for some data, and indirects */
> +	mapsize = vring_size(RINGSIZE, ALIGN)
> +		+ RINGSIZE * 2 * sizeof(int)
> +		+ RINGSIZE * 6 * sizeof(struct vring_desc);
> +	mapsize = (mapsize + getpagesize() - 1) & ~(getpagesize() - 1);
> +	ftruncate(fd, mapsize);
> +
> +	/* Parent and child use separate addresses, to check our mapping logic! */
> +	host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
> +	guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
> +
> +	pipe(to_guest);
> +	pipe(to_host);
> +
> +	CPU_ZERO(&cpu_set);
> +	find_cpus(&first_cpu, &last_cpu);
> +	printf("Using CPUS %u and %u\n", first_cpu, last_cpu);
> +	fflush(stdout);
> +
> +	if (fork() != 0) {
> +		struct vringh vrh;
> +		bool notify = false;
> +		int status;
> +
> +		/* We are the host: never access guest addresses! */
> +		munmap(guest_map, mapsize);
> +
> +		__user_addr_min = host_map;
> +		__user_addr_max = __user_addr_min + mapsize;
> +		user_addr_offset = host_map - guest_map;
> +		assert(user_addr_offset);
> +
> +		close(to_guest[0]);
> +		close(to_host[1]);
> +
> +		vring_init(&vrh.vring, RINGSIZE, host_map, ALIGN);
> +		vringh_init_user(&vrh, features, RINGSIZE, true,
> +				 vrh.vring.desc, vrh.vring.avail, vrh.vring.used);
> +		CPU_SET(first_cpu, &cpu_set);
> +		if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set))
> +			err(1, "Could not set affinity to cpu %u", first_cpu);
> +
> +		while (xfers < NUM_XFERS) {
> +			struct iovec host_riov[2], host_wiov[2];
> +			struct vringh_iov riov, wiov;
> +			char buf[5];
> +			u16 head;
> +			int rlen, err;
> +
> +			riov.iov = host_riov;
> +			riov.max = ARRAY_SIZE(host_riov);
> +			riov.allocated = false;
> +
> +			wiov.iov = host_wiov;
> +			wiov.max = ARRAY_SIZE(host_wiov);
> +			wiov.allocated = false;
> +
> +			err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange_iov,
> +						  &head, GFP_KERNEL);
> +			if (err == 0) {
> +				char buf[128];
> +
> +				if (notify) {
> +					write(to_guest[1], "", 1);
> +					notifies++;
> +					notify = false;
> +				}
> +
> +				if (vringh_notify_enable_user(&vrh))
> +					continue;
> +
> +				/* Swallow all notifies at once. */
> +				if (read(to_host[0], buf, sizeof(buf)) < 1)
> +					break;
> +
> +				vringh_notify_disable_user(&vrh);
> +				receives++;
> +				continue;
> +			}
> +			if (err != 1)
> +				errx(1, "vringh_getdesc_user: %i", err);
> +
> +			/* We simply copy bytes. */
> +			rlen = vringh_iov_pull_user(&riov, buf, sizeof(buf));
> +			if (rlen < 0)
> +				errx(1, "vringh_iov_pull_user: %i", rlen);
> +			err = vringh_iov_push_user(&wiov, buf, rlen);
> +			if (err != rlen)
> +				errx(1, "vringh_iov_push_user: %i", err);
> +			xfers++;
> +			assert(wiov.i == wiov.max);
> +
> +			err = vringh_complete_user(&vrh, head, rlen, &notify);
> +			if (err != 0)
> +				errx(1, "vringh_complete_user: %i", err);
> +		}
> +
> +		if (notify) {
> +			write(to_guest[1], "", 1);
> +			notifies++;
> +			notify = false;
> +		}
> +		wait(&status);
> +		if (!WIFEXITED(status))
> +			errx(1, "Child died with signal %i?", WTERMSIG(status));
> +		if (WEXITSTATUS(status) != 0)
> +			errx(1, "Child exited %i?", WEXITSTATUS(status));
> +		printf("Host: notified %lu, pinged %lu\n", notifies, receives);
> +		return 0;
> +	} else {
> +		struct guest_virtio_device gvdev;
> +		struct virtqueue *vq;
> +		unsigned int *data;
> +		struct vring_desc *indirects;
> +		unsigned int finished = 0;
> +
> +		/* We pass sg[]s pointing into here, but we need RINGSIZE+1 */
> +		data = guest_map + vring_size(RINGSIZE, ALIGN);
> +		indirects = (void *)data + (RINGSIZE + 1) * 2 * sizeof(int);
> +
> +		/* We are the guest. */
> +		munmap(host_map, mapsize);
> +
> +		close(to_guest[1]);
> +		close(to_host[0]);
> +
> +		gvdev.vdev.features[0] = features;
> +		gvdev.to_host_fd = to_host[1];
> +
> +		CPU_SET(first_cpu, &cpu_set);
> +		if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set))
> +			err(1, "Could not set affinity to cpu %u", first_cpu);
> +
> +		vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &gvdev.vdev, true,
> +					 guest_map, parallel_notify_host,
> +					 never_callback_guest, "guest vq");
> +
> +		/* Don't kfree indirects. */
> +		__kfree_ignore_start = indirects;
> +		__kfree_ignore_end = indirects + RINGSIZE * 6;
> +
> +		while (xfers < NUM_XFERS) {
> +			struct scatterlist sg[6];
> +			unsigned int num_sg, len;
> +			int *din, *dout, err;
> +
> +			/* Consume bufs. */
> +			while ((din = virtqueue_get_buf(vq, &len)) != NULL) {
> +				dout = din + 1;
> +				assert(*dout == *din);
> +				assert(len == 4);
> +				finished++;
> +			}
> +
> +			/* Produce a buffer. */
> +			din = data + (xfers % (RINGSIZE + 1)) * 2;
> +			dout = din + 1;
> +
> +			*din = xfers;
> +			switch ((xfers / sizeof(*din)) % 3) {
> +			case 0:
> +				/* Nasty three-element sg list. */
> +				sg_init_table(sg, num_sg = 3);
> +				sg_set_buf(&sg[0], (void *)din, 1);
> +				sg_set_buf(&sg[1], (void *)din + 1, 2);
> +				sg_set_buf(&sg[2], (void *)din + 3, 1);
> +				sg_init_table(sg + num_sg, num_sg);
> +				sg_set_buf(&sg[num_sg+0], (void *)dout, 1);
> +				sg_set_buf(&sg[num_sg+1], (void *)dout + 1, 2);
> +				sg_set_buf(&sg[num_sg+2], (void *)dout + 3, 1);
> +				break;
> +			case 1:
> +				sg_init_table(sg, num_sg = 2);
> +				sg_set_buf(&sg[0], (void *)din, 1);
> +				sg_set_buf(&sg[1], (void *)din + 1, 3);
> +				sg_init_table(sg + num_sg, num_sg);
> +				sg_set_buf(&sg[num_sg+0], (void *)dout, 1);
> +				sg_set_buf(&sg[num_sg+1], (void *)dout + 1, 3);
> +				break;
> +			case 2:
> +				sg_init_table(sg, num_sg = 1);
> +				sg_set_buf(&sg[0], (void *)din, 4);
> +				sg_init_table(sg + num_sg, num_sg);
> +				sg_set_buf(&sg[num_sg+0], (void *)dout, 4);
> +				break;
> +			}
> +
> +			/* May allocate an indirect, so force it to allocate
> +			 * user addr */
> +			__kmalloc_fake = indirects + (xfers % RINGSIZE) * 6;
> +			err = virtqueue_add_buf(vq, sg, num_sg, num_sg, din,
> +						GFP_KERNEL);
> +			if (err == -ENOSPC) {
> +				char buf[128];
> +
> +				if (!virtqueue_enable_cb_delayed(vq))
> +					continue;
> +				/* Swallow all notifies at once. */
> +				if (read(to_guest[0], buf, sizeof(buf)) < 1)
> +					break;
> +				
> +				receives++;
> +				virtqueue_disable_cb(vq);
> +				continue;
> +			}
> +
> +			if (err)
> +				errx(1, "virtqueue_add_buf: %i", err);
> +
> +			xfers++;
> +			virtqueue_kick(vq);
> +		}
> +
> +		/* Any extra? */
> +		while (finished != xfers) {
> +			char buf[128];
> +			int *din, *dout;
> +			unsigned int len;
> +
> +			/* Consume bufs. */
> +			din = virtqueue_get_buf(vq, &len);
> +			if (din) {
> +				dout = din + 1;
> +				assert(*dout == *din);
> +				assert(len == 4);
> +				finished++;
> +				continue;
> +			}
> +
> +			if (!virtqueue_enable_cb_delayed(vq))
> +				continue;
> +			if (read(to_guest[0], buf, sizeof(buf)) < 1)
> +				break;
> +				
> +			receives++;
> +			virtqueue_disable_cb(vq);
> +		}
> +
> +		printf("Guest: notified %lu, pinged %lu\n",
> +		       gvdev.notifies, receives);
> +		return 0;
> +	}
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	struct virtio_device vdev;
> +	struct virtqueue *vq;
> +	struct vringh vrh;
> +	struct scatterlist guest_sg[RINGSIZE];
> +	struct iovec host_riov[2], host_wiov[2];
> +	struct vringh_iov riov, wiov;
> +	char buf[28];
> +	u16 head;
> +	int err;
> +	unsigned i;
> +	bool notify = false;
> +	void *ret;
> +
> +	vdev.features[0] = 0;
> +
> +	if (argv[1] && strcmp(argv[1], "--indirect") == 0) {
> +		vdev.features[0] |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
> +		argv++;
> +	}
> +
> +	if (argv[1] && strcmp(argv[1], "--eventidx") == 0) {
> +		vdev.features[0] |= (1 << VIRTIO_RING_F_EVENT_IDX);
> +		argv++;
> +	}
> +
> +	if (argv[1] && strcmp(argv[1], "--parallel") == 0)
> +		return parallel_test(vdev.features[0]);
> +
> +	if (posix_memalign(&__user_addr_min, PAGE_SIZE, USER_MEM) != 0)
> +		abort();
> +	__user_addr_max = __user_addr_min + USER_MEM;
> +	memset(__user_addr_min, 0, vring_size(RINGSIZE, ALIGN));
> +
> +	/* Set up guest side. */
> +	vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true,
> +				 __user_addr_min,
> +				 never_notify_host, never_callback_guest,
> +				 "guest vq");
> +
> +	/* Set up host side. */
> +	vring_init(&vrh.vring, RINGSIZE, __user_addr_min, ALIGN);
> +	vringh_init_user(&vrh, vdev.features[0], RINGSIZE, true,
> +			 vrh.vring.desc, vrh.vring.avail, vrh.vring.used);
> +
> +	/* No descriptor to get yet... */
> +	err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange_iov,
> +				  &head, GFP_KERNEL);
> +	if (err != 0)
> +		errx(1, "vringh_getdesc_user: %i", err);
> +
> +	/* Guest puts in a descriptor. */
> +	memcpy(__user_addr_max - 1, "a", 1);
> +	sg_init_table(guest_sg, 1);
> +	sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1);
> +	sg_init_table(guest_sg+1, 1);
> +	sg_set_buf(&guest_sg[1], __user_addr_max - 3, 2);
> +
> +	/* May allocate an indirect, so force it to allocate user addr */
> +	__kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN);
> +	err = virtqueue_add_buf(vq, guest_sg, 1, 1, &err, GFP_KERNEL);
> +	if (err)
> +		errx(1, "virtqueue_add_buf: %i", err);
> +	__kmalloc_fake = NULL;
> +
> +	/* Host retreives it. */
> +	riov.iov = host_riov;
> +	riov.max = ARRAY_SIZE(host_riov);
> +	riov.allocated = false;
> +
> +	wiov.iov = host_wiov;
> +	wiov.max = ARRAY_SIZE(host_wiov);
> +	wiov.allocated = false;
> +
> +	err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange_iov,
> +				  &head, GFP_KERNEL);
> +	if (err != 1)
> +		errx(1, "vringh_getdesc_user: %i", err);
> +
> +	assert(riov.max == 1);
> +	assert(riov.iov[0].iov_base == __user_addr_max - 1);
> +	assert(riov.iov[0].iov_len == 1);
> +	assert(wiov.max == 1);
> +	assert(wiov.iov[0].iov_base == __user_addr_max - 3);
> +	assert(wiov.iov[0].iov_len == 2);
> +
> +	err = vringh_iov_pull_user(&riov, buf, 5);
> +	if (err != 1)
> +		errx(1, "vringh_iov_pull_kern: %i", err);
> +	assert(buf[0] == 'a');
> +	assert(riov.i == 1);
> +	assert(vringh_iov_pull_kern(&riov, buf, 5) == 0);
> +
> +	memcpy(buf, "bcdef", 5);
> +	err = vringh_iov_push_user(&wiov, buf, 5);
> +	if (err != 2)
> +		errx(1, "vringh_iov_push_user: %i", err);
> +	assert(memcmp(__user_addr_max - 3, "bc", 2) == 0);
> +	assert(wiov.i == 1);
> +	assert(vringh_iov_push_kern(&wiov, buf, 5) == 0);
> +
> +	/* Host is done. */
> +	err = vringh_complete_user(&vrh, head, err, &notify);
> +	if (err != 0)
> +		errx(1, "vringh_complete_user: %i", err);
> +
> +	/* Guest should see used token now. */
> +	__kfree_ignore_start = __user_addr_min + vring_size(RINGSIZE, ALIGN);
> +	__kfree_ignore_end = __kfree_ignore_start + 1;
> +	ret = virtqueue_get_buf(vq, &i);
> +	if (ret != &err)
> +		errx(1, "virtqueue_get_buf: %p", ret);
> +	assert(i == 2);
> +
> +	/* Guest puts in a huge descriptor. */
> +	sg_init_table(guest_sg, RINGSIZE);
> +	for (i = 0; i < RINGSIZE; i++) {
> +		sg_set_buf(&guest_sg[i],
> +			   __user_addr_max - USER_MEM/4, USER_MEM/4);
> +	}
> +
> +	/* Fill contents with recognisable garbage. */
> +	for (i = 0; i < USER_MEM/4; i++)
> +		((char *)__user_addr_max - USER_MEM/4)[i] = i;
> +
> +	/* This will allocate an indirect, so force it to allocate user addr */
> +	__kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN);
> +	err = virtqueue_add_buf(vq, guest_sg, RINGSIZE, 0, &err, GFP_KERNEL);
> +	if (err)
> +		errx(1, "virtqueue_add_buf (large): %i", err);
> +	__kmalloc_fake = NULL;
> +
> +	/* Host picks it up (allocates new iov). */
> +	riov.iov = host_riov;
> +	riov.max = ARRAY_SIZE(host_riov);
> +	riov.allocated = false;
> +
> +	wiov.iov = host_wiov;
> +	wiov.max = ARRAY_SIZE(host_wiov);
> +	wiov.allocated = false;
> +
> +	err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange_iov,
> +				  &head, GFP_KERNEL);
> +	if (err != 1)
> +		errx(1, "vringh_getdesc_user: %i", err);
> +
> +	assert(riov.allocated);
> +	assert(riov.iov != host_riov);
> +	assert(riov.max == RINGSIZE);
> +
> +	assert(!wiov.allocated);
> +	assert(wiov.max == 0);
> +
> +	/* Pull data back out (in odd chunks), should be as expected. */
> +	for (i = 0; i < RINGSIZE * USER_MEM/4; i += 3) {
> +		err = vringh_iov_pull_user(&riov, buf, 3);
> +		if (err != 3 && i + err != RINGSIZE * USER_MEM/4)
> +			errx(1, "vringh_iov_pull_user large: %i", err);
> +		assert(buf[0] == (char)i);
> +		assert(err < 2 || buf[1] == (char)(i + 1));
> +		assert(err < 3 || buf[2] == (char)(i + 2));
> +	}
> +	assert(wiov.i == wiov.max);
> +
> +	kfree(riov.iov);
> +
> +	/* Test weird (but legal!) indirect. */
> +	if (vdev.features[0] & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
> +		struct vring_virtqueue *vvq = to_vvq(vq);
> +		char *data = __user_addr_max - USER_MEM/4;
> +		struct vring_desc *d = __user_addr_max - USER_MEM/2;
> +		unsigned int n = vvq->free_head;
> +
> +		/* Force creation of direct, which we modify. */
> +		vvq->indirect = false;
> +		
> +		sg_init_table(guest_sg, 4);
> +		sg_set_buf(&guest_sg[0], d, sizeof(*d)*2);
> +		sg_set_buf(&guest_sg[1], d + 2, sizeof(*d)*1);
> +		sg_set_buf(&guest_sg[2], data + 6, 4);
> +		sg_set_buf(&guest_sg[3], d + 3, sizeof(*d)*3);
> +
> +		err = virtqueue_add_buf(vq, guest_sg, 4, 0, &err, GFP_KERNEL);
> +		if (err)
> +			errx(1, "virtqueue_add_buf (indirect): %i", err);
> +
> +		/* They're used in order, but double-check... */
> +		assert(vvq->vring.desc[n].addr == (unsigned long)d);
> +		assert(vvq->vring.desc[n+1].addr == (unsigned long)(d+2));
> +		assert(vvq->vring.desc[n+2].addr == (unsigned long)data + 6);
> +		assert(vvq->vring.desc[n+3].addr == (unsigned long)(d+3));
> +		vvq->vring.desc[n].flags |= VRING_DESC_F_INDIRECT;
> +		vvq->vring.desc[n+1].flags |= VRING_DESC_F_INDIRECT;
> +		vvq->vring.desc[n+3].flags |= VRING_DESC_F_INDIRECT;
> +
> +		/* First indirect */
> +		d[0].addr = (unsigned long)data;
> +		d[0].len = 1;
> +		d[0].flags = VRING_DESC_F_NEXT;
> +		d[0].next = 1;
> +		d[1].addr = (unsigned long)data + 1;
> +		d[1].len = 2;
> +		d[1].flags = 0;
> +
> +		/* Second indirect */
> +		d[2].addr = (unsigned long)data + 3;
> +		d[2].len = 3;
> +		d[2].flags = 0;
> +
> +		/* Third indirect */
> +		d[3].addr = (unsigned long)data + 10;
> +		d[3].len = 5;
> +		d[3].flags = VRING_DESC_F_NEXT;
> +		d[3].next = 1;
> +		d[4].addr = (unsigned long)data + 15;
> +		d[4].len = 6;
> +		d[4].flags = VRING_DESC_F_NEXT;
> +		d[4].next = 2;
> +		d[5].addr = (unsigned long)data + 21;
> +		d[5].len = 7;
> +		d[5].flags = 0;
> +
> +		/* Host picks it up (allocates new iov). */
> +		riov.iov = host_riov;
> +		riov.max = ARRAY_SIZE(host_riov);
> +		riov.allocated = false;
> +
> +		wiov.iov = host_wiov;
> +		wiov.max = ARRAY_SIZE(host_wiov);
> +		wiov.allocated = false;
> +
> +		err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange_iov,
> +					  &head, GFP_KERNEL);
> +		if (err != 1)
> +			errx(1, "vringh_getdesc_user: %i", err);
> +
> +		if (head != n)
> +			errx(1, "vringh_getdesc_user: head %i not %i", head, n);
> +
> +		assert(riov.max == 7);
> +		assert(riov.allocated);
> +		err = vringh_iov_pull_user(&riov, buf, 29);
> +		assert(err == 28);
> +
> +		/* Data should be linear. */
> +		for (i = 0; i < err; i++)
> +			assert(buf[i] == i);
> +		kfree(riov.iov);
> +	}
> +
> +	/* Don't leak memory... */
> +	vring_del_virtqueue(vq);
> +	free(__user_addr_min);
> +
> +	return 0;
> +}
> 


-- 
Asias
_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux