xref: /openbmc/linux/tools/virtio/virtio_test.c (revision 4a7d6455)
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>
134e53f78eSMichael S. Tsirkin #include <linux/vhost.h>
144e53f78eSMichael S. Tsirkin #include <linux/virtio.h>
154e53f78eSMichael S. Tsirkin #include <linux/virtio_ring.h>
164e53f78eSMichael S. Tsirkin #include "../../drivers/vhost/test.h"
174e53f78eSMichael S. Tsirkin 
184e53f78eSMichael S. Tsirkin struct vq_info {
194e53f78eSMichael S. Tsirkin 	int kick;
204e53f78eSMichael S. Tsirkin 	int call;
214e53f78eSMichael S. Tsirkin 	int num;
224e53f78eSMichael S. Tsirkin 	int idx;
234e53f78eSMichael S. Tsirkin 	void *ring;
244e53f78eSMichael S. Tsirkin 	/* copy used for control */
254e53f78eSMichael S. Tsirkin 	struct vring vring;
264e53f78eSMichael S. Tsirkin 	struct virtqueue *vq;
274e53f78eSMichael S. Tsirkin };
284e53f78eSMichael S. Tsirkin 
294e53f78eSMichael S. Tsirkin struct vdev_info {
304e53f78eSMichael S. Tsirkin 	struct virtio_device vdev;
314e53f78eSMichael S. Tsirkin 	int control;
324e53f78eSMichael S. Tsirkin 	struct pollfd fds[1];
334e53f78eSMichael S. Tsirkin 	struct vq_info vqs[1];
344e53f78eSMichael S. Tsirkin 	int nvqs;
354e53f78eSMichael S. Tsirkin 	void *buf;
364e53f78eSMichael S. Tsirkin 	size_t buf_size;
374e53f78eSMichael S. Tsirkin 	struct vhost_memory *mem;
384e53f78eSMichael S. Tsirkin };
394e53f78eSMichael S. Tsirkin 
404e53f78eSMichael S. Tsirkin void vq_notify(struct virtqueue *vq)
414e53f78eSMichael S. Tsirkin {
424e53f78eSMichael S. Tsirkin 	struct vq_info *info = vq->priv;
434e53f78eSMichael S. Tsirkin 	unsigned long long v = 1;
444e53f78eSMichael S. Tsirkin 	int r;
454e53f78eSMichael S. Tsirkin 	r = write(info->kick, &v, sizeof v);
464e53f78eSMichael S. Tsirkin 	assert(r == sizeof v);
474e53f78eSMichael S. Tsirkin }
484e53f78eSMichael S. Tsirkin 
494e53f78eSMichael S. Tsirkin void vq_callback(struct virtqueue *vq)
504e53f78eSMichael S. Tsirkin {
514e53f78eSMichael S. Tsirkin }
524e53f78eSMichael S. Tsirkin 
534e53f78eSMichael S. Tsirkin 
544e53f78eSMichael S. Tsirkin void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
554e53f78eSMichael S. Tsirkin {
564e53f78eSMichael S. Tsirkin 	struct vhost_vring_state state = { .index = info->idx };
574e53f78eSMichael S. Tsirkin 	struct vhost_vring_file file = { .index = info->idx };
584e53f78eSMichael S. Tsirkin 	unsigned long long features = dev->vdev.features[0];
594e53f78eSMichael S. Tsirkin 	struct vhost_vring_addr addr = {
604e53f78eSMichael S. Tsirkin 		.index = info->idx,
614e53f78eSMichael S. Tsirkin 		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
624e53f78eSMichael S. Tsirkin 		.avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
634e53f78eSMichael S. Tsirkin 		.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
644e53f78eSMichael S. Tsirkin 	};
654e53f78eSMichael S. Tsirkin 	int r;
664e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
674e53f78eSMichael S. Tsirkin 	assert(r >= 0);
684e53f78eSMichael S. Tsirkin 	state.num = info->vring.num;
694e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
704e53f78eSMichael S. Tsirkin 	assert(r >= 0);
714e53f78eSMichael S. Tsirkin 	state.num = 0;
724e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
734e53f78eSMichael S. Tsirkin 	assert(r >= 0);
744e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
754e53f78eSMichael S. Tsirkin 	assert(r >= 0);
764e53f78eSMichael S. Tsirkin 	file.fd = info->kick;
774e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
784e53f78eSMichael S. Tsirkin 	assert(r >= 0);
794e53f78eSMichael S. Tsirkin 	file.fd = info->call;
804e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
814e53f78eSMichael S. Tsirkin 	assert(r >= 0);
824e53f78eSMichael S. Tsirkin }
834e53f78eSMichael S. Tsirkin 
844e53f78eSMichael S. Tsirkin static void vq_info_add(struct vdev_info *dev, int num)
854e53f78eSMichael S. Tsirkin {
864e53f78eSMichael S. Tsirkin 	struct vq_info *info = &dev->vqs[dev->nvqs];
874e53f78eSMichael S. Tsirkin 	int r;
884e53f78eSMichael S. Tsirkin 	info->idx = dev->nvqs;
894e53f78eSMichael S. Tsirkin 	info->kick = eventfd(0, EFD_NONBLOCK);
904e53f78eSMichael S. Tsirkin 	info->call = eventfd(0, EFD_NONBLOCK);
914e53f78eSMichael S. Tsirkin 	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
924e53f78eSMichael S. Tsirkin 	assert(r >= 0);
934e53f78eSMichael S. Tsirkin 	memset(info->ring, 0, vring_size(num, 4096));
944e53f78eSMichael S. Tsirkin 	vring_init(&info->vring, num, info->ring, 4096);
957b21e34fSRusty Russell 	info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev,
967b21e34fSRusty Russell 				       true, info->ring,
974e53f78eSMichael S. Tsirkin 				       vq_notify, vq_callback, "test");
984e53f78eSMichael S. Tsirkin 	assert(info->vq);
994e53f78eSMichael S. Tsirkin 	info->vq->priv = info;
1004e53f78eSMichael S. Tsirkin 	vhost_vq_setup(dev, info);
1014e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].fd = info->call;
1024e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].events = POLLIN;
1034e53f78eSMichael S. Tsirkin 	dev->nvqs++;
1044e53f78eSMichael S. Tsirkin }
1054e53f78eSMichael S. Tsirkin 
1064e53f78eSMichael S. Tsirkin static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
1074e53f78eSMichael S. Tsirkin {
1084e53f78eSMichael S. Tsirkin 	int r;
1094e53f78eSMichael S. Tsirkin 	memset(dev, 0, sizeof *dev);
1104e53f78eSMichael S. Tsirkin 	dev->vdev.features[0] = features;
1114e53f78eSMichael S. Tsirkin 	dev->vdev.features[1] = features >> 32;
1124e53f78eSMichael S. Tsirkin 	dev->buf_size = 1024;
1134e53f78eSMichael S. Tsirkin 	dev->buf = malloc(dev->buf_size);
1144e53f78eSMichael S. Tsirkin 	assert(dev->buf);
1154e53f78eSMichael S. Tsirkin         dev->control = open("/dev/vhost-test", O_RDWR);
1164e53f78eSMichael S. Tsirkin 	assert(dev->control >= 0);
1174e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
1184e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1194e53f78eSMichael S. Tsirkin 	dev->mem = malloc(offsetof(struct vhost_memory, regions) +
1204e53f78eSMichael S. Tsirkin 			  sizeof dev->mem->regions[0]);
1214e53f78eSMichael S. Tsirkin 	assert(dev->mem);
1224e53f78eSMichael S. Tsirkin 	memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
1234e53f78eSMichael S. Tsirkin                           sizeof dev->mem->regions[0]);
1244e53f78eSMichael S. Tsirkin 	dev->mem->nregions = 1;
1254e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
1264e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].userspace_addr = (long)dev->buf;
1274e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].memory_size = dev->buf_size;
1284e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
1294e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1304e53f78eSMichael S. Tsirkin }
1314e53f78eSMichael S. Tsirkin 
1324e53f78eSMichael S. Tsirkin /* TODO: this is pretty bad: we get a cache line bounce
1334e53f78eSMichael S. Tsirkin  * for the wait queue on poll and another one on read,
1344e53f78eSMichael S. Tsirkin  * plus the read which is there just to clear the
1354e53f78eSMichael S. Tsirkin  * current state. */
1364e53f78eSMichael S. Tsirkin static void wait_for_interrupt(struct vdev_info *dev)
1374e53f78eSMichael S. Tsirkin {
1384e53f78eSMichael S. Tsirkin 	int i;
1394e53f78eSMichael S. Tsirkin 	unsigned long long val;
1404e53f78eSMichael S. Tsirkin 	poll(dev->fds, dev->nvqs, -1);
1414e53f78eSMichael S. Tsirkin 	for (i = 0; i < dev->nvqs; ++i)
1424e53f78eSMichael S. Tsirkin 		if (dev->fds[i].revents & POLLIN) {
1434e53f78eSMichael S. Tsirkin 			read(dev->fds[i].fd, &val, sizeof val);
1444e53f78eSMichael S. Tsirkin 		}
1454e53f78eSMichael S. Tsirkin }
1464e53f78eSMichael S. Tsirkin 
14764d09888SMichael S. Tsirkin static void run_test(struct vdev_info *dev, struct vq_info *vq,
14864d09888SMichael S. Tsirkin 		     bool delayed, int bufs)
1494e53f78eSMichael S. Tsirkin {
1504e53f78eSMichael S. Tsirkin 	struct scatterlist sl;
1514e53f78eSMichael S. Tsirkin 	long started = 0, completed = 0;
1524e53f78eSMichael S. Tsirkin 	long completed_before;
1534e53f78eSMichael S. Tsirkin 	int r, test = 1;
1544e53f78eSMichael S. Tsirkin 	unsigned len;
1554e53f78eSMichael S. Tsirkin 	long long spurious = 0;
1564e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
1574e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1584e53f78eSMichael S. Tsirkin 	for (;;) {
1594e53f78eSMichael S. Tsirkin 		virtqueue_disable_cb(vq->vq);
1604e53f78eSMichael S. Tsirkin 		completed_before = completed;
1614e53f78eSMichael S. Tsirkin 		do {
1624e53f78eSMichael S. Tsirkin 			if (started < bufs) {
1634e53f78eSMichael S. Tsirkin 				sg_init_one(&sl, dev->buf, dev->buf_size);
1644e53f78eSMichael S. Tsirkin 				r = virtqueue_add_buf(vq->vq, &sl, 1, 0,
165f96fde41SRusty Russell 						      dev->buf + started,
166f96fde41SRusty Russell 						      GFP_ATOMIC);
1674e53f78eSMichael S. Tsirkin 				if (likely(r >= 0)) {
1684e53f78eSMichael S. Tsirkin 					++started;
1694e53f78eSMichael S. Tsirkin 					virtqueue_kick(vq->vq);
1704e53f78eSMichael S. Tsirkin 				}
1714e53f78eSMichael S. Tsirkin 			} else
1724e53f78eSMichael S. Tsirkin 				r = -1;
1734e53f78eSMichael S. Tsirkin 
1744e53f78eSMichael S. Tsirkin 			/* Flush out completed bufs if any */
1754e53f78eSMichael S. Tsirkin 			if (virtqueue_get_buf(vq->vq, &len)) {
1764e53f78eSMichael S. Tsirkin 				++completed;
1774e53f78eSMichael S. Tsirkin 				r = 0;
1784e53f78eSMichael S. Tsirkin 			}
1794e53f78eSMichael S. Tsirkin 
1804e53f78eSMichael S. Tsirkin 		} while (r >= 0);
1814e53f78eSMichael S. Tsirkin 		if (completed == completed_before)
1824e53f78eSMichael S. Tsirkin 			++spurious;
1834e53f78eSMichael S. Tsirkin 		assert(completed <= bufs);
1844e53f78eSMichael S. Tsirkin 		assert(started <= bufs);
1854e53f78eSMichael S. Tsirkin 		if (completed == bufs)
1864e53f78eSMichael S. Tsirkin 			break;
18764d09888SMichael S. Tsirkin 		if (delayed) {
18864d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb_delayed(vq->vq))
18964d09888SMichael S. Tsirkin 				wait_for_interrupt(dev);
19064d09888SMichael S. Tsirkin 		} else {
19164d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb(vq->vq))
1924e53f78eSMichael S. Tsirkin 				wait_for_interrupt(dev);
1934e53f78eSMichael S. Tsirkin 		}
1944e53f78eSMichael S. Tsirkin 	}
1954e53f78eSMichael S. Tsirkin 	test = 0;
1964e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
1974e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1984e53f78eSMichael S. Tsirkin 	fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
1994e53f78eSMichael S. Tsirkin }
2004e53f78eSMichael S. Tsirkin 
2014e53f78eSMichael S. Tsirkin const char optstring[] = "h";
2024e53f78eSMichael S. Tsirkin const struct option longopts[] = {
2034e53f78eSMichael S. Tsirkin 	{
2044e53f78eSMichael S. Tsirkin 		.name = "help",
2054e53f78eSMichael S. Tsirkin 		.val = 'h',
2064e53f78eSMichael S. Tsirkin 	},
2074e53f78eSMichael S. Tsirkin 	{
2084423fe40SMichael S. Tsirkin 		.name = "event-idx",
2094423fe40SMichael S. Tsirkin 		.val = 'E',
2104423fe40SMichael S. Tsirkin 	},
2114423fe40SMichael S. Tsirkin 	{
2124423fe40SMichael S. Tsirkin 		.name = "no-event-idx",
2134423fe40SMichael S. Tsirkin 		.val = 'e',
2144423fe40SMichael S. Tsirkin 	},
2154423fe40SMichael S. Tsirkin 	{
2164e53f78eSMichael S. Tsirkin 		.name = "indirect",
2174e53f78eSMichael S. Tsirkin 		.val = 'I',
2184e53f78eSMichael S. Tsirkin 	},
2194e53f78eSMichael S. Tsirkin 	{
2204e53f78eSMichael S. Tsirkin 		.name = "no-indirect",
2214e53f78eSMichael S. Tsirkin 		.val = 'i',
2224e53f78eSMichael S. Tsirkin 	},
2234e53f78eSMichael S. Tsirkin 	{
22464d09888SMichael S. Tsirkin 		.name = "delayed-interrupt",
22564d09888SMichael S. Tsirkin 		.val = 'D',
22664d09888SMichael S. Tsirkin 	},
22764d09888SMichael S. Tsirkin 	{
22864d09888SMichael S. Tsirkin 		.name = "no-delayed-interrupt",
22964d09888SMichael S. Tsirkin 		.val = 'd',
23064d09888SMichael S. Tsirkin 	},
23164d09888SMichael S. Tsirkin 	{
2324e53f78eSMichael S. Tsirkin 	}
2334e53f78eSMichael S. Tsirkin };
2344e53f78eSMichael S. Tsirkin 
2354a7d6455SCong Ding static void help(void)
2364e53f78eSMichael S. Tsirkin {
2374423fe40SMichael S. Tsirkin 	fprintf(stderr, "Usage: virtio_test [--help]"
2384423fe40SMichael S. Tsirkin 		" [--no-indirect]"
2394423fe40SMichael S. Tsirkin 		" [--no-event-idx]"
24064d09888SMichael S. Tsirkin 		" [--delayed-interrupt]"
2414423fe40SMichael S. Tsirkin 		"\n");
2424e53f78eSMichael S. Tsirkin }
2434e53f78eSMichael S. Tsirkin 
2444e53f78eSMichael S. Tsirkin int main(int argc, char **argv)
2454e53f78eSMichael S. Tsirkin {
2464e53f78eSMichael S. Tsirkin 	struct vdev_info dev;
2474423fe40SMichael S. Tsirkin 	unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
2484423fe40SMichael S. Tsirkin 		(1ULL << VIRTIO_RING_F_EVENT_IDX);
2494e53f78eSMichael S. Tsirkin 	int o;
25064d09888SMichael S. Tsirkin 	bool delayed = false;
2514e53f78eSMichael S. Tsirkin 
2524e53f78eSMichael S. Tsirkin 	for (;;) {
2534e53f78eSMichael S. Tsirkin 		o = getopt_long(argc, argv, optstring, longopts, NULL);
2544e53f78eSMichael S. Tsirkin 		switch (o) {
2554e53f78eSMichael S. Tsirkin 		case -1:
2564e53f78eSMichael S. Tsirkin 			goto done;
2574e53f78eSMichael S. Tsirkin 		case '?':
2584e53f78eSMichael S. Tsirkin 			help();
2594e53f78eSMichael S. Tsirkin 			exit(2);
2604423fe40SMichael S. Tsirkin 		case 'e':
2614423fe40SMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
2624423fe40SMichael S. Tsirkin 			break;
2634e53f78eSMichael S. Tsirkin 		case 'h':
2644e53f78eSMichael S. Tsirkin 			help();
2654e53f78eSMichael S. Tsirkin 			goto done;
2664e53f78eSMichael S. Tsirkin 		case 'i':
2674e53f78eSMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
2684e53f78eSMichael S. Tsirkin 			break;
26964d09888SMichael S. Tsirkin 		case 'D':
27064d09888SMichael S. Tsirkin 			delayed = true;
27164d09888SMichael S. Tsirkin 			break;
2724e53f78eSMichael S. Tsirkin 		default:
2734e53f78eSMichael S. Tsirkin 			assert(0);
2744e53f78eSMichael S. Tsirkin 			break;
2754e53f78eSMichael S. Tsirkin 		}
2764e53f78eSMichael S. Tsirkin 	}
2774e53f78eSMichael S. Tsirkin 
2784e53f78eSMichael S. Tsirkin done:
2794e53f78eSMichael S. Tsirkin 	vdev_info_init(&dev, features);
2804e53f78eSMichael S. Tsirkin 	vq_info_add(&dev, 256);
28164d09888SMichael S. Tsirkin 	run_test(&dev, &dev.vqs[0], delayed, 0x100000);
2824e53f78eSMichael S. Tsirkin 	return 0;
2834e53f78eSMichael S. Tsirkin }
284