Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -17399,6 +17399,78 @@ printf "%s\n" "#define HAVE_IPPROTO_MPTCP 1" >>confdefs.h

fi

# Check for UDP_SEGMENT sockopt
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UDP_SEGMENT socket option" >&5
printf %s "checking UDP_SEGMENT socket option... " >&6; }
if test ${iperf3_cv_header_udp_segment+y}
then :
printf %s "(cached) " >&6
else case e in #(
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <linux/udp.h>
int
main (void)
{
int foo = UDP_SEGMENT;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
iperf3_cv_header_udp_segment=yes
else case e in #(
e) iperf3_cv_header_udp_segment=no ;;
esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_udp_segment" >&5
printf "%s\n" "$iperf3_cv_header_udp_segment" >&6; }
if test "x$iperf3_cv_header_udp_segment" = "xyes"; then

printf "%s\n" "#define HAVE_UDP_SEGMENT 1" >>confdefs.h

fi

# Check for UDP_GRO sockopt
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking UDP_GRO socket option" >&5
printf %s "checking UDP_GRO socket option... " >&6; }
if test ${iperf3_cv_header_udp_gro+y}
then :
printf %s "(cached) " >&6
else case e in #(
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <linux/udp.h>
int
main (void)
{
int foo = UDP_GRO;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
iperf3_cv_header_udp_gro=yes
else case e in #(
e) iperf3_cv_header_udp_gro=no ;;
esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_udp_gro" >&5
printf "%s\n" "$iperf3_cv_header_udp_gro" >&6; }
if test "x$iperf3_cv_header_udp_gro" = "xyes"; then

printf "%s\n" "#define HAVE_UDP_GRO 1" >>confdefs.h

fi

# Check if we need -lrt for clock_gettime
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
printf %s "checking for library containing clock_gettime... " >&6; }
Expand Down
24 changes: 24 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,30 @@ if test "x$iperf3_cv_header_ipproto_mptcp" = "xyes"; then
AC_DEFINE([HAVE_IPPROTO_MPTCP], [1], [Have MPTCP protocol.])
fi

# Check for UDP_SEGMENT sockopt
AC_CACHE_CHECK([UDP_SEGMENT socket option],
[iperf3_cv_header_udp_segment],
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <linux/udp.h>]],
[[int foo = UDP_SEGMENT;]])],
iperf3_cv_header_udp_segment=yes,
iperf3_cv_header_udp_segment=no))
if test "x$iperf3_cv_header_udp_segment" = "xyes"; then
AC_DEFINE([HAVE_UDP_SEGMENT], [1], [Have UDP_SEGMENT sockopt.])
fi

# Check for UDP_GRO sockopt
AC_CACHE_CHECK([UDP_GRO socket option],
[iperf3_cv_header_udp_gro],
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <linux/udp.h>]],
[[int foo = UDP_GRO;]])],
iperf3_cv_header_udp_gro=yes,
iperf3_cv_header_udp_gro=no))
if test "x$iperf3_cv_header_udp_gro" = "xyes"; then
AC_DEFINE([HAVE_UDP_GRO], [1], [Have UDP_GRO sockopt.])
fi

# Check if we need -lrt for clock_gettime
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
# Check for clock_gettime support
Expand Down
9 changes: 9 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ struct iperf_settings
int cntl_ka_keepidle; /* Control TCP connection Keepalive idle time (TCP_KEEPIDLE) */
int cntl_ka_interval; /* Control TCP connection Keepalive interval between retries (TCP_KEEPINTV) */
int cntl_ka_count; /* Control TCP connection Keepalive number of retries (TCP_KEEPCNT) */
/* GSO/GRO fields always present to allow client-server negotiation regardless of local support */
int gso;
int gso_dg_size;
int gso_bf_size;
int gro;
int gro_bf_size;
};

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

#define GSO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
#define GRO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE

