hc
2024-01-03 2f7c68cb55ecb7331f2381deb497c27155f32faf
kernel/tools/testing/selftests/net/udpgso_bench_tx.c
....@@ -5,6 +5,8 @@
55 #include <arpa/inet.h>
66 #include <errno.h>
77 #include <error.h>
8
+#include <linux/errqueue.h>
9
+#include <linux/net_tstamp.h>
810 #include <netinet/if_ether.h>
911 #include <netinet/in.h>
1012 #include <netinet/ip.h>
....@@ -19,8 +21,11 @@
1921 #include <string.h>
2022 #include <sys/socket.h>
2123 #include <sys/time.h>
24
+#include <sys/poll.h>
2225 #include <sys/types.h>
2326 #include <unistd.h>
27
+
28
+#include "../kselftest.h"
2429
2530 #ifndef ETH_MAX_MTU
2631 #define ETH_MAX_MTU 0xFFFFU
....@@ -34,8 +39,16 @@
3439 #define SO_ZEROCOPY 60
3540 #endif
3641
42
+#ifndef SO_EE_ORIGIN_ZEROCOPY
43
+#define SO_EE_ORIGIN_ZEROCOPY 5
44
+#endif
45
+
3746 #ifndef MSG_ZEROCOPY
3847 #define MSG_ZEROCOPY 0x4000000
48
+#endif
49
+
50
+#ifndef ENOTSUPP
51
+#define ENOTSUPP 524
3952 #endif
4053
4154 #define NUM_PKT 100
....@@ -48,10 +61,25 @@
4861 static int cfg_payload_len = (1472 * 42);
4962 static int cfg_port = 8000;
5063 static int cfg_runtime_ms = -1;
64
+static bool cfg_poll;
65
+static int cfg_poll_loop_timeout_ms = 2000;
5166 static bool cfg_segment;
5267 static bool cfg_sendmmsg;
5368 static bool cfg_tcp;
69
+static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
70
+static bool cfg_tx_tstamp;
71
+static bool cfg_audit;
72
+static bool cfg_verbose;
5473 static bool cfg_zerocopy;
74
+static int cfg_msg_nr;
75
+static uint16_t cfg_gso_size;
76
+static unsigned long total_num_msgs;
77
+static unsigned long total_num_sends;
78
+static unsigned long stat_tx_ts;
79
+static unsigned long stat_tx_ts_errors;
80
+static unsigned long tstart;
81
+static unsigned long tend;
82
+static unsigned long stat_zcopies;
5583
5684 static socklen_t cfg_alen;
5785 static struct sockaddr_storage cfg_dst_addr;
....@@ -108,21 +136,138 @@
108136 }
109137 }
110138
111
-static void flush_zerocopy(int fd)
139
+static void flush_cmsg(struct cmsghdr *cmsg)
112140 {
113
- struct msghdr msg = {0}; /* flush */
141
+ struct sock_extended_err *err;
142
+ struct scm_timestamping *tss;
143
+ __u32 lo;
144
+ __u32 hi;
145
+ int i;
146
+
147
+ switch (cmsg->cmsg_level) {
148
+ case SOL_SOCKET:
149
+ if (cmsg->cmsg_type == SO_TIMESTAMPING) {
150
+ i = (cfg_tx_ts == SOF_TIMESTAMPING_TX_HARDWARE) ? 2 : 0;
151
+ tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
152
+ if (tss->ts[i].tv_sec == 0)
153
+ stat_tx_ts_errors++;
154
+ } else {
155
+ error(1, 0, "unknown SOL_SOCKET cmsg type=%u\n",
156
+ cmsg->cmsg_type);
157
+ }
158
+ break;
159
+ case SOL_IP:
160
+ case SOL_IPV6:
161
+ switch (cmsg->cmsg_type) {
162
+ case IP_RECVERR:
163
+ case IPV6_RECVERR:
164
+ {
165
+ err = (struct sock_extended_err *)CMSG_DATA(cmsg);
166
+ switch (err->ee_origin) {
167
+ case SO_EE_ORIGIN_TIMESTAMPING:
168
+ /* Got a TX timestamp from error queue */
169
+ stat_tx_ts++;
170
+ break;
171
+ case SO_EE_ORIGIN_ICMP:
172
+ case SO_EE_ORIGIN_ICMP6:
173
+ if (cfg_verbose)
174
+ fprintf(stderr,
175
+ "received ICMP error: type=%u, code=%u\n",
176
+ err->ee_type, err->ee_code);
177
+ break;
178
+ case SO_EE_ORIGIN_ZEROCOPY:
179
+ {
180
+ lo = err->ee_info;
181
+ hi = err->ee_data;
182
+ /* range of IDs acknowledged */
183
+ stat_zcopies += hi - lo + 1;
184
+ break;
185
+ }
186
+ case SO_EE_ORIGIN_LOCAL:
187
+ if (cfg_verbose)
188
+ fprintf(stderr,
189
+ "received packet with local origin: %u\n",
190
+ err->ee_origin);
191
+ break;
192
+ default:
193
+ error(0, 1, "received packet with origin: %u",
194
+ err->ee_origin);
195
+ }
196
+ break;
197
+ }
198
+ default:
199
+ error(0, 1, "unknown IP msg type=%u\n",
200
+ cmsg->cmsg_type);
201
+ break;
202
+ }
203
+ break;
204
+ default:
205
+ error(0, 1, "unknown cmsg level=%u\n",
206
+ cmsg->cmsg_level);
207
+ }
208
+}
209
+
210
+static void flush_errqueue_recv(int fd)
211
+{
212
+ char control[CMSG_SPACE(sizeof(struct scm_timestamping)) +
213
+ CMSG_SPACE(sizeof(struct sock_extended_err)) +
214
+ CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
215
+ struct msghdr msg = {0};
216
+ struct cmsghdr *cmsg;
114217 int ret;
115218
116219 while (1) {
220
+ msg.msg_control = control;
221
+ msg.msg_controllen = sizeof(control);
117222 ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
118223 if (ret == -1 && errno == EAGAIN)
119224 break;
120225 if (ret == -1)
121226 error(1, errno, "errqueue");
122
- if (msg.msg_flags != (MSG_ERRQUEUE | MSG_CTRUNC))
227
+ if (msg.msg_flags != MSG_ERRQUEUE)
123228 error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
229
+ if (cfg_audit) {
230
+ for (cmsg = CMSG_FIRSTHDR(&msg);
231
+ cmsg;
232
+ cmsg = CMSG_NXTHDR(&msg, cmsg))
233
+ flush_cmsg(cmsg);
234
+ }
124235 msg.msg_flags = 0;
125236 }
237
+}
238
+
239
+static void flush_errqueue(int fd, const bool do_poll,
240
+ unsigned long poll_timeout, const bool poll_err)
241
+{
242
+ if (do_poll) {
243
+ struct pollfd fds = {0};
244
+ int ret;
245
+
246
+ fds.fd = fd;
247
+ ret = poll(&fds, 1, poll_timeout);
248
+ if (ret == 0) {
249
+ if ((cfg_verbose) && (poll_err))
250
+ fprintf(stderr, "poll timeout\n");
251
+ } else if (ret < 0) {
252
+ error(1, errno, "poll");
253
+ }
254
+ }
255
+
256
+ flush_errqueue_recv(fd);
257
+}
258
+
259
+static void flush_errqueue_retry(int fd, unsigned long num_sends)
260
+{
261
+ unsigned long tnow, tstop;
262
+ bool first_try = true;
263
+
264
+ tnow = gettimeofday_ms();
265
+ tstop = tnow + cfg_poll_loop_timeout_ms;
266
+ do {
267
+ flush_errqueue(fd, true, tstop - tnow, first_try);
268
+ first_try = false;
269
+ tnow = gettimeofday_ms();
270
+ } while ((stat_zcopies != num_sends) && (tnow < tstop));
126271 }
127272
128273 static int send_tcp(int fd, char *data)
....@@ -166,15 +311,39 @@
166311 return count;
167312 }
168313
314
+static void send_ts_cmsg(struct cmsghdr *cm)
315
+{
316
+ uint32_t *valp;
317
+
318
+ cm->cmsg_level = SOL_SOCKET;
319
+ cm->cmsg_type = SO_TIMESTAMPING;
320
+ cm->cmsg_len = CMSG_LEN(sizeof(cfg_tx_ts));
321
+ valp = (void *)CMSG_DATA(cm);
322
+ *valp = cfg_tx_ts;
323
+}
324
+
169325 static int send_udp_sendmmsg(int fd, char *data)
170326 {
327
+ char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
171328 const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
172329 struct mmsghdr mmsgs[max_nr_msg];
173330 struct iovec iov[max_nr_msg];
174331 unsigned int off = 0, left;
332
+ size_t msg_controllen = 0;
175333 int i = 0, ret;
176334
177335 memset(mmsgs, 0, sizeof(mmsgs));
336
+
337
+ if (cfg_tx_tstamp) {
338
+ struct msghdr msg = {0};
339
+ struct cmsghdr *cmsg;
340
+
341
+ msg.msg_control = control;
342
+ msg.msg_controllen = sizeof(control);
343
+ cmsg = CMSG_FIRSTHDR(&msg);
344
+ send_ts_cmsg(cmsg);
345
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
346
+ }
178347
179348 left = cfg_payload_len;
180349 while (left) {
....@@ -186,6 +355,13 @@
186355
187356 mmsgs[i].msg_hdr.msg_iov = iov + i;
188357 mmsgs[i].msg_hdr.msg_iovlen = 1;
358
+
359
+ mmsgs[i].msg_hdr.msg_name = (void *)&cfg_dst_addr;
360
+ mmsgs[i].msg_hdr.msg_namelen = cfg_alen;
361
+ if (msg_controllen) {
362
+ mmsgs[i].msg_hdr.msg_control = control;
363
+ mmsgs[i].msg_hdr.msg_controllen = msg_controllen;
364
+ }
189365
190366 off += iov[i].iov_len;
191367 left -= iov[i].iov_len;
....@@ -205,16 +381,19 @@
205381
206382 cm->cmsg_level = SOL_UDP;
207383 cm->cmsg_type = UDP_SEGMENT;
208
- cm->cmsg_len = CMSG_LEN(sizeof(cfg_mss));
384
+ cm->cmsg_len = CMSG_LEN(sizeof(cfg_gso_size));
209385 valp = (void *)CMSG_DATA(cm);
210
- *valp = cfg_mss;
386
+ *valp = cfg_gso_size;
211387 }
212388
213389 static int send_udp_segment(int fd, char *data)
214390 {
215
- char control[CMSG_SPACE(sizeof(cfg_mss))] = {0};
391
+ char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
392
+ CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
216393 struct msghdr msg = {0};
217394 struct iovec iov = {0};
395
+ size_t msg_controllen;
396
+ struct cmsghdr *cmsg;
218397 int ret;
219398
220399 iov.iov_base = data;
....@@ -225,8 +404,16 @@
225404
226405 msg.msg_control = control;
227406 msg.msg_controllen = sizeof(control);
228
- send_udp_segment_cmsg(CMSG_FIRSTHDR(&msg));
407
+ cmsg = CMSG_FIRSTHDR(&msg);
408
+ send_udp_segment_cmsg(cmsg);
409
+ msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
410
+ if (cfg_tx_tstamp) {
411
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
412
+ send_ts_cmsg(cmsg);
413
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
414
+ }
229415
416
+ msg.msg_controllen = msg_controllen;
230417 msg.msg_name = (void *)&cfg_dst_addr;
231418 msg.msg_namelen = cfg_alen;
232419
....@@ -234,14 +421,16 @@
234421 if (ret == -1)
235422 error(1, errno, "sendmsg");
236423 if (ret != iov.iov_len)
237
- error(1, 0, "sendmsg: %u != %lu\n", ret, iov.iov_len);
424
+ error(1, 0, "sendmsg: %u != %llu\n", ret,
425
+ (unsigned long long)iov.iov_len);
238426
239427 return 1;
240428 }
241429
242430 static void usage(const char *filepath)
243431 {
244
- error(1, 0, "Usage: %s [-46cmStuz] [-C cpu] [-D dst ip] [-l secs] [-p port] [-s sendsize]",
432
+ error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] "
433
+ "[-L secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
245434 filepath);
246435 }
247436
....@@ -251,7 +440,7 @@
251440 int max_len, hdrlen;
252441 int c;
253442
254
- while ((c = getopt(argc, argv, "46cC:D:l:mp:s:Stuz")) != -1) {
443
+ while ((c = getopt(argc, argv, "46acC:D:Hl:L:mM:p:s:PS:tTuvz")) != -1) {
255444 switch (c) {
256445 case '4':
257446 if (cfg_family != PF_UNSPEC)
....@@ -265,6 +454,9 @@
265454 cfg_family = PF_INET6;
266455 cfg_alen = sizeof(struct sockaddr_in6);
267456 break;
457
+ case 'a':
458
+ cfg_audit = true;
459
+ break;
268460 case 'c':
269461 cfg_cache_trash = true;
270462 break;
....@@ -277,27 +469,49 @@
277469 case 'l':
278470 cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
279471 break;
472
+ case 'L':
473
+ cfg_poll_loop_timeout_ms = strtoul(optarg, NULL, 10) * 1000;
474
+ break;
280475 case 'm':
281476 cfg_sendmmsg = true;
282477 break;
478
+ case 'M':
479
+ cfg_msg_nr = strtoul(optarg, NULL, 10);
480
+ break;
283481 case 'p':
284482 cfg_port = strtoul(optarg, NULL, 0);
483
+ break;
484
+ case 'P':
485
+ cfg_poll = true;
285486 break;
286487 case 's':
287488 cfg_payload_len = strtoul(optarg, NULL, 0);
288489 break;
289490 case 'S':
491
+ cfg_gso_size = strtoul(optarg, NULL, 0);
290492 cfg_segment = true;
493
+ break;
494
+ case 'H':
495
+ cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE;
496
+ cfg_tx_tstamp = true;
291497 break;
292498 case 't':
293499 cfg_tcp = true;
294500 break;
501
+ case 'T':
502
+ cfg_tx_tstamp = true;
503
+ break;
295504 case 'u':
296505 cfg_connected = false;
506
+ break;
507
+ case 'v':
508
+ cfg_verbose = true;
297509 break;
298510 case 'z':
299511 cfg_zerocopy = true;
300512 break;
513
+ default:
514
+ exit(1);
301515 }
302516 }
303517
....@@ -315,6 +529,8 @@
315529 error(1, 0, "connectionless tcp makes no sense");
316530 if (cfg_segment && cfg_sendmmsg)
317531 error(1, 0, "cannot combine segment offload and sendmmsg");
532
+ if (cfg_tx_tstamp && !(cfg_segment || cfg_sendmmsg))
533
+ error(1, 0, "Options -T and -H require either -S or -m option");
318534
319535 if (cfg_family == PF_INET)
320536 hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
....@@ -323,6 +539,8 @@
323539
324540 cfg_mss = ETH_DATA_LEN - hdrlen;
325541 max_len = ETH_MAX_MTU - hdrlen;
542
+ if (!cfg_gso_size)
543
+ cfg_gso_size = cfg_mss;
326544
327545 if (cfg_payload_len > max_len)
328546 error(1, 0, "payload length %u exceeds max %u",
....@@ -347,11 +565,80 @@
347565 error(1, errno, "setsockopt path mtu");
348566 }
349567
568
+static void set_tx_timestamping(int fd)
569
+{
570
+ int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
571
+ SOF_TIMESTAMPING_OPT_TSONLY;
572
+
573
+ if (cfg_tx_ts == SOF_TIMESTAMPING_TX_SOFTWARE)
574
+ val |= SOF_TIMESTAMPING_SOFTWARE;
575
+ else
576
+ val |= SOF_TIMESTAMPING_RAW_HARDWARE;
577
+
578
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)))
579
+ error(1, errno, "setsockopt tx timestamping");
580
+}
581
+
582
+static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
583
+{
584
+ unsigned long tdelta;
585
+
586
+ tdelta = tend - tstart;
587
+ if (!tdelta)
588
+ return;
589
+
590
+ fprintf(stderr, "Summary over %lu.%03lu seconds...\n",
591
+ tdelta / 1000, tdelta % 1000);
592
+ fprintf(stderr,
593
+ "sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s)\n",
594
+ cfg_tcp ? "tcp" : "udp",
595
+ ((num_msgs * cfg_payload_len) >> 10) / tdelta,
596
+ num_sends, num_sends * 1000 / tdelta,
597
+ num_msgs, num_msgs * 1000 / tdelta);
598
+
599
+ if (cfg_tx_tstamp) {
600
+ if (stat_tx_ts_errors)
601
+ error(1, 0,
602
+ "Expected clean TX Timestamps: %9lu msgs received %6lu errors",
603
+ stat_tx_ts, stat_tx_ts_errors);
604
+ if (stat_tx_ts != num_sends)
605
+ error(1, 0,
606
+ "Unexpected number of TX Timestamps: %9lu expected %9lu received",
607
+ num_sends, stat_tx_ts);
608
+ fprintf(stderr,
609
+ "Tx Timestamps: %19lu received %17lu errors\n",
610
+ stat_tx_ts, stat_tx_ts_errors);
611
+ }
612
+
613
+ if (cfg_zerocopy) {
614
+ if (stat_zcopies != num_sends)
615
+ error(1, 0, "Unexpected number of Zerocopy completions: %9lu expected %9lu received",
616
+ num_sends, stat_zcopies);
617
+ fprintf(stderr,
618
+ "Zerocopy acks: %19lu\n",
619
+ stat_zcopies);
620
+ }
621
+}
622
+
623
+static void print_report(unsigned long num_msgs, unsigned long num_sends)
624
+{
625
+ fprintf(stderr,
626
+ "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
627
+ cfg_tcp ? "tcp" : "udp",
628
+ (num_msgs * cfg_payload_len) >> 20,
629
+ num_sends, num_msgs);
630
+
631
+ if (cfg_audit) {
632
+ total_num_msgs += num_msgs;
633
+ total_num_sends += num_sends;
634
+ }
635
+}
636
+
350637 int main(int argc, char **argv)
351638 {
352639 unsigned long num_msgs, num_sends;
353640 unsigned long tnow, treport, tstop;
354
- int fd, i, val;
641
+ int fd, i, val, ret;
355642
356643 parse_opts(argc, argv);
357644
....@@ -371,8 +658,16 @@
371658
372659 if (cfg_zerocopy) {
373660 val = 1;
374
- if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)))
661
+
662
+ ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY,
663
+ &val, sizeof(val));
664
+ if (ret) {
665
+ if (errno == ENOPROTOOPT || errno == ENOTSUPP) {
666
+ fprintf(stderr, "SO_ZEROCOPY not supported");
667
+ exit(KSFT_SKIP);
668
+ }
375669 error(1, errno, "setsockopt zerocopy");
670
+ }
376671 }
377672
378673 if (cfg_connected &&
....@@ -382,8 +677,13 @@
382677 if (cfg_segment)
383678 set_pmtu_discover(fd, cfg_family == PF_INET);
384679
680
+ if (cfg_tx_tstamp)
681
+ set_tx_timestamping(fd);
682
+
385683 num_msgs = num_sends = 0;
386684 tnow = gettimeofday_ms();
685
+ tstart = tnow;
686
+ tend = tnow;
387687 tstop = tnow + cfg_runtime_ms;
388688 treport = tnow + 1000;
389689
....@@ -398,17 +698,15 @@
398698 else
399699 num_sends += send_udp(fd, buf[i]);
400700 num_msgs++;
701
+ if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
702
+ flush_errqueue(fd, cfg_poll, 500, true);
401703
402
- if (cfg_zerocopy && ((num_msgs & 0xF) == 0))
403
- flush_zerocopy(fd);
704
+ if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
705
+ break;
404706
405707 tnow = gettimeofday_ms();
406
- if (tnow > treport) {
407
- fprintf(stderr,
408
- "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
409
- cfg_tcp ? "tcp" : "udp",
410
- (num_msgs * cfg_payload_len) >> 20,
411
- num_sends, num_msgs);
708
+ if (tnow >= treport) {
709
+ print_report(num_msgs, num_sends);
412710 num_msgs = num_sends = 0;
413711 treport = tnow + 1000;
414712 }
....@@ -419,8 +717,18 @@
419717
420718 } while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
421719
720
+ if (cfg_zerocopy || cfg_tx_tstamp)
721
+ flush_errqueue_retry(fd, num_sends);
722
+
422723 if (close(fd))
423724 error(1, errno, "close");
424725
726
+ if (cfg_audit) {
727
+ tend = tnow;
728
+ total_num_msgs += num_msgs;
729
+ total_num_sends += num_sends;
730
+ print_audit_report(total_num_msgs, total_num_sends);
731
+ }
732
+
425733 return 0;
426734 }