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