xref: /openbmc/linux/tools/virtio/virtio_test.c (revision 46f9c2b9)
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>
144e53f78eSMichael S. Tsirkin #include <linux/vhost.h>
154e53f78eSMichael S. Tsirkin #include <linux/virtio.h>
164e53f78eSMichael S. Tsirkin #include <linux/virtio_ring.h>
174e53f78eSMichael S. Tsirkin #include "../../drivers/vhost/test.h"
184e53f78eSMichael S. Tsirkin 
1961d0b5a4SRusty Russell /* Unused */
2061d0b5a4SRusty Russell void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
2161d0b5a4SRusty Russell 
224e53f78eSMichael S. Tsirkin struct vq_info {
234e53f78eSMichael S. Tsirkin 	int kick;
244e53f78eSMichael S. Tsirkin 	int call;
254e53f78eSMichael S. Tsirkin 	int num;
264e53f78eSMichael S. Tsirkin 	int idx;
274e53f78eSMichael S. Tsirkin 	void *ring;
284e53f78eSMichael S. Tsirkin 	/* copy used for control */
294e53f78eSMichael S. Tsirkin 	struct vring vring;
304e53f78eSMichael S. Tsirkin 	struct virtqueue *vq;
314e53f78eSMichael S. Tsirkin };
324e53f78eSMichael S. Tsirkin 
334e53f78eSMichael S. Tsirkin struct vdev_info {
344e53f78eSMichael S. Tsirkin 	struct virtio_device vdev;
354e53f78eSMichael S. Tsirkin 	int control;
364e53f78eSMichael S. Tsirkin 	struct pollfd fds[1];
374e53f78eSMichael S. Tsirkin 	struct vq_info vqs[1];
384e53f78eSMichael S. Tsirkin 	int nvqs;
394e53f78eSMichael S. Tsirkin 	void *buf;
404e53f78eSMichael S. Tsirkin 	size_t buf_size;
414e53f78eSMichael S. Tsirkin 	struct vhost_memory *mem;
424e53f78eSMichael S. Tsirkin };
434e53f78eSMichael S. Tsirkin 
4446f9c2b9SHeinz Graalfs bool vq_notify(struct virtqueue *vq)
454e53f78eSMichael S. Tsirkin {
464e53f78eSMichael S. Tsirkin 	struct vq_info *info = vq->priv;
474e53f78eSMichael S. Tsirkin 	unsigned long long v = 1;
484e53f78eSMichael S. Tsirkin 	int r;
494e53f78eSMichael S. Tsirkin 	r = write(info->kick, &v, sizeof v);
504e53f78eSMichael S. Tsirkin 	assert(r == sizeof v);
5146f9c2b9SHeinz Graalfs 	return true;
524e53f78eSMichael S. Tsirkin }
534e53f78eSMichael S. Tsirkin 
544e53f78eSMichael S. Tsirkin void vq_callback(struct virtqueue *vq)
554e53f78eSMichael S. Tsirkin {
564e53f78eSMichael S. Tsirkin }
574e53f78eSMichael S. Tsirkin 
584e53f78eSMichael S. Tsirkin 
594e53f78eSMichael S. Tsirkin void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
604e53f78eSMichael S. Tsirkin {
614e53f78eSMichael S. Tsirkin 	struct vhost_vring_state state = { .index = info->idx };
624e53f78eSMichael S. Tsirkin 	struct vhost_vring_file file = { .index = info->idx };
634e53f78eSMichael S. Tsirkin 	unsigned long long features = dev->vdev.features[0];
644e53f78eSMichael S. Tsirkin 	struct vhost_vring_addr addr = {
654e53f78eSMichael S. Tsirkin 		.index = info->idx,
664e53f78eSMichael S. Tsirkin 		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
674e53f78eSMichael S. Tsirkin 		.avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
684e53f78eSMichael S. Tsirkin 		.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
694e53f78eSMichael S. Tsirkin 	};
704e53f78eSMichael S. Tsirkin 	int r;
714e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
724e53f78eSMichael S. Tsirkin 	assert(r >= 0);
734e53f78eSMichael S. Tsirkin 	state.num = info->vring.num;
744e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
754e53f78eSMichael S. Tsirkin 	assert(r >= 0);
764e53f78eSMichael S. Tsirkin 	state.num = 0;
774e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
784e53f78eSMichael S. Tsirkin 	assert(r >= 0);
794e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
804e53f78eSMichael S. Tsirkin 	assert(r >= 0);
814e53f78eSMichael S. Tsirkin 	file.fd = info->kick;
824e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
834e53f78eSMichael S. Tsirkin 	assert(r >= 0);
844e53f78eSMichael S. Tsirkin 	file.fd = info->call;
854e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
864e53f78eSMichael S. Tsirkin 	assert(r >= 0);
874e53f78eSMichael S. Tsirkin }
884e53f78eSMichael S. Tsirkin 
894e53f78eSMichael S. Tsirkin static void vq_info_add(struct vdev_info *dev, int num)
904e53f78eSMichael S. Tsirkin {
914e53f78eSMichael S. Tsirkin 	struct vq_info *info = &dev->vqs[dev->nvqs];
924e53f78eSMichael S. Tsirkin 	int r;
934e53f78eSMichael S. Tsirkin 	info->idx = dev->nvqs;
944e53f78eSMichael S. Tsirkin 	info->kick = eventfd(0, EFD_NONBLOCK);
954e53f78eSMichael S. Tsirkin 	info->call = eventfd(0, EFD_NONBLOCK);
964e53f78eSMichael S. Tsirkin 	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
974e53f78eSMichael S. Tsirkin 	assert(r >= 0);
984e53f78eSMichael S. Tsirkin 	memset(info->ring, 0, vring_size(num, 4096));
994e53f78eSMichael S. Tsirkin 	vring_init(&info->vring, num, info->ring, 4096);
10073640c99SMichael S. Tsirkin 	info->vq = vring_new_virtqueue(info->idx,
10173640c99SMichael S. Tsirkin 				       info->vring.num, 4096, &dev->vdev,
1027b21e34fSRusty Russell 				       true, info->ring,
1034e53f78eSMichael S. Tsirkin 				       vq_notify, vq_callback, "test");
1044e53f78eSMichael S. Tsirkin 	assert(info->vq);
1054e53f78eSMichael S. Tsirkin 	info->vq->priv = info;
1064e53f78eSMichael S. Tsirkin 	vhost_vq_setup(dev, info);
1074e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].fd = info->call;
1084e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].events = POLLIN;
1094e53f78eSMichael S. Tsirkin 	dev->nvqs++;
1104e53f78eSMichael S. Tsirkin }
1114e53f78eSMichael S. Tsirkin 
1124e53f78eSMichael S. Tsirkin static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
1134e53f78eSMichael S. Tsirkin {
1144e53f78eSMichael S. Tsirkin 	int r;
1154e53f78eSMichael S. Tsirkin 	memset(dev, 0, sizeof *dev);
1164e53f78eSMichael S. Tsirkin 	dev->vdev.features[0] = features;
1174e53f78eSMichael S. Tsirkin 	dev->vdev.features[1] = features >> 32;
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;
1754e53f78eSMichael S. Tsirkin 					virtqueue_kick(vq->vq);
1764e53f78eSMichael S. Tsirkin 				}
1774e53f78eSMichael S. Tsirkin 			} else
1784e53f78eSMichael S. Tsirkin 				r = -1;
1794e53f78eSMichael S. Tsirkin 
1804e53f78eSMichael S. Tsirkin 			/* Flush out completed bufs if any */
1814e53f78eSMichael S. Tsirkin 			if (virtqueue_get_buf(vq->vq, &len)) {
1824e53f78eSMichael S. Tsirkin 				++completed;
1834e53f78eSMichael S. Tsirkin 				r = 0;
1844e53f78eSMichael S. Tsirkin 			}
1854e53f78eSMichael S. Tsirkin 
186de929b04SRusty Russell 		} while (r == 0);
1874e53f78eSMichael S. Tsirkin 		if (completed == completed_before)
1884e53f78eSMichael S. Tsirkin 			++spurious;
1894e53f78eSMichael S. Tsirkin 		assert(completed <= bufs);
1904e53f78eSMichael S. Tsirkin 		assert(started <= bufs);
1914e53f78eSMichael S. Tsirkin 		if (completed == bufs)
1924e53f78eSMichael S. Tsirkin 			break;
19364d09888SMichael S. Tsirkin 		if (delayed) {
19464d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb_delayed(vq->vq))
19564d09888SMichael S. Tsirkin 				wait_for_interrupt(dev);
19664d09888SMichael S. Tsirkin 		} else {
19764d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb(vq->vq))
1984e53f78eSMichael S. Tsirkin 				wait_for_interrupt(dev);
1994e53f78eSMichael S. Tsirkin 		}
2004e53f78eSMichael S. Tsirkin 	}
2014e53f78eSMichael S. Tsirkin 	test = 0;
2024e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
2034e53f78eSMichael S. Tsirkin 	assert(r >= 0);
2044e53f78eSMichael S. Tsirkin 	fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
2054e53f78eSMichael S. Tsirkin }
2064e53f78eSMichael S. Tsirkin 
2074e53f78eSMichael S. Tsirkin const char optstring[] = "h";
2084e53f78eSMichael S. Tsirkin const struct option longopts[] = {
2094e53f78eSMichael S. Tsirkin 	{
2104e53f78eSMichael S. Tsirkin 		.name = "help",
2114e53f78eSMichael S. Tsirkin 		.val = 'h',
2124e53f78eSMichael S. Tsirkin 	},
2134e53f78eSMichael S. Tsirkin 	{
2144423fe40SMichael S. Tsirkin 		.name = "event-idx",
2154423fe40SMichael S. Tsirkin 		.val = 'E',
2164423fe40SMichael S. Tsirkin 	},
2174423fe40SMichael S. Tsirkin 	{
2184423fe40SMichael S. Tsirkin 		.name = "no-event-idx",
2194423fe40SMichael S. Tsirkin 		.val = 'e',
2204423fe40SMichael S. Tsirkin 	},
2214423fe40SMichael S. Tsirkin 	{
2224e53f78eSMichael S. Tsirkin 		.name = "indirect",
2234e53f78eSMichael S. Tsirkin 		.val = 'I',
2244e53f78eSMichael S. Tsirkin 	},
2254e53f78eSMichael S. Tsirkin 	{
2264e53f78eSMichael S. Tsirkin 		.name = "no-indirect",
2274e53f78eSMichael S. Tsirkin 		.val = 'i',
2284e53f78eSMichael S. Tsirkin 	},
2294e53f78eSMichael S. Tsirkin 	{
23064d09888SMichael S. Tsirkin 		.name = "delayed-interrupt",
23164d09888SMichael S. Tsirkin 		.val = 'D',
23264d09888SMichael S. Tsirkin 	},
23364d09888SMichael S. Tsirkin 	{
23464d09888SMichael S. Tsirkin 		.name = "no-delayed-interrupt",
23564d09888SMichael S. Tsirkin 		.val = 'd',
23664d09888SMichael S. Tsirkin 	},
23764d09888SMichael S. Tsirkin 	{
2384e53f78eSMichael S. Tsirkin 	}
2394e53f78eSMichael S. Tsirkin };
2404e53f78eSMichael S. Tsirkin 
2414a7d6455SCong Ding static void help(void)
2424e53f78eSMichael S. Tsirkin {
2434423fe40SMichael S. Tsirkin 	fprintf(stderr, "Usage: virtio_test [--help]"
2444423fe40SMichael S. Tsirkin 		" [--no-indirect]"
2454423fe40SMichael S. Tsirkin 		" [--no-event-idx]"
24664d09888SMichael S. Tsirkin 		" [--delayed-interrupt]"
2474423fe40SMichael S. Tsirkin 		"\n");
2484e53f78eSMichael S. Tsirkin }
2494e53f78eSMichael S. Tsirkin 
2504e53f78eSMichael S. Tsirkin int main(int argc, char **argv)
2514e53f78eSMichael S. Tsirkin {
2524e53f78eSMichael S. Tsirkin 	struct vdev_info dev;
2534423fe40SMichael S. Tsirkin 	unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
2544423fe40SMichael S. Tsirkin 		(1ULL << VIRTIO_RING_F_EVENT_IDX);
2554e53f78eSMichael S. Tsirkin 	int o;
25664d09888SMichael S. Tsirkin 	bool delayed = false;
2574e53f78eSMichael S. Tsirkin 
2584e53f78eSMichael S. Tsirkin 	for (;;) {
2594e53f78eSMichael S. Tsirkin 		o = getopt_long(argc, argv, optstring, longopts, NULL);
2604e53f78eSMichael S. Tsirkin 		switch (o) {
2614e53f78eSMichael S. Tsirkin 		case -1:
2624e53f78eSMichael S. Tsirkin 			goto done;
2634e53f78eSMichael S. Tsirkin 		case '?':
2644e53f78eSMichael S. Tsirkin 			help();
2654e53f78eSMichael S. Tsirkin 			exit(2);
2664423fe40SMichael S. Tsirkin 		case 'e':
2674423fe40SMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
2684423fe40SMichael S. Tsirkin 			break;
2694e53f78eSMichael S. Tsirkin 		case 'h':
2704e53f78eSMichael S. Tsirkin 			help();
2714e53f78eSMichael S. Tsirkin 			goto done;
2724e53f78eSMichael S. Tsirkin 		case 'i':
2734e53f78eSMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
2744e53f78eSMichael S. Tsirkin 			break;
27564d09888SMichael S. Tsirkin 		case 'D':
27664d09888SMichael S. Tsirkin 			delayed = true;
27764d09888SMichael S. Tsirkin 			break;
2784e53f78eSMichael S. Tsirkin 		default:
2794e53f78eSMichael S. Tsirkin 			assert(0);
2804e53f78eSMichael S. Tsirkin 			break;
2814e53f78eSMichael S. Tsirkin 		}
2824e53f78eSMichael S. Tsirkin 	}
2834e53f78eSMichael S. Tsirkin 
2844e53f78eSMichael S. Tsirkin done:
2854e53f78eSMichael S. Tsirkin 	vdev_info_init(&dev, features);
2864e53f78eSMichael S. Tsirkin 	vq_info_add(&dev, 256);
28764d09888SMichael S. Tsirkin 	run_test(&dev, &dev.vqs[0], delayed, 0x100000);
2884e53f78eSMichael S. Tsirkin 	return 0;
2894e53f78eSMichael S. Tsirkin }
290