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