Commit 4e53f78e5b06c073a5c10814c72e98c1ca8a9f10

Authored by Michael S. Tsirkin
1 parent 71ccc212e5

tools/virtio: virtio_test tool

This is the userspace part of the tool: it includes a bunch of stubs for
linux APIs, somewhat simular to linuxsched. This makes it possible to
recompile the ring code in userspace.

A small test example is implemented combining this with vhost_test
module.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

Showing 5 changed files with 487 additions and 0 deletions Side-by-side Diff

tools/virtio/Makefile
  1 +all: test mod
  2 +test: virtio_test
  3 +virtio_test: virtio_ring.o virtio_test.o
  4 +CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -MMD
  5 +vpath %.c ../../drivers/virtio
  6 +mod:
  7 + ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test
  8 +.PHONY: all test mod clean
  9 +clean:
  10 + ${RM} *.o vhost_test/*.o vhost_test/.*.cmd \
  11 + vhost_test/Module.symvers vhost_test/modules.order *.d
  12 +-include *.d
tools/virtio/linux/device.h
  1 +#ifndef LINUX_DEVICE_H
  2 +#endif
tools/virtio/linux/slab.h
  1 +#ifndef LINUX_SLAB_H
  2 +#endif
tools/virtio/linux/virtio.h
  1 +#ifndef LINUX_VIRTIO_H
  2 +#define LINUX_VIRTIO_H
  3 +
  4 +#include <stdbool.h>
  5 +#include <stdlib.h>
  6 +#include <stddef.h>
  7 +#include <stdio.h>
  8 +#include <string.h>
  9 +#include <assert.h>
  10 +
  11 +#include <linux/types.h>
  12 +#include <errno.h>
  13 +
  14 +typedef unsigned long long dma_addr_t;
  15 +
  16 +struct scatterlist {
  17 + unsigned long page_link;
  18 + unsigned int offset;
  19 + unsigned int length;
  20 + dma_addr_t dma_address;
  21 +};
  22 +
  23 +struct page {
  24 + unsigned long long dummy;
  25 +};
  26 +
  27 +#define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond))
  28 +
  29 +/* Physical == Virtual */
  30 +#define virt_to_phys(p) ((unsigned long)p)
  31 +#define phys_to_virt(a) ((void *)(unsigned long)(a))
  32 +/* Page address: Virtual / 4K */
  33 +#define virt_to_page(p) ((struct page*)((virt_to_phys(p) / 4096) * \
  34 + sizeof(struct page)))
  35 +#define offset_in_page(p) (((unsigned long)p) % 4096)
  36 +#define sg_phys(sg) ((sg->page_link & ~0x3) / sizeof(struct page) * 4096 + \
  37 + sg->offset)
  38 +static inline void sg_mark_end(struct scatterlist *sg)
  39 +{
  40 + /*
  41 + * Set termination bit, clear potential chain bit
  42 + */
  43 + sg->page_link |= 0x02;
  44 + sg->page_link &= ~0x01;
  45 +}
  46 +static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
  47 +{
  48 + memset(sgl, 0, sizeof(*sgl) * nents);
  49 + sg_mark_end(&sgl[nents - 1]);
  50 +}
  51 +static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
  52 +{
  53 + unsigned long page_link = sg->page_link & 0x3;
  54 +
  55 + /*
  56 + * In order for the low bit stealing approach to work, pages
  57 + * must be aligned at a 32-bit boundary as a minimum.
  58 + */
  59 + BUG_ON((unsigned long) page & 0x03);
  60 + sg->page_link = page_link | (unsigned long) page;
  61 +}
  62 +
  63 +static inline void sg_set_page(struct scatterlist *sg, struct page *page,
  64 + unsigned int len, unsigned int offset)
  65 +{
  66 + sg_assign_page(sg, page);
  67 + sg->offset = offset;
  68 + sg->length = len;
  69 +}
  70 +
  71 +static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
  72 + unsigned int buflen)
  73 +{
  74 + sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
  75 +}
  76 +
  77 +static inline void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
  78 +{
  79 + sg_init_table(sg, 1);
  80 + sg_set_buf(sg, buf, buflen);
  81 +}
  82 +
  83 +typedef __u16 u16;
  84 +
  85 +typedef enum {
  86 + GFP_KERNEL,
  87 + GFP_ATOMIC,
  88 +} gfp_t;
  89 +typedef enum {
  90 + IRQ_NONE,
  91 + IRQ_HANDLED
  92 +} irqreturn_t;
  93 +
  94 +static inline void *kmalloc(size_t s, gfp_t gfp)
  95 +{
  96 + return malloc(s);
  97 +}
  98 +
  99 +static inline void kfree(void *p)
  100 +{
  101 + free(p);
  102 +}
  103 +
  104 +#define container_of(ptr, type, member) ({ \
  105 + const typeof( ((type *)0)->member ) *__mptr = (ptr); \
  106 + (type *)( (char *)__mptr - offsetof(type,member) );})
  107 +
  108 +#define uninitialized_var(x) x = x
  109 +
  110 +# ifndef likely
  111 +# define likely(x) (__builtin_expect(!!(x), 1))
  112 +# endif
  113 +# ifndef unlikely
  114 +# define unlikely(x) (__builtin_expect(!!(x), 0))
  115 +# endif
  116 +
  117 +#define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
  118 +#ifdef DEBUG
  119 +#define pr_debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
  120 +#else
  121 +#define pr_debug(format, ...) do {} while (0)
  122 +#endif
  123 +#define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__)
  124 +#define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__)
  125 +
  126 +/* TODO: empty stubs for now. Broken but enough for virtio_ring.c */
  127 +#define list_add_tail(a, b) do {} while (0)
  128 +#define list_del(a) do {} while (0)
  129 +
  130 +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
  131 +#define BITS_PER_BYTE 8
  132 +#define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE)
  133 +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
  134 +/* TODO: Not atomic as it should be:
  135 + * we don't use this for anything important. */
  136 +static inline void clear_bit(int nr, volatile unsigned long *addr)
  137 +{
  138 + unsigned long mask = BIT_MASK(nr);
  139 + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
  140 +
  141 + *p &= ~mask;
  142 +}
  143 +
  144 +static inline int test_bit(int nr, const volatile unsigned long *addr)
  145 +{
  146 + return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
  147 +}
  148 +
  149 +/* The only feature we care to support */
  150 +#define virtio_has_feature(dev, feature) \
  151 + test_bit((feature), (dev)->features)
  152 +/* end of stubs */
  153 +
  154 +struct virtio_device {
  155 + void *dev;
  156 + unsigned long features[1];
  157 +};
  158 +
  159 +struct virtqueue {
  160 + /* TODO: commented as list macros are empty stubs for now.
  161 + * Broken but enough for virtio_ring.c
  162 + * struct list_head list; */
  163 + void (*callback)(struct virtqueue *vq);
  164 + const char *name;
  165 + struct virtio_device *vdev;
  166 + void *priv;
  167 +};
  168 +
  169 +#define EXPORT_SYMBOL_GPL(__EXPORT_SYMBOL_GPL_name) \
  170 + void __EXPORT_SYMBOL_GPL##__EXPORT_SYMBOL_GPL_name() { \
  171 +}
  172 +#define MODULE_LICENSE(__MODULE_LICENSE_value) \
  173 + const char *__MODULE_LICENSE_name = __MODULE_LICENSE_value
  174 +
  175 +#define CONFIG_SMP
  176 +
  177 +#if defined(__i386__) || defined(__x86_64__)
  178 +#define barrier() asm volatile("" ::: "memory")
  179 +#define mb() __sync_synchronize()
  180 +
  181 +#define smp_mb() mb()
  182 +# define smp_rmb() barrier()
  183 +# define smp_wmb() barrier()
  184 +#else
  185 +#error Please fill in barrier macros
  186 +#endif
  187 +
  188 +/* Interfaces exported by virtio_ring. */
  189 +int virtqueue_add_buf_gfp(struct virtqueue *vq,
  190 + struct scatterlist sg[],
  191 + unsigned int out_num,
  192 + unsigned int in_num,
  193 + void *data,
  194 + gfp_t gfp);
  195 +
  196 +static inline int virtqueue_add_buf(struct virtqueue *vq,
  197 + struct scatterlist sg[],
  198 + unsigned int out_num,
  199 + unsigned int in_num,
  200 + void *data)
  201 +{
  202 + return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC);
  203 +}
  204 +
  205 +void virtqueue_kick(struct virtqueue *vq);
  206 +
  207 +void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
  208 +
  209 +void virtqueue_disable_cb(struct virtqueue *vq);
  210 +
  211 +bool virtqueue_enable_cb(struct virtqueue *vq);
  212 +
  213 +void *virtqueue_detach_unused_buf(struct virtqueue *vq);
  214 +struct virtqueue *vring_new_virtqueue(unsigned int num,
  215 + unsigned int vring_align,
  216 + struct virtio_device *vdev,
  217 + void *pages,
  218 + void (*notify)(struct virtqueue *vq),
  219 + void (*callback)(struct virtqueue *vq),
  220 + const char *name);
  221 +void vring_del_virtqueue(struct virtqueue *vq);
  222 +
  223 +#endif
