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