1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <getopt.h> 4 #include <string.h> 5 #include <poll.h> 6 #include <sys/eventfd.h> 7 #include <stdlib.h> 8 #include <assert.h> 9 #include <unistd.h> 10 #include <sys/ioctl.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <fcntl.h> 14 #include <stdbool.h> 15 #include <linux/virtio_types.h> 16 #include <linux/vhost.h> 17 #include <linux/virtio.h> 18 #include <linux/virtio_ring.h> 19 #include "../../drivers/vhost/test.h" 20 21 /* Unused */ 22 void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; 23 24 struct vq_info { 25 int kick; 26 int call; 27 int num; 28 int idx; 29 void *ring; 30 /* copy used for control */ 31 struct vring vring; 32 struct virtqueue *vq; 33 }; 34 35 struct vdev_info { 36 struct virtio_device vdev; 37 int control; 38 struct pollfd fds[1]; 39 struct vq_info vqs[1]; 40 int nvqs; 41 void *buf; 42 size_t buf_size; 43 struct vhost_memory *mem; 44 }; 45 46 bool vq_notify(struct virtqueue *vq) 47 { 48 struct vq_info *info = vq->priv; 49 unsigned long long v = 1; 50 int r; 51 r = write(info->kick, &v, sizeof v); 52 assert(r == sizeof v); 53 return true; 54 } 55 56 void vq_callback(struct virtqueue *vq) 57 { 58 } 59 60 61 void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info) 62 { 63 struct vhost_vring_state state = { .index = info->idx }; 64 struct vhost_vring_file file = { .index = info->idx }; 65 unsigned long long features = dev->vdev.features; 66 struct vhost_vring_addr addr = { 67 .index = info->idx, 68 .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc, 69 .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail, 70 .used_user_addr = (uint64_t)(unsigned long)info->vring.used, 71 }; 72 int r; 73 r = ioctl(dev->control, VHOST_SET_FEATURES, &features); 74 assert(r >= 0); 75 state.num = info->vring.num; 76 r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); 77 assert(r >= 0); 78 state.num = 0; 79 r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state); 80 assert(r >= 0); 81 r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr); 82 assert(r >= 0); 83 file.fd = info->kick; 84 r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); 85 assert(r >= 0); 86 file.fd = info->call; 87 r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); 88 assert(r >= 0); 89 } 90 91 static void vq_info_add(struct vdev_info *dev, int num) 92 { 93 struct vq_info *info = &dev->vqs[dev->nvqs]; 94 int r; 95 info->idx = dev->nvqs; 96 info->kick = eventfd(0, EFD_NONBLOCK); 97 info->call = eventfd(0, EFD_NONBLOCK); 98 r = posix_memalign(&info->ring, 4096, vring_size(num, 4096)); 99 assert(r >= 0); 100 memset(info->ring, 0, vring_size(num, 4096)); 101 vring_init(&info->vring, num, info->ring, 4096); 102 info->vq = vring_new_virtqueue(info->idx, 103 info->vring.num, 4096, &dev->vdev, 104 true, false, info->ring, 105 vq_notify, vq_callback, "test"); 106 assert(info->vq); 107 info->vq->priv = info; 108 vhost_vq_setup(dev, info); 109 dev->fds[info->idx].fd = info->call; 110 dev->fds[info->idx].events = POLLIN; 111 dev->nvqs++; 112 } 113 114 static void vdev_info_init(struct vdev_info* dev, unsigned long long features) 115 { 116 int r; 117 memset(dev, 0, sizeof *dev); 118 dev->vdev.features = features; 119 dev->buf_size = 1024; 120 dev->buf = malloc(dev->buf_size); 121 assert(dev->buf); 122 dev->control = open("/dev/vhost-test", O_RDWR); 123 assert(dev->control >= 0); 124 r = ioctl(dev->control, VHOST_SET_OWNER, NULL); 125 assert(r >= 0); 126 dev->mem = malloc(offsetof(struct vhost_memory, regions) + 127 sizeof dev->mem->regions[0]); 128 assert(dev->mem); 129 memset(dev->mem, 0, offsetof(struct vhost_memory, regions) + 130 sizeof dev->mem->regions[0]); 131 dev->mem->nregions = 1; 132 dev->mem->regions[0].guest_phys_addr = (long)dev->buf; 133 dev->mem->regions[0].userspace_addr = (long)dev->buf; 134 dev->mem->regions[0].memory_size = dev->buf_size; 135 r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); 136 assert(r >= 0); 137 } 138 139 /* TODO: this is pretty bad: we get a cache line bounce 140 * for the wait queue on poll and another one on read, 141 * plus the read which is there just to clear the 142 * current state. */ 143 static void wait_for_interrupt(struct vdev_info *dev) 144 { 145 int i; 146 unsigned long long val; 147 poll(dev->fds, dev->nvqs, -1); 148 for (i = 0; i < dev->nvqs; ++i) 149 if (dev->fds[i].revents & POLLIN) { 150 read(dev->fds[i].fd, &val, sizeof val); 151 } 152 } 153 154 static void run_test(struct vdev_info *dev, struct vq_info *vq, 155 bool delayed, int bufs) 156 { 157 struct scatterlist sl; 158 long started = 0, completed = 0; 159 long completed_before; 160 int r, test = 1; 161 unsigned len; 162 long long spurious = 0; 163 r = ioctl(dev->control, VHOST_TEST_RUN, &test); 164 assert(r >= 0); 165 for (;;) { 166 virtqueue_disable_cb(vq->vq); 167 completed_before = completed; 168 do { 169 if (started < bufs) { 170 sg_init_one(&sl, dev->buf, dev->buf_size); 171 r = virtqueue_add_outbuf(vq->vq, &sl, 1, 172 dev->buf + started, 173 GFP_ATOMIC); 174 if (likely(r == 0)) { 175 ++started; 176 if (unlikely(!virtqueue_kick(vq->vq))) 177 r = -1; 178 } 179 } else 180 r = -1; 181 182 /* Flush out completed bufs if any */ 183 if (virtqueue_get_buf(vq->vq, &len)) { 184 ++completed; 185 r = 0; 186 } 187 188 } while (r == 0); 189 if (completed == completed_before) 190 ++spurious; 191 assert(completed <= bufs); 192 assert(started <= bufs); 193 if (completed == bufs) 194 break; 195 if (delayed) { 196 if (virtqueue_enable_cb_delayed(vq->vq)) 197 wait_for_interrupt(dev); 198 } else { 199 if (virtqueue_enable_cb(vq->vq)) 200 wait_for_interrupt(dev); 201 } 202 } 203 test = 0; 204 r = ioctl(dev->control, VHOST_TEST_RUN, &test); 205 assert(r >= 0); 206 fprintf(stderr, "spurious wakeups: 0x%llx\n", spurious); 207 } 208 209 const char optstring[] = "h"; 210 const struct option longopts[] = { 211 { 212 .name = "help", 213 .val = 'h', 214 }, 215 { 216 .name = "event-idx", 217 .val = 'E', 218 }, 219 { 220 .name = "no-event-idx", 221 .val = 'e', 222 }, 223 { 224 .name = "indirect", 225 .val = 'I', 226 }, 227 { 228 .name = "no-indirect", 229 .val = 'i', 230 }, 231 { 232 .name = "virtio-1", 233 .val = '1', 234 }, 235 { 236 .name = "no-virtio-1", 237 .val = '0', 238 }, 239 { 240 .name = "delayed-interrupt", 241 .val = 'D', 242 }, 243 { 244 .name = "no-delayed-interrupt", 245 .val = 'd', 246 }, 247 { 248 } 249 }; 250 251 static void help(void) 252 { 253 fprintf(stderr, "Usage: virtio_test [--help]" 254 " [--no-indirect]" 255 " [--no-event-idx]" 256 " [--no-virtio-1]" 257 " [--delayed-interrupt]" 258 "\n"); 259 } 260 261 int main(int argc, char **argv) 262 { 263 struct vdev_info dev; 264 unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | 265 (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1); 266 int o; 267 bool delayed = false; 268 269 for (;;) { 270 o = getopt_long(argc, argv, optstring, longopts, NULL); 271 switch (o) { 272 case -1: 273 goto done; 274 case '?': 275 help(); 276 exit(2); 277 case 'e': 278 features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX); 279 break; 280 case 'h': 281 help(); 282 goto done; 283 case 'i': 284 features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC); 285 break; 286 case '0': 287 features &= ~(1ULL << VIRTIO_F_VERSION_1); 288 break; 289 case 'D': 290 delayed = true; 291 break; 292 default: 293 assert(0); 294 break; 295 } 296 } 297 298 done: 299 vdev_info_init(&dev, features); 300 vq_info_add(&dev, 256); 301 run_test(&dev, &dev.vqs[0], delayed, 0x100000); 302 return 0; 303 } 304