/* -*- c-file-style: "linux" -*-
- *
- * Copyright (C) 1996-2001 by Andrew Tridgell
+ *
+ * Copyright (C) 1996-2001 by Andrew Tridgell
* Copyright (C) Paul Mackerras 1996
* Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
- *
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
static int no_flush;
extern int bwlimit;
+extern size_t bwlimit_writemax;
extern int verbose;
extern int io_timeout;
extern int am_server;
if (last_io && io_timeout && (t-last_io) >= io_timeout) {
if (!am_server && !am_daemon) {
- rprintf(FERROR,"io timeout after %d seconds - exiting\n",
+ rprintf(FERROR, "io timeout after %d seconds - exiting\n",
(int)(t-last_io));
}
exit_cleanup(RERR_TIMEOUT);
if (kludge_around_eof)
exit_cleanup(0);
else {
- rprintf(FERROR,
- "%s: connection unexpectedly closed "
+ rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed "
"(%.0f bytes read so far)\n",
- RSYNC_NAME, (double)stats.total_read);
+ (double)stats.total_read);
exit_cleanup(RERR_STREAMIO);
}
/* this prevents us trying to write errors on a dead socket */
io_multiplexing_close();
- rprintf(FERROR, "%s: read error: %s\n",
- RSYNC_NAME, strerror(err));
+ rsyserr(FERROR, err, "read error");
exit_cleanup(RERR_STREAMIO);
}
*/
static int read_timeout(int fd, char *buf, size_t len)
{
- int n, ret=0;
+ int n, ret = 0;
io_flush(NORMAL_FLUSH);
whine_about_eof();
return -1; /* doesn't return */
} else if (n < 0) {
- if (errno == EINTR || errno == EWOULDBLOCK ||
- errno == EAGAIN)
+ if (errno == EINTR || errno == EWOULDBLOCK
+ || errno == EAGAIN)
continue;
die_from_readerr(errno);
}
/**
* Read from the file descriptor handling multiplexing - return number
* of bytes read.
- *
- * Never returns <= 0.
+ *
+ * Never returns <= 0.
*/
static int read_unbuffered(int fd, char *buf, size_t len)
{
static void readfd(int fd, char *buffer, size_t N)
{
int ret;
- size_t total=0;
+ size_t total = 0;
while (total < N) {
ret = read_unbuffered(fd, buffer + total, N-total);
* use a bit less bandwidth than specified, because it doesn't make up
* for slow periods. But arguably this is a feature. In addition, we
* ought to take the time used to write the data into account.
+ *
+ * During some phases of big transfers (file FOO is uptodate) this is
+ * called with a small bytes_written every time. As the kernel has to
+ * round small waits up to guarantee that we actually wait at least the
+ * requested number of microseconds, this can become grossly inaccurate.
+ * We therefore keep track of the bytes we've written over time and only
+ * sleep when the accumulated delay is at least 1 tenth of a second.
**/
static void sleep_for_bwlimit(int bytes_written)
{
- struct timeval tv;
+ static struct timeval prior_tv;
+ static long total_written = 0;
+ struct timeval tv, start_tv;
+ long elapsed_usec, sleep_usec;
+
+#define ONE_SEC 1000000L /* # of microseconds in a second */
if (!bwlimit)
return;
- assert(bytes_written > 0);
- assert(bwlimit > 0);
+ total_written += bytes_written;
+
+ gettimeofday(&start_tv, NULL);
+ if (prior_tv.tv_sec) {
+ elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC
+ + (start_tv.tv_usec - prior_tv.tv_usec);
+ total_written -= elapsed_usec * bwlimit / (ONE_SEC/1024);
+ if (total_written < 0)
+ total_written = 0;
+ }
- tv.tv_usec = bytes_written * 1000 / bwlimit;
- tv.tv_sec = tv.tv_usec / 1000000;
- tv.tv_usec = tv.tv_usec % 1000000;
+ sleep_usec = total_written * (ONE_SEC/1024) / bwlimit;
+ if (sleep_usec < ONE_SEC / 10) {
+ prior_tv = start_tv;
+ return;
+ }
+ tv.tv_sec = sleep_usec / ONE_SEC;
+ tv.tv_usec = sleep_usec % ONE_SEC;
select(0, NULL, NULL, NULL, &tv);
+
+ gettimeofday(&prior_tv, NULL);
+ elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC
+ + (prior_tv.tv_usec - start_tv.tv_usec);
+ total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024);
}
if (msg_fd_in >= 0) {
FD_ZERO(&r_fds);
FD_SET(msg_fd_in,&r_fds);
- if (msg_fd_in > fd_count)
+ if (msg_fd_in > fd_count)
fd_count = msg_fd_in;
}
if (FD_ISSET(fd, &w_fds)) {
int ret;
size_t n = len-total;
+ if (bwlimit && n > bwlimit_writemax)
+ n = bwlimit_writemax;
ret = write(fd,buf+total,n);
if (ret < 0) {
/* Don't try to write errors back
* across the stream */
io_multiplexing_close();
- rprintf(FERROR, RSYNC_NAME
- ": writefd_unbuffered failed to write %ld bytes: phase \"%s\": %s\n",
- (long) len, io_write_phase,
- strerror(errno));
+ rsyserr(FERROR, errno,
+ "writefd_unbuffered failed to write %ld bytes: phase \"%s\"",
+ (long) len, io_write_phase);
exit_cleanup(RERR_STREAMIO);
}
sleep_for_bwlimit(ret);
-
+
total += ret;
if (io_timeout)
void io_flush(int flush_it_all)
{
int fd = multiplex_out_fd;
-
+
msg_list_push(flush_it_all);
if (!io_buffer_count || no_flush)
void io_printf(int fd, const char *format, ...)
{
- va_list ap;
+ va_list ap;
char buf[1024];
int len;