#endif /* !__IPERF_H */
11 changes: 11 additions & 0 deletions src/iperf3.1
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,17 @@ or high-bitrate UDP tests. Both client and server need to be running
at least version 3.1 for this option to work. It may become the
default behavior at some point in the future.
.TP
.BR --gsro
Enable UDP Generic Segmentation Offload (GSO) on the sender and
Generic Receive Offload (GRO) on the receiver, where supported by
the operating system and network hardware (currently Linux only).
GSO allows the network stack to aggregate multiple UDP datagrams
into larger packets, improving throughput and reducing CPU overhead.
This is a client-only option; the client communicates the GSO/GRO
settings to the server automatically.
This feature is disabled by default and must be explicitly enabled
with this flag.
.TP
.BR --repeating-payload
Use repeating pattern in payload, instead of random bytes.
The same payload is used in iperf2 (ASCII '0..9' repeating).
Expand Down
93 changes: 91 additions & 2 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#if defined(HAVE_IPPROTO_MPTCP)
{"mptcp", no_argument, NULL, 'm'},
#endif
{"gsro", no_argument, NULL, OPT_GSRO},
{"debug", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
Expand All @@ -1212,6 +1213,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)

blksize = 0;
server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag =0;
int gsro_flag = 0;
#if defined(HAVE_SSL)
char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL;
FILE *ptr_file;
Expand Down Expand Up @@ -1790,6 +1792,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->mptcp = 1;
break;
#endif
case OPT_GSRO:
/* Enable GSO/GRO which is disabled by default */
/* Flag is available regardless of local support to allow client to request server to use it */
gsro_flag = 1;
test->settings->gso = 1;
test->settings->gro = 1;
break;
case 'h':
usage_long(stdout);
exit(0);
Expand All @@ -1809,6 +1818,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IECLIENTONLY;
return -1;
}
if (test->role == 's' && gsro_flag) {
i_errno = IECLIENTONLY;
return -1;
}

/* Show platform support warnings only after confirming we're in client mode */
if (gsro_flag) {
#if !defined(HAVE_UDP_SEGMENT) && !defined(HAVE_UDP_GRO)
warning("--gsro requested but UDP GSO/GRO not supported on this client; will only be enabled on server if supported");
#elif !defined(HAVE_UDP_SEGMENT)
warning("--gsro requested but UDP GSO not supported on this client; will be enabled on server if supported");
#elif !defined(HAVE_UDP_GRO)
warning("--gsro requested but UDP GRO not supported on this client; will be enabled on server if supported");
#endif
}

#if defined(HAVE_SSL)

Expand Down Expand Up @@ -1914,6 +1938,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IEUDPBLOCKSIZE;
return -1;
}

if (test->protocol->id == Pudp && test->settings->gso) {
test->settings->gso_dg_size = blksize;
/* use the multiple of datagram size for the best efficiency. */
if (test->settings->gso_dg_size > 0) {
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
} else {
/* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
}
}

test->settings->blksize = blksize;

if (!rate_flag)
Expand Down Expand Up @@ -2457,6 +2493,16 @@ send_parameters(struct iperf_test *test)
cJSON_AddNumberToObject(j, "pacing_timer", test->settings->pacing_timer);
if (test->settings->burst)
cJSON_AddNumberToObject(j, "burst", test->settings->burst);

/* Send UDP GSO/GRO settings from client to server */
/* Always send these fields to allow server to use GSO/GRO even if client doesn't support it */
if (test->protocol->id == Pudp) {
cJSON_AddNumberToObject(j, "gso", test->settings->gso);
cJSON_AddNumberToObject(j, "gso_dg_size", test->settings->gso_dg_size);
cJSON_AddNumberToObject(j, "gso_bf_size", test->settings->gso_bf_size);
cJSON_AddNumberToObject(j, "gro", test->settings->gro);
cJSON_AddNumberToObject(j, "gro_bf_size", test->settings->gro_bf_size);
}
if (test->settings->tos)
cJSON_AddNumberToObject(j, "TOS", test->settings->tos);
if (test->settings->flowlabel)
Expand Down Expand Up @@ -2580,6 +2626,31 @@ get_parameters(struct iperf_test *test)
test->settings->socket_bufsize = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "len", cJSON_Number)) != NULL)
test->settings->blksize = j_p->valueint;

/* Accept UDP GSO/GRO settings provided by the client */
/* Always accept these fields to allow server to use GSO/GRO based on its own support */
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso", cJSON_Number)) != NULL)
test->settings->gso = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_dg_size", cJSON_Number)) != NULL)
test->settings->gso_dg_size = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_bf_size", cJSON_Number)) != NULL)
test->settings->gso_bf_size = j_p->valueint;

