1 // SPDX-License-Identifier: GPL-2.0
7 #include <sys/eventfd.h>
11 #include <sys/ioctl.h>
13 #include <sys/types.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"
22 #define RANDOM_BATCH -1
25 void *__kmalloc_fake
, *__kfree_ignore_start
, *__kfree_ignore_end
;
33 /* copy used for control */
39 struct virtio_device vdev
;
42 struct vq_info vqs
[1];
46 struct vhost_memory
*mem
;
49 static const struct vhost_vring_file no_backend
= { .fd
= -1 },
50 backend
= { .fd
= 1 };
51 static const struct vhost_vring_state null_state
= {};
53 bool vq_notify(struct virtqueue
*vq
)
55 struct vq_info
*info
= vq
->priv
;
56 unsigned long long v
= 1;
58 r
= write(info
->kick
, &v
, sizeof v
);
59 assert(r
== sizeof v
);
63 void vq_callback(struct virtqueue
*vq
)
68 void vhost_vq_setup(struct vdev_info
*dev
, struct vq_info
*info
)
70 struct vhost_vring_state state
= { .index
= info
->idx
};
71 struct vhost_vring_file file
= { .index
= info
->idx
};
72 unsigned long long features
= dev
->vdev
.features
;
73 struct vhost_vring_addr addr
= {
75 .desc_user_addr
= (uint64_t)(unsigned long)info
->vring
.desc
,
76 .avail_user_addr
= (uint64_t)(unsigned long)info
->vring
.avail
,
77 .used_user_addr
= (uint64_t)(unsigned long)info
->vring
.used
,
80 r
= ioctl(dev
->control
, VHOST_SET_FEATURES
, &features
);
82 state
.num
= info
->vring
.num
;
83 r
= ioctl(dev
->control
, VHOST_SET_VRING_NUM
, &state
);
86 r
= ioctl(dev
->control
, VHOST_SET_VRING_BASE
, &state
);
88 r
= ioctl(dev
->control
, VHOST_SET_VRING_ADDR
, &addr
);
91 r
= ioctl(dev
->control
, VHOST_SET_VRING_KICK
, &file
);
94 r
= ioctl(dev
->control
, VHOST_SET_VRING_CALL
, &file
);
98 static void vq_reset(struct vq_info
*info
, int num
, struct virtio_device
*vdev
)
101 vring_del_virtqueue(info
->vq
);
103 memset(info
->ring
, 0, vring_size(num
, 4096));
104 vring_init(&info
->vring
, num
, info
->ring
, 4096);
105 info
->vq
= vring_new_virtqueue(info
->idx
, num
, 4096, vdev
, true, false,
106 info
->ring
, vq_notify
, vq_callback
, "test");
108 info
->vq
->priv
= info
;
111 static void vq_info_add(struct vdev_info
*dev
, int num
)
113 struct vq_info
*info
= &dev
->vqs
[dev
->nvqs
];
115 info
->idx
= dev
->nvqs
;
116 info
->kick
= eventfd(0, EFD_NONBLOCK
);
117 info
->call
= eventfd(0, EFD_NONBLOCK
);
118 r
= posix_memalign(&info
->ring
, 4096, vring_size(num
, 4096));
120 vq_reset(info
, num
, &dev
->vdev
);
121 vhost_vq_setup(dev
, info
);
122 dev
->fds
[info
->idx
].fd
= info
->call
;
123 dev
->fds
[info
->idx
].events
= POLLIN
;
127 static void vdev_info_init(struct vdev_info
* dev
, unsigned long long features
)
130 memset(dev
, 0, sizeof *dev
);
131 dev
->vdev
.features
= features
;
132 INIT_LIST_HEAD(&dev
->vdev
.vqs
);
133 spin_lock_init(&dev
->vdev
.vqs_list_lock
);
134 dev
->buf_size
= 1024;
135 dev
->buf
= malloc(dev
->buf_size
);
137 dev
->control
= open("/dev/vhost-test", O_RDWR
);
138 assert(dev
->control
>= 0);
139 r
= ioctl(dev
->control
, VHOST_SET_OWNER
, NULL
);
141 dev
->mem
= malloc(offsetof(struct vhost_memory
, regions
) +
142 sizeof dev
->mem
->regions
[0]);
144 memset(dev
->mem
, 0, offsetof(struct vhost_memory
, regions
) +
145 sizeof dev
->mem
->regions
[0]);
146 dev
->mem
->nregions
= 1;
147 dev
->mem
->regions
[0].guest_phys_addr
= (long)dev
->buf
;
148 dev
->mem
->regions
[0].userspace_addr
= (long)dev
->buf
;
149 dev
->mem
->regions
[0].memory_size
= dev
->buf_size
;
150 r
= ioctl(dev
->control
, VHOST_SET_MEM_TABLE
, dev
->mem
);
154 /* TODO: this is pretty bad: we get a cache line bounce
155 * for the wait queue on poll and another one on read,
156 * plus the read which is there just to clear the
158 static void wait_for_interrupt(struct vdev_info
*dev
)
161 unsigned long long val
;
162 poll(dev
->fds
, dev
->nvqs
, -1);
163 for (i
= 0; i
< dev
->nvqs
; ++i
)
164 if (dev
->fds
[i
].revents
& POLLIN
) {
165 read(dev
->fds
[i
].fd
, &val
, sizeof val
);
169 static void run_test(struct vdev_info
*dev
, struct vq_info
*vq
,
170 bool delayed
, int batch
, int reset_n
, int bufs
)
172 struct scatterlist sl
;
173 long started
= 0, completed
= 0, next_reset
= reset_n
;
174 long completed_before
, started_before
;
177 long long spurious
= 0;
178 const bool random_batch
= batch
== RANDOM_BATCH
;
180 r
= ioctl(dev
->control
, VHOST_TEST_RUN
, &test
);
183 next_reset
= INT_MAX
;
187 virtqueue_disable_cb(vq
->vq
);
188 completed_before
= completed
;
189 started_before
= started
;
191 const bool reset
= completed
> next_reset
;
193 batch
= (random() % vq
->vring
.num
) + 1;
195 while (started
< bufs
&&
196 (started
- completed
) < batch
) {
197 sg_init_one(&sl
, dev
->buf
, dev
->buf_size
);
198 r
= virtqueue_add_outbuf(vq
->vq
, &sl
, 1,
201 if (unlikely(r
!= 0)) {
203 started
> started_before
)
212 if (unlikely(!virtqueue_kick(vq
->vq
))) {
222 r
= ioctl(dev
->control
, VHOST_TEST_SET_BACKEND
,
227 /* Flush out completed bufs if any */
228 while (virtqueue_get_buf(vq
->vq
, &len
)) {
234 struct vhost_vring_state s
= { .index
= 0 };
236 vq_reset(vq
, vq
->vring
.num
, &dev
->vdev
);
238 r
= ioctl(dev
->control
, VHOST_GET_VRING_BASE
,
243 r
= ioctl(dev
->control
, VHOST_SET_VRING_BASE
,
247 r
= ioctl(dev
->control
, VHOST_TEST_SET_BACKEND
,
252 while (completed
> next_reset
)
253 next_reset
+= completed
;
256 if (completed
== completed_before
&& started
== started_before
)
258 assert(completed
<= bufs
);
259 assert(started
<= bufs
);
260 if (completed
== bufs
)
263 if (virtqueue_enable_cb_delayed(vq
->vq
))
264 wait_for_interrupt(dev
);
266 if (virtqueue_enable_cb(vq
->vq
))
267 wait_for_interrupt(dev
);
271 r
= ioctl(dev
->control
, VHOST_TEST_RUN
, &test
);
274 "spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
275 spurious
, started
, completed
);
278 const char optstring
[] = "h";
279 const struct option longopts
[] = {
289 .name
= "no-event-idx",
297 .name
= "no-indirect",
305 .name
= "no-virtio-1",
309 .name
= "delayed-interrupt",
313 .name
= "no-delayed-interrupt",
319 .has_arg
= required_argument
,
324 .has_arg
= optional_argument
,
330 static void help(int status
)
332 fprintf(stderr
, "Usage: virtio_test [--help]"
336 " [--delayed-interrupt]"
337 " [--batch=random/N]"
344 int main(int argc
, char **argv
)
346 struct vdev_info dev
;
347 unsigned long long features
= (1ULL << VIRTIO_RING_F_INDIRECT_DESC
) |
348 (1ULL << VIRTIO_RING_F_EVENT_IDX
) | (1ULL << VIRTIO_F_VERSION_1
);
349 long batch
= 1, reset
= 0;
351 bool delayed
= false;
354 o
= getopt_long(argc
, argv
, optstring
, longopts
, NULL
);
361 features
&= ~(1ULL << VIRTIO_RING_F_EVENT_IDX
);
366 features
&= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC
);
369 features
&= ~(1ULL << VIRTIO_F_VERSION_1
);
375 if (0 == strcmp(optarg
, "random")) {
376 batch
= RANDOM_BATCH
;
378 batch
= strtol(optarg
, NULL
, 10);
380 assert(batch
< (long)INT_MAX
+ 1);
387 reset
= strtol(optarg
, NULL
, 10);
389 assert(reset
< (long)INT_MAX
+ 1);
399 vdev_info_init(&dev
, features
);
400 vq_info_add(&dev
, 256);
401 run_test(&dev
, &dev
.vqs
[0], delayed
, batch
, reset
, 0x100000);