udpgso_bench_rx.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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. #ifndef UDP_GRO
  32. #define UDP_GRO 104
  33. #endif
  34. static int cfg_port = 8000;
  35. static bool cfg_tcp;
  36. static bool cfg_verify;
  37. static bool cfg_read_all;
  38. static bool cfg_gro_segment;
  39. static int cfg_family = PF_INET6;
  40. static int cfg_alen = sizeof(struct sockaddr_in6);
  41. static int cfg_expected_pkt_nr;
  42. static int cfg_expected_pkt_len;
  43. static int cfg_expected_gso_size;
  44. static int cfg_connect_timeout_ms;
  45. static int cfg_rcv_timeout_ms;
  46. static struct sockaddr_storage cfg_bind_addr;
  47. static bool interrupted;
  48. static unsigned long packets, bytes;
  49. static void sigint_handler(int signum)
  50. {
  51. if (signum == SIGINT)
  52. interrupted = true;
  53. }
  54. static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
  55. {
  56. struct sockaddr_in6 *addr6 = (void *) sockaddr;
  57. struct sockaddr_in *addr4 = (void *) sockaddr;
  58. switch (domain) {
  59. case PF_INET:
  60. addr4->sin_family = AF_INET;
  61. addr4->sin_port = htons(cfg_port);
  62. if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
  63. error(1, 0, "ipv4 parse error: %s", str_addr);
  64. break;
  65. case PF_INET6:
  66. addr6->sin6_family = AF_INET6;
  67. addr6->sin6_port = htons(cfg_port);
  68. if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
  69. error(1, 0, "ipv6 parse error: %s", str_addr);
  70. break;
  71. default:
  72. error(1, 0, "illegal domain");
  73. }
  74. }
  75. static unsigned long gettimeofday_ms(void)
  76. {
  77. struct timeval tv;
  78. gettimeofday(&tv, NULL);
  79. return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
  80. }
  81. static void do_poll(int fd, int timeout_ms)
  82. {
  83. struct pollfd pfd;
  84. int ret;
  85. pfd.events = POLLIN;
  86. pfd.revents = 0;
  87. pfd.fd = fd;
  88. do {
  89. ret = poll(&pfd, 1, 10);
  90. if (interrupted)
  91. break;
  92. if (ret == -1)
  93. error(1, errno, "poll");
  94. if (ret == 0) {
  95. if (!timeout_ms)
  96. continue;
  97. timeout_ms -= 10;
  98. if (timeout_ms <= 0) {
  99. interrupted = true;
  100. break;
  101. }
  102. /* no events and more time to wait, do poll again */
  103. continue;
  104. }
  105. if (pfd.revents != POLLIN)
  106. error(1, errno, "poll: 0x%x expected 0x%x\n",
  107. pfd.revents, POLLIN);
  108. } while (!ret);
  109. }
  110. static int do_socket(bool do_tcp)
  111. {
  112. int fd, val;
  113. fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
  114. if (fd == -1)
  115. error(1, errno, "socket");
  116. val = 1 << 21;
  117. if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)))
  118. error(1, errno, "setsockopt rcvbuf");
  119. val = 1;
  120. if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)))
  121. error(1, errno, "setsockopt reuseport");
  122. if (bind(fd, (void *)&cfg_bind_addr, cfg_alen))
  123. error(1, errno, "bind");
  124. if (do_tcp) {
  125. int accept_fd = fd;
  126. if (listen(accept_fd, 1))
  127. error(1, errno, "listen");
  128. do_poll(accept_fd, cfg_connect_timeout_ms);
  129. if (interrupted)
  130. exit(0);
  131. fd = accept(accept_fd, NULL, NULL);
  132. if (fd == -1)
  133. error(1, errno, "accept");
  134. if (close(accept_fd))
  135. error(1, errno, "close accept fd");
  136. }
  137. return fd;
  138. }
  139. /* Flush all outstanding bytes for the tcp receive queue */
  140. static void do_flush_tcp(int fd)
  141. {
  142. int ret;
  143. while (true) {
  144. /* MSG_TRUNC flushes up to len bytes */
  145. ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
  146. if (ret == -1 && errno == EAGAIN)
  147. return;
  148. if (ret == -1)
  149. error(1, errno, "flush");
  150. if (ret == 0) {
  151. /* client detached */
  152. exit(0);
  153. }
  154. packets++;
  155. bytes += ret;
  156. }
  157. }
  158. static char sanitized_char(char val)
  159. {
  160. return (val >= 'a' && val <= 'z') ? val : '.';
  161. }
  162. static void do_verify_udp(const char *data, int len)
  163. {
  164. char cur = data[0];
  165. int i;
  166. /* verify contents */
  167. if (cur < 'a' || cur > 'z')
  168. error(1, 0, "data initial byte out of range");
  169. for (i = 1; i < len; i++) {
  170. if (cur == 'z')
  171. cur = 'a';
  172. else
  173. cur++;
  174. if (data[i] != cur)
  175. error(1, 0, "data[%d]: len %d, %c(%hhu) != %c(%hhu)\n",
  176. i, len,
  177. sanitized_char(data[i]), data[i],
  178. sanitized_char(cur), cur);
  179. }
  180. }
  181. static int recv_msg(int fd, char *buf, int len, int *gso_size)
  182. {
  183. char control[CMSG_SPACE(sizeof(int))] = {0};
  184. struct msghdr msg = {0};
  185. struct iovec iov = {0};
  186. struct cmsghdr *cmsg;
  187. int ret;
  188. iov.iov_base = buf;
  189. iov.iov_len = len;
  190. msg.msg_iov = &iov;
  191. msg.msg_iovlen = 1;
  192. msg.msg_control = control;
  193. msg.msg_controllen = sizeof(control);
  194. *gso_size = -1;
  195. ret = recvmsg(fd, &msg, MSG_TRUNC | MSG_DONTWAIT);
  196. if (ret != -1) {
  197. for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
  198. cmsg = CMSG_NXTHDR(&msg, cmsg)) {
  199. if (cmsg->cmsg_level == SOL_UDP
  200. && cmsg->cmsg_type == UDP_GRO) {
  201. *gso_size = *(int *)CMSG_DATA(cmsg);
  202. break;
  203. }
  204. }
  205. }
  206. return ret;
  207. }
  208. /* Flush all outstanding datagrams. Verify first few bytes of each. */
  209. static void do_flush_udp(int fd)
  210. {
  211. static char rbuf[ETH_MAX_MTU];
  212. int ret, len, gso_size = 0, budget = 256;
  213. len = cfg_read_all ? sizeof(rbuf) : 0;
  214. while (budget--) {
  215. /* MSG_TRUNC will make return value full datagram length */
  216. if (!cfg_expected_gso_size)
  217. ret = recv(fd, rbuf, len, MSG_TRUNC | MSG_DONTWAIT);
  218. else
  219. ret = recv_msg(fd, rbuf, len, &gso_size);
  220. if (ret == -1 && errno == EAGAIN)
  221. break;
  222. if (ret == -1)
  223. error(1, errno, "recv");
  224. if (cfg_expected_pkt_len && ret != cfg_expected_pkt_len)
  225. error(1, 0, "recv: bad packet len, got %d,"
  226. " expected %d\n", ret, cfg_expected_pkt_len);
  227. if (len && cfg_verify) {
  228. if (ret == 0)
  229. error(1, errno, "recv: 0 byte datagram\n");
  230. do_verify_udp(rbuf, ret);
  231. }
  232. if (cfg_expected_gso_size && cfg_expected_gso_size != gso_size)
  233. error(1, 0, "recv: bad gso size, got %d, expected %d "
  234. "(-1 == no gso cmsg))\n", gso_size,
  235. cfg_expected_gso_size);
  236. packets++;
  237. bytes += ret;
  238. if (cfg_expected_pkt_nr && packets >= cfg_expected_pkt_nr)
  239. break;
  240. }
  241. }
  242. static void usage(const char *filepath)
  243. {
  244. error(1, 0, "Usage: %s [-C connect_timeout] [-Grtv] [-b addr] [-p port]"
  245. " [-l pktlen] [-n packetnr] [-R rcv_timeout] [-S gsosize]",
  246. filepath);
  247. }
  248. static void parse_opts(int argc, char **argv)
  249. {
  250. const char *bind_addr = NULL;
  251. int c;
  252. while ((c = getopt(argc, argv, "4b:C:Gl:n:p:rR:S:tv")) != -1) {
  253. switch (c) {
  254. case '4':
  255. cfg_family = PF_INET;
  256. cfg_alen = sizeof(struct sockaddr_in);
  257. break;
  258. case 'b':
  259. bind_addr = optarg;
  260. break;
  261. case 'C':
  262. cfg_connect_timeout_ms = strtoul(optarg, NULL, 0);
  263. break;
  264. case 'G':
  265. cfg_gro_segment = true;
  266. break;
  267. case 'l':
  268. cfg_expected_pkt_len = strtoul(optarg, NULL, 0);
  269. break;
  270. case 'n':
  271. cfg_expected_pkt_nr = strtoul(optarg, NULL, 0);
  272. break;
  273. case 'p':
  274. cfg_port = strtoul(optarg, NULL, 0);
  275. break;
  276. case 'r':
  277. cfg_read_all = true;
  278. break;
  279. case 'R':
  280. cfg_rcv_timeout_ms = strtoul(optarg, NULL, 0);
  281. break;
  282. case 'S':
  283. cfg_expected_gso_size = strtol(optarg, NULL, 0);
  284. break;
  285. case 't':
  286. cfg_tcp = true;
  287. break;
  288. case 'v':
  289. cfg_verify = true;
  290. cfg_read_all = true;
  291. break;
  292. default:
  293. exit(1);
  294. }
  295. }
  296. if (!bind_addr)
  297. bind_addr = cfg_family == PF_INET6 ? "::" : "0.0.0.0";
  298. setup_sockaddr(cfg_family, bind_addr, &cfg_bind_addr);
  299. if (optind != argc)
  300. usage(argv[0]);
  301. if (cfg_tcp && cfg_verify)
  302. error(1, 0, "TODO: implement verify mode for tcp");
  303. }
  304. static void do_recv(void)
  305. {
  306. int timeout_ms = cfg_tcp ? cfg_rcv_timeout_ms : cfg_connect_timeout_ms;
  307. unsigned long tnow, treport;
  308. int fd;
  309. fd = do_socket(cfg_tcp);
  310. if (cfg_gro_segment && !cfg_tcp) {
  311. int val = 1;
  312. if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)))
  313. error(1, errno, "setsockopt UDP_GRO");
  314. }
  315. treport = gettimeofday_ms() + 1000;
  316. do {
  317. do_poll(fd, timeout_ms);
  318. if (cfg_tcp)
  319. do_flush_tcp(fd);
  320. else
  321. do_flush_udp(fd);
  322. tnow = gettimeofday_ms();
  323. if (!cfg_expected_pkt_nr && tnow > treport) {
  324. if (packets)
  325. fprintf(stderr,
  326. "%s rx: %6lu MB/s %8lu calls/s\n",
  327. cfg_tcp ? "tcp" : "udp",
  328. bytes >> 20, packets);
  329. bytes = packets = 0;
  330. treport = tnow + 1000;
  331. }
  332. timeout_ms = cfg_rcv_timeout_ms;
  333. } while (!interrupted);
  334. if (cfg_expected_pkt_nr && (packets != cfg_expected_pkt_nr))
  335. error(1, 0, "wrong packet number! got %ld, expected %d\n",
  336. packets, cfg_expected_pkt_nr);
  337. if (close(fd))
  338. error(1, errno, "close");
  339. }
  340. int main(int argc, char **argv)
  341. {
  342. parse_opts(argc, argv);
  343. signal(SIGINT, sigint_handler);
  344. do_recv();
  345. return 0;
  346. }