tools/virtio/virtio_test.c
  1 +#define _GNU_SOURCE
  2 +#include <getopt.h>
  3 +#include <string.h>
  4 +#include <poll.h>
  5 +#include <sys/eventfd.h>
  6 +#include <stdlib.h>
  7 +#include <assert.h>
  8 +#include <unistd.h>
  9 +#include <sys/ioctl.h>
  10 +#include <sys/stat.h>
  11 +#include <sys/types.h>
  12 +#include <fcntl.h>
  13 +#include <linux/vhost.h>
  14 +#include <linux/virtio.h>
  15 +#include <linux/virtio_ring.h>
  16 +#include "../../drivers/vhost/test.h"
  17 +
  18 +struct vq_info {
  19 + int kick;
  20 + int call;
  21 + int num;
  22 + int idx;
  23 + void *ring;
  24 + /* copy used for control */
  25 + struct vring vring;
  26 + struct virtqueue *vq;
  27 +};
  28 +
  29 +struct vdev_info {
  30 + struct virtio_device vdev;
  31 + int control;
  32 + struct pollfd fds[1];
  33 + struct vq_info vqs[1];
  34 + int nvqs;
  35 + void *buf;
  36 + size_t buf_size;
  37 + struct vhost_memory *mem;
  38 +};
  39 +
  40 +void vq_notify(struct virtqueue *vq)
  41 +{
  42 + struct vq_info *info = vq->priv;
  43 + unsigned long long v = 1;
  44 + int r;
  45 + r = write(info->kick, &v, sizeof v);
  46 + assert(r == sizeof v);
  47 +}
  48 +
  49 +void vq_callback(struct virtqueue *vq)
  50 +{
  51 +}
  52 +
  53 +
  54 +void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
  55 +{
  56 + struct vhost_vring_state state = { .index = info->idx };
  57 + struct vhost_vring_file file = { .index = info->idx };
  58 + unsigned long long features = dev->vdev.features[0];
  59 + struct vhost_vring_addr addr = {
  60 + .index = info->idx,
  61 + .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
  62 + .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
  63 + .used_user_addr = (uint64_t)(unsigned long)info->vring.used,
  64 + };
  65 + int r;
  66 + r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
  67 + assert(r >= 0);
  68 + state.num = info->vring.num;
  69 + r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
  70 + assert(r >= 0);
  71 + state.num = 0;
  72 + r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
  73 + assert(r >= 0);
  74 + r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
  75 + assert(r >= 0);
  76 + file.fd = info->kick;
  77 + r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
  78 + assert(r >= 0);
  79 + file.fd = info->call;
  80 + r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
  81 + assert(r >= 0);
  82 +}
  83 +
  84 +static void vq_info_add(struct vdev_info *dev, int num)
  85 +{
  86 + struct vq_info *info = &dev->vqs[dev->nvqs];
  87 + int r;
  88 + info->idx = dev->nvqs;
  89 + info->kick = eventfd(0, EFD_NONBLOCK);
  90 + info->call = eventfd(0, EFD_NONBLOCK);
  91 + r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
  92 + assert(r >= 0);
  93 + memset(info->ring, 0, vring_size(num, 4096));
  94 + vring_init(&info->vring, num, info->ring, 4096);
  95 + info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, info->ring,
  96 + vq_notify, vq_callback, "test");
  97 + assert(info->vq);
  98 + info->vq->priv = info;
  99 + vhost_vq_setup(dev, info);
  100 + dev->fds[info->idx].fd = info->call;
  101 + dev->fds[info->idx].events = POLLIN;
  102 + dev->nvqs++;
  103 +}
  104 +
  105 +static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
  106 +{
  107 + int r;
  108 + memset(dev, 0, sizeof *dev);
  109 + dev->vdev.features[0] = features;
  110 + dev->vdev.features[1] = features >> 32;
  111 + dev->buf_size = 1024;
  112 + dev->buf = malloc(dev->buf_size);
  113 + assert(dev->buf);
  114 + dev->control = open("/dev/vhost-test", O_RDWR);
  115 + assert(dev->control >= 0);
  116 + r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
  117 + assert(r >= 0);
  118 + dev->mem = malloc(offsetof(struct vhost_memory, regions) +
  119 + sizeof dev->mem->regions[0]);
  120 + assert(dev->mem);
  121 + memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
  122 + sizeof dev->mem->regions[0]);
  123 + dev->mem->nregions = 1;
  124 + dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
  125 + dev->mem->regions[0].userspace_addr = (long)dev->buf;
  126 + dev->mem->regions[0].memory_size = dev->buf_size;
  127 + r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
  128 + assert(r >= 0);
  129 +}
  130 +
  131 +/* TODO: this is pretty bad: we get a cache line bounce
  132 + * for the wait queue on poll and another one on read,
  133 + * plus the read which is there just to clear the
  134 + * current state. */
  135 +static void wait_for_interrupt(struct vdev_info *dev)
  136 +{
  137 + int i;
  138 + unsigned long long val;
  139 + poll(dev->fds, dev->nvqs, -1);
  140 + for (i = 0; i < dev->nvqs; ++i)
  141 + if (dev->fds[i].revents & POLLIN) {
  142 + read(dev->fds[i].fd, &val, sizeof val);
  143 + }
  144 +}
  145 +
  146 +static void run_test(struct vdev_info *dev, struct vq_info *vq, int bufs)
  147 +{
  148 + struct scatterlist sl;
  149 + long started = 0, completed = 0;
  150 + long completed_before;
  151 + int r, test = 1;
  152 + unsigned len;
  153 + long long spurious = 0;
  154 + r = ioctl(dev->control, VHOST_TEST_RUN, &test);
  155 + assert(r >= 0);
  156 + for (;;) {
  157 + virtqueue_disable_cb(vq->vq);
  158 + completed_before = completed;
  159 + do {
  160 + if (started < bufs) {
  161 + sg_init_one(&sl, dev->buf, dev->buf_size);
  162 + r = virtqueue_add_buf(vq->vq, &sl, 1, 0,
  163 + dev->buf + started);
  164 + if (likely(r >= 0)) {
  165 + ++started;
  166 + virtqueue_kick(vq->vq);
  167 + }
  168 + } else
  169 + r = -1;
  170 +
  171 + /* Flush out completed bufs if any */
  172 + if (virtqueue_get_buf(vq->vq, &len)) {
  173 + ++completed;
  174 + r = 0;
  175 + }
  176 +
  177 + } while (r >= 0);
  178 + if (completed == completed_before)
  179 + ++spurious;
  180 + assert(completed <= bufs);
  181 + assert(started <= bufs);
  182 + if (completed == bufs)
  183 + break;
  184 + if (virtqueue_enable_cb(vq->vq)) {
  185 + wait_for_interrupt(dev);
  186 + }
  187 + }
  188 + test = 0;
  189 + r = ioctl(dev->control, VHOST_TEST_RUN, &test);
  190 + assert(r >= 0);
  191 + fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
  192 +}
  193 +
  194 +const char optstring[] = "h";
  195 +const struct option longopts[] = {
  196 + {
  197 + .name = "help",
  198 + .val = 'h',
  199 + },
  200 + {
  201 + .name = "indirect",
  202 + .val = 'I',
  203 + },
  204 + {
  205 + .name = "no-indirect",
  206 + .val = 'i',
  207 + },
  208 + {
  209 + }
  210 +};
  211 +
  212 +static void help()
  213 +{
  214 + fprintf(stderr, "Usage: virtio_test [--help] [--no-indirect]\n");
  215 +}
  216 +
  217 +int main(int argc, char **argv)
  218 +{
  219 + struct vdev_info dev;
  220 + unsigned long long features = 1ULL << VIRTIO_RING_F_INDIRECT_DESC;
  221 + int o;
  222 +
  223 + for (;;) {
  224 + o = getopt_long(argc, argv, optstring, longopts, NULL);
  225 + switch (o) {
  226 + case -1:
  227 + goto done;
  228 + case '?':
  229 + help();
  230 + exit(2);
  231 + case 'h':
  232 + help();
  233 + goto done;
  234 + case 'i':
  235 + features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
  236 + break;
  237 + default:
  238 + assert(0);
  239 + break;
  240 + }
  241 + }
  242 +
  243 +done:
  244 + vdev_info_init(&dev, features);
  245 + vq_info_add(&dev, 256);
  246 + run_test(&dev, &dev.vqs[0], 0x100000);
  247 + return 0;
  248 +}