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