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 2354e53f78eSMichael S. Tsirkin static void help() 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