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