Skip to content

Commit 0481860

Browse files
authored
Merge pull request #1717 from davidBar-On/issue-1678-SKIP_RX_COPY-MSG_TRUNC
Add SKIP-RX-COPY support - using MSG_TRUNC socket option
2 parents 16c4ba9 + c49deb0 commit 0481860

9 files changed

Lines changed: 102 additions & 20 deletions

File tree

configure.ac

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,20 @@ if test "x$iperf3_cv_header_tcp_info_snd_wnd" = "xyes"; then
349349
AC_DEFINE([HAVE_TCP_INFO_SND_WND], [1], [Have tcpi_snd_wnd field in tcp_info.])
350350
fi
351351

352+
353+
# Check for MSG_TRUNC (mostly on Linux)
354+
AC_CACHE_CHECK([MSG_TRUNC recv option],
355+
[iperf3_cv_header_msg_trunc],
356+
AC_COMPILE_IFELSE(
357+
[AC_LANG_PROGRAM([[#include <sys/types.h>
358+
#include <sys/socket.h>
359+
#include <netinet/in.h>]],
360+
[[int foo = MSG_TRUNC;]])],
361+
iperf3_cv_header_msg_trunc=yes,
362+
iperf3_cv_header_msg_trunc=no))
363+
if test "x$iperf3_cv_header_msg_trunc" = "xyes"; then
364+
AC_DEFINE([HAVE_MSG_TRUNC], [1], [Have MSG_TRUNC recv option.])
365+
fi
352366
# Check for IPPROTO_MPTCP (Linux)
353367
AC_CACHE_CHECK([MPTCP protocol],
354368
[iperf3_cv_header_ipproto_mptcp],

src/iperf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ struct iperf_settings
180180
char *client_password;
181181
EVP_PKEY *client_rsa_pubkey;
182182
#endif // HAVE_SSL
183+
int skip_rx_copy; /* Whether to ignore received messages data, using MSG_TRUNC option */
183184
int connect_timeout; /* socket connection timeout, in ms */
184185
int idle_timeout; /* server idle time timeout */
185186
unsigned int snd_timeout; /* Timeout for sending tcp messages in active mode, in us */

src/iperf_api.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
11411141
#if defined(HAVE_DONT_FRAGMENT)
11421142
{"dont-fragment", no_argument, NULL, OPT_DONT_FRAGMENT},
11431143
#endif /* HAVE_DONT_FRAGMENT */
1144+
#if defined(HAVE_MSG_TRUNC)
1145+
{"skip-rx-copy", no_argument, NULL, OPT_SKIP_RX_COPY},
1146+
#endif /* HAVE_MSG_TRUNC */
11441147
#if defined(HAVE_SSL)
11451148
{"username", required_argument, NULL, OPT_CLIENT_USERNAME},
11461149
{"rsa-public-key-path", required_argument, NULL, OPT_CLIENT_RSA_PUBLIC_KEY},
@@ -1687,6 +1690,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
16871690
test->use_pkcs1_padding = 1;
16881691
break;
16891692
#endif /* HAVE_SSL */
1693+
#if defined(HAVE_MSG_TRUNC)
1694+
case OPT_SKIP_RX_COPY:
1695+
test->settings->skip_rx_copy = 1;
1696+
client_flag = 1;
1697+
break;
1698+
#endif /* HAVE_MSG_TRUNC */
16901699
case OPT_PACING_TIMER:
16911700
test->settings->pacing_timer = unit_atoi(optarg);
16921701
client_flag = 1;
@@ -2384,6 +2393,8 @@ send_parameters(struct iperf_test *test)
23842393
cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken);
23852394
}
23862395
#endif // HAVE_SSL
2396+
if (test->settings->skip_rx_copy)
2397+
cJSON_AddNumberToObject(j, "skip_rx_copy", test->settings->skip_rx_copy);
23872398
cJSON_AddStringToObject(j, "client_version", IPERF_VERSION);
23882399

23892400
if (test->debug) {
@@ -2494,6 +2505,8 @@ get_parameters(struct iperf_test *test)
24942505
if ((j_p = iperf_cJSON_GetObjectItemType(j, "authtoken", cJSON_String)) != NULL)
24952506
test->settings->authtoken = strdup(j_p->valuestring);
24962507
#endif //HAVE_SSL
2508+
if ((j_p = cJSON_GetObjectItem(j, "skip_rx_copy")) != NULL)
2509+
test->settings->skip_rx_copy = j_p->valueint;
24972510
if (test->mode && test->protocol->id == Ptcp && has_tcpinfo_retransmits())
24982511
test->sender_has_retransmits = 1;
24992512
if (test->settings->rate)
@@ -3108,13 +3121,15 @@ iperf_defaults(struct iperf_test *testp)
31083121
testp->settings->rcv_timeout.secs = DEFAULT_NO_MSG_RCVD_TIMEOUT / SEC_TO_mS;
31093122
testp->settings->rcv_timeout.usecs = (DEFAULT_NO_MSG_RCVD_TIMEOUT % SEC_TO_mS) * mS_TO_US;
31103123
testp->zerocopy = 0;
3124+
testp->settings->skip_rx_copy = 0;
31113125
testp->settings->cntl_ka = 0;
31123126
testp->settings->cntl_ka_keepidle = 0;
31133127
testp->settings->cntl_ka_interval = 0;
31143128
testp->settings->cntl_ka_count = 0;
31153129

31163130
testp->json_callback = NULL;
31173131

3132+
31183133
memset(testp->cookie, 0, COOKIE_SIZE);
31193134

31203135
testp->multisend = 10; /* arbitrary */
@@ -3414,6 +3429,7 @@ iperf_reset_test(struct iperf_test *test)
34143429
test->settings->tos = 0;
34153430
test->settings->dont_fragment = 0;
34163431
test->zerocopy = 0;
3432+
test->settings->skip_rx_copy = 0;
34173433

34183434
#if defined(HAVE_SSL)
34193435
if (test->settings->authtoken) {

src/iperf_api.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
101101
#define OPT_JSON_STREAM 28
102102
#define OPT_SND_TIMEOUT 29
103103
#define OPT_USE_PKCS1_PADDING 30
104+
#define OPT_SKIP_RX_COPY 32
104105
#define OPT_CNTL_KA 31
105106

106107
/* states */

src/iperf_locale.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
209209
" -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n"
210210
#endif /* HAVE_FLOWLABEL */
211211
" -Z, --zerocopy use a 'zero copy' method of sending data\n"
212+
#if defined(HAVE_MSG_TRUNC)
213+
" --skip-rx-copy ignore received messages using MSG_TRUNC option\n"
214+
#endif /* HAVE_MSG_TRUNC */
212215
" -O, --omit N perform pre-test for N seconds and omit the pre-test statistics\n"
213216
" -T, --title str prefix every output line with this string\n"
214217
" --extra-data str data string to include in client and server JSON\n"

src/iperf_tcp.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,28 @@ int
5757
iperf_tcp_recv(struct iperf_stream *sp)
5858
{
5959
int r;
60+
int sock_opt;
61+
62+
#if defined(HAVE_MSG_TRUNC)
63+
sock_opt = sp->test->settings->skip_rx_copy ? MSG_TRUNC : 0;
64+
#else
65+
sock_opt = 0;
66+
#endif /* HAVE_MSG_TRUNC */
67+
68+
r = Nrecv_no_select(sp->socket, sp->buffer, sp->settings->blksize, Ptcp, sock_opt);
6069

61-
r = Nread_no_select(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
6270

6371
if (r < 0)
6472
return r;
6573

6674
/* Only count bytes received while we're in the correct state. */
6775
if (sp->test->state == TEST_RUNNING) {
68-
sp->result->bytes_received += r;
69-
sp->result->bytes_received_this_interval += r;
76+
sp->result->bytes_received += r;
77+
sp->result->bytes_received_this_interval += r;
7078
}
7179
else {
72-
if (sp->test->debug)
73-
printf("Late receive, state = %d-%s\n", sp->test->state, state_to_text(sp->test->state));
80+
if (sp->test->debug)
81+
printf("Late receive, state = %d-%s\n", sp->test->state, state_to_text(sp->test->state));
7482
}
7583

7684
return r;
@@ -87,12 +95,12 @@ iperf_tcp_send(struct iperf_stream *sp)
8795
int r;
8896

8997
if (!sp->pending_size)
90-
sp->pending_size = sp->settings->blksize;
98+
sp->pending_size = sp->settings->blksize;
9199

92100
if (sp->test->zerocopy)
93-
r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
101+
r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
94102
else
95-
r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
103+
r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
96104

97105
if (r < 0)
98106
return r;
@@ -102,8 +110,8 @@ iperf_tcp_send(struct iperf_stream *sp)
102110
sp->result->bytes_sent_this_interval += r;
103111

104112
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
105-
printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
106-
r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
113+
printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
114+
r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
107115

108116
return r;
109117
}

src/iperf_udp.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,18 @@ iperf_udp_recv(struct iperf_stream *sp)
6161
int first_packet = 0;
6262
double transit = 0, d = 0;
6363
struct iperf_time sent_time, arrival_time, temp_time;
64-
struct iperf_test *test = sp->test;
64+
struct iperf_test *test = sp->test;
65+
int sock_opt = 0;
6566

66-
r = Nread_no_select(sp->socket, sp->buffer, size, Pudp);
67+
#if defined(HAVE_MSG_TRUNC)
68+
// UDP recv() with MSG_TRUNC reads only the size bytes, but return the length of the full packet
69+
if (sp->test->settings->skip_rx_copy) {
70+
sock_opt = MSG_TRUNC;
71+
size = sizeof(sec) + sizeof(usec) + sizeof(pcount);
72+
}
73+
#endif /* HAVE_MSG_TRUNC */
74+
75+
r = Nrecv_no_select(sp->socket, sp->buffer, size, Pudp, sock_opt);
6776

6877
/*
6978
* If we got an error in the read, or if we didn't read anything

src/net.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -365,16 +365,27 @@ netannounce(int domain, int proto, const char *local, const char *bind_dev, int
365365
return s;
366366
}
367367

368-
369368
/*******************************************************************/
370-
/* reads 'count' bytes from a socket */
369+
/* Nread - reads 'count' bytes from a socket */
371370
/********************************************************************/
372371

373372
int
374373
Nread(int fd, char *buf, size_t count, int prot)
374+
{
375+
return Nrecv(fd, buf, count, prot, 0);
376+
}
377+
378+
/*******************************************************************/
379+
/* Nrecv - reads 'count' bytes from a socket */
380+
/********************************************************************/
381+
382+
int
383+
Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt)
375384
{
376385
register ssize_t r;
377-
register size_t nleft = count;
386+
// `nleft` must be signed as it may get negative value for SKIP-RX-COPY UDP (MSG_TRUNC in sock_opt).
387+
register ssize_t nleft = count;
388+
register size_t total = 0;
378389
struct iperf_time ftimeout = { 0, 0 };
379390

380391
fd_set rfdset;
@@ -403,7 +414,11 @@ Nread(int fd, char *buf, size_t count, int prot)
403414
}
404415

405416
while (nleft > 0) {
406-
r = read(fd, buf, nleft);
417+
if (sock_opt)
418+
r = recv(fd, buf, nleft, sock_opt);
419+
else
420+
r = read(fd, buf, nleft);
421+
407422
if (r < 0) {
408423
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
409424
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
@@ -413,7 +428,8 @@ Nread(int fd, char *buf, size_t count, int prot)
413428
} else if (r == 0)
414429
break;
415430

416-
nleft -= r;
431+
total += r;
432+
nleft -= r;
417433
buf += r;
418434

419435
/*
@@ -449,20 +465,32 @@ Nread(int fd, char *buf, size_t count, int prot)
449465
}
450466
}
451467
}
452-
return count - nleft;
468+
return total;
453469
}
454470

455471
/********************************************************************/
456-
/* reads 'count' bytes from a socket - but without using select() */
472+
/* Nreads 'count' bytes from a socket - but without using select() */
457473
/********************************************************************/
458474
int
459475
Nread_no_select(int fd, char *buf, size_t count, int prot)
476+
{
477+
return Nrecv_no_select(fd, buf, count, prot, 0);
478+
}
479+
480+
/********************************************************************/
481+
/* Nrecv reads 'count' bytes from a socket - but without using select() */
482+
/********************************************************************/
483+
int
484+
Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt)
460485
{
461486
register ssize_t r;
462487
register size_t nleft = count;
463488

464489
while (nleft > 0) {
465-
r = read(fd, buf, nleft);
490+
if (sock_opt)
491+
r = recv(fd, buf, nleft, sock_opt);
492+
else
493+
r = read(fd, buf, nleft);
466494
if (r < 0) {
467495
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
468496
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)

src/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ int create_socket(int domain, int type, int proto, const char *local, const char
3232
int netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout);
3333
int netannounce(int domain, int proto, const char *local, const char *bind_dev, int port);
3434
int Nread(int fd, char *buf, size_t count, int prot);
35+
int Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt);
3536
int Nread_no_select(int fd, char *buf, size_t count, int prot);
37+
int Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt);
3638
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
3739
int has_sendfile(void);
3840
int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribute__((hot)) */;

0 commit comments

Comments
 (0)