From c458a115b7cbe057c8507a3f37702cf70ea10563 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Tue, 30 May 2017 14:15:28 -0700 Subject: [PATCH] Fix -F problems (#588) Attempt to fix some brokenness in -F from #301. In some work related to #125, we introduced a bug in which chunks of a file being read for the -F option were not completely sent, particularly with TCP sockets. We attempt to fix this by detecting cases in which not all data passed to a socket could be actually sent (for example due to full socket buffers) and preserving that data for future send iterations. The ending statistics in the "diskfile" JSON structure were wrong, and did not properly distinguish between sender-side and receiver-side statistics. This has been fixed (at least for the client side). Specifically mention in the manpage that "iperf -F" is not a file transfer tool. --- src/iperf.h | 1 + src/iperf3.1 | 11 ++++++---- src/iperf_api.c | 57 +++++++++++++++++++++++++++++++++++++++++-------- src/iperf_tcp.c | 3 +++ src/iperf_udp.c | 3 +++ 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index c270d73..bb951dc 100755 --- a/src/iperf.h +++ b/src/iperf.h @@ -153,6 +153,7 @@ struct iperf_stream int buffer_fd; /* data to send, file descriptor */ char *buffer; /* data to send, mmapped */ int diskfile_fd; /* file to send, file descriptor */ + int diskfile_left; /* remaining file data on disk */ /* * for udp measurements - This can be a structure outside stream, and diff --git a/src/iperf3.1 b/src/iperf3.1 index b990d2c..ad3f3cb 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -30,10 +30,13 @@ pause \fIn\fR seconds between periodic throughput reports; default is 1, use 0 to disable .TP .BR -F ", " --file " \fIname\fR" -client-side: read from the file and write to the network, instead -of using random data; -server-side: read from the network and write to the file, instead -of throwing the data away +Use a file as the source (on the sender) or sink (on the receiver) of +data, rather than just generating random data or throwing it away. +This feature is used for finding whether or not the storage subsystem +is the bottleneck for file transfers. +It does not turn iperf3 into a file transfer tool. +The length, attributes, and in some cases contents of the received +file may not match those of the original file. .TP .BR -A ", " --affinity " \fIn/n,m\fR" Set the CPU affinity, if possible (Linux and FreeBSD only). diff --git a/src/iperf_api.c b/src/iperf_api.c index 69da387..70bee3c 100755 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -2743,15 +2743,22 @@ iperf_print_results(struct iperf_test *test) if (sp->diskfile_fd >= 0) { if (fstat(sp->diskfile_fd, &sb) == 0) { /* In the odd case that it's a zero-sized file, say it was all transferred. */ - int percent = 100; + int percent_sent = 100, percent_received = 100; if (sb.st_size > 0) { - percent = (int) ( ( (double) bytes_sent / (double) sb.st_size ) * 100.0 ); + percent_sent = (int) ( ( (double) bytes_sent / (double) sb.st_size ) * 100.0 ); + percent_received = (int) ( ( (double) bytes_received / (double) sb.st_size ) * 100.0 ); } unit_snprintf(sbuf, UNIT_LEN, (double) sb.st_size, 'A'); if (test->json_output) - cJSON_AddItemToObject(json_summary_stream, "diskfile", iperf_json_printf("sent: %d size: %d percent: %d filename: %s", (int64_t) bytes_sent, (int64_t) sb.st_size, (int64_t) percent, test->diskfile_name)); + cJSON_AddItemToObject(json_summary_stream, "diskfile", iperf_json_printf("sent: %d received: %d size: %d percent_sent: %d percent_received: %d filename: %s", (int64_t) bytes_sent, (int64_t) bytes_received, (int64_t) sb.st_size, (int64_t) percent_sent, (int64_t) percent_received, test->diskfile_name)); else - iperf_printf(test, report_diskfile, ubuf, sbuf, percent, test->diskfile_name); + if (test->sender) { + iperf_printf(test, report_diskfile, ubuf, sbuf, percent_sent, test->diskfile_name); + } + else { + unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A'); + iperf_printf(test, report_diskfile, ubuf, sbuf, percent_received, test->diskfile_name); + } } } @@ -3281,12 +3288,44 @@ static int diskfile_send(struct iperf_stream *sp) { int r; + static int rtot; - r = read(sp->diskfile_fd, sp->buffer, sp->test->settings->blksize); - if (r == 0) - sp->test->done = 1; - else - r = sp->snd2(sp); + /* if needed, read enough data from the disk to fill up the buffer */ + if (sp->diskfile_left < sp->test->settings->blksize && !sp->test->done) { + r = read(sp->diskfile_fd, sp->buffer, sp->test->settings->blksize - + sp->diskfile_left); + rtot += r; + if (sp->test->debug) { + printf("read %d bytes from file, %d total\n", r, rtot); + if (r != sp->test->settings->blksize - sp->diskfile_left) + printf("possible eof\n"); + } + /* If there's no data left in the file or in the buffer, we're done */ + if (r == 0 && sp->diskfile_left == 0) { + sp->test->done = 1; + if (sp->test->debug) + printf("done\n"); + } + } + + r = sp->snd2(sp); + if (r < 0) { + return r; + } + /* + * Compute how much data is in the buffer but didn't get sent. + * If there are bytes that got left behind, slide them to the + * front of the buffer so they can hopefully go out on the next + * pass. + */ + sp->diskfile_left = sp->test->settings->blksize - r; + if (sp->diskfile_left && sp->diskfile_left < sp->test->settings->blksize) { + memcpy(sp->buffer, + sp->buffer + (sp->test->settings->blksize - sp->diskfile_left), + sp->diskfile_left); + if (sp->test->debug) + printf("Shifting %d bytes by %d\n", sp->diskfile_left, (sp->test->settings->blksize - sp->diskfile_left)); + } return r; } diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index 3353030..2c1a7dc 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -89,6 +89,9 @@ iperf_tcp_send(struct iperf_stream *sp) sp->result->bytes_sent += r; sp->result->bytes_sent_this_interval += r; + if (sp->test->debug) + printf("sent %d bytes of %d, total %lu\n", r, sp->settings->blksize, sp->result->bytes_sent); + return r; } diff --git a/src/iperf_udp.c b/src/iperf_udp.c index e006b09..70b62a2 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -187,6 +187,9 @@ iperf_udp_send(struct iperf_stream *sp) sp->result->bytes_sent += r; sp->result->bytes_sent_this_interval += r; + if (sp->test->debug) + printf("sent %d bytes of %d, total %lu\n", r, sp->settings->blksize, sp->result->bytes_sent); + return r; }