Skip to content

Commit 4ffe24d

Browse files
authored
Merge pull request #1925 from gegles/gsro
Add UDP GSO/GRO support (Linux) and --gsro switch
2 parents 6b76500 + 6d26be3 commit 4ffe24d

12 files changed

Lines changed: 616 additions & 118 deletions

configure

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17399,6 +17399,78 @@ printf "%s\n" "#define HAVE_IPPROTO_MPTCP 1" >>confdefs.h
1739917399

1740017400
fi
1740117401

17402+
# Check for UDP_SEGMENT sockopt
17403+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UDP_SEGMENT socket option" >&5
17404+
printf %s "checking UDP_SEGMENT socket option... " >&6; }
17405+
if test ${iperf3_cv_header_udp_segment+y}
17406+
then :
17407+
printf %s "(cached) " >&6
17408+
else case e in #(
17409+
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
17410+
/* end confdefs.h. */
17411+
#include <linux/udp.h>
17412+
int
17413+
main (void)
17414+
{
17415+
int foo = UDP_SEGMENT;
17416+
;
17417+
return 0;
17418+
}
17419+
_ACEOF
17420+
if ac_fn_c_try_compile "$LINENO"
17421+
then :
17422+
iperf3_cv_header_udp_segment=yes
17423+
else case e in #(
17424+
e) iperf3_cv_header_udp_segment=no ;;
17425+
esac
17426+
fi
17427+
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
17428+
esac
17429+
fi
17430+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_udp_segment" >&5
17431+
printf "%s\n" "$iperf3_cv_header_udp_segment" >&6; }
17432+
if test "x$iperf3_cv_header_udp_segment" = "xyes"; then
17433+
17434+
printf "%s\n" "#define HAVE_UDP_SEGMENT 1" >>confdefs.h
17435+
17436+
fi
17437+
17438+
# Check for UDP_GRO sockopt
17439+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UDP_GRO socket option" >&5
17440+
printf %s "checking UDP_GRO socket option... " >&6; }
17441+
if test ${iperf3_cv_header_udp_gro+y}
17442+
then :
17443+
printf %s "(cached) " >&6
17444+
else case e in #(
17445+
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
17446+
/* end confdefs.h. */
17447+
#include <linux/udp.h>
17448+
int
17449+
main (void)
17450+
{
17451+
int foo = UDP_GRO;
17452+
;
17453+
return 0;
17454+
}
17455+
_ACEOF
17456+
if ac_fn_c_try_compile "$LINENO"
17457+
then :
17458+
iperf3_cv_header_udp_gro=yes
17459+
else case e in #(
17460+
e) iperf3_cv_header_udp_gro=no ;;
17461+
esac
17462+
fi
17463+
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
17464+
esac
17465+
fi
17466+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_udp_gro" >&5
17467+
printf "%s\n" "$iperf3_cv_header_udp_gro" >&6; }
17468+
if test "x$iperf3_cv_header_udp_gro" = "xyes"; then
17469+
17470+
printf "%s\n" "#define HAVE_UDP_GRO 1" >>confdefs.h
17471+
17472+
fi
17473+
1740217474
# Check if we need -lrt for clock_gettime
1740317475
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
1740417476
printf %s "checking for library containing clock_gettime... " >&6; }

configure.ac

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,30 @@ if test "x$iperf3_cv_header_ipproto_mptcp" = "xyes"; then
375375
AC_DEFINE([HAVE_IPPROTO_MPTCP], [1], [Have MPTCP protocol.])
376376
fi
377377

