Root/tools/virtio/virtio_test.c

Source at commit ec7cab4cbb721bff91ec924ec691efd8daf36579 created 9 years 7 months ago.
By Maarten ter Huurne, MIPS: JZ4740: A320: Updated quickstart documentation.
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
18struct 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
29struct 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
40void 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
49void vq_callback(struct virtqueue *vq)
50{
51}
52
53
54void 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
84static 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
105static 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. */
135static 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
146static 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
194const char optstring[] = "h";
195const 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
212static void help()
213{
214    fprintf(stderr, "Usage: virtio_test [--help] [--no-indirect]\n");
215}
216
217int 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
243done:
244    vdev_info_init(&dev, features);
245    vq_info_add(&dev, 256);
246    run_test(&dev, &dev.vqs[0], 0x100000);
247    return 0;
248}
249

Archive Download this file



interactive