xref: /openbmc/linux/tools/virtio/virtio_test.c (revision 61d0b5a4)
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 
444e53f78eSMichael S. Tsirkin void 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);
514e53f78eSMichael S. Tsirkin }
524e53f78eSMichael S. Tsirkin 
534e53f78eSMichael S. Tsirkin void vq_callback(struct virtqueue *vq)
544e53f78eSMichael S. Tsirkin {
554e53f78eSMichael S. Tsirkin }
564e53f78eSMichael S. Tsirkin 
574e53f78eSMichael S. Tsirkin 
584e53f78eSMichael S. Tsirkin void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
594e53f78eSMichael S. Tsirkin {
604e53f78eSMichael S. Tsirkin 	struct vhost_vring_state state = { .index = info->idx };
614e53f78eSMichael S. Tsirkin 	struct vhost_vring_file file = { .index = info->idx };
624e53f78eSMichael S. Tsirkin 	unsigned long long features = dev->vdev.features[0];
634e53f78eSMichael S. Tsirkin 	struct vhost_vring_addr addr = {
644e53f78eSMichael S. Tsirkin 		.index = info->idx,
654e53f78eSMichael S. Tsirkin 		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
664e53f78eSMichael S. Tsirkin 		.avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
674e53f78eSMichael S. Tsirkin 		.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
684e53f78eSMichael S. Tsirkin 	};
694e53f78eSMichael S. Tsirkin 	int r;
704e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
714e53f78eSMichael S. Tsirkin 	assert(r >= 0);
724e53f78eSMichael S. Tsirkin 	state.num = info->vring.num;
734e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
744e53f78eSMichael S. Tsirkin 	assert(r >= 0);
754e53f78eSMichael S. Tsirkin 	state.num = 0;
764e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
774e53f78eSMichael S. Tsirkin 	assert(r >= 0);
784e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
794e53f78eSMichael S. Tsirkin 	assert(r >= 0);
804e53f78eSMichael S. Tsirkin 	file.fd = info->kick;
814e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
824e53f78eSMichael S. Tsirkin 	assert(r >= 0);
834e53f78eSMichael S. Tsirkin 	file.fd = info->call;
844e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
854e53f78eSMichael S. Tsirkin 	assert(r >= 0);
864e53f78eSMichael S. Tsirkin }
874e53f78eSMichael S. Tsirkin 
884e53f78eSMichael S. Tsirkin static void vq_info_add(struct vdev_info *dev, int num)
894e53f78eSMichael S. Tsirkin {
904e53f78eSMichael S. Tsirkin 	struct vq_info *info = &dev->vqs[dev->nvqs];
914e53f78eSMichael S. Tsirkin 	int r;
924e53f78eSMichael S. Tsirkin 	info->idx = dev->nvqs;
934e53f78eSMichael S. Tsirkin 	info->kick = eventfd(0, EFD_NONBLOCK);
944e53f78eSMichael S. Tsirkin 	info->call = eventfd(0, EFD_NONBLOCK);
954e53f78eSMichael S. Tsirkin 	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
964e53f78eSMichael S. Tsirkin 	assert(r >= 0);
974e53f78eSMichael S. Tsirkin 	memset(info->ring, 0, vring_size(num, 4096));
984e53f78eSMichael S. Tsirkin 	vring_init(&info->vring, num, info->ring, 4096);
9973640c99SMichael S. Tsirkin 	info->vq = vring_new_virtqueue(info->idx,
10073640c99SMichael S. Tsirkin 				       info->vring.num, 4096, &dev->vdev,
1017b21e34fSRusty Russell 				       true, info->ring,
1024e53f78eSMichael S. Tsirkin 				       vq_notify, vq_callback, "test");
1034e53f78eSMichael S. Tsirkin 	assert(info->vq);
1044e53f78eSMichael S. Tsirkin 	info->vq->priv = info;
1054e53f78eSMichael S. Tsirkin 	vhost_vq_setup(dev, info);
1064e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].fd = info->call;
1074e53f78eSMichael S. Tsirkin 	dev->fds[info->idx].events = POLLIN;
1084e53f78eSMichael S. Tsirkin 	dev->nvqs++;
1094e53f78eSMichael S. Tsirkin }
1104e53f78eSMichael S. Tsirkin 
1114e53f78eSMichael S. Tsirkin static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
1124e53f78eSMichael S. Tsirkin {
1134e53f78eSMichael S. Tsirkin 	int r;
1144e53f78eSMichael S. Tsirkin 	memset(dev, 0, sizeof *dev);
1154e53f78eSMichael S. Tsirkin 	dev->vdev.features[0] = features;
1164e53f78eSMichael S. Tsirkin 	dev->vdev.features[1] = features >> 32;
1174e53f78eSMichael S. Tsirkin 	dev->buf_size = 1024;
1184e53f78eSMichael S. Tsirkin 	dev->buf = malloc(dev->buf_size);
1194e53f78eSMichael S. Tsirkin 	assert(dev->buf);
1204e53f78eSMichael S. Tsirkin         dev->control = open("/dev/vhost-test", O_RDWR);
1214e53f78eSMichael S. Tsirkin 	assert(dev->control >= 0);
1224e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
1234e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1244e53f78eSMichael S. Tsirkin 	dev->mem = malloc(offsetof(struct vhost_memory, regions) +
1254e53f78eSMichael S. Tsirkin 			  sizeof dev->mem->regions[0]);
1264e53f78eSMichael S. Tsirkin 	assert(dev->mem);
1274e53f78eSMichael S. Tsirkin 	memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
1284e53f78eSMichael S. Tsirkin                           sizeof dev->mem->regions[0]);
1294e53f78eSMichael S. Tsirkin 	dev->mem->nregions = 1;
1304e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
1314e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].userspace_addr = (long)dev->buf;
1324e53f78eSMichael S. Tsirkin 	dev->mem->regions[0].memory_size = dev->buf_size;
1334e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
1344e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1354e53f78eSMichael S. Tsirkin }
1364e53f78eSMichael S. Tsirkin 
1374e53f78eSMichael S. Tsirkin /* TODO: this is pretty bad: we get a cache line bounce
1384e53f78eSMichael S. Tsirkin  * for the wait queue on poll and another one on read,
1394e53f78eSMichael S. Tsirkin  * plus the read which is there just to clear the
1404e53f78eSMichael S. Tsirkin  * current state. */
1414e53f78eSMichael S. Tsirkin static void wait_for_interrupt(struct vdev_info *dev)
1424e53f78eSMichael S. Tsirkin {
1434e53f78eSMichael S. Tsirkin 	int i;
1444e53f78eSMichael S. Tsirkin 	unsigned long long val;
1454e53f78eSMichael S. Tsirkin 	poll(dev->fds, dev->nvqs, -1);
1464e53f78eSMichael S. Tsirkin 	for (i = 0; i < dev->nvqs; ++i)
1474e53f78eSMichael S. Tsirkin 		if (dev->fds[i].revents & POLLIN) {
1484e53f78eSMichael S. Tsirkin 			read(dev->fds[i].fd, &val, sizeof val);
1494e53f78eSMichael S. Tsirkin 		}
1504e53f78eSMichael S. Tsirkin }
1514e53f78eSMichael S. Tsirkin 
15264d09888SMichael S. Tsirkin static void run_test(struct vdev_info *dev, struct vq_info *vq,
15364d09888SMichael S. Tsirkin 		     bool delayed, int bufs)
1544e53f78eSMichael S. Tsirkin {
1554e53f78eSMichael S. Tsirkin 	struct scatterlist sl;
1564e53f78eSMichael S. Tsirkin 	long started = 0, completed = 0;
1574e53f78eSMichael S. Tsirkin 	long completed_before;
1584e53f78eSMichael S. Tsirkin 	int r, test = 1;
1594e53f78eSMichael S. Tsirkin 	unsigned len;
1604e53f78eSMichael S. Tsirkin 	long long spurious = 0;
1614e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
1624e53f78eSMichael S. Tsirkin 	assert(r >= 0);
1634e53f78eSMichael S. Tsirkin 	for (;;) {
1644e53f78eSMichael S. Tsirkin 		virtqueue_disable_cb(vq->vq);
1654e53f78eSMichael S. Tsirkin 		completed_before = completed;
1664e53f78eSMichael S. Tsirkin 		do {
1674e53f78eSMichael S. Tsirkin 			if (started < bufs) {
1684e53f78eSMichael S. Tsirkin 				sg_init_one(&sl, dev->buf, dev->buf_size);
1694e53f78eSMichael S. Tsirkin 				r = virtqueue_add_buf(vq->vq, &sl, 1, 0,
170f96fde41SRusty Russell 						      dev->buf + started,
171f96fde41SRusty Russell 						      GFP_ATOMIC);
172de929b04SRusty Russell 				if (likely(r == 0)) {
1734e53f78eSMichael S. Tsirkin 					++started;
1744e53f78eSMichael S. Tsirkin 					virtqueue_kick(vq->vq);
1754e53f78eSMichael S. Tsirkin 				}
1764e53f78eSMichael S. Tsirkin 			} else
1774e53f78eSMichael S. Tsirkin 				r = -1;
1784e53f78eSMichael S. Tsirkin 
1794e53f78eSMichael S. Tsirkin 			/* Flush out completed bufs if any */
1804e53f78eSMichael S. Tsirkin 			if (virtqueue_get_buf(vq->vq, &len)) {
1814e53f78eSMichael S. Tsirkin 				++completed;
1824e53f78eSMichael S. Tsirkin 				r = 0;
1834e53f78eSMichael S. Tsirkin 			}
1844e53f78eSMichael S. Tsirkin 
185de929b04SRusty Russell 		} while (r == 0);
1864e53f78eSMichael S. Tsirkin 		if (completed == completed_before)
1874e53f78eSMichael S. Tsirkin 			++spurious;
1884e53f78eSMichael S. Tsirkin 		assert(completed <= bufs);
1894e53f78eSMichael S. Tsirkin 		assert(started <= bufs);
1904e53f78eSMichael S. Tsirkin 		if (completed == bufs)
1914e53f78eSMichael S. Tsirkin 			break;
19264d09888SMichael S. Tsirkin 		if (delayed) {
19364d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb_delayed(vq->vq))
19464d09888SMichael S. Tsirkin 				wait_for_interrupt(dev);
19564d09888SMichael S. Tsirkin 		} else {
19664d09888SMichael S. Tsirkin 			if (virtqueue_enable_cb(vq->vq))
1974e53f78eSMichael S. Tsirkin 				wait_for_interrupt(dev);
1984e53f78eSMichael S. Tsirkin 		}
1994e53f78eSMichael S. Tsirkin 	}
2004e53f78eSMichael S. Tsirkin 	test = 0;
2014e53f78eSMichael S. Tsirkin 	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
2024e53f78eSMichael S. Tsirkin 	assert(r >= 0);
2034e53f78eSMichael S. Tsirkin 	fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious);
2044e53f78eSMichael S. Tsirkin }
2054e53f78eSMichael S. Tsirkin 
2064e53f78eSMichael S. Tsirkin const char optstring[] = "h";
2074e53f78eSMichael S. Tsirkin const struct option longopts[] = {
2084e53f78eSMichael S. Tsirkin 	{
2094e53f78eSMichael S. Tsirkin 		.name = "help",
2104e53f78eSMichael S. Tsirkin 		.val = 'h',
2114e53f78eSMichael S. Tsirkin 	},
2124e53f78eSMichael S. Tsirkin 	{
2134423fe40SMichael S. Tsirkin 		.name = "event-idx",
2144423fe40SMichael S. Tsirkin 		.val = 'E',
2154423fe40SMichael S. Tsirkin 	},
2164423fe40SMichael S. Tsirkin 	{
2174423fe40SMichael S. Tsirkin 		.name = "no-event-idx",
2184423fe40SMichael S. Tsirkin 		.val = 'e',
2194423fe40SMichael S. Tsirkin 	},
2204423fe40SMichael S. Tsirkin 	{
2214e53f78eSMichael S. Tsirkin 		.name = "indirect",
2224e53f78eSMichael S. Tsirkin 		.val = 'I',
2234e53f78eSMichael S. Tsirkin 	},
2244e53f78eSMichael S. Tsirkin 	{
2254e53f78eSMichael S. Tsirkin 		.name = "no-indirect",
2264e53f78eSMichael S. Tsirkin 		.val = 'i',
2274e53f78eSMichael S. Tsirkin 	},
2284e53f78eSMichael S. Tsirkin 	{
22964d09888SMichael S. Tsirkin 		.name = "delayed-interrupt",
23064d09888SMichael S. Tsirkin 		.val = 'D',
23164d09888SMichael S. Tsirkin 	},
23264d09888SMichael S. Tsirkin 	{
23364d09888SMichael S. Tsirkin 		.name = "no-delayed-interrupt",
23464d09888SMichael S. Tsirkin 		.val = 'd',
23564d09888SMichael S. Tsirkin 	},
23664d09888SMichael S. Tsirkin 	{
2374e53f78eSMichael S. Tsirkin 	}
2384e53f78eSMichael S. Tsirkin };
2394e53f78eSMichael S. Tsirkin 
2404a7d6455SCong Ding static void help(void)
2414e53f78eSMichael S. Tsirkin {
2424423fe40SMichael S. Tsirkin 	fprintf(stderr, "Usage: virtio_test [--help]"
2434423fe40SMichael S. Tsirkin 		" [--no-indirect]"
2444423fe40SMichael S. Tsirkin 		" [--no-event-idx]"
24564d09888SMichael S. Tsirkin 		" [--delayed-interrupt]"
2464423fe40SMichael S. Tsirkin 		"\n");
2474e53f78eSMichael S. Tsirkin }
2484e53f78eSMichael S. Tsirkin 
2494e53f78eSMichael S. Tsirkin int main(int argc, char **argv)
2504e53f78eSMichael S. Tsirkin {
2514e53f78eSMichael S. Tsirkin 	struct vdev_info dev;
2524423fe40SMichael S. Tsirkin 	unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
2534423fe40SMichael S. Tsirkin 		(1ULL << VIRTIO_RING_F_EVENT_IDX);
2544e53f78eSMichael S. Tsirkin 	int o;
25564d09888SMichael S. Tsirkin 	bool delayed = false;
2564e53f78eSMichael S. Tsirkin 
2574e53f78eSMichael S. Tsirkin 	for (;;) {
2584e53f78eSMichael S. Tsirkin 		o = getopt_long(argc, argv, optstring, longopts, NULL);
2594e53f78eSMichael S. Tsirkin 		switch (o) {
2604e53f78eSMichael S. Tsirkin 		case -1:
2614e53f78eSMichael S. Tsirkin 			goto done;
2624e53f78eSMichael S. Tsirkin 		case '?':
2634e53f78eSMichael S. Tsirkin 			help();
2644e53f78eSMichael S. Tsirkin 			exit(2);
2654423fe40SMichael S. Tsirkin 		case 'e':
2664423fe40SMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
2674423fe40SMichael S. Tsirkin 			break;
2684e53f78eSMichael S. Tsirkin 		case 'h':
2694e53f78eSMichael S. Tsirkin 			help();
2704e53f78eSMichael S. Tsirkin 			goto done;
2714e53f78eSMichael S. Tsirkin 		case 'i':
2724e53f78eSMichael S. Tsirkin 			features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
2734e53f78eSMichael S. Tsirkin 			break;
27464d09888SMichael S. Tsirkin 		case 'D':
27564d09888SMichael S. Tsirkin 			delayed = true;
27664d09888SMichael S. Tsirkin 			break;
2774e53f78eSMichael S. Tsirkin 		default:
2784e53f78eSMichael S. Tsirkin 			assert(0);
2794e53f78eSMichael S. Tsirkin 			break;
2804e53f78eSMichael S. Tsirkin 		}
2814e53f78eSMichael S. Tsirkin 	}
2824e53f78eSMichael S. Tsirkin 
2834e53f78eSMichael S. Tsirkin done:
2844e53f78eSMichael S. Tsirkin 	vdev_info_init(&dev, features);
2854e53f78eSMichael S. Tsirkin 	vq_info_add(&dev, 256);
28664d09888SMichael S. Tsirkin 	run_test(&dev, &dev.vqs[0], delayed, 0x100000);
2874e53f78eSMichael S. Tsirkin 	return 0;
2884e53f78eSMichael S. Tsirkin }
289