378+
# Check for UDP_SEGMENT sockopt
379+
AC_CACHE_CHECK([UDP_SEGMENT socket option],
380+
[iperf3_cv_header_udp_segment],
381+
AC_COMPILE_IFELSE(
382+
[AC_LANG_PROGRAM([[#include <linux/udp.h>]],
383+
[[int foo = UDP_SEGMENT;]])],
384+
iperf3_cv_header_udp_segment=yes,
385+
iperf3_cv_header_udp_segment=no))
386+
if test "x$iperf3_cv_header_udp_segment" = "xyes"; then
387+
AC_DEFINE([HAVE_UDP_SEGMENT], [1], [Have UDP_SEGMENT sockopt.])
388+
fi
389+
390+
# Check for UDP_GRO sockopt
391+
AC_CACHE_CHECK([UDP_GRO socket option],
392+
[iperf3_cv_header_udp_gro],
393+
AC_COMPILE_IFELSE(
394+
[AC_LANG_PROGRAM([[#include <linux/udp.h>]],
395+
[[int foo = UDP_GRO;]])],
396+
iperf3_cv_header_udp_gro=yes,
397+
iperf3_cv_header_udp_gro=no))
398+
if test "x$iperf3_cv_header_udp_gro" = "xyes"; then
399+
AC_DEFINE([HAVE_UDP_GRO], [1], [Have UDP_GRO sockopt.])
400+
fi
401+
378402
# Check if we need -lrt for clock_gettime
379403
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
380404
# Check for clock_gettime support

src/iperf.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ struct iperf_settings
191191
int cntl_ka_keepidle; /* Control TCP connection Keepalive idle time (TCP_KEEPIDLE) */
192192
int cntl_ka_interval; /* Control TCP connection Keepalive interval between retries (TCP_KEEPINTV) */
193193
int cntl_ka_count; /* Control TCP connection Keepalive number of retries (TCP_KEEPCNT) */
194+
/* GSO/GRO fields always present to allow client-server negotiation regardless of local support */
195+
int gso;
196+
int gso_dg_size;
197+
int gso_bf_size;
198+
int gro;
199+
int gro_bf_size;
194200
};
195201

196202
struct iperf_test;
@@ -486,4 +492,7 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error
486492
/* In Reverse mode, maximum number of packets to wait for "accept" response - to handle out of order packets */
487493
#define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2
488494

495+
#define GSO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
496+
#define GRO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
497+
489498
#endif /* !__IPERF_H */

src/iperf3.1

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,17 @@ or high-bitrate UDP tests. Both client and server need to be running
514514
at least version 3.1 for this option to work. It may become the
515515
default behavior at some point in the future.
516516
.TP
517+
.BR --gsro
518+
Enable UDP Generic Segmentation Offload (GSO) on the sender and
519+
Generic Receive Offload (GRO) on the receiver, where supported by
520+
the operating system and network hardware (currently Linux only).
521+
GSO allows the network stack to aggregate multiple UDP datagrams
522+
into larger packets, improving throughput and reducing CPU overhead.
523+
This is a client-only option; the client communicates the GSO/GRO
524+
settings to the server automatically.
525+
This feature is disabled by default and must be explicitly enabled
526+
with this flag.
527+
.TP
517528
.BR --repeating-payload
518529
Use repeating pattern in payload, instead of random bytes.
519530
The same payload is used in iperf2 (ASCII '0..9' repeating).

src/iperf_api.c

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
11891189
#if defined(HAVE_IPPROTO_MPTCP)
11901190
{"mptcp", no_argument, NULL, 'm'},
11911191
#endif
1192+
{"gsro", no_argument, NULL, OPT_GSRO},
11921193
{"debug", optional_argument, NULL, 'd'},
11931194
{"help", no_argument, NULL, 'h'},
11941195
{NULL, 0, NULL, 0}
@@ -1212,6 +1213,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
12121213

12131214
blksize = 0;
12141215
server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag =0;
1216+
int gsro_flag = 0;
12151217
#if defined(HAVE_SSL)
12161218
char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL;
12171219
FILE *ptr_file;
@@ -1790,6 +1792,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
17901792
test->mptcp = 1;
17911793
break;
17921794
#endif
1795+
case OPT_GSRO:
1796+
/* Enable GSO/GRO which is disabled by default */
1797+
/* Flag is available regardless of local support to allow client to request server to use it */
1798+
gsro_flag = 1;
1799+
test->settings->gso = 1;
1800+
test->settings->gro = 1;
1801+
break;
17931802
case 'h':
17941803
usage_long(stdout);
17951804
exit(0);
@@ -1809,6 +1818,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
18091818
i_errno = IECLIENTONLY;
18101819
return -1;
18111820
}
1821+
if (test->role == 's' && gsro_flag) {
1822+
i_errno = IECLIENTONLY;
1823+
return -1;
1824+
}
1825+
1826+
/* Show platform support warnings only after confirming we're in client mode */
1827+
if (gsro_flag) {
1828+
#if !defined(HAVE_UDP_SEGMENT) && !defined(HAVE_UDP_GRO)
1829+
warning("--gsro requested but UDP GSO/GRO not supported on this client; will only be enabled on server if supported");
1830+
#elif !defined(HAVE_UDP_SEGMENT)
1831+
warning("--gsro requested but UDP GSO not supported on this client; will be enabled on server if supported");
1832+
#elif !defined(HAVE_UDP_GRO)
1833+
warning("--gsro requested but UDP GRO not supported on this client; will be enabled on server if supported");
1834+
#endif
1835+
}
18121836

18131837
#if defined(HAVE_SSL)
18141838

@@ -1914,6 +1938,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
19141938
i_errno = IEUDPBLOCKSIZE;
19151939
return -1;
19161940
}
1941+
1942+
if (test->protocol->id == Pudp && test->settings->gso) {
1943+
test->settings->gso_dg_size = blksize;
1944+
/* use the multiple of datagram size for the best efficiency. */
1945+
if (test->settings->gso_dg_size > 0) {
1946+
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
1947+
} else {
1948+
/* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
1949+
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
1950+
}
1951+
}
1952+
19171953
test->settings->blksize = blksize;
19181954

19191955
if (!rate_flag)
@@ -2457,6 +2493,16 @@ send_parameters(struct iperf_test *test)
24572493
cJSON_AddNumberToObject(j, "pacing_timer", test->settings->pacing_timer);
24582494
if (test->settings->burst)
24592495
cJSON_AddNumberToObject(j, "burst", test->settings->burst);
2496+
2497+
/* Send UDP GSO/GRO settings from client to server */
2498+
/* Always send these fields to allow server to use GSO/GRO even if client doesn't support it */
2499+
if (test->protocol->id == Pudp) {
2500+
cJSON_AddNumberToObject(j, "gso", test->settings->gso);
2501+
cJSON_AddNumberToObject(j, "gso_dg_size", test->settings->gso_dg_size);
2502+
cJSON_AddNumberToObject(j, "gso_bf_size", test->settings->gso_bf_size);
2503+
cJSON_AddNumberToObject(j, "gro", test->settings->gro);
2504+
cJSON_AddNumberToObject(j, "gro_bf_size", test->settings->gro_bf_size);
2505+
}
24602506
if (test->settings->tos)
24612507
cJSON_AddNumberToObject(j, "TOS", test->settings->tos);
24622508
if (test->settings->flowlabel)
@@ -2580,6 +2626,31 @@ get_parameters(struct iperf_test *test)
25802626
test->settings->socket_bufsize = j_p->valueint;
25812627
if ((j_p = iperf_cJSON_GetObjectItemType(j, "len", cJSON_Number)) != NULL)
25822628
test->settings->blksize = j_p->valueint;
2629+
2630+
/* Accept UDP GSO/GRO settings provided by the client */
2631+
/* Always accept these fields to allow server to use GSO/GRO based on its own support */
2632+
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso", cJSON_Number)) != NULL)
2633+
test->settings->gso = j_p->valueint;
2634+
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_dg_size", cJSON_Number)) != NULL)
2635+
test->settings->gso_dg_size = j_p->valueint;
2636+
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_bf_size", cJSON_Number)) != NULL)
2637+
test->settings->gso_bf_size = j_p->valueint;
2638+
2639+
/* Backward-compatibility: If client didn't send GSO params, derive from blksize. */
2640+
if (test->protocol->id == Pudp && test->settings->gso == 1 && test->settings->gso_dg_size == 0) {
2641+
test->settings->gso_dg_size = test->settings->blksize;
2642+
if (test->settings->gso_dg_size > 0) {
2643+
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
2644+
} else {
2645+
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
2646+
}
2647+
}
2648+
2649+
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro", cJSON_Number)) != NULL)
2650+
test->settings->gro = j_p->valueint;
2651+
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro_bf_size", cJSON_Number)) != NULL)
2652+
test->settings->gro_bf_size = j_p->valueint;
2653+
25832654
if ((j_p = iperf_cJSON_GetObjectItemType(j, "bandwidth", cJSON_Number)) != NULL)
25842655
test->settings->rate = j_p->valueint;
25852656
if ((j_p = iperf_cJSON_GetObjectItemType(j, "fqrate", cJSON_Number)) != NULL)
@@ -3239,6 +3310,12 @@ iperf_defaults(struct iperf_test *testp)
32393310
testp->settings->fqrate = 0;
32403311
testp->settings->pacing_timer = DEFAULT_PACING_TIMER;
32413312
testp->settings->burst = 0;
3313+
/* Always initialize GSO/GRO fields to allow client-server negotiation */
3314+
testp->settings->gso = 0; /* Disable GSO by default, enabled via --gsro */
3315+
testp->settings->gso_dg_size = 0;
3316+
testp->settings->gso_bf_size = GSO_BF_MAX_SIZE;
3317+
testp->settings->gro = 0; /* Disable GRO by default, enabled via --gsro */
3318+
testp->settings->gro_bf_size = GRO_BF_MAX_SIZE;
32423319
testp->settings->mss = 0;
32433320
testp->settings->bytes = 0;
32443321
testp->settings->blocks = 0;
@@ -3552,6 +3629,10 @@ iperf_reset_test(struct iperf_test *test)
35523629
test->settings->burst = 0;
35533630
test->settings->mss = 0;
35543631
test->settings->tos = 0;
3632+
/* Always initialize GSO/GRO fields */
3633+
test->settings->gso_dg_size = 0;
3634+
test->settings->gso_bf_size = GSO_BF_MAX_SIZE;
3635+
test->settings->gro_bf_size = GRO_BF_MAX_SIZE;
35553636
test->settings->dont_fragment = 0;
35563637
test->zerocopy = 0;
35573638
test->settings->skip_rx_copy = 0;
@@ -4716,6 +4797,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
47164797
{
47174798
struct iperf_stream *sp;
47184799
int ret = 0;
4800+
int size;
47194801

47204802
char template[1024];
47214803
if (test->tmp_template) {
@@ -4774,13 +4856,20 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
47744856
free(sp);
47754857
return NULL;
47764858
}
4777-
if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) {
4859+
size = test->settings->blksize;
4860+
if (test->protocol->id == Pudp && test->settings->gso && (size < test->settings->gso_bf_size))
4861+
size = test->settings->gso_bf_size;
4862+
if (test->protocol->id == Pudp && test->settings->gro && (size < test->settings->gro_bf_size))
4863+
size = test->settings->gro_bf_size;
4864+
if (sp->test->debug)
4865+
printf("Buffer %d bytes\n", size);
4866+
if (ftruncate(sp->buffer_fd, size) < 0) {
47784867
i_errno = IECREATESTREAM;
47794868
free(sp->result);
47804869
free(sp);
47814870
return NULL;
47824871
}
4783-
sp->buffer = (char *) mmap(NULL, test->settings->blksize, PROT_READ|PROT_WRITE, MAP_SHARED, sp->buffer_fd, 0);
4872+
sp->buffer = (char *) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, sp->buffer_fd, 0);
47844873
if (sp->buffer == MAP_FAILED) {
47854874
i_errno = IECREATESTREAM;
47864875
free(sp->result);

src/iperf_api.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
106106
#define OPT_SKIP_RX_COPY 32
107107
#define OPT_JSON_STREAM_FULL_OUTPUT 33
108108
#define OPT_SERVER_MAX_DURATION 34
109+
#define OPT_GSRO 35
109110

110111
/* states */
111112
#define TEST_START 1

src/iperf_client_api.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,17 @@ iperf_connect(struct iperf_test *test)
516516
printf("Setting UDP block size to %d\n", test->settings->blksize);
517517
}
518518
}
519+
/* Initialize GSO parameters when --gsro is used */
520+
if (test->settings->gso) {
521+
test->settings->gso_dg_size = test->settings->blksize;
522+
/* use the multiple of datagram size for the best efficiency. */
523+
if (test->settings->gso_dg_size > 0) {
524+
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
525+
} else {
526+
/* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
527+
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
528+
}
529+
}
519530

520531
/*
521532
* Regardless of whether explicitly or implicitly set, if the

src/iperf_config.h.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@
132132
/* Have TCP_USER_TIMEOUT sockopt. */
133133
#undef HAVE_TCP_USER_TIMEOUT
134134

135+
/* Have UDP_GRO sockopt. */
136+
#undef HAVE_UDP_GRO
137+
138+
/* Have UDP_SEGMENT sockopt. */
139+
#undef HAVE_UDP_SEGMENT
140+
135141
/* Define to 1 if you have the <unistd.h> header file. */
136142
#undef HAVE_UNISTD_H
137143

src/iperf_locale.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
219219
" --extra-data str data string to include in client and server JSON\n"
220220
" --get-server-output get results from server\n"
221221
" --udp-counters-64bit use 64-bit counters in UDP test packets\n"
222+
" --gsro enable UDP GSO/GRO on both client and server (client-only option)\n"
222223
" --repeating-payload use repeating pattern in payload, instead of\n"
223224
" randomized payload (like in iperf2)\n"
224225
#if defined(HAVE_DONT_FRAGMENT)

0 commit comments

Comments
 (0)