vhost_net_test.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <getopt.h>
  4. #include <limits.h>
  5. #include <string.h>
  6. #include <poll.h>
  7. #include <sys/eventfd.h>
  8. #include <stdlib.h>
  9. #include <assert.h>
  10. #include <unistd.h>
  11. #include <sys/ioctl.h>
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. #include <fcntl.h>
  15. #include <stdbool.h>
  16. #include <linux/vhost.h>
  17. #include <linux/if.h>
  18. #include <linux/if_tun.h>
  19. #include <linux/in.h>
  20. #include <linux/if_packet.h>
  21. #include <linux/virtio_net.h>
  22. #include <netinet/ether.h>
  23. #define HDR_LEN sizeof(struct virtio_net_hdr_mrg_rxbuf)
  24. #define TEST_BUF_LEN 256
  25. #define TEST_PTYPE ETH_P_LOOPBACK
  26. #define DESC_NUM 256
  27. /* Used by implementation of kmalloc() in tools/virtio/linux/kernel.h */
  28. void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
  29. struct vq_info {
  30. int kick;
  31. int call;
  32. int idx;
  33. long started;
  34. long completed;
  35. struct pollfd fds;
  36. void *ring;
  37. /* copy used for control */
  38. struct vring vring;
  39. struct virtqueue *vq;
  40. };
  41. struct vdev_info {
  42. struct virtio_device vdev;
  43. int control;
  44. struct vq_info vqs[2];
  45. int nvqs;
  46. void *buf;
  47. size_t buf_size;
  48. char *test_buf;
  49. char *res_buf;
  50. struct vhost_memory *mem;
  51. int sock;
  52. int ifindex;
  53. unsigned char mac[ETHER_ADDR_LEN];
  54. };
  55. static int tun_alloc(struct vdev_info *dev, char *tun_name)
  56. {
  57. struct ifreq ifr;
  58. int len = HDR_LEN;
  59. int fd, e;
  60. fd = open("/dev/net/tun", O_RDWR);
  61. if (fd < 0) {
  62. perror("Cannot open /dev/net/tun");
  63. return fd;
  64. }
  65. memset(&ifr, 0, sizeof(ifr));
  66. ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
  67. strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
  68. e = ioctl(fd, TUNSETIFF, &ifr);
  69. if (e < 0) {
  70. perror("ioctl[TUNSETIFF]");
  71. close(fd);
  72. return e;
  73. }
  74. e = ioctl(fd, TUNSETVNETHDRSZ, &len);
  75. if (e < 0) {
  76. perror("ioctl[TUNSETVNETHDRSZ]");
  77. close(fd);
  78. return e;
  79. }
  80. e = ioctl(fd, SIOCGIFHWADDR, &ifr);
  81. if (e < 0) {
  82. perror("ioctl[SIOCGIFHWADDR]");
  83. close(fd);
  84. return e;
  85. }
  86. memcpy(dev->mac, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
  87. return fd;
  88. }
  89. static void vdev_create_socket(struct vdev_info *dev, char *tun_name)
  90. {
  91. struct ifreq ifr;
  92. dev->sock = socket(AF_PACKET, SOCK_RAW, htons(TEST_PTYPE));
  93. assert(dev->sock != -1);
  94. strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
  95. assert(ioctl(dev->sock, SIOCGIFINDEX, &ifr) >= 0);
  96. dev->ifindex = ifr.ifr_ifindex;
  97. /* Set the flags that bring the device up */
  98. assert(ioctl(dev->sock, SIOCGIFFLAGS, &ifr) >= 0);
  99. ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
  100. assert(ioctl(dev->sock, SIOCSIFFLAGS, &ifr) >= 0);
  101. }
  102. static void vdev_send_packet(struct vdev_info *dev)
  103. {
  104. char *sendbuf = dev->test_buf + HDR_LEN;
  105. struct sockaddr_ll saddrll = {0};
  106. int sockfd = dev->sock;
  107. int ret;
  108. saddrll.sll_family = PF_PACKET;
  109. saddrll.sll_ifindex = dev->ifindex;
  110. saddrll.sll_halen = ETH_ALEN;
  111. saddrll.sll_protocol = htons(TEST_PTYPE);
  112. ret = sendto(sockfd, sendbuf, TEST_BUF_LEN, 0,
  113. (struct sockaddr *)&saddrll,
  114. sizeof(struct sockaddr_ll));
  115. assert(ret >= 0);
  116. }
  117. static bool vq_notify(struct virtqueue *vq)
  118. {
  119. struct vq_info *info = vq->priv;
  120. unsigned long long v = 1;
  121. int r;
  122. r = write(info->kick, &v, sizeof(v));
  123. assert(r == sizeof(v));
  124. return true;
  125. }
  126. static void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
  127. {
  128. struct vhost_vring_addr addr = {
  129. .index = info->idx,
  130. .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
  131. .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
  132. .used_user_addr = (uint64_t)(unsigned long)info->vring.used,
  133. };
  134. struct vhost_vring_state state = { .index = info->idx };
  135. struct vhost_vring_file file = { .index = info->idx };
  136. int r;
  137. state.num = info->vring.num;
  138. r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
  139. assert(r >= 0);
  140. state.num = 0;
  141. r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
  142. assert(r >= 0);
  143. r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
  144. assert(r >= 0);
  145. file.fd = info->kick;
  146. r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
  147. assert(r >= 0);
  148. }
  149. static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
  150. {
  151. if (info->vq)
  152. vring_del_virtqueue(info->vq);
  153. memset(info->ring, 0, vring_size(num, 4096));
  154. vring_init(&info->vring, num, info->ring, 4096);
  155. info->vq = vring_new_virtqueue(info->idx, num, 4096, vdev, true, false,
  156. info->ring, vq_notify, NULL, "test");
  157. assert(info->vq);
  158. info->vq->priv = info;
  159. }
  160. static void vq_info_add(struct vdev_info *dev, int idx, int num, int fd)
  161. {
  162. struct vhost_vring_file backend = { .index = idx, .fd = fd };
  163. struct vq_info *info = &dev->vqs[idx];
  164. int r;
  165. info->idx = idx;
  166. info->kick = eventfd(0, EFD_NONBLOCK);
  167. r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
  168. assert(r >= 0);
  169. vq_reset(info, num, &dev->vdev);
  170. vhost_vq_setup(dev, info);
  171. r = ioctl(dev->control, VHOST_NET_SET_BACKEND, &backend);
  172. assert(!r);
  173. }
  174. static void vdev_info_init(struct vdev_info *dev, unsigned long long features)
  175. {
  176. struct ether_header *eh;
  177. int i, r;
  178. dev->vdev.features = features;
  179. INIT_LIST_HEAD(&dev->vdev.vqs);
  180. spin_lock_init(&dev->vdev.vqs_list_lock);
  181. dev->buf_size = (HDR_LEN + TEST_BUF_LEN) * 2;
  182. dev->buf = malloc(dev->buf_size);
  183. assert(dev->buf);
  184. dev->test_buf = dev->buf;
  185. dev->res_buf = dev->test_buf + HDR_LEN + TEST_BUF_LEN;
  186. memset(dev->test_buf, 0, HDR_LEN + TEST_BUF_LEN);
  187. eh = (struct ether_header *)(dev->test_buf + HDR_LEN);
  188. eh->ether_type = htons(TEST_PTYPE);
  189. memcpy(eh->ether_dhost, dev->mac, ETHER_ADDR_LEN);
  190. memcpy(eh->ether_shost, dev->mac, ETHER_ADDR_LEN);
  191. for (i = sizeof(*eh); i < TEST_BUF_LEN; i++)
  192. dev->test_buf[i + HDR_LEN] = (char)i;
  193. dev->control = open("/dev/vhost-net", O_RDWR);
  194. assert(dev->control >= 0);
  195. r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
  196. assert(r >= 0);
  197. dev->mem = malloc(offsetof(struct vhost_memory, regions) +
  198. sizeof(dev->mem->regions[0]));
  199. assert(dev->mem);
  200. memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
  201. sizeof(dev->mem->regions[0]));
  202. dev->mem->nregions = 1;
  203. dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
  204. dev->mem->regions[0].userspace_addr = (long)dev->buf;
  205. dev->mem->regions[0].memory_size = dev->buf_size;
  206. r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
  207. assert(r >= 0);
  208. r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
  209. assert(r >= 0);
  210. dev->nvqs = 2;
  211. }
  212. static void wait_for_interrupt(struct vq_info *vq)
  213. {
  214. unsigned long long val;
  215. poll(&vq->fds, 1, 100);
  216. if (vq->fds.revents & POLLIN)
  217. read(vq->fds.fd, &val, sizeof(val));
  218. }
  219. static void verify_res_buf(char *res_buf)
  220. {
  221. int i;
  222. for (i = ETHER_HDR_LEN; i < TEST_BUF_LEN; i++)
  223. assert(res_buf[i] == (char)i);
  224. }
  225. static void run_tx_test(struct vdev_info *dev, struct vq_info *vq,
  226. bool delayed, int bufs)
  227. {
  228. long long spurious = 0;
  229. struct scatterlist sl;
  230. unsigned int len;
  231. int r;
  232. for (;;) {
  233. long started_before = vq->started;
  234. long completed_before = vq->completed;
  235. virtqueue_disable_cb(vq->vq);
  236. do {
  237. while (vq->started < bufs &&
  238. (vq->started - vq->completed) < 1) {
  239. sg_init_one(&sl, dev->test_buf, HDR_LEN + TEST_BUF_LEN);
  240. r = virtqueue_add_outbuf(vq->vq, &sl, 1,
  241. dev->test_buf + vq->started,
  242. GFP_ATOMIC);
  243. if (unlikely(r != 0))
  244. break;
  245. ++vq->started;
  246. if (unlikely(!virtqueue_kick(vq->vq))) {
  247. r = -1;
  248. break;
  249. }
  250. }
  251. if (vq->started >= bufs)
  252. r = -1;
  253. /* Flush out completed bufs if any */
  254. while (virtqueue_get_buf(vq->vq, &len)) {
  255. int n;
  256. n = recvfrom(dev->sock, dev->res_buf, TEST_BUF_LEN, 0, NULL, NULL);
  257. assert(n == TEST_BUF_LEN);
  258. verify_res_buf(dev->res_buf);
  259. ++vq->completed;
  260. r = 0;
  261. }
  262. } while (r == 0);
  263. if (vq->completed == completed_before && vq->started == started_before)
  264. ++spurious;
  265. assert(vq->completed <= bufs);
  266. assert(vq->started <= bufs);
  267. if (vq->completed == bufs)
  268. break;
  269. if (delayed) {
  270. if (virtqueue_enable_cb_delayed(vq->vq))
  271. wait_for_interrupt(vq);
  272. } else {
  273. if (virtqueue_enable_cb(vq->vq))
  274. wait_for_interrupt(vq);
  275. }
  276. }
  277. printf("TX spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
  278. spurious, vq->started, vq->completed);
  279. }
  280. static void run_rx_test(struct vdev_info *dev, struct vq_info *vq,
  281. bool delayed, int bufs)
  282. {
  283. long long spurious = 0;
  284. struct scatterlist sl;
  285. unsigned int len;
  286. int r;
  287. for (;;) {
  288. long started_before = vq->started;
  289. long completed_before = vq->completed;
  290. do {
  291. while (vq->started < bufs &&
  292. (vq->started - vq->completed) < 1) {
  293. sg_init_one(&sl, dev->res_buf, HDR_LEN + TEST_BUF_LEN);
  294. r = virtqueue_add_inbuf(vq->vq, &sl, 1,
  295. dev->res_buf + vq->started,
  296. GFP_ATOMIC);
  297. if (unlikely(r != 0))
  298. break;
  299. ++vq->started;
  300. vdev_send_packet(dev);
  301. if (unlikely(!virtqueue_kick(vq->vq))) {
  302. r = -1;
  303. break;
  304. }
  305. }
  306. if (vq->started >= bufs)
  307. r = -1;
  308. /* Flush out completed bufs if any */
  309. while (virtqueue_get_buf(vq->vq, &len)) {
  310. struct ether_header *eh;
  311. eh = (struct ether_header *)(dev->res_buf + HDR_LEN);
  312. /* tun netdev is up and running, only handle the
  313. * TEST_PTYPE packet.
  314. */
  315. if (eh->ether_type == htons(TEST_PTYPE)) {
  316. assert(len == TEST_BUF_LEN + HDR_LEN);
  317. verify_res_buf(dev->res_buf + HDR_LEN);
  318. }
  319. ++vq->completed;
  320. r = 0;
  321. }
  322. } while (r == 0);
  323. if (vq->completed == completed_before && vq->started == started_before)
  324. ++spurious;
  325. assert(vq->completed <= bufs);
  326. assert(vq->started <= bufs);
  327. if (vq->completed == bufs)
  328. break;
  329. }
  330. printf("RX spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
  331. spurious, vq->started, vq->completed);
  332. }
  333. static const char optstring[] = "h";
  334. static const struct option longopts[] = {
  335. {
  336. .name = "help",
  337. .val = 'h',
  338. },
  339. {
  340. .name = "event-idx",
  341. .val = 'E',
  342. },
  343. {
  344. .name = "no-event-idx",
  345. .val = 'e',
  346. },
  347. {
  348. .name = "indirect",
  349. .val = 'I',
  350. },
  351. {
  352. .name = "no-indirect",
  353. .val = 'i',
  354. },
  355. {
  356. .name = "virtio-1",
  357. .val = '1',
  358. },
  359. {
  360. .name = "no-virtio-1",
  361. .val = '0',
  362. },
  363. {
  364. .name = "delayed-interrupt",
  365. .val = 'D',
  366. },
  367. {
  368. .name = "no-delayed-interrupt",
  369. .val = 'd',
  370. },
  371. {
  372. .name = "buf-num",
  373. .val = 'n',
  374. .has_arg = required_argument,
  375. },
  376. {
  377. .name = "batch",
  378. .val = 'b',
  379. .has_arg = required_argument,
  380. },
  381. {
  382. }
  383. };
  384. static void help(int status)
  385. {
  386. fprintf(stderr, "Usage: vhost_net_test [--help]"
  387. " [--no-indirect]"
  388. " [--no-event-idx]"
  389. " [--no-virtio-1]"
  390. " [--delayed-interrupt]"
  391. " [--buf-num]"
  392. "\n");
  393. exit(status);
  394. }
  395. int main(int argc, char **argv)
  396. {
  397. unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
  398. (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
  399. char tun_name[IFNAMSIZ];
  400. long nbufs = 0x100000;
  401. struct vdev_info dev;
  402. bool delayed = false;
  403. int o, fd;
  404. for (;;) {
  405. o = getopt_long(argc, argv, optstring, longopts, NULL);
  406. switch (o) {
  407. case -1:
  408. goto done;
  409. case '?':
  410. help(2);
  411. case 'e':
  412. features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
  413. break;
  414. case 'h':
  415. help(0);
  416. case 'i':
  417. features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
  418. break;
  419. case '0':
  420. features &= ~(1ULL << VIRTIO_F_VERSION_1);
  421. break;
  422. case 'D':
  423. delayed = true;
  424. break;
  425. case 'n':
  426. nbufs = strtol(optarg, NULL, 10);
  427. assert(nbufs > 0);
  428. break;
  429. default:
  430. assert(0);
  431. break;
  432. }
  433. }
  434. done:
  435. memset(&dev, 0, sizeof(dev));
  436. snprintf(tun_name, IFNAMSIZ, "tun_%d", getpid());
  437. fd = tun_alloc(&dev, tun_name);
  438. assert(fd >= 0);
  439. vdev_info_init(&dev, features);
  440. vq_info_add(&dev, 0, DESC_NUM, fd);
  441. vq_info_add(&dev, 1, DESC_NUM, fd);
  442. vdev_create_socket(&dev, tun_name);
  443. run_rx_test(&dev, &dev.vqs[0], delayed, nbufs);
  444. run_tx_test(&dev, &dev.vqs[1], delayed, nbufs);
  445. return 0;
  446. }