From 27bbb4e85a6ac3828ac90d334e08e199c6e3b2c0 Mon Sep 17 00:00:00 2001 From: Radharapu Rakesh Date: Wed, 12 Mar 2025 20:17:44 +0530 Subject: [PATCH] Add support for UDP GSO Signed-off-by: Radharapu Rakesh --- src/iperf.h | 2 ++ src/iperf_api.c | 15 ++++++++++--- src/iperf_locale.c | 4 ++++ src/iperf_locale.h | 1 + src/iperf_udp.c | 56 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 7ced5c80d..c51d066cb 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -350,6 +350,8 @@ struct iperf_test void (*json_callback) (struct iperf_test *, char *); /* allow user apps to receive the JSON strings,instead of writing them to the output file */ int zerocopy; /* -Z option - use sendfile */ + int udp_gso; /* -G option - use UDP_GSO feature */ + uint16_t udp_gso_size; int debug; /* -d option - enable debug */ enum debug_level debug_level; /* -d option option - level of debug messages to show */ int get_server_output; /* --get-server-output */ diff --git a/src/iperf_api.c b/src/iperf_api.c index db614adf6..42e2b7601 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1115,6 +1115,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"flowlabel", required_argument, NULL, 'L'}, #endif /* HAVE_FLOWLABEL */ {"zerocopy", no_argument, NULL, 'Z'}, + {"udp_gso", no_argument, NULL, 'G'}, {"omit", required_argument, NULL, 'O'}, {"file", required_argument, NULL, 'F'}, {"repeating-payload", no_argument, NULL, OPT_REPEATING_PAYLOAD}, @@ -1189,7 +1190,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) FILE *ptr_file; #endif /* HAVE_SSL */ - while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:mhX:", longopts, NULL)) != -1) { + while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZG:O:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { switch (flag) { case 'p': portno = atoi(optarg); @@ -1493,6 +1494,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->zerocopy = 1; client_flag = 1; break; + case 'G': + test->udp_gso = 1; + test->udp_gso_size = strtoul(optarg, NULL, 0); + printf("UDP_GSO enabled for this run\n"); + client_flag = 1; + break; case OPT_REPEATING_PAYLOAD: test->repeating_payload = 1; client_flag = 1; @@ -4184,9 +4191,11 @@ iperf_print_results(struct iperf_test *test) iperf_printf(test, report_receiver_not_available_format, sp->socket); } else { - if (sp->omitted_cnt_error > -1) { + if (test->udp_gso) + iperf_printf(test, report_bw_udp_hw_gso_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, "--NA--", " --NA-- ", report_receiver); + else if (sp->omitted_cnt_error > -1) { iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - receiver_omitted_packet_count), lost_percent, report_receiver); - } else { + } else { iperf_printf(test, report_bw_udp_format_no_omitted_error, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (receiver_packet_count - receiver_omitted_packet_count), report_receiver); } } diff --git a/src/iperf_locale.c b/src/iperf_locale.c index c0c5c7007..70a25613e 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -209,6 +209,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n" #endif /* HAVE_FLOWLABEL */ " -Z, --zerocopy use a 'zero copy' method of sending data\n" + " -G, --use udp_gso use 'udp_gso' feature for sending data\n" " -O, --omit N perform pre-test for N seconds and omit the pre-test statistics\n" " -T, --title str prefix every output line with this string\n" " --extra-data str data string to include in client and server JSON\n" @@ -399,6 +400,9 @@ const char report_bw_retrans_cwnd_format[] = const char report_bw_udp_format[] = "[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRId64 "/%" PRId64 " (%.2g%%) %s\n"; +const char report_bw_udp_hw_gso_format[] = +"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %s %s\n"; + const char report_bw_udp_format_no_omitted_error[] = "[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms Unknown/%" PRId64 " %s\n"; diff --git a/src/iperf_locale.h b/src/iperf_locale.h index bc9c96cb4..709bac9e4 100644 --- a/src/iperf_locale.h +++ b/src/iperf_locale.h @@ -78,6 +78,7 @@ extern const char report_bw_format[] ; extern const char report_bw_retrans_format[] ; extern const char report_bw_retrans_cwnd_format[] ; extern const char report_bw_udp_format[] ; +extern const char report_bw_udp_hw_gso_format[] ; extern const char report_bw_udp_format_no_omitted_error[] ; extern const char report_bw_udp_sender_format[] ; extern const char report_summary[] ; diff --git a/src/iperf_udp.c b/src/iperf_udp.c index b848a8f44..a4d203ef3 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -47,6 +47,57 @@ #include "net.h" #include "cjson.h" +#ifndef UDP_SEGMENT +#define UDP_SEGMENT 103 +#endif + +#ifndef SOL_UDP +#define SOL_UDP 17 +#endif + +/* iperf_udp_send_segment + * + * sends the UDP data segment + */ +static int iperf_udp_send_segment(struct iperf_stream *sp, char *buf) +{ + int ret = 0; + uint16_t *valp; + struct msghdr msg = {0}; + struct iovec iov = {0}; + struct cmsghdr *cmsg; + struct sockaddr_in addr4; + char control[CMSG_SPACE(sizeof(sp->test->udp_gso_size))] = {0}; + + iov.iov_base = buf; + iov.iov_len = sp->settings->blksize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + /* Construct CMSG for UDP_GSO */ + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_UDP; + cmsg->cmsg_type = UDP_SEGMENT; + cmsg->cmsg_len = CMSG_LEN(sizeof(sp->test->udp_gso_size)); + valp = (void *)CMSG_DATA(cmsg); + *valp = sp->test->udp_gso_size; + msg.msg_controllen = CMSG_SPACE(sizeof(sp->test->udp_gso_size)); + + /*set up sock address */ + addr4.sin_family = AF_INET; + addr4.sin_port = htons(sp->test->server_port); + if (inet_pton(AF_INET, sp->test->server_hostname, &(addr4.sin_addr)) != 1) + printf("ipv4 parse error: %s", sp->test->server_hostname); + msg.msg_name = (void *)&addr4; + msg.msg_namelen = sizeof(struct sockaddr_in); + + ret = sendmsg(sp->socket, &msg, 0); + + return ret; +} + /* iperf_udp_recv * * receives the data for UDP @@ -237,7 +288,10 @@ iperf_udp_send(struct iperf_stream *sp) } - r = Nwrite(sp->socket, sp->buffer, size, Pudp); + if (!sp->test->udp_gso) + r = Nwrite(sp->socket, sp->buffer, size, Pudp); + else + r = iperf_udp_send_segment(sp, sp->buffer); if (r <= 0) { --sp->packet_count; /* Don't count messages that no data was sent from them.