From ca73fe72a479216bd91952a4e4e263985eab3f8b Mon Sep 17 00:00:00 2001 From: Marcos Schwarz Date: Thu, 7 Dec 2023 12:37:42 -0300 Subject: [PATCH 1/2] Add UDP GSO/GRO support --- configure.ac | 24 ++++++ src/iperf.h | 20 +++++ src/iperf_api.c | 55 ++++++++++++- src/iperf_api.h | 1 + src/iperf_client_api.c | 51 +++++++----- src/iperf_udp.c | 178 ++++++++++++++++++++++++++++++++++++++++- src/net.c | 129 +++++++++++++++++++++++++++++ src/net.h | 6 ++ 8 files changed, 438 insertions(+), 26 deletions(-) diff --git a/configure.ac b/configure.ac index 2594b395e..d326e0a7c 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ]], + [[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 ]], + [[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 diff --git a/src/iperf.h b/src/iperf.h index 5bbd1dc2f..c652c35d9 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -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; @@ -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 diff --git a/src/iperf_api.c b/src/iperf_api.c index 105be3564..287976bf3 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -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} @@ -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); @@ -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) @@ -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; @@ -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; @@ -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) { @@ -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); diff --git a/src/iperf_api.h b/src/iperf_api.h index 9e70d44f3..a117b8db9 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -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 diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 7ad4c939b..8f51c25e4 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -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; diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 5f69ee366..1180d7075 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -39,6 +39,7 @@ #endif #include #include +#include #include "iperf.h" #include "iperf_api.h" @@ -75,6 +76,25 @@ iperf_udp_recv(struct iperf_stream *sp) double transit = 0, d = 0; struct iperf_time sent_time, arrival_time, temp_time; +#ifdef HAVE_UDP_GRO + int tmp_r; + int dgram_sz; + int cnt = 0; + char *dgram_buf; + + if (sp->test->settings->gro) { + size = sp->settings->gro_bf_size; + r = Nread_gro(sp->socket, sp->buffer, size, Pudp, &dgram_sz); + if (dgram_sz == -1) { + /* + * For corner case where the socket configuration is + * successful but the kernel network layer doesn't provide + * GRO-format data or ancillary info. + */ + dgram_sz = sp->settings->blksize; + } + } else +#endif r = Nread(sp->socket, sp->buffer, size, Pudp); /* @@ -99,6 +119,41 @@ iperf_udp_recv(struct iperf_stream *sp) sp->result->bytes_received += r; sp->result->bytes_received_this_interval += r; + if (sp->test->debug) + printf("received %d bytes of %d, total %" PRIu64 "\n", r, size, sp->result->bytes_received); + +#ifdef HAVE_UDP_GRO + dgram_buf = sp->buffer; + tmp_r = r; + while (tmp_r > 0) { + cnt++; + if (sp->test->debug) + printf("%d (%d) remaining %d\n", cnt, dgram_sz, tmp_r); + + if (sp->test->udp_counters_64bit) { + memcpy(&sec, dgram_buf, sizeof(sec)); + memcpy(&usec, dgram_buf+4, sizeof(usec)); + memcpy(&pcount, dgram_buf+8, sizeof(pcount)); + sec = ntohl(sec); + usec = ntohl(usec); + pcount = be64toh(pcount); + sent_time.secs = sec; + sent_time.usecs = usec; + } + else { + uint32_t pc; + memcpy(&sec, dgram_buf, sizeof(sec)); + memcpy(&usec, dgram_buf+4, sizeof(usec)); + memcpy(&pc, dgram_buf+8, sizeof(pc)); + sec = ntohl(sec); + usec = ntohl(usec); + pcount = ntohl(pc); + sent_time.secs = sec; + sent_time.usecs = usec; + } + dgram_buf += dgram_sz; + tmp_r -= dgram_sz; +#else /* Dig the various counters out of the incoming UDP packet */ if (sp->test->udp_counters_64bit) { memcpy(&sec, sp->buffer, sizeof(sec)); @@ -121,6 +176,7 @@ iperf_udp_recv(struct iperf_stream *sp) sent_time.secs = sec; sent_time.usecs = usec; } +#endif /* HAVE_UDP_GRO */ if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) fprintf(stderr, "pcount %" PRIu64 " packet_count %" PRIu64 "\n", pcount, sp->packet_count); @@ -194,6 +250,9 @@ iperf_udp_recv(struct iperf_stream *sp) d = -d; sp->prev_transit = transit; sp->jitter += (d - sp->jitter) / 16.0; +#ifdef HAVE_UDP_GRO + } // while (tmp_r > 0) +#endif } else { if (sp->test->debug) @@ -215,6 +274,61 @@ iperf_udp_send(struct iperf_stream *sp) int size = sp->settings->blksize; struct iperf_time before; +#ifdef HAVE_UDP_SEGMENT + int dgram_sz; + int buf_sz; + int cnt = 0; + char *dgram_buf; + + if (sp->test->settings->gso) { + dgram_sz = sp->settings->gso_dg_size; + buf_sz = sp->settings->gso_bf_size; + } else { + dgram_sz = buf_sz = size; + } + + dgram_buf = sp->buffer; + + while (buf_sz > 0) { + cnt++; + + if (sp->test->debug) + printf("%d (%d) remaining %d\n", cnt, dgram_sz, buf_sz); + + iperf_time_now(&before); + ++sp->packet_count; + + if (sp->test->udp_counters_64bit) { + + uint32_t sec, usec; + uint64_t pcount; + + sec = htonl(before.secs); + usec = htonl(before.usecs); + pcount = htobe64(sp->packet_count); + + memcpy(dgram_buf, &sec, sizeof(sec)); + memcpy(dgram_buf+4, &usec, sizeof(usec)); + memcpy(dgram_buf+8, &pcount, sizeof(pcount)); + + } + else { + + uint32_t sec, usec, pcount; + + sec = htonl(before.secs); + usec = htonl(before.usecs); + pcount = htonl(sp->packet_count); + + memcpy(dgram_buf, &sec, sizeof(sec)); + memcpy(dgram_buf+4, &usec, sizeof(usec)); + memcpy(dgram_buf+8, &pcount, sizeof(pcount)); + + } + dgram_buf += dgram_sz; + buf_sz -= dgram_sz; + } +#else iperf_time_now(&before); ++sp->packet_count; @@ -246,7 +360,14 @@ iperf_udp_send(struct iperf_stream *sp) memcpy(sp->buffer+8, &pcount, sizeof(pcount)); } +#endif /* HAVE_UDP_SEGMENT */ +#ifdef HAVE_UDP_SEGMENT + if (sp->test->settings->gso) { + size = sp->settings->gso_bf_size; + r = Nwrite_gso(sp->socket, sp->buffer, size, Pudp, sp->test->settings->gso_dg_size); + } else +#endif r = Nwrite(sp->socket, sp->buffer, size, Pudp); if (r <= 0) { @@ -263,7 +384,7 @@ iperf_udp_send(struct iperf_stream *sp) sp->result->bytes_sent_this_interval += r; if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) - printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent); + printf("sent %d bytes of %d, total %" PRIu64 "\n", r, size, sp->result->bytes_sent); return r; } @@ -373,6 +494,42 @@ iperf_udp_buffercheck(struct iperf_test *test, int s) return rc; } +#ifdef HAVE_UDP_SEGMENT +int +iperf_udp_gso(struct iperf_test *test, int s) +{ + int rc; + int gso = test->settings->gso_dg_size; + + rc = setsockopt(s, IPPROTO_UDP, UDP_SEGMENT, (char*) &gso, sizeof(gso)); + if (rc) { + iperf_printf(test, "No GSO (%d)\n", rc); + test->settings->gso = 0; + } else + iperf_printf(test, "GSO (%d)\n", gso); + + return rc; +} +#endif + +#ifdef HAVE_UDP_GRO +int +iperf_udp_gro(struct iperf_test *test, int s) +{ + int rc; + int gro = 1; + + rc = setsockopt(s, IPPROTO_UDP, UDP_GRO, (char*) &gro, sizeof(gro)); + if (rc) { + iperf_printf(test, "No GRO (%d)\n", rc); + test->settings->gro = 0; + } else + iperf_printf(test, "GRO\n"); + + return rc; +} +#endif + /* * iperf_udp_accept * @@ -432,6 +589,15 @@ iperf_udp_accept(struct iperf_test *test) } } +#ifdef HAVE_UDP_SEGMENT + if (test->settings->gso) + iperf_udp_gso(test, s); +#endif +#ifdef HAVE_UDP_GRO + if (test->settings->gro) + iperf_udp_gro(test, s); +#endif + #if defined(HAVE_SO_MAX_PACING_RATE) /* If socket pacing is specified, try it. */ if (test->settings->fqrate) { @@ -530,6 +696,16 @@ iperf_udp_connect(struct iperf_test *test) if (rc < 0) /* error */ return rc; + +#ifdef HAVE_UDP_SEGMENT + if (test->settings->gso) + iperf_udp_gso(test, s); +#endif +#ifdef HAVE_UDP_GRO + if (test->settings->gro) + iperf_udp_gro(test, s); +#endif + /* * If the socket buffer was too small, but it was the default * size, then try explicitly setting it to something larger. diff --git a/src/net.c b/src/net.c index c82caff1b..365942bb3 100644 --- a/src/net.c +++ b/src/net.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef HAVE_SENDFILE #ifdef linux @@ -452,6 +453,61 @@ Nread(int fd, char *buf, size_t count, int prot) return count - nleft; } +#ifdef HAVE_UDP_GRO +static int recv_msg_gro(int fd, char *buf, int len, int *gso_size) +{ + char control[CMSG_SPACE(sizeof(uint16_t))] = {0}; + struct msghdr msg = {0}; + struct iovec iov = {0}; + struct cmsghdr *cmsg; + uint16_t *gsosizeptr; + int ret; + + iov.iov_base = buf; + iov.iov_len = len; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + *gso_size = -1; + ret = recvmsg(fd, &msg, MSG_DONTWAIT); + + if (ret != -1) { + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_UDP && cmsg->cmsg_type == UDP_GRO) { + + gsosizeptr = (uint16_t *) CMSG_DATA(cmsg); + *gso_size = *gsosizeptr; + break; + } + } + } + + return ret; +} + +int +Nread_gro(int fd, char *buf, size_t count, int prot, int *dgram_sz) +{ + register ssize_t r; + + r = recv_msg_gro(fd, buf, count, dgram_sz); + + if (r < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } else { + printf("\nUnexpected error (%d)\n", errno); + return NET_HARDERROR; + } + } + + return r; +} +#endif /* HAVE_UDP_GRO */ /* * N W R I T E @@ -489,6 +545,79 @@ Nwrite(int fd, const char *buf, size_t count, int prot) return count; } +#ifdef HAVE_UDP_SEGMENT +static void udp_msg_gso(struct cmsghdr *cm, uint16_t gso_size) +{ + uint16_t *valp; + + cm->cmsg_level = IPPROTO_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(gso_size)); + valp = (void *) CMSG_DATA(cm); + *valp = gso_size; +} + +static int udp_sendmsg_gso(int fd, const char *data, size_t count, uint16_t gso_size) +{ + char control[CMSG_SPACE(sizeof(gso_size))] = {0}; + struct msghdr msg = {0}; + struct iovec iov = {0}; + size_t msg_controllen; + struct cmsghdr *cmsg; + int ret; + + iov.iov_base = data; + iov.iov_len = count; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + cmsg = CMSG_FIRSTHDR(&msg); + + udp_msg_gso(cmsg, gso_size); + + msg_controllen = CMSG_SPACE(sizeof(gso_size)); + msg.msg_controllen = msg_controllen; + + ret = sendmsg(fd, &msg, 0); + + if (ret != iov.iov_len) + printf("msg: %u != %llu\n", ret, (unsigned long long) iov.iov_len); + + return ret; +} + +int +Nwrite_gso(int fd, const char *buf, size_t count, int prot, uint16_t gso_size) +{ + register ssize_t r; + + r = udp_sendmsg_gso(fd, buf, count, gso_size); + + if (r < 0) { + switch (errno) { + case EINTR: + case EAGAIN: +#if (EAGAIN != EWOULDBLOCK) + case EWOULDBLOCK: +#endif + printf("\nerrono (%d)\n", errno); + return 0; + + case ENOBUFS: + printf("\nUnexpected error ENOBUFS (%d)\n", ENOBUFS); + return NET_SOFTERROR; + + default: + printf("\nUnexpected error (%d)\n", errno); + return NET_HARDERROR; + } + } + return r; +} +#endif /* HAVE_UDP_SEGMENT */ int has_sendfile(void) diff --git a/src/net.h b/src/net.h index f0e1b4f98..e4e2dc93f 100644 --- a/src/net.h +++ b/src/net.h @@ -38,6 +38,12 @@ int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribut int setnonblocking(int fd, int nonblocking); int getsockdomain(int sock); int parse_qos(const char *tos); +#ifdef HAVE_UDP_GRO +int Nread_gro(int fd, char *buf, size_t count, int prot, int *dgram_sz); +#endif +#ifdef HAVE_UDP_SEGMENT +int Nwrite_gso(int fd, const char *buf, size_t count, int prot, uint16_t gso_size); +#endif #define NET_SOFTERROR -1 #define NET_HARDERROR -2 From 513fce12a0c316c11146774b8dba73ad5f50e3d2 Mon Sep 17 00:00:00 2001 From: Marcos Schwarz Date: Tue, 12 Dec 2023 17:40:37 -0300 Subject: [PATCH 2/2] Add UDP GSO/GRO support --- src/iperf_api.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/iperf_api.c b/src/iperf_api.c index 287976bf3..4432ac39c 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1752,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)