Improvements to make --bwlimit work better.
[rsync/rsync.git] / io.c
diff --git a/io.c b/io.c
index 7bb3b08..6028565 100644 (file)
--- a/io.c
+++ b/io.c
@@ -1,19 +1,19 @@
 /* -*- 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.
@@ -22,7 +22,7 @@
 /**
  * @file io.c
  *
- * Socket and pipe IO utilities used in rsync.
+ * Socket and pipe I/O utilities used in rsync.
  *
  * rsync provides its own multiplexing system, which is used to send
  * stderr and stdout over a single socket.  We need this because
@@ -47,6 +47,7 @@ static time_t last_io;
 static int no_flush;
 
 extern int bwlimit;
+extern size_t bwlimit_writemax;
 extern int verbose;
 extern int io_timeout;
 extern int am_server;
@@ -63,9 +64,9 @@ const char phase_unknown[] = "unknown";
  * not very helpful.  So instead we try to make io_phase_name point to
  * something useful.
  *
- * For buffered/multiplexed IO these names will be somewhat
+ * For buffered/multiplexed I/O these names will be somewhat
  * approximate; perhaps for ease of support we would rather make the
- * buffer always flush when a single application-level IO finishes.
+ * buffer always flush when a single application-level I/O finishes.
  *
  * @todo Perhaps we want some simple stack functionality, but there's
  * no need to overdo it.
@@ -137,7 +138,7 @@ static void check_timeout(void)
 
        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);
@@ -146,7 +147,7 @@ static void check_timeout(void)
 
 /** Setup the fd used to receive MSG_* messages.  Only needed when
  * we're the generator because the sender and receiver both use the
- * multiplexed IO setup. */
+ * multiplexed I/O setup. */
 void set_msg_fd_in(int fd)
 {
        msg_fd_in = fd;
@@ -154,7 +155,7 @@ void set_msg_fd_in(int fd)
 
 /** Setup the fd used to send our MSG_* messages.  Only needed when
  * we're the receiver because the generator and the sender both use
- * the multiplexed IO setup. */
+ * the multiplexed I/O setup. */
 void set_msg_fd_out(int fd)
 {
        msg_fd_out = fd;
@@ -208,13 +209,17 @@ static void read_msg_fd(void)
 
        switch (tag) {
        case MSG_DONE:
-               if (len != 0)
+               if (len != 0) {
+                       rprintf(FERROR, "invalid message %d:%d\n", tag, len);
                        exit_cleanup(RERR_STREAMIO);
+               }
                redo_list_add(-1);
                break;
        case MSG_REDO:
-               if (len != 4)
+               if (len != 4) {
+                       rprintf(FERROR, "invalid message %d:%d\n", tag, len);
                        exit_cleanup(RERR_STREAMIO);
+               }
                read_loop(fd, buf, 4);
                redo_list_add(IVAL(buf,0));
                break;
@@ -231,6 +236,7 @@ static void read_msg_fd(void)
                }
                break;
        default:
+               rprintf(FERROR, "unknown message %d:%d\n", tag, len);
                exit_cleanup(RERR_STREAMIO);
        }
 
@@ -329,10 +335,9 @@ static void whine_about_eof(void)
        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);
        }
@@ -344,14 +349,13 @@ static void die_from_readerr(int err)
        /* 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);
 }
 
 
 /**
- * Read from a socket with IO timeout. return the number of bytes
+ * Read from a socket with I/O timeout. return the number of bytes
  * read. If no bytes can be read then exit, never return a number <= 0.
  *
  * TODO: If the remote shell connection fails, then current versions
@@ -363,7 +367,7 @@ static void die_from_readerr(int err)
  */
 static int read_timeout(int fd, char *buf, size_t len)
 {
-       int n, ret=0;
+       int n, ret = 0;
 
        io_flush(NORMAL_FLUSH);
 
@@ -503,8 +507,8 @@ static int read_timeout(int fd, char *buf, size_t len)
                        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);
                }
@@ -582,8 +586,8 @@ static void read_loop(int fd, char *buf, size_t len)
 /**
  * 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)
 {
@@ -666,7 +670,7 @@ 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);
@@ -736,22 +740,51 @@ unsigned char read_byte(int f)
  * 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; 
 
-       tv.tv_usec = bytes_written * 1000 / bwlimit;
-       tv.tv_sec  = tv.tv_usec / 1000000;
-       tv.tv_usec = tv.tv_usec % 1000000;
+       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;
+       }
+
+       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);
 }
 
 
@@ -780,7 +813,7 @@ static void writefd_unbuffered(int fd,char *buf,size_t len)
                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;
                }
 
@@ -809,6 +842,8 @@ static void writefd_unbuffered(int fd,char *buf,size_t len)
                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) {
@@ -824,15 +859,14 @@ static void writefd_unbuffered(int fd,char *buf,size_t len)
                                /* 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)
@@ -891,7 +925,7 @@ static void mplex_write(int fd, enum msgcode code, char *buf, size_t len)
 void io_flush(int flush_it_all)
 {
        int fd = multiplex_out_fd;
-       
+
        msg_list_push(flush_it_all);
 
        if (!io_buffer_count || no_flush)
@@ -1001,10 +1035,11 @@ void write_byte(int f,unsigned char c)
 
 
 /**
- * Read a line of up to @p maxlen characters into @p buf.  Does not
- * contain a trailing newline or carriage return.
+ * Read a line of up to @p maxlen characters into @p buf (not counting
+ * the trailing null).  Strips the (required) trailing newline and all
+ * carriage returns.
  *
- * @return 1 for success; 0 for io error or truncation.
+ * @return 1 for success; 0 for I/O error or truncation.
  **/
 int read_line(int f, char *buf, size_t maxlen)
 {
@@ -1013,27 +1048,21 @@ int read_line(int f, char *buf, size_t maxlen)
                read_buf(f, buf, 1);
                if (buf[0] == 0)
                        return 0;
-               if (buf[0] == '\n') {
-                       buf[0] = 0;
+               if (buf[0] == '\n')
                        break;
-               }
                if (buf[0] != '\r') {
                        buf++;
                        maxlen--;
                }
        }
-       if (maxlen == 0) {
-               *buf = 0;
-               return 0;
-       }
-
-       return 1;
+       *buf = '\0';
+       return maxlen > 0;
 }
 
 
 void io_printf(int fd, const char *format, ...)
 {
-       va_list ap;  
+       va_list ap;
        char buf[1024];
        int len;