xref: /openbmc/linux/tools/virtio/virtio_test.c (revision 2d7ce0e8)
14e53f78eSMichael S. Tsirkin #define _GNU_SOURCE
24e53f78eSMichael S. Tsirkin #include <getopt.h>
34e53f78eSMichael S. Tsirkin #include <string.h>
44e53f78eSMichael S. Tsirkin #include <poll.h>
54e53f78eSMichael S. Tsirkin #include <sys/eventfd.h>
64e53f78eSMichael S. Tsirkin #include <stdlib.h>
74e53f78eSMichael S. Tsirkin #include <assert.h>
84e53f78eSMichael S. Tsirkin #include <unistd.h>
94e53f78eSMichael S. Tsirkin #include <sys/ioctl.h>
104e53f78eSMichael S. Tsirkin #include <sys/stat.h>
114e53f78eSMichael S. Tsirkin #include <sys/types.h>
124e53f78eSMichael S. Tsirkin #include <fcntl.h>
1361d0b5a4SRusty Russell #include <stdbool.h>
142d7ce0e8SMichael S. Tsirkin #include <linux/virtio_types.h>
154e53f78eSMichael S. Tsirkin #include <linux/vhost.h>
164e53f78eSMichael S. Tsirkin #include <linux/virtio.h>
174e53f78eSMichael S. Tsirkin #include <linux/virtio_ring.h>
184e53f78eSMichael S. Tsirkin #include "../../drivers/vhost/test.h"
194e53f78eSMichael S. Tsirkin 
2061d0b5a4SRusty Russell /* Unused */
2161d0b5a4SRusty Russell void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
2261d0b5a4SRusty Russell 
234e53f78eSMichael S. Tsirkin struct vq_info {
244e53f78eSMichael S. Tsirkin 	int kick;
254e53f78eSMichael S. Tsirkin 	int call;
264e53f78eSMichael S. Tsirkin 	int num;
274e53f78eSMichael S. Tsirkin 	int idx;
284e53f78eSMichael S. Tsirkin 	void *ring;
294e53f78eSMichael S. Tsirkin 	/* copy used for control */
304e53f78eSMichael S. Tsirkin 	struct vring vring;
314e53f78eSMichael S. Tsirkin 	struct virtqueue *vq;
324e53f78eSMichael S. Tsirkin };
334e53f78eSMichael S. Tsirkin 
344e53f78eSMichael S. Tsirkin struct vdev_info {
354e53f78eSMichael S. Tsirkin 	struct virtio_device vdev;
364e53f78eSMichael S. Tsirkin 	int control;
374e53f78eSMichael S. Tsirkin 	struct pollfd fds[1];
384e53f78eSMichael S. Tsirkin 	struct vq_info vqs[1];
394e53f78eSMichael S. Tsirkin 	int nvqs;
404e53f78eSMichael S. Tsirkin 	void *buf;
414e53f78eSMichael S. Tsirkin 	size_t buf_size;
424e53f78eSMichael S. Tsirkin 	struct vhost_memory *mem;
434e53f78eSMichael S. Tsirkin };
444e53f78eSMichael S. Tsirkin 
4546f9c2b9SHeinz Graalfs bool vq_notify(struct virtqueue *vq)
464e53f78eSMichael S. Tsirkin {
474e53f78eSMichael S. Tsirkin 	struct vq_info *info = vq->priv;
484e53f78eSMichael S. Tsirkin 	unsigned long long v = 1;
494e53f78eSMichael S. Tsirkin 	int r;
504e53f78eSMichael S. Tsirkin 	r = write(info->kick, &v, sizeof v);
514e53f78eSMichael S. Tsirkin 	assert(r == sizeof v);
5246f9c2b9SHeinz Graalfs 	return true;
534e53f78eSMichael S. Tsirkin }
544e53f78eSMichael S. Tsirkin 
554e53f78eSMichael S. Tsirkin void vq_callback(struct virtqueue *vq)
564e53f78eSMichael S. Tsirkin {
574e53f78eSMichael S. Tsirkin }
584e53f78eSMichael S. Tsirkin 
594e53f78eSMichael S. Tsirkin 
604e53f78eSMichael S. Tsirkin void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
614e53f78eSMichael S. Tsirkin {
624e53f78eSMichael S. Tsirkin 	struct vhost_vring_state state = { .index = info->idx };
634e53f78eSMichael S. Tsirkin 	struct vhost_vring_file file = { .index = info->idx };
64e16e12beSMichael S. Tsirkin 	unsigned long long features = dev->vdev.features;
654e53f78eSMichael S. Tsirkin 	struct vhost_vring_addr addr = {
664e53f78eSMichael S. Tsirkin 		.index = info->idx,
674e53f78eSMichael S. Tsirkin 		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
684e53f78eSMichael S. Tsirkin 		.avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
694e53f78eSMichael S. Tsirkin 		.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
704e53f78eSMichael S. Tsirkin 	};
714e53f78eSMichael S. Tsirkin 	int r;
724e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
734e53f78eSMichael S. Tsirkin 	assert(r >= 0);
744e53f78eSMichael S. Tsirkin 	state.num = info->vring.num;
754e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
764e53f78eSMichael S. Tsirkin 	assert(r >= 0);
774e53f78eSMichael S. Tsirkin 	state.num = 0;
784e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
794e53f78eSMichael S. Tsirkin 	assert(r >= 0);
804e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
814e53f78eSMichael S. Tsirkin 	assert(r >= 0);
824e53f78eSMichael S. Tsirkin 	file.fd = info->kick;
834e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
844e53f78eSMichael S. Tsirkin 	assert(r >= 0);
854e53f78eSMichael S. Tsirkin 	file.fd = info->call;
864e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
874e53f78eSMichael S. Tsirkin 	assert(r >= 0);
884e53f78eSMichael S. Tsirkin }
894e53f78eSMichael S. Tsirkin 
904e53f78eSMichael S. Tsirkin static void vq_info_add(struct vdev_info *dev, int num)
914e53f78eSMichael S. Tsirkin {
924e53f78eSMichael S. Tsirkin 	struct vq_info *info = &dev->vqs[dev->nvqs];
934e53f78eSMichael S. Tsirkin 	int r;
944e53f78eSMichael S. Tsirkin 	info->idx = dev->nvqs;
954e53f78eSMichael S. Tsirkin 	info->kick = eventfd(0, EFD_NONBLOCK);
964e53f78eSMichael S. Tsirkin 	info->call = eventfd(0, EFD_NONBLOCK);
974e53f78eSMichael S. Tsirkin 	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
984e53f78eSMichael S. Tsirkin 	assert(r >= 0);
994e53f78eSMichael S. Tsirkin 	memset(info->ring, 0, vring_size(num, 4096));
1004e53f78eSMichael S. Tsirkin 	vring_init(&info->vring, num, info->ring, 4096);
10173640c99SMichael S. Tsirkin 	info->vq = vring_new_virtqueue(info->idx,
10273640c99SMichael S. Tsirkin 				       info->vring.num, 4096, &dev->vdev,
1037b21e34fSRusty Russell 				       true, info->ring,
1044e53f78eSMichael S. Tsirkin 				       vq_notify, vq_callback, "test");
1054e53f78eSMichael S. Tsirkin 	assert(info->vq);
1064e53f78eSMichael S. Tsirkin 	info->vq->priv = info;
1074e53f78eSMichael S. Tsirkin 	vhost_vq_setup(dev, info);
1084e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].fd = info->call;
1094e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].events = POLLIN;
1104e53f78eSMichael S. Tsirkin 	dev->nvqs++;
1114e53f78eSMichael S. Tsirkin }
1124e53f78eSMichael S. Tsirkin 
1134e53f78eSMichael S. Tsirkin static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
1144e53f78eSMichael S. Tsirkin {
1154e53f78eSMichael S. Tsirkin 	int r;
1164e53f78eSMichael S. Tsirkin 	memset(dev, 0, sizeof *dev);
117e16e12beSMichael S. Tsirkin 	dev->vdev.features = features;
1184e53f78eSMichael S. Tsirkin 	dev->buf_size = 1024;
1194e53f78eSMichael S. Tsirkin 	dev->buf = malloc(dev->buf_size);
1204e53f78eSMichael S. Tsirkin 	assert(dev->buf);
1214e53f78eSMichael S. Tsirkin         dev->control = open("/dev/vhost-test", O_RDWR);
1224e53f78eSMichael S. Tsirkin 	assert(dev->control >= 0);
1234e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
1244e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1254e53f78eSMichael S. Tsirkin 	dev->mem = malloc(offsetof(struct vhost_memory, regions) +
1264e53f78eSMichael S. Tsirkin 			  sizeof dev->mem->regions[0]);
1274e53f78eSMichael S. Tsirkin 	assert(dev->mem);
1284e53f78eSMichael S. Tsirkin 	memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
1294e53f78eSMichael S. Tsirkin                           sizeof dev->mem->regions[0]);
1304e53f78eSMichael S. Tsirkin 	dev->mem->nregions = 1;
1314e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
1324e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].userspace_addr = (long)dev->buf;
1334e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].memory_size = dev->buf_size;
1344e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
1354e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1364e53f78eSMichael S. Tsirkin }
1374e53f78eSMichael S. Tsirkin 
1384e53f78eSMichael S. Tsirkin /* TODO: this is pretty bad: we get a cache line bounce
1394e53f78eSMichael S. Tsirkin  * for the wait queue on poll and another one on read,
1404e53f78eSMichael S. Tsirkin  * plus the read which is there just to clear the
1414e53f78eSMichael S. Tsirkin  * current state. */
1424e53f78eSMichael S. Tsirkin static void wait_for_interrupt(struct vdev_info *dev)
1434e53f78eSMichael S. Tsirkin {
1444e53f78eSMichael S. Tsirkin 	int i;
1454e53f78eSMichael S. Tsirkin 	unsigned long long val;
1464e53f78eSMichael S. Tsirkin 	poll(dev->fds, dev->nvqs, -1);
1474e53f78eSMichael S. Tsirkin 	for (i = 0; i < dev->nvqs; ++i)
1484e53f78eSMichael S. Tsirkin 		if (dev->fds[i].revents & POLLIN) {
1494e53f78eSMichael S. Tsirkin 			read(dev->fds[i].fd, &val, sizeof val);
1504e53f78eSMichael S. Tsirkin 		}
1514e53f78eSMichael S. Tsirkin }
1524e53f78eSMichael S. Tsirkin 
15364d09888SMichael S. Tsirkin static void run_test(struct vdev_info *dev, struct vq_info *vq,
15464d09888SMichael S. Tsirkin 		     bool delayed, int bufs)
1554e53f78eSMichael S. Tsirkin {
1564e53f78eSMichael S. Tsirkin 	struct scatterlist sl;
1574e53f78eSMichael S. Tsirkin 	long started = 0, completed = 0;
1584e53f78eSMichael S. Tsirkin 	long completed_before;
1594e53f78eSMichael S. Tsirkin 	int r, test = 1;
1604e53f78eSMichael S. Tsirkin 	unsigned len;
1614e53f78eSMichael S. Tsirkin 	long long spurious = 0;
1624e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
1634e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1644e53f78eSMichael S. Tsirkin 	for (;;) {
1654e53f78eSMichael S. Tsirkin 		virtqueue_disable_cb(vq->vq);
1664e53f78eSMichael S. Tsirkin 		completed_before = completed;
1674e53f78eSMichael S. Tsirkin 		do {
1684e53f78eSMichael S. Tsirkin 			if (started < bufs) {
1694e53f78eSMichael S. Tsirkin 				sg_init_one(&sl, dev->buf, dev->buf_size);
170cf994e0aSRusty Russell 				r = virtqueue_add_outbuf(vq->vq, &sl, 1,
171f96fde41SRusty Russell 							 dev->buf + started,
172f96fde41SRusty Russell 							 GFP_ATOMIC);
173de929b04SRusty Russell 				if (likely(r == 0)) {
1744e53f78eSMichael S. Tsirkin 					++started;
175be40d5ccSJoel Stanley 					if (unlikely(!virtqueue_kick(vq->vq)))
17653c18c99SHeinz Graalfs 						r = -1;
1774e53f78eSMichael S. Tsirkin 				}
1784e53f78eSMichael S. Tsirkin 			} else
1794e53f78eSMichael S. Tsirkin 				r = -1;
1804e53f78eSMichael S. Tsirkin 
1814e53f78eSMichael S. Tsirkin 			/* Flush out completed bufs if any */
1824e53f78eSMichael S. Tsirkin 			if (virtqueue_get_buf(vq->vq, &len)) {
1834e53f78eSMichael S. Tsirkin 				++completed;
1844e53f78eSMichael S. Tsirkin 				r = 0;
1854e53f78eSMichael S. Tsirkin 			}
1864e53f78eSMichael S. Tsirkin 
187de929b04SRusty Russell 		} while (r == 0);
1884e53f78eSMichael S. Tsirkin 		if (completed == completed_before)
1894e53f78eSMichael S. Tsirkin 			++spurious;
1904e53f78eSMichael S. Tsirkin 		assert(completed <= bufs);
1914e53f78eSMichael S. Tsirkin 		assert(started <= bufs);
1924e53f78eSMichael S. Tsirkin 		if (completed == bufs)
1934e53f78eSMichael S. Tsirkin 			break;
19464d09888SMichael S. Tsirkin 		if (delayed) {
19564d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb_delayed(vq->vq))
19664d09888SMichael S. Tsirkin 				wait_for_interrupt(dev);
19764d09888SMichael S. Tsirkin 		} else {
19864d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb(vq->vq))
1994e53f78eSMichael S. Tsirkin 				wait_for_interrupt(dev);
2004e53f78eSMichael S. Tsirkin 		}
2014e53f78eSMichael S. Tsirkin 	}
2024e53f78eSMichael S. Tsirkin 	test = 0;
2034e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
2044e53f78eSMichael S. Tsirkin 	assert(r >= 0);
2054e53f78eSMichael S. Tsirkin 	fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
2064e53f78eSMichael S. Tsirkin }
2074e53f78eSMichael S. Tsirkin 
2084e53f78eSMichael S. Tsirkin const char optstring[] = "h";
2094e53f78eSMichael S. Tsirkin const struct option longopts[] = {
2104e53f78eSMichael S. Tsirkin 	{
2114e53f78eSMichael S. Tsirkin 		.name = "help",
2124e53f78eSMichael S. Tsirkin 		.val = 'h',
2134e53f78eSMichael S. Tsirkin 	},
2144e53f78eSMichael S. Tsirkin 	{
2154423fe40SMichael S. Tsirkin 		.name = "event-idx",
2164423fe40SMichael S. Tsirkin 		.val = 'E',
2174423fe40SMichael S. Tsirkin 	},
2184423fe40SMichael S. Tsirkin 	{
2194423fe40SMichael S. Tsirkin 		.name = "no-event-idx",
2204423fe40SMichael S. Tsirkin 		.val = 'e',
2214423fe40SMichael S. Tsirkin 	},
2224423fe40SMichael S. Tsirkin 	{
2234e53f78eSMichael S. Tsirkin 		.name = "indirect",
2244e53f78eSMichael S. Tsirkin 		.val = 'I',
2254e53f78eSMichael S. Tsirkin 	},
2264e53f78eSMichael S. Tsirkin 	{
2274e53f78eSMichael S. Tsirkin 		.name = "no-indirect",
2284e53f78eSMichael S. Tsirkin 		.val = 'i',
2294e53f78eSMichael S. Tsirkin 	},
2304e53f78eSMichael S. Tsirkin 	{
23164d09888SMichael S. Tsirkin 		.name = "delayed-interrupt",
23264d09888SMichael S. Tsirkin 		.val = 'D',
23364d09888SMichael S. Tsirkin 	},
23464d09888SMichael S. Tsirkin 	{
23564d09888SMichael S. Tsirkin 		.name = "no-delayed-interrupt",
23664d09888SMichael S. Tsirkin 		.val = 'd',
23764d09888SMichael S. Tsirkin 	},
23864d09888SMichael S. Tsirkin 	{
2394e53f78eSMichael S. Tsirkin 	}
2404e53f78eSMichael S. Tsirkin };
2414e53f78eSMichael S. Tsirkin 
2424a7d6455SCong Ding static void help(void)
2434e53f78eSMichael S. Tsirkin {
2444423fe40SMichael S. Tsirkin 	fprintf(stderr, "Usage: virtio_test [--help]"
2454423fe40SMichael S. Tsirkin 		" [--no-indirect]"
2464423fe40SMichael S. Tsirkin 		" [--no-event-idx]"
24764d09888SMichael S. Tsirkin 		" [--delayed-interrupt]"
2484423fe40SMichael S. Tsirkin 		"\n");
2494e53f78eSMichael S. Tsirkin }
2504e53f78eSMichael S. Tsirkin 
2514e53f78eSMichael S. Tsirkin int main(int argc, char **argv)
2524e53f78eSMichael S. Tsirkin {
2534e53f78eSMichael S. Tsirkin 	struct vdev_info dev;
2544423fe40SMichael S. Tsirkin 	unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
2554423fe40SMichael S. Tsirkin 		(1ULL << VIRTIO_RING_F_EVENT_IDX);
2564e53f78eSMichael S. Tsirkin 	int o;
25764d09888SMichael S. Tsirkin 	bool delayed = false;
2584e53f78eSMichael S. Tsirkin 
2594e53f78eSMichael S. Tsirkin 	for (;;) {
2604e53f78eSMichael S. Tsirkin 		o = getopt_long(argc, argv, optstring, longopts, NULL);
2614e53f78eSMichael S. Tsirkin 		switch (o) {
2624e53f78eSMichael S. Tsirkin 		case -1:
2634e53f78eSMichael S. Tsirkin 			goto done;
2644e53f78eSMichael S. Tsirkin 		case '?':
2654e53f78eSMichael S. Tsirkin 			help();
2664e53f78eSMichael S. Tsirkin 			exit(2);
2674423fe40SMichael S. Tsirkin 		case 'e':
2684423fe40SMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
2694423fe40SMichael S. Tsirkin 			break;
2704e53f78eSMichael S. Tsirkin 		case 'h':
2714e53f78eSMichael S. Tsirkin 			help();
2724e53f78eSMichael S. Tsirkin 			goto done;
2734e53f78eSMichael S. Tsirkin 		case 'i':
2744e53f78eSMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
2754e53f78eSMichael S. Tsirkin 			break;
27664d09888SMichael S. Tsirkin 		case 'D':
27764d09888SMichael S. Tsirkin 			delayed = true;
27864d09888SMichael S. Tsirkin 			break;
2794e53f78eSMichael S. Tsirkin 		default:
2804e53f78eSMichael S. Tsirkin 			assert(0);
2814e53f78eSMichael S. Tsirkin 			break;
2824e53f78eSMichael S. Tsirkin 		}
2834e53f78eSMichael S. Tsirkin 	}
2844e53f78eSMichael S. Tsirkin 
2854e53f78eSMichael S. Tsirkin done:
2864e53f78eSMichael S. Tsirkin 	vdev_info_init(&dev, features);
2874e53f78eSMichael S. Tsirkin 	vq_info_add(&dev, 256);
28864d09888SMichael S. Tsirkin 	run_test(&dev, &dev.vqs[0], delayed, 0x100000);
2894e53f78eSMichael S. Tsirkin 	return 0;
2904e53f78eSMichael S. Tsirkin }
291