udpgso_bench_rx.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <arpa/inet.h>
  4. #include <error.h>
  5. #include <errno.h>
  6. #include <limits.h>
  7. #include <linux/errqueue.h>
  8. #include <linux/if_packet.h>
  9. #include <linux/socket.h>
  10. #include <linux/sockios.h>
  11. #include <net/ethernet.h>
  12. #include <net/if.h>
  13. #include <netinet/ip.h>
  14. #include <netinet/ip6.h>
  15. #include <netinet/tcp.h>
  16. #include <netinet/udp.h>
  17. #include <poll.h>
  18. #include <sched.h>
  19. #include <stdbool.h>
  20. #include <stdio.h>
  21. #include <stdint.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/socket.h>
  26. #include <sys/stat.h>
  27. #include <sys/time.h>
  28. #include <sys/types.h>
  29. #include <sys/wait.h>
  30. #include <unistd.h>
  31. static int cfg_port = 8000;
  32. static bool cfg_tcp;
  33. static bool cfg_verify;
  34. static bool interrupted;
  35. static unsigned long packets, bytes;
  36. static void sigint_handler(int signum)
  37. {
  38. if (signum == SIGINT)
  39. interrupted = true;
  40. }
  41. static unsigned long gettimeofday_ms(void)
  42. {
  43. struct timeval tv;
  44. gettimeofday(&tv, NULL);
  45. return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
  46. }
  47. static void do_poll(int fd)
  48. {
  49. struct pollfd pfd;
  50. int ret;
  51. pfd.events = POLLIN;
  52. pfd.revents = 0;
  53. pfd.fd = fd;
  54. do {
  55. ret = poll(&pfd, 1, 10);
  56. if (ret == -1)
  57. error(1, errno, "poll");
  58. if (ret == 0)
  59. continue;
  60. if (pfd.revents != POLLIN)
  61. error(1, errno, "poll: 0x%x expected 0x%x\n",
  62. pfd.revents, POLLIN);
  63. } while (!ret && !interrupted);
  64. }
  65. static int do_socket(bool do_tcp)
  66. {
  67. struct sockaddr_in6 addr = {0};
  68. int fd, val;
  69. fd = socket(PF_INET6, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
  70. if (fd == -1)
  71. error(1, errno, "socket");
  72. val = 1 << 21;
  73. if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)))
  74. error(1, errno, "setsockopt rcvbuf");
  75. val = 1;
  76. if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)))
  77. error(1, errno, "setsockopt reuseport");
  78. addr.sin6_family = PF_INET6;
  79. addr.sin6_port = htons(cfg_port);
  80. addr.sin6_addr = in6addr_any;
  81. if (bind(fd, (void *) &addr, sizeof(addr)))
  82. error(1, errno, "bind");
  83. if (do_tcp) {
  84. int accept_fd = fd;
  85. if (listen(accept_fd, 1))
  86. error(1, errno, "listen");
  87. do_poll(accept_fd);
  88. fd = accept(accept_fd, NULL, NULL);
  89. if (fd == -1)
  90. error(1, errno, "accept");
  91. if (close(accept_fd))
  92. error(1, errno, "close accept fd");
  93. }
  94. return fd;
  95. }
  96. /* Flush all outstanding bytes for the tcp receive queue */
  97. static void do_flush_tcp(int fd)
  98. {
  99. int ret;
  100. while (true) {
  101. /* MSG_TRUNC flushes up to len bytes */
  102. ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
  103. if (ret == -1 && errno == EAGAIN)
  104. return;
  105. if (ret == -1)
  106. error(1, errno, "flush");
  107. if (ret == 0) {
  108. /* client detached */
  109. exit(0);
  110. }
  111. packets++;
  112. bytes += ret;
  113. }
  114. }
  115. static char sanitized_char(char val)
  116. {
  117. return (val >= 'a' && val <= 'z') ? val : '.';
  118. }
  119. static void do_verify_udp(const char *data, int len)
  120. {
  121. char cur = data[0];
  122. int i;
  123. /* verify contents */
  124. if (cur < 'a' || cur > 'z')
  125. error(1, 0, "data initial byte out of range");
  126. for (i = 1; i < len; i++) {
  127. if (cur == 'z')
  128. cur = 'a';
  129. else
  130. cur++;
  131. if (data[i] != cur)
  132. error(1, 0, "data[%d]: len %d, %c(%hhu) != %c(%hhu)\n",
  133. i, len,
  134. sanitized_char(data[i]), data[i],
  135. sanitized_char(cur), cur);
  136. }
  137. }
  138. /* Flush all outstanding datagrams. Verify first few bytes of each. */
  139. static void do_flush_udp(int fd)
  140. {
  141. static char rbuf[ETH_DATA_LEN];
  142. int ret, len, budget = 256;
  143. len = cfg_verify ? sizeof(rbuf) : 0;
  144. while (budget--) {
  145. /* MSG_TRUNC will make return value full datagram length */
  146. ret = recv(fd, rbuf, len, MSG_TRUNC | MSG_DONTWAIT);
  147. if (ret == -1 && errno == EAGAIN)
  148. return;
  149. if (ret == -1)
  150. error(1, errno, "recv");
  151. if (len) {
  152. if (ret == 0)
  153. error(1, errno, "recv: 0 byte datagram\n");
  154. do_verify_udp(rbuf, ret);
  155. }
  156. packets++;
  157. bytes += ret;
  158. }
  159. }
  160. static void usage(const char *filepath)
  161. {
  162. error(1, 0, "Usage: %s [-tv] [-p port]", filepath);
  163. }
  164. static void parse_opts(int argc, char **argv)
  165. {
  166. int c;
  167. while ((c = getopt(argc, argv, "ptv")) != -1) {
  168. switch (c) {
  169. case 'p':
  170. cfg_port = htons(strtoul(optarg, NULL, 0));
  171. break;
  172. case 't':
  173. cfg_tcp = true;
  174. break;
  175. case 'v':
  176. cfg_verify = true;
  177. break;
  178. }
  179. }
  180. if (optind != argc)
  181. usage(argv[0]);
  182. if (cfg_tcp && cfg_verify)
  183. error(1, 0, "TODO: implement verify mode for tcp");
  184. }
  185. static void do_recv(void)
  186. {
  187. unsigned long tnow, treport;
  188. int fd;
  189. fd = do_socket(cfg_tcp);
  190. treport = gettimeofday_ms() + 1000;
  191. do {
  192. do_poll(fd);
  193. if (cfg_tcp)
  194. do_flush_tcp(fd);
  195. else
  196. do_flush_udp(fd);
  197. tnow = gettimeofday_ms();
  198. if (tnow > treport) {
  199. if (packets)
  200. fprintf(stderr,
  201. "%s rx: %6lu MB/s %8lu calls/s\n",
  202. cfg_tcp ? "tcp" : "udp",
  203. bytes >> 20, packets);
  204. bytes = packets = 0;
  205. treport = tnow + 1000;
  206. }
  207. } while (!interrupted);
  208. if (close(fd))
  209. error(1, errno, "close");
  210. }
  211. int main(int argc, char **argv)
  212. {
  213. parse_opts(argc, argv);
  214. signal(SIGINT, sigint_handler);
  215. do_recv();
  216. return 0;
  217. }