Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UDP GSO/GRO support #1616

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
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
64 changes: 61 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 @@ -1739,6 +1752,15 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IEUDPBLOCKSIZE;
return -1;
}

#ifdef HAVE_UDP_SEGMENT
if (test->protocol->id == Pudp && test->settings->gso) {
test->settings->gso_dg_size = 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

test->settings->blksize = blksize;

if (!rate_flag)
Expand Down Expand Up @@ -2314,8 +2336,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 +2941,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 +3251,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 +4362,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 +4421,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