/* Backward-compatibility: If client didn't send GSO params, derive from blksize. */
if (test->protocol->id == Pudp && test->settings->gso == 1 && test->settings->gso_dg_size == 0) {
test->settings->gso_dg_size = test->settings->blksize;
if (test->settings->gso_dg_size > 0) {
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
} else {
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
}
}

if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro", cJSON_Number)) != NULL)
test->settings->gro = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro_bf_size", cJSON_Number)) != NULL)
test->settings->gro_bf_size = j_p->valueint;

if ((j_p = iperf_cJSON_GetObjectItemType(j, "bandwidth", cJSON_Number)) != NULL)
test->settings->rate = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "fqrate", cJSON_Number)) != NULL)
Expand Down Expand Up @@ -3239,6 +3310,12 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->fqrate = 0;
testp->settings->pacing_timer = DEFAULT_PACING_TIMER;
testp->settings->burst = 0;
/* Always initialize GSO/GRO fields to allow client-server negotiation */
testp->settings->gso = 0; /* Disable GSO by default, enabled via --gsro */
testp->settings->gso_dg_size = 0;
testp->settings->gso_bf_size = GSO_BF_MAX_SIZE;
testp->settings->gro = 0; /* Disable GRO by default, enabled via --gsro */
testp->settings->gro_bf_size = GRO_BF_MAX_SIZE;
testp->settings->mss = 0;
testp->settings->bytes = 0;
testp->settings->blocks = 0;
Expand Down Expand Up @@ -3552,6 +3629,10 @@ iperf_reset_test(struct iperf_test *test)
test->settings->burst = 0;
test->settings->mss = 0;
test->settings->tos = 0;
/* Always initialize GSO/GRO fields */
test->settings->gso_dg_size = 0;
test->settings->gso_bf_size = GSO_BF_MAX_SIZE;
test->settings->gro_bf_size = GRO_BF_MAX_SIZE;
test->settings->dont_fragment = 0;
test->zerocopy = 0;
test->settings->skip_rx_copy = 0;
Expand Down Expand Up @@ -4716,6 +4797,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
{
struct iperf_stream *sp;
int ret = 0;
int size;

char template[1024];
if (test->tmp_template) {
Expand Down Expand Up @@ -4774,13 +4856,20 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
free(sp);
return NULL;
}
if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) {
size = test->settings->blksize;
if (test->protocol->id == Pudp && test->settings->gso && (size < test->settings->gso_bf_size))
size = test->settings->gso_bf_size;
if (test->protocol->id == Pudp && test->settings->gro && (size < test->settings->gro_bf_size))
size = test->settings->gro_bf_size;
if (sp->test->debug)
printf("Buffer %d bytes\n", size);
if (ftruncate(sp->buffer_fd, size) < 0) {
i_errno = IECREATESTREAM;
free(sp->result);
free(sp);
return NULL;
}
sp->buffer = (char *) mmap(NULL, test->settings->blksize, PROT_READ|PROT_WRITE, MAP_SHARED, sp->buffer_fd, 0);
sp->buffer = (char *) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, sp->buffer_fd, 0);
if (sp->buffer == MAP_FAILED) {
i_errno = IECREATESTREAM;
free(sp->result);
Expand Down
1 change: 1 addition & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
#define OPT_SKIP_RX_COPY 32
#define OPT_JSON_STREAM_FULL_OUTPUT 33
#define OPT_SERVER_MAX_DURATION 34
#define OPT_GSRO 35

/* states */
#define TEST_START 1
Expand Down
11 changes: 11 additions & 0 deletions src/iperf_client_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,17 @@ iperf_connect(struct iperf_test *test)
printf("Setting UDP block size to %d\n", test->settings->blksize);
}
}
/* Initialize GSO parameters when --gsro is used */
if (test->settings->gso) {
test->settings->gso_dg_size = test->settings->blksize;
/* use the multiple of datagram size for the best efficiency. */
if (test->settings->gso_dg_size > 0) {
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
} else {
/* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
}
}

/*
* Regardless of whether explicitly or implicitly set, if the
Expand Down
6 changes: 6 additions & 0 deletions src/iperf_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
/* Have TCP_USER_TIMEOUT sockopt. */
#undef HAVE_TCP_USER_TIMEOUT

/* Have UDP_GRO sockopt. */
#undef HAVE_UDP_GRO

/* Have UDP_SEGMENT sockopt. */
#undef HAVE_UDP_SEGMENT

/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H

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