root/tools/testing/selftests/net/udpgso_bench_rx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sigint_handler
  2. setup_sockaddr
  3. gettimeofday_ms
  4. do_poll
  5. do_socket
  6. do_flush_tcp
  7. sanitized_char
  8. do_verify_udp
  9. recv_msg
  10. do_flush_udp
  11. usage
  12. parse_opts
  13. do_recv
  14. main

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

/* [<][>][^][v][top][bottom][index][help] */