Skip to content

Commit

Permalink
Add UDP GSO/GRO support
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosfsch committed Dec 7, 2023
1 parent ecf8dd5 commit ca73fe7
Show file tree
Hide file tree
Showing 8 changed files with 438 additions and 26 deletions.
24 changes: 24 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,30 @@ if test "x$iperf3_cv_header_tcp_info_snd_wnd" = "xyes"; then
AC_DEFINE([HAVE_TCP_INFO_SND_WND], [1], [Have tcpi_snd_wnd field in tcp_info.])
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
20 changes: 20 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,15 @@ struct iperf_settings
int idle_timeout; /* server idle time timeout */
unsigned int snd_timeout; /* Timeout for sending tcp messages in active mode, in us */
struct iperf_time rcv_timeout; /* Timeout for receiving messages in active mode, in us */
#ifdef HAVE_UDP_SEGMENT
int gso;
int gso_dg_size;
int gso_bf_size;
#endif
#ifdef HAVE_UDP_GRO
int gro;
int gro_bf_size;
#endif
};

struct iperf_test;
Expand Down Expand Up @@ -473,3 +482,14 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error
#define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2

#endif /* !__IPERF_H */

#define GSO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
#define GSO_DEF 0
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
#define GRO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
#define GRO_DEF 0
#ifndef UDP_GRO
#define UDP_GRO 104
#endif
55 changes: 52 additions & 3 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"idle-timeout", required_argument, NULL, OPT_IDLE_TIMEOUT},
{"rcv-timeout", required_argument, NULL, OPT_RCV_TIMEOUT},
{"snd-timeout", required_argument, NULL, OPT_SND_TIMEOUT},
#if defined(HAVE_UDP_SEGMENT) || defined(HAVE_UDP_GRO)
{"gsro", no_argument, NULL, OPT_GSRO},
#endif
{"debug", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
Expand Down Expand Up @@ -1619,6 +1622,16 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->settings->connect_timeout = unit_atoi(optarg);
client_flag = 1;
break;
#if defined(HAVE_UDP_SEGMENT) || defined(HAVE_UDP_GRO)
case OPT_GSRO:
#ifdef HAVE_UDP_SEGMENT
test->settings->gso = 1;
#endif
#ifdef HAVE_UDP_GRO
test->settings->gro = 1;
#endif
break;
#endif
case 'h':
usage_long(stdout);
exit(0);
Expand Down Expand Up @@ -2314,8 +2327,16 @@ get_parameters(struct iperf_test *test)
iperf_set_test_bidirectional(test, 1);
if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL)
test->settings->socket_bufsize = j_p->valueint;
if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL)
if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL) {
test->settings->blksize = j_p->valueint;
#ifdef HAVE_UDP_SEGMENT
if (test->protocol->id == Pudp && test->settings->gso == 1) {
test->settings->gso_dg_size = j_p->valueint;
/* use the multiple of datagram size for the best efficiency. */
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
}
#endif
}
if ((j_p = cJSON_GetObjectItem(j, "bandwidth")) != NULL)
test->settings->rate = j_p->valueint;
if ((j_p = cJSON_GetObjectItem(j, "fqrate")) != NULL)
Expand Down Expand Up @@ -2911,6 +2932,15 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->fqrate = 0;
testp->settings->pacing_timer = DEFAULT_PACING_TIMER;
testp->settings->burst = 0;
#ifdef HAVE_UDP_SEGMENT
testp->settings->gso = GSO_DEF;
testp->settings->gso_dg_size = 0;
testp->settings->gso_bf_size = GSO_BF_MAX_SIZE;
#endif
#ifdef HAVE_UDP_GRO
testp->settings->gro = GRO_DEF;
testp->settings->gro_bf_size = GRO_BF_MAX_SIZE;
#endif
testp->settings->mss = 0;
testp->settings->bytes = 0;
testp->settings->blocks = 0;
Expand Down Expand Up @@ -3212,6 +3242,13 @@ iperf_reset_test(struct iperf_test *test)
test->settings->burst = 0;
test->settings->mss = 0;
test->settings->tos = 0;
#ifdef HAVE_UDP_SEGMENT
test->settings->gso_dg_size = 0;
test->settings->gso_bf_size = GSO_BF_MAX_SIZE;
#endif
#ifdef HAVE_UDP_GRO
test->settings->gro_bf_size = GRO_BF_MAX_SIZE;
#endif
test->settings->dont_fragment = 0;
test->zerocopy = 0;

Expand Down Expand Up @@ -4316,6 +4353,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 @@ -4374,13 +4412,24 @@ 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;
#ifdef HAVE_UDP_SEGMENT
if (test->protocol->id == Pudp && test->settings->gso && (size < test->settings->gso_bf_size))
size = test->settings->gso_bf_size;
#endif
#ifdef HAVE_UDP_GRO
if (test->protocol->id == Pudp && test->settings->gro && (size < test->settings->gro_bf_size))
size = test->settings->gro_bf_size;
#endif
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_PRIVATE, sp->buffer_fd, 0);
sp->buffer = (char *) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, 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 @@ -101,6 +101,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
#define OPT_DONT_FRAGMENT 26
#define OPT_RCV_TIMEOUT 27
#define OPT_SND_TIMEOUT 28
#define OPT_GSRO 29

/* states */
#define TEST_START 1
Expand Down
51 changes: 29 additions & 22 deletions src/iperf_client_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,29 +467,36 @@ iperf_connect(struct iperf_test *test)
* the user always has the option to override.
*/
if (test->protocol->id == Pudp) {
if (test->settings->blksize == 0) {
if (test->ctrl_sck_mss) {
test->settings->blksize = test->ctrl_sck_mss;
}
else {
test->settings->blksize = DEFAULT_UDP_BLKSIZE;
}
if (test->verbose) {
printf("Setting UDP block size to %d\n", test->settings->blksize);
}
}
if (test->settings->blksize == 0) {
if (test->ctrl_sck_mss) {
test->settings->blksize = test->ctrl_sck_mss;
}
else {
test->settings->blksize = DEFAULT_UDP_BLKSIZE;
}
if (test->verbose) {
printf("Setting UDP block size to %d\n", test->settings->blksize);
}
}
#ifdef HAVE_UDP_SEGMENT
if (test->settings->gso) {
test->settings->gso_dg_size = test->settings->blksize;
/* use the multiple of datagram size for the best efficiency. */
test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
}
#endif

/*
* Regardless of whether explicitly or implicitly set, if the
* block size is larger than the MSS, print a warning.
*/
if (test->ctrl_sck_mss > 0 &&
test->settings->blksize > test->ctrl_sck_mss) {
char str[WARN_STR_LEN];
snprintf(str, sizeof(str),
"UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss);
warning(str);
}
/*
* Regardless of whether explicitly or implicitly set, if the
* block size is larger than the MSS, print a warning.
*/
if (test->ctrl_sck_mss > 0 &&
test->settings->blksize > test->ctrl_sck_mss) {
char str[WARN_STR_LEN];
snprintf(str, sizeof(str),
"UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss);
warning(str);
}
}

return 0;
Expand Down
Loading

0 comments on commit ca73fe7

Please sign in to comment.