Blob Blame History Raw
This patch adds the --direct-io option, which opens files with O_DIRECT.

TODO: we probably need to make our I/O aligned on 512-byte boundaries.

Written by: Dag Wieers

To use this patch, run these commands for a successful build:

    patch -p1 <patches/direct-io.diff
    ./configure                         (optional if already run)
    make

based-on: d73762eea3f15f2c56bb3fa9394ad1883c25c949
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -24,6 +24,7 @@
 #include <popt.h>
 #include <zlib.h>
 
+extern int direct_io;
 extern int module_id;
 extern int local_server;
 extern int sanitize_paths;
@@ -747,6 +748,7 @@ void usage(enum logcode F)
   rprintf(F,"     --partial               keep partially transferred files\n");
   rprintf(F,"     --partial-dir=DIR       put a partially transferred file into DIR\n");
   rprintf(F,"     --delay-updates         put all updated files into place at transfer's end\n");
+  rprintf(F,"     --direct-io             don't use buffer cache for xfer file I/O\n");
   rprintf(F," -m, --prune-empty-dirs      prune empty directory chains from the file-list\n");
   rprintf(F,"     --numeric-ids           don't map uid/gid values by user/group name\n");
   rprintf(F,"     --usermap=STRING        custom username mapping\n");
@@ -982,6 +984,8 @@ static struct poptOption long_options[] = {
   {"partial-dir",      0,  POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
   {"delay-updates",    0,  POPT_ARG_VAL,    &delay_updates, 1, 0, 0 },
   {"no-delay-updates", 0,  POPT_ARG_VAL,    &delay_updates, 0, 0, 0 },
+  {"direct-io",       'n', POPT_ARG_VAL,    &direct_io, 1, 0, 0 },
+  {"no-direct-io",     0,  POPT_ARG_VAL,    &direct_io, 0, 0, 0 },
   {"prune-empty-dirs",'m', POPT_ARG_VAL,    &prune_empty_dirs, 1, 0, 0 },
   {"no-prune-empty-dirs",0,POPT_ARG_VAL,    &prune_empty_dirs, 0, 0, 0 },
   {"no-m",             0,  POPT_ARG_VAL,    &prune_empty_dirs, 0, 0, 0 },
diff --git a/rsync.yo b/rsync.yo
--- a/rsync.yo
+++ b/rsync.yo
@@ -405,6 +405,7 @@ to the detailed description below for a complete description.  verb(
      --partial               keep partially transferred files
      --partial-dir=DIR       put a partially transferred file into DIR
      --delay-updates         put all updated files into place at end
+     --direct-io             don't use buffer cache for xfer file I/O
  -m, --prune-empty-dirs      prune empty directory chains from file-list
      --numeric-ids           don't map uid/gid values by user/group name
      --usermap=STRING        custom username mapping
@@ -2435,6 +2436,14 @@ See also the "atomic-rsync" perl script in the "support" subdir for an
 update algorithm that is even more atomic (it uses bf(--link-dest) and a
 parallel hierarchy of files).
 
+dit(bf(--direct-io)) This option opens files with a direct-I/O flag that
+makes the file I/O avoid the buffer cache.  The option only affects one
+side of the transfer (unless the transfer is local).  If you want it to
+affect both sides, use the bf(--remote-option) (bf(-M)) option to specify
+it for the remote side.  For instance, this specifies it for both sides:
+
+quote(tt(  rsync -av {,-M}--direct-io /src/ host:/dest/))
+
 dit(bf(-m, --prune-empty-dirs)) This option tells the receiving rsync to get
 rid of empty directories from the file-list, including nested directories
 that have no non-directory children.  This is useful for avoiding the
diff --git a/syscall.c b/syscall.c
--- a/syscall.c
+++ b/syscall.c
@@ -43,6 +43,8 @@ extern int preallocate_files;
 extern int preserve_perms;
 extern int preserve_executability;
 
+int direct_io = 0;
+
 #ifndef S_BLKSIZE
 # if defined hpux || defined __hpux__ || defined __hpux
 #  define S_BLKSIZE 1024
@@ -81,7 +83,12 @@ int do_symlink(const char *lnk, const char *fname)
 	 * and write the lnk into it. */
 	if (am_root < 0) {
 		int ok, len = strlen(lnk);
-		int fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
+		int flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+		if (direct_io)
+			flags |= O_DIRECT;
+
+		int fd = open(fname, flags, S_IWUSR|S_IRUSR);
 		if (fd < 0)
 			return -1;
 		ok = write(fd, lnk, len) == len;
@@ -202,6 +209,9 @@ int do_open(const char *pathname, int flags, mode_t mode)
 		RETURN_ERROR_IF_RO_OR_LO;
 	}
 
+	if (direct_io)
+		flags |= O_DIRECT;
+
 	return open(pathname, flags | O_BINARY, mode);
 }
 
@@ -545,6 +555,9 @@ int do_open_nofollow(const char *pathname, int flags)
 #endif
 	}
 
+	if (direct_io)
+		flags |= O_DIRECT;
+
 #ifdef O_NOFOLLOW
 	fd = open(pathname, flags|O_NOFOLLOW);
 #else
diff -Nurp a/rsync.1 b/rsync.1
--- a/rsync.1
+++ b/rsync.1
@@ -481,6 +481,7 @@ to the detailed description below for a
      \-\-partial               keep partially transferred files
      \-\-partial\-dir=DIR       put a partially transferred file into DIR
      \-\-delay\-updates         put all updated files into place at end
+     \-\-direct\-io             don'\&t use buffer cache for xfer file I/O
  \-m, \-\-prune\-empty\-dirs      prune empty directory chains from file\-list
      \-\-numeric\-ids           don'\&t map uid/gid values by user/group name
      \-\-usermap=STRING        custom username mapping
@@ -2761,6 +2762,18 @@ See also the \(dq\&atomic\-rsync\(dq\& p
 update algorithm that is even more atomic (it uses \fB\-\-link\-dest\fP and a
 parallel hierarchy of files).
 .IP 
+.IP "\fB\-\-direct\-io\fP"
+This option opens files with a direct\-I/O flag that
+makes the file I/O avoid the buffer cache.  The option only affects one
+side of the transfer (unless the transfer is local).  If you want it to
+affect both sides, use the \fB\-\-remote\-option\fP (\fB\-M\fP) option to specify
+it for the remote side.  For instance, this specifies it for both sides:
+.IP 
+.RS 
+\f(CW  rsync \-av {,\-M}\-\-direct\-io /src/ host:/dest/\fP
+.RE
+
+.IP 
 .IP "\fB\-m, \-\-prune\-empty\-dirs\fP"
 This option tells the receiving rsync to get
 rid of empty directories from the file\-list, including nested directories