napi-test.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: run NAPI receive test. Meant to be run from the associated
  5. * script, napi-test.sh. That will invoke this test program
  6. * as either a sender or receiver, with the queue flags passed
  7. * in for testing.
  8. *
  9. */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <stdint.h>
  13. #include <assert.h>
  14. #include <errno.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include <string.h>
  18. #include <arpa/inet.h>
  19. #include <linux/if_packet.h>
  20. #include <linux/socket.h>
  21. #include <sys/socket.h>
  22. #include <sys/stat.h>
  23. #include "liburing.h"
  24. #include "helpers.h"
  25. static const char receiver_address[] = "10.10.10.20";
  26. static const int port = 9999;
  27. #define BUF_SIZE 4096
  28. static char buffer[BUF_SIZE];
  29. static unsigned current_byte = 0;
  30. static void do_setsockopt(int fd, int level, int optname, int val)
  31. {
  32. int ret = setsockopt(fd, level, optname, &val, sizeof(val));
  33. assert(ret == 0);
  34. }
  35. static int sender(int queue_flags)
  36. {
  37. unsigned long long written = 0;
  38. struct sockaddr_in addr;
  39. struct io_uring ring;
  40. int i, ret, fd;
  41. /*
  42. * Sender doesn't use the ring, but try and set one up with the same
  43. * flags that the receiver will use. If that fails, we know the
  44. * receiver will have failed too - just skip the test in that case.
  45. */
  46. ret = io_uring_queue_init(1, &ring, queue_flags);
  47. if (ret)
  48. return T_EXIT_SKIP;
  49. io_uring_queue_exit(&ring);
  50. memset(&addr, 0, sizeof(addr));
  51. addr.sin_family = AF_INET;
  52. addr.sin_port = htons(port);
  53. ret = inet_pton(AF_INET, receiver_address, &addr.sin_addr);
  54. assert(ret == 1);
  55. fd = socket(PF_INET, SOCK_STREAM, 0);
  56. assert(fd >= 0);
  57. /* don't race with receiver, give it 1 second to connect */
  58. i = 0;
  59. do {
  60. ret = connect(fd, (void *)&addr, sizeof(addr));
  61. if (!ret)
  62. break;
  63. if (ret == -1 && errno == ECONNREFUSED) {
  64. if (i >= 10000) {
  65. fprintf(stderr, "Gave up trying to connect\n");
  66. return 1;
  67. }
  68. usleep(100);
  69. continue;
  70. }
  71. i++;
  72. } while (1);
  73. while (written < 8 * 1024 * 1024) {
  74. for (i = 0; i < BUF_SIZE; i++)
  75. buffer[i] = current_byte + i;
  76. ret = write(fd, buffer, BUF_SIZE);
  77. if (ret <= 0) {
  78. if (!ret || errno == ECONNRESET)
  79. break;
  80. fprintf(stderr, "write failed %i %i\n", ret, errno);
  81. return 1;
  82. }
  83. written += ret;
  84. current_byte += ret;
  85. }
  86. close(fd);
  87. return 0;
  88. }
  89. static int receiver(int queue_flags)
  90. {
  91. struct io_uring_sqe *sqe;
  92. struct io_uring_cqe *cqe;
  93. struct io_uring ring;
  94. struct io_uring_napi napi = { };
  95. struct sockaddr_in addr;
  96. int fd, listen_fd;
  97. int i, ret;
  98. ret = io_uring_queue_init(8, &ring, queue_flags);
  99. if (ret < 0) {
  100. if (ret == -EINVAL)
  101. return T_EXIT_SKIP;
  102. fprintf(stderr, "queue_init: %s\n", strerror(-ret));
  103. return 1;
  104. }
  105. napi.prefer_busy_poll = 1;
  106. napi.busy_poll_to = 50;
  107. io_uring_register_napi(&ring, &napi);
  108. memset(&addr, 0, sizeof(addr));
  109. addr.sin_family = AF_INET;
  110. addr.sin_port = htons(port);
  111. addr.sin_addr.s_addr = INADDR_ANY;
  112. listen_fd = socket(AF_INET, SOCK_STREAM, 0);
  113. assert(listen_fd >= 0);
  114. do_setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, 1);
  115. ret = bind(listen_fd, (void *)&addr, sizeof(addr));
  116. if (ret) {
  117. fprintf(stderr, "bind failed %i %i\n", ret, errno);
  118. return 1;
  119. }
  120. ret = listen(listen_fd, 8);
  121. assert(ret == 0);
  122. fd = accept(listen_fd, NULL, NULL);
  123. assert(fd >= 0);
  124. while (1) {
  125. sqe = io_uring_get_sqe(&ring);
  126. io_uring_prep_recv(sqe, fd, buffer, BUF_SIZE, 0);
  127. ret = io_uring_submit(&ring);
  128. if (ret != 1) {
  129. fprintf(stderr, "io_uring_submit: %i\n", ret);
  130. return 1;
  131. }
  132. ret = io_uring_wait_cqe(&ring, &cqe);
  133. if (ret < 0) {
  134. fprintf(stderr, "io_uring_wait_cqe: %i\n", ret);
  135. return 1;
  136. }
  137. ret = cqe->res;
  138. if (ret <= 0) {
  139. if (!ret)
  140. break;
  141. fprintf(stderr, "recv failed %i %i\n", ret, errno);
  142. return 1;
  143. }
  144. for (i = 0; i < ret; i++) {
  145. char expected = current_byte + i;
  146. if (buffer[i] != expected) {
  147. fprintf(stderr, "data mismatch: idx %i, %c vs %c\n",
  148. i, buffer[i], expected);
  149. return 1;
  150. }
  151. }
  152. current_byte += ret;
  153. io_uring_cqe_seen(&ring, cqe);
  154. }
  155. close(fd);
  156. io_uring_queue_exit(&ring);
  157. return 0;
  158. }
  159. int main(int argc, char **argv)
  160. {
  161. int queue_flags;
  162. int is_rx;
  163. if (geteuid()) {
  164. fprintf(stdout, "NAPI test requires root\n");
  165. return T_EXIT_SKIP;
  166. }
  167. if (argc == 1) {
  168. struct stat sb;
  169. if (!stat("napi-test.sh", &sb)) {
  170. return system("bash napi-test.sh");
  171. } else if (!stat("test/napi-test.sh", &sb)) {
  172. return system("bash test/napi-test.sh");
  173. } else {
  174. fprintf(stderr, "Can't find napi-test.sh\n");
  175. return T_EXIT_SKIP;
  176. }
  177. } else if (argc == 2) {
  178. return T_EXIT_SKIP;
  179. } else if (argc != 3) {
  180. return T_EXIT_SKIP;
  181. }
  182. if (!strcmp(argv[1], "receive"))
  183. is_rx = 1;
  184. else if (!strcmp(argv[1], "send"))
  185. is_rx = 0;
  186. else
  187. return T_EXIT_FAIL;
  188. queue_flags = strtoul(argv[2], NULL, 16);
  189. if (is_rx)
  190. return receiver(queue_flags);
  191. return sender(queue_flags);
  192. }