linux/tools/virtio/virtio_test.c
<<
>>
Prefs
   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 <stdbool.h>
  14#include <linux/virtio_types.h>
  15#include <linux/vhost.h>
  16#include <linux/virtio.h>
  17#include <linux/virtio_ring.h>
  18#include "../../drivers/vhost/test.h"
  19
  20/* Unused */
  21void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
  22
  23struct vq_info {
  24        int kick;
  25        int call;
  26        int num;
  27        int idx;
  28        void *ring;
  29        /* copy used for control */
  30        struct vring vring;
  31        struct virtqueue *vq;
  32};
  33
  34struct vdev_info {
  35        struct virtio_device vdev;
  36        int control;
  37        struct pollfd fds[1];
  38        struct vq_info vqs[1];
  39        int nvqs;
  40        void *buf;
  41        size_t buf_size;
  42        struct vhost_memory *mem;
  43};
  44
  45bool vq_notify(struct virtqueue *vq)
  46{
  47        struct vq_info *info = vq->priv;
  48        unsigned long long v = 1;
  49        int r;
  50        r = write(info->kick, &v, sizeof v);
  51        assert(r == sizeof v);
  52        return true;
  53}
  54
  55void vq_callback(struct virtqueue *vq)
  56{
  57}
  58
  59
  60void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
  61{
  62        struct vhost_vring_state state = { .index = info->idx };
  63        struct vhost_vring_file file = { .index = info->idx };
  64        unsigned long long features = dev->vdev.features;
  65        struct vhost_vring_addr addr = {
  66                .index = info->idx,
  67                .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
  68                .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
  69                .used_user_addr = (uint64_t)(unsigned long)info->vring.used,
  70        };
  71        int r;
  72        r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
  73        assert(r >= 0);
  74        state.num = info->vring.num;
  75        r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
  76        assert(r >= 0);
  77        state.num = 0;
  78        r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
  79        assert(r >= 0);
  80        r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
  81        assert(r >= 0);
  82        file.fd = info->kick;
  83        r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
  84        assert(r >= 0);
  85        file.fd = info->call;
  86        r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
  87        assert(r >= 0);
  88}
  89
  90static void vq_info_add(struct vdev_info *dev, int num)
  91{
  92        struct vq_info *info = &dev->vqs[dev->nvqs];
  93        int r;
  94        info->idx = dev->nvqs;
  95        info->kick = eventfd(0, EFD_NONBLOCK);
  96        info->call = eventfd(0, EFD_NONBLOCK);
  97        r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
  98        assert(r >= 0);
  99        memset(info->ring, 0, vring_size(num, 4096));
 100        vring_init(&info->vring, num, info->ring, 4096);
 101        info->vq = vring_new_virtqueue(info->idx,
 102                                       info->vring.num, 4096, &dev->vdev,
 103                                       true, info->ring,
 104                                       vq_notify, vq_callback, "test");
 105        assert(info->vq);
 106        info->vq->priv = info;
 107        vhost_vq_setup(dev, info);
 108        dev->fds[info->idx].fd = info->call;
 109        dev->fds[info->idx].events = POLLIN;
 110        dev->nvqs++;
 111}
 112
 113static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
 114{
 115        int r;
 116        memset(dev, 0, sizeof *dev);
 117        dev->vdev.features = features;
 118        dev->buf_size = 1024;
 119        dev->buf = malloc(dev->buf_size);
 120        assert(dev->buf);
 121        dev->control = open("/dev/vhost-test", O_RDWR);
 122        assert(dev->control >= 0);
 123        r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
 124        assert(r >= 0);
 125        dev->mem = malloc(offsetof(struct vhost_memory, regions) +
 126                          sizeof dev->mem->regions[0]);
 127        assert(dev->mem);
 128        memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
 129                          sizeof dev->mem->regions[0]);
 130        dev->mem->nregions = 1;
 131        dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
 132        dev->mem->regions[0].userspace_addr = (long)dev->buf;
 133        dev->mem->regions[0].memory_size = dev->buf_size;
 134        r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
 135        assert(r >= 0);
 136}
 137
 138/* TODO: this is pretty bad: we get a cache line bounce
 139 * for the wait queue on poll and another one on read,
 140 * plus the read which is there just to clear the
 141 * current state. */
 142static void wait_for_interrupt(struct vdev_info *dev)
 143{
 144        int i;
 145        unsigned long long val;
 146        poll(dev->fds, dev->nvqs, -1);
 147        for (i = 0; i < dev->nvqs; ++i)
 148                if (dev->fds[i].revents & POLLIN) {
 149                        read(dev->fds[i].fd, &val, sizeof val);
 150                }
 151}
 152
 153static void run_test(struct vdev_info *dev, struct vq_info *vq,
 154                     bool delayed, int bufs)
 155{
 156        struct scatterlist sl;
 157        long started = 0, completed = 0;
 158        long completed_before;
 159        int r, test = 1;
 160        unsigned len;
 161        long long spurious = 0;
 162        r = ioctl(dev->control, VHOST_TEST_RUN, &test);
 163        assert(r >= 0);
 164        for (;;) {
 165                virtqueue_disable_cb(vq->vq);
 166                completed_before = completed;
 167                do {
 168                        if (started < bufs) {
 169                                sg_init_one(&sl, dev->buf, dev->buf_size);
 170                                r = virtqueue_add_outbuf(vq->vq, &sl, 1,
 171                                                         dev->buf + started,
 172                                                         GFP_ATOMIC);
 173                                if (likely(r == 0)) {
 174                                        ++started;
 175                                        if (unlikely(!virtqueue_kick(vq->vq)))
 176                                                r = -1;
 177                                }
 178                        } else
 179                                r = -1;
 180
 181                        /* Flush out completed bufs if any */
 182                        if (virtqueue_get_buf(vq->vq, &len)) {
 183                                ++completed;
 184                                r = 0;
 185                        }
 186
 187                } while (r == 0);
 188                if (completed == completed_before)
 189                        ++spurious;
 190                assert(completed <= bufs);
 191                assert(started <= bufs);
 192                if (completed == bufs)
 193                        break;
 194                if (delayed) {
 195                        if (virtqueue_enable_cb_delayed(vq->vq))
 196                                wait_for_interrupt(dev);
 197                } else {
 198                        if (virtqueue_enable_cb(vq->vq))
 199                                wait_for_interrupt(dev);
 200                }
 201        }
 202        test = 0;
 203        r = ioctl(dev->control, VHOST_TEST_RUN, &test);
 204        assert(r >= 0);
 205        fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
 206}
 207
 208const char optstring[] = "h";
 209const struct option longopts[] = {
 210        {
 211                .name = "help",
 212                .val = 'h',
 213        },
 214        {
 215                .name = "event-idx",
 216                .val = 'E',
 217        },
 218        {
 219                .name = "no-event-idx",
 220                .val = 'e',
 221        },
 222        {
 223                .name = "indirect",
 224                .val = 'I',
 225        },
 226        {
 227                .name = "no-indirect",
 228                .val = 'i',
 229        },
 230        {
 231                .name = "virtio-1",
 232                .val = '1',
 233        },
 234        {
 235                .name = "no-virtio-1",
 236                .val = '0',
 237        },
 238        {
 239                .name = "delayed-interrupt",
 240                .val = 'D',
 241        },
 242        {
 243                .name = "no-delayed-interrupt",
 244                .val = 'd',
 245        },
 246        {
 247        }
 248};
 249
 250static void help(void)
 251{
 252        fprintf(stderr, "Usage: virtio_test [--help]"
 253                " [--no-indirect]"
 254                " [--no-event-idx]"
 255                " [--no-virtio-1]"
 256                " [--delayed-interrupt]"
 257                "\n");
 258}
 259
 260int main(int argc, char **argv)
 261{
 262        struct vdev_info dev;
 263        unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
 264                (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
 265        int o;
 266        bool delayed = false;
 267
 268        for (;;) {
 269                o = getopt_long(argc, argv, optstring, longopts, NULL);
 270                switch (o) {
 271                case -1:
 272                        goto done;
 273                case '?':
 274                        help();
 275                        exit(2);
 276                case 'e':
 277                        features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
 278                        break;
 279                case 'h':
 280                        help();
 281                        goto done;
 282                case 'i':
 283                        features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
 284                        break;
 285                case '0':
 286                        features &= ~(1ULL << VIRTIO_F_VERSION_1);
 287                        break;
 288                case 'D':
 289                        delayed = true;
 290                        break;
 291                default:
 292                        assert(0);
 293                        break;
 294                }
 295        }
 296
 297done:
 298        vdev_info_init(&dev, features);
 299        vq_info_add(&dev, 256);
 300        run_test(&dev, &dev.vqs[0], delayed, 0x100000);
 301        return 0;
 302}
 303