udpgso_bench_tx.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <arpa/inet.h>
  4. #include <errno.h>
  5. #include <error.h>
  6. #include <netinet/if_ether.h>
  7. #include <netinet/in.h>
  8. #include <netinet/ip.h>
  9. #include <netinet/ip6.h>
  10. #include <netinet/udp.h>
  11. #include <poll.h>
  12. #include <sched.h>
  13. #include <signal.h>
  14. #include <stdbool.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/socket.h>
  19. #include <sys/time.h>
  20. #include <sys/types.h>
  21. #include <unistd.h>
  22. #ifndef ETH_MAX_MTU
  23. #define ETH_MAX_MTU 0xFFFFU
  24. #endif
  25. #ifndef UDP_SEGMENT
  26. #define UDP_SEGMENT 103
  27. #endif
  28. #ifndef SO_ZEROCOPY
  29. #define SO_ZEROCOPY 60
  30. #endif
  31. #ifndef MSG_ZEROCOPY
  32. #define MSG_ZEROCOPY 0x4000000
  33. #endif
  34. #define NUM_PKT 100
  35. static bool cfg_cache_trash;
  36. static int cfg_cpu = -1;
  37. static int cfg_connected = true;
  38. static int cfg_family = PF_UNSPEC;
  39. static uint16_t cfg_mss;
  40. static int cfg_payload_len = (1472 * 42);
  41. static int cfg_port = 8000;
  42. static int cfg_runtime_ms = -1;
  43. static bool cfg_segment;
  44. static bool cfg_sendmmsg;
  45. static bool cfg_tcp;
  46. static bool cfg_zerocopy;
  47. static socklen_t cfg_alen;
  48. static struct sockaddr_storage cfg_dst_addr;
  49. static bool interrupted;
  50. static char buf[NUM_PKT][ETH_MAX_MTU];
  51. static void sigint_handler(int signum)
  52. {
  53. if (signum == SIGINT)
  54. interrupted = true;
  55. }
  56. static unsigned long gettimeofday_ms(void)
  57. {
  58. struct timeval tv;
  59. gettimeofday(&tv, NULL);
  60. return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
  61. }
  62. static int set_cpu(int cpu)
  63. {
  64. cpu_set_t mask;
  65. CPU_ZERO(&mask);
  66. CPU_SET(cpu, &mask);
  67. if (sched_setaffinity(0, sizeof(mask), &mask))
  68. error(1, 0, "setaffinity %d", cpu);
  69. return 0;
  70. }
  71. static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
  72. {
  73. struct sockaddr_in6 *addr6 = (void *) sockaddr;
  74. struct sockaddr_in *addr4 = (void *) sockaddr;
  75. switch (domain) {
  76. case PF_INET:
  77. addr4->sin_family = AF_INET;
  78. addr4->sin_port = htons(cfg_port);
  79. if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
  80. error(1, 0, "ipv4 parse error: %s", str_addr);
  81. break;
  82. case PF_INET6:
  83. addr6->sin6_family = AF_INET6;
  84. addr6->sin6_port = htons(cfg_port);
  85. if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
  86. error(1, 0, "ipv6 parse error: %s", str_addr);
  87. break;
  88. default:
  89. error(1, 0, "illegal domain");
  90. }
  91. }
  92. static void flush_zerocopy(int fd)
  93. {
  94. struct msghdr msg = {0}; /* flush */
  95. int ret;
  96. while (1) {
  97. ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
  98. if (ret == -1 && errno == EAGAIN)
  99. break;
  100. if (ret == -1)
  101. error(1, errno, "errqueue");
  102. if (msg.msg_flags != (MSG_ERRQUEUE | MSG_CTRUNC))
  103. error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
  104. msg.msg_flags = 0;
  105. }
  106. }
  107. static int send_tcp(int fd, char *data)
  108. {
  109. int ret, done = 0, count = 0;
  110. while (done < cfg_payload_len) {
  111. ret = send(fd, data + done, cfg_payload_len - done,
  112. cfg_zerocopy ? MSG_ZEROCOPY : 0);
  113. if (ret == -1)
  114. error(1, errno, "write");
  115. done += ret;
  116. count++;
  117. }
  118. return count;
  119. }
  120. static int send_udp(int fd, char *data)
  121. {
  122. int ret, total_len, len, count = 0;
  123. total_len = cfg_payload_len;
  124. while (total_len) {
  125. len = total_len < cfg_mss ? total_len : cfg_mss;
  126. ret = sendto(fd, data, len, cfg_zerocopy ? MSG_ZEROCOPY : 0,
  127. cfg_connected ? NULL : (void *)&cfg_dst_addr,
  128. cfg_connected ? 0 : cfg_alen);
  129. if (ret == -1)
  130. error(1, errno, "write");
  131. if (ret != len)
  132. error(1, errno, "write: %uB != %uB\n", ret, len);
  133. total_len -= len;
  134. count++;
  135. }
  136. return count;
  137. }
  138. static int send_udp_sendmmsg(int fd, char *data)
  139. {
  140. const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
  141. struct mmsghdr mmsgs[max_nr_msg];
  142. struct iovec iov[max_nr_msg];
  143. unsigned int off = 0, left;
  144. int i = 0, ret;
  145. memset(mmsgs, 0, sizeof(mmsgs));
  146. left = cfg_payload_len;
  147. while (left) {
  148. if (i == max_nr_msg)
  149. error(1, 0, "sendmmsg: exceeds max_nr_msg");
  150. iov[i].iov_base = data + off;
  151. iov[i].iov_len = cfg_mss < left ? cfg_mss : left;
  152. mmsgs[i].msg_hdr.msg_iov = iov + i;
  153. mmsgs[i].msg_hdr.msg_iovlen = 1;
  154. off += iov[i].iov_len;
  155. left -= iov[i].iov_len;
  156. i++;
  157. }
  158. ret = sendmmsg(fd, mmsgs, i, cfg_zerocopy ? MSG_ZEROCOPY : 0);
  159. if (ret == -1)
  160. error(1, errno, "sendmmsg");
  161. return ret;
  162. }
  163. static void send_udp_segment_cmsg(struct cmsghdr *cm)
  164. {
  165. uint16_t *valp;
  166. cm->cmsg_level = SOL_UDP;
  167. cm->cmsg_type = UDP_SEGMENT;
  168. cm->cmsg_len = CMSG_LEN(sizeof(cfg_mss));
  169. valp = (void *)CMSG_DATA(cm);
  170. *valp = cfg_mss;
  171. }
  172. static int send_udp_segment(int fd, char *data)
  173. {
  174. char control[CMSG_SPACE(sizeof(cfg_mss))] = {0};
  175. struct msghdr msg = {0};
  176. struct iovec iov = {0};
  177. int ret;
  178. iov.iov_base = data;
  179. iov.iov_len = cfg_payload_len;
  180. msg.msg_iov = &iov;
  181. msg.msg_iovlen = 1;
  182. msg.msg_control = control;
  183. msg.msg_controllen = sizeof(control);
  184. send_udp_segment_cmsg(CMSG_FIRSTHDR(&msg));
  185. msg.msg_name = (void *)&cfg_dst_addr;
  186. msg.msg_namelen = cfg_alen;
  187. ret = sendmsg(fd, &msg, cfg_zerocopy ? MSG_ZEROCOPY : 0);
  188. if (ret == -1)
  189. error(1, errno, "sendmsg");
  190. if (ret != iov.iov_len)
  191. error(1, 0, "sendmsg: %u != %lu\n", ret, iov.iov_len);
  192. return 1;
  193. }
  194. static void usage(const char *filepath)
  195. {
  196. error(1, 0, "Usage: %s [-46cmStuz] [-C cpu] [-D dst ip] [-l secs] [-p port] [-s sendsize]",
  197. filepath);
  198. }
  199. static void parse_opts(int argc, char **argv)
  200. {
  201. int max_len, hdrlen;
  202. int c;
  203. while ((c = getopt(argc, argv, "46cC:D:l:mp:s:Stuz")) != -1) {
  204. switch (c) {
  205. case '4':
  206. if (cfg_family != PF_UNSPEC)
  207. error(1, 0, "Pass one of -4 or -6");
  208. cfg_family = PF_INET;
  209. cfg_alen = sizeof(struct sockaddr_in);
  210. break;
  211. case '6':
  212. if (cfg_family != PF_UNSPEC)
  213. error(1, 0, "Pass one of -4 or -6");
  214. cfg_family = PF_INET6;
  215. cfg_alen = sizeof(struct sockaddr_in6);
  216. break;
  217. case 'c':
  218. cfg_cache_trash = true;
  219. break;
  220. case 'C':
  221. cfg_cpu = strtol(optarg, NULL, 0);
  222. break;
  223. case 'D':
  224. setup_sockaddr(cfg_family, optarg, &cfg_dst_addr);
  225. break;
  226. case 'l':
  227. cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
  228. break;
  229. case 'm':
  230. cfg_sendmmsg = true;
  231. break;
  232. case 'p':
  233. cfg_port = strtoul(optarg, NULL, 0);
  234. break;
  235. case 's':
  236. cfg_payload_len = strtoul(optarg, NULL, 0);
  237. break;
  238. case 'S':
  239. cfg_segment = true;
  240. break;
  241. case 't':
  242. cfg_tcp = true;
  243. break;
  244. case 'u':
  245. cfg_connected = false;
  246. break;
  247. case 'z':
  248. cfg_zerocopy = true;
  249. break;
  250. }
  251. }
  252. if (optind != argc)
  253. usage(argv[0]);
  254. if (cfg_family == PF_UNSPEC)
  255. error(1, 0, "must pass one of -4 or -6");
  256. if (cfg_tcp && !cfg_connected)
  257. error(1, 0, "connectionless tcp makes no sense");
  258. if (cfg_segment && cfg_sendmmsg)
  259. error(1, 0, "cannot combine segment offload and sendmmsg");
  260. if (cfg_family == PF_INET)
  261. hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
  262. else
  263. hdrlen = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
  264. cfg_mss = ETH_DATA_LEN - hdrlen;
  265. max_len = ETH_MAX_MTU - hdrlen;
  266. if (cfg_payload_len > max_len)
  267. error(1, 0, "payload length %u exceeds max %u",
  268. cfg_payload_len, max_len);
  269. }
  270. static void set_pmtu_discover(int fd, bool is_ipv4)
  271. {
  272. int level, name, val;
  273. if (is_ipv4) {
  274. level = SOL_IP;
  275. name = IP_MTU_DISCOVER;
  276. val = IP_PMTUDISC_DO;
  277. } else {
  278. level = SOL_IPV6;
  279. name = IPV6_MTU_DISCOVER;
  280. val = IPV6_PMTUDISC_DO;
  281. }
  282. if (setsockopt(fd, level, name, &val, sizeof(val)))
  283. error(1, errno, "setsockopt path mtu");
  284. }
  285. int main(int argc, char **argv)
  286. {
  287. unsigned long num_msgs, num_sends;
  288. unsigned long tnow, treport, tstop;
  289. int fd, i, val;
  290. parse_opts(argc, argv);
  291. if (cfg_cpu > 0)
  292. set_cpu(cfg_cpu);
  293. for (i = 0; i < sizeof(buf[0]); i++)
  294. buf[0][i] = 'a' + (i % 26);
  295. for (i = 1; i < NUM_PKT; i++)
  296. memcpy(buf[i], buf[0], sizeof(buf[0]));
  297. signal(SIGINT, sigint_handler);
  298. fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
  299. if (fd == -1)
  300. error(1, errno, "socket");
  301. if (cfg_zerocopy) {
  302. val = 1;
  303. if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)))
  304. error(1, errno, "setsockopt zerocopy");
  305. }
  306. if (cfg_connected &&
  307. connect(fd, (void *)&cfg_dst_addr, cfg_alen))
  308. error(1, errno, "connect");
  309. if (cfg_segment)
  310. set_pmtu_discover(fd, cfg_family == PF_INET);
  311. num_msgs = num_sends = 0;
  312. tnow = gettimeofday_ms();
  313. tstop = tnow + cfg_runtime_ms;
  314. treport = tnow + 1000;
  315. i = 0;
  316. do {
  317. if (cfg_tcp)
  318. num_sends += send_tcp(fd, buf[i]);
  319. else if (cfg_segment)
  320. num_sends += send_udp_segment(fd, buf[i]);
  321. else if (cfg_sendmmsg)
  322. num_sends += send_udp_sendmmsg(fd, buf[i]);
  323. else
  324. num_sends += send_udp(fd, buf[i]);
  325. num_msgs++;
  326. if (cfg_zerocopy && ((num_msgs & 0xF) == 0))
  327. flush_zerocopy(fd);
  328. tnow = gettimeofday_ms();
  329. if (tnow > treport) {
  330. fprintf(stderr,
  331. "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
  332. cfg_tcp ? "tcp" : "udp",
  333. (num_msgs * cfg_payload_len) >> 20,
  334. num_sends, num_msgs);
  335. num_msgs = num_sends = 0;
  336. treport = tnow + 1000;
  337. }
  338. /* cold cache when writing buffer */
  339. if (cfg_cache_trash)
  340. i = ++i < NUM_PKT ? i : 0;
  341. } while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
  342. if (close(fd))
  343. error(1, errno, "close");
  344. return 0;
  345. }