From: Asias He <asias@redhat.com>
To: Rusty Russell <rusty@rustcorp.com.au>
Cc: "Michael S . Tsirkin" <mst@redhat.com>,
virtualization@lists.linux-foundation.org
Subject: Re: [PATCH 4/6] tools/virtio: add vring_test.
Date: Tue, 22 Jan 2013 16:25:01 +0800 [thread overview]
Message-ID: <50FE4CDD.9040005@redhat.com> (raw)
In-Reply-To: <1358418584-26345-4-git-send-email-rusty@rustcorp.com.au>
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@rustcorp.com.au>
> ---
> 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, ¬ify);
> + 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, ¬ify);
> + 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
next prev parent reply other threads:[~2013-01-22 8:25 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-01-17 10:29 [PATCH 1/6] virtio_host: host-side implementation of virtio rings Rusty Russell
2013-01-17 10:29 ` [PATCH 2/6] tools/virtio: fix compile Rusty Russell
2013-01-17 10:29 ` [PATCH 3/6] tools/virtio: separate headers more Rusty Russell
2013-01-17 10:29 ` [PATCH 4/6] tools/virtio: add vring_test Rusty Russell
2013-01-22 8:25 ` Asias He [this message]
2013-01-22 23:03 ` Rusty Russell
2013-01-23 1:40 ` Asias He
2013-01-24 2:22 ` Rusty Russell
2013-01-17 10:29 ` [PATCH 5/6] vringh: separate callback for notification Rusty Russell
2013-01-17 10:29 ` [PATCH 6/6] tools/virtio: adapt for API changes Rusty Russell
2013-01-17 11:23 ` [PATCH 1/6] virtio_host: host-side implementation of virtio rings Michael S. Tsirkin
2013-01-17 11:49 ` Sjur Brændeland
2013-01-17 12:08 ` Michael S. Tsirkin
2013-01-21 2:36 ` Rusty Russell
2013-01-22 14:54 ` Sjur Brændeland
2013-01-21 2:34 ` Rusty Russell
2013-01-21 9:41 ` Michael S. Tsirkin
2013-01-21 11:52 ` Rusty Russell
2013-01-21 12:24 ` Michael S. Tsirkin
2013-01-21 12:40 ` Michael S. Tsirkin
2013-01-21 22:57 ` Rusty Russell
2013-01-22 6:57 ` Rusty Russell
2013-01-22 7:13 ` Rusty Russell
2013-01-22 8:12 ` Asias He
2013-01-23 1:56 ` Rusty Russell
2013-02-04 20:29 ` Sjur Brændeland
2013-02-04 21:44 ` Rusty Russell
2013-02-12 18:58 ` Sjur Brændeland
2013-02-13 10:25 ` Rusty Russell
2013-02-14 14:54 ` Sjur Brændeland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=50FE4CDD.9040005@redhat.com \
--to=asias@redhat.com \
--cc=mst@redhat.com \
--cc=rusty@rustcorp.com.au \
--cc=virtualization@lists.linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).