The patches for 3.0.9.
[rsync.git/patches.git] / drop-cache.diff
1 From: Tobi Oetiker tobi{at}oetiker.ch
2 Date: 2007-04-23
3
4 I am using rsync for hard-link backup. I found that there is a
5 major problem with frequent backup filling up the file system cache
6 with all the data from the files being backed up. The effect is
7 that all the other 'sensible' data in the cache gets thrown out in
8 the process. This is rather unfortunate as the performance of the
9 system becomes very bad after running rsync.
10
11 Some research showed, that
12
13   posix_fadvise64(fd, 0, 0,POSIX_FADV_DONTNEED);
14
15 would tell the OS that it should  not keep the file in cache. I
16 have written a patch for rsync that adds the
17
18   --drop-cache
19
20 option which activates posix_fadvise64.
21
22 There are some caveats though:
23
24   * When calling posix_fadvise64 while writing a file, only the
25     part of the cache will be release which has already been
26     written to disk. This means we have to call fdatasync before
27     calling posix_fadvise64 and this will unfortunately slow down
28     operations considerably. On my test system I get 240 KByte/s.
29
30     The patch has been optimized, so that the impact on large files
31     will be considerably lowered by calling posix_fadvise64 only
32     after a few megabytes have been written.
33
34   * When reading a file which has been cached *Before* rsync read
35     it, the content of the file will be released from cache never
36     the less, which may not be intended. I have unfortunately not
37     found a method for determining if a file is in cache or not
38     (ideas?)
39
40     I found that running rsync of an lvm snapshot is a good way
41     around this problem, since the snapshot data is cached
42     separately from the original. It has the additional benefit of
43     making the backups more consistent.
44
45   * I don't really know the rsync code, so it may be that the patch
46     is calling fadvise for files where this would not be necessary.
47
48   * The patch is tested only on Linux 2.6.18
49
50 If you have any input on this, please let me know.
51
52 You can get the latest edition of the patch from
53
54   http://tobi.oetiker.ch/patches/
55
56 cheers
57 tobi
58
59 Changes:
60
61  2007-04-23
62
63 * pass --drop-cache on to the remote server
64 * make test works now
65
66 To use this patch, run these commands for a successful build:
67
68     patch -p1 <patches/drop-cache.diff
69     ./configure
70     make
71
72 based-on: 40afd365cc8ca968fd16e161d24df5b8a8a520cc
73 diff --git a/checksum.c b/checksum.c
74 --- a/checksum.c
75 +++ b/checksum.c
76 @@ -24,6 +24,10 @@
77  extern int checksum_seed;
78  extern int protocol_version;
79  
80 +#ifdef HAVE_POSIX_FADVISE64
81 +#define close(fd) fadv_close(fd)
82 +#endif
83 +
84  /*
85    a simple 32 bit checksum that can be upadted from either end
86    (inspired by Mark Adler's Adler-32 checksum)
87 diff --git a/cleanup.c b/cleanup.c
88 --- a/cleanup.c
89 +++ b/cleanup.c
90 @@ -47,7 +47,13 @@ void close_all(void)
91         int fd;
92         int ret;
93         STRUCT_STAT st;
94 +#endif
95 +
96 +#ifdef HAVE_POSIX_FADVISE64
97 +       fadv_close_all();
98 +#endif
99  
100 +#ifdef SHUTDOWN_ALL_SOCKETS
101         max_fd = sysconf(_SC_OPEN_MAX) - 1;
102         for (fd = max_fd; fd >= 0; fd--) {
103                 if ((ret = do_fstat(fd, &st)) == 0) {
104 diff --git a/configure.ac b/configure.ac
105 --- a/configure.ac
106 +++ b/configure.ac
107 @@ -570,6 +570,7 @@ AC_FUNC_ALLOCA
108  AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
109      fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
110      memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
111 +    posix_fadvise64 \
112      strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
113      setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
114      seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
115 diff --git a/fileio.c b/fileio.c
116 --- a/fileio.c
117 +++ b/fileio.c
118 @@ -29,6 +29,12 @@ extern int sparse_files;
119  
120  static OFF_T sparse_seek = 0;
121  
122 +#ifdef HAVE_POSIX_FADVISE64
123 +#define close(fd) fadv_close(fd)
124 +#define read(fd,buf,len) fadv_read(fd,buf,len)
125 +#define write(fd,buf,len) fadv_write(fd,buf,len)
126 +#endif
127 +
128  int sparse_end(int f, OFF_T size)
129  {
130         int ret;
131 diff --git a/generator.c b/generator.c
132 --- a/generator.c
133 +++ b/generator.c
134 @@ -113,6 +113,10 @@ static int need_retouch_dir_times;
135  static int need_retouch_dir_perms;
136  static const char *solo_file = NULL;
137  
138 +#ifdef HAVE_POSIX_FADVISE64
139 +#define close(fd) fadv_close(fd)
140 +#endif
141 +
142  /* For calling delete_item() and delete_dir_contents(). */
143  #define DEL_NO_UID_WRITE       (1<<0) /* file/dir has our uid w/o write perm */
144  #define DEL_RECURSE            (1<<1) /* if dir, delete all contents */
145 diff --git a/options.c b/options.c
146 --- a/options.c
147 +++ b/options.c
148 @@ -60,6 +60,7 @@ int preserve_uid = 0;
149  int preserve_gid = 0;
150  int preserve_times = 0;
151  int update_only = 0;
152 +int drop_cache = 0;
153  int cvs_exclude = 0;
154  int dry_run = 0;
155  int do_xfers = 1;
156 @@ -325,6 +326,9 @@ void usage(enum logcode F)
157    rprintf(F,"     --backup-dir=DIR        make backups into hierarchy based in DIR\n");
158    rprintf(F,"     --suffix=SUFFIX         set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
159    rprintf(F," -u, --update                skip files that are newer on the receiver\n");
160 +#ifdef HAVE_POSIX_FADVISE64
161 +  rprintf(F,"     --drop-cache            tell OS to drop caching of file data\n");
162 +#endif
163    rprintf(F,"     --inplace               update destination files in-place (SEE MAN PAGE)\n");
164    rprintf(F,"     --append                append data onto shorter files\n");
165    rprintf(F,"     --append-verify         like --append, but with old data in file checksum\n");
166 @@ -533,6 +537,9 @@ static struct poptOption long_options[] = {
167    {"no-one-file-system",0, POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
168    {"no-x",             0,  POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
169    {"update",          'u', POPT_ARG_NONE,   &update_only, 0, 0, 0 },
170 +#ifdef HAVE_POSIX_FADVISE64
171 +  {"drop-cache",       0,  POPT_ARG_NONE,   &drop_cache, 0, 0, 0 },
172 +#endif
173    {"existing",         0,  POPT_ARG_NONE,   &ignore_non_existing, 0, 0, 0 },
174    {"ignore-non-existing",0,POPT_ARG_NONE,   &ignore_non_existing, 0, 0, 0 },
175    {"ignore-existing",  0,  POPT_ARG_NONE,   &ignore_existing, 0, 0, 0 },
176 @@ -1730,6 +1737,11 @@ void server_options(char **args, int *argc_p)
177         if (!am_sender)
178                 args[ac++] = "--sender";
179  
180 +#ifdef HAVE_POSIX_FADVISE64
181 +       if (drop_cache)
182 +               args[ac++] = "--drop-cache";
183 +#endif
184 +
185         x = 1;
186         argstr[0] = '-';
187  
188 diff --git a/receiver.c b/receiver.c
189 --- a/receiver.c
190 +++ b/receiver.c
191 @@ -64,6 +64,10 @@ static flist_ndx_list batch_redo_list;
192  /* We're either updating the basis file or an identical copy: */
193  static int updating_basis_or_equiv;
194  
195 +#ifdef HAVE_POSIX_FADVISE64
196 +#define close(fd) fadv_close(fd)
197 +#endif
198 +
199  #define TMPNAME_SUFFIX ".XXXXXX"
200  #define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
201  
202 diff --git a/rsync.yo b/rsync.yo
203 --- a/rsync.yo
204 +++ b/rsync.yo
205 @@ -369,6 +369,7 @@ to the detailed description below for a complete description.  verb(
206       --super                 receiver attempts super-user activities
207       --fake-super            store/recover privileged attrs using xattrs
208   -S, --sparse                handle sparse files efficiently
209 +     --drop-cache            tell OS to drop caching of file data
210   -n, --dry-run               perform a trial run with no changes made
211   -W, --whole-file            copy files whole (w/o delta-xfer algorithm)
212   -x, --one-file-system       don't cross filesystem boundaries
213 @@ -1122,6 +1123,10 @@ dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
214  up less space on the destination.  Conflicts with bf(--inplace) because it's
215  not possible to overwrite data in a sparse fashion.
216  
217 +dit(bf(--drop-cache)) Tell the OS to drop the caching of the file data.  This
218 +prevents rsync from filling up the filesystem cache.  This can sometimes help
219 +to make a system perform better by keeping non-rsync files in the disk cache.
220 +
221  dit(bf(-n, --dry-run)) This makes rsync perform a trial run that doesn't
222  make any changes (and produces mostly the same output as a real run).  It
223  is most commonly used in combination with the bf(-v, --verbose) and/or
224 diff --git a/sender.c b/sender.c
225 --- a/sender.c
226 +++ b/sender.c
227 @@ -45,6 +45,10 @@ extern int write_batch;
228  extern struct stats stats;
229  extern struct file_list *cur_flist, *first_flist, *dir_flist;
230  
231 +#ifdef HAVE_POSIX_FADVISE64
232 +#define close(fd) fadv_close(fd)
233 +#endif
234 +
235  /**
236   * @file
237   *
238 diff --git a/t_unsafe.c b/t_unsafe.c
239 --- a/t_unsafe.c
240 +++ b/t_unsafe.c
241 @@ -28,6 +28,7 @@ int am_root = 0;
242  int read_only = 0;
243  int list_only = 0;
244  int verbose = 0;
245 +int drop_cache = 0;
246  int preserve_perms = 0;
247  int preserve_executability = 0;
248  
249 diff --git a/util.c b/util.c
250 --- a/util.c
251 +++ b/util.c
252 @@ -25,6 +25,7 @@
253  
254  extern int verbose;
255  extern int module_id;
256 +extern int drop_cache;
257  extern int modify_window;
258  extern int relative_paths;
259  extern int preserve_times;
260 @@ -42,6 +43,131 @@ char curr_dir[MAXPATHLEN];
261  unsigned int curr_dir_len;
262  int curr_dir_depth; /* This is only set for a sanitizing daemon. */
263  
264 +#ifdef HAVE_POSIX_FADVISE64
265 +#define FADV_BUFFER_SIZE  1024*1024*16
266 +
267 +static struct stat fadv_fd_stat[1024];
268 +static off_t fadv_fd_pos[1024];
269 +static int fadv_fd_init = 0;
270 +static int fadv_max_fd = 0;
271 +static int fadv_close_ring_tail = 0;
272 +static int fadv_close_ring_head = 0;
273 +static int fadv_close_ring_size = 0;
274 +static int fadv_close_ring[1024];
275 +static int fadv_close_buffer_size = 0;
276 +
277 +static void fadv_fd_init_func(void)
278 +{
279 +       if (fadv_fd_init == 0) {
280 +               int i;
281 +               fadv_fd_init = 1;
282 +               if (fadv_max_fd == 0){
283 +                       fadv_max_fd = sysconf(_SC_OPEN_MAX) - 20;
284 +                       if (fadv_max_fd < 0)
285 +                               fadv_max_fd = 1;
286 +                       if (fadv_max_fd > 1000)
287 +                               fadv_max_fd = 1000;
288 +               }
289 +               for (i = 0; i < fadv_max_fd; i++) {
290 +                       fadv_fd_pos[i] = 0;
291 +                       fadv_fd_stat[i].st_dev = 0;
292 +                       fadv_fd_stat[i].st_ino = 0;
293 +               }
294 +       }
295 +}
296 +
297 +static void fadv_drop(int fd, int sync)
298 +{
299 +       struct stat sb;
300 +       int pos;
301 +
302 +       /* Trail 1 MB behind in dropping. we do this to make
303 +        * sure that the same block or stripe does not have
304 +        * to be written twice. */
305 +       if (fd > fadv_max_fd)
306 +               return;
307 +       pos = lseek(fd, 0, SEEK_CUR) - 1024*1024;
308 +       fadv_fd_init_func();
309 +       fstat(fd, &sb);
310 +       if (fadv_fd_stat[fd].st_dev == sb.st_dev
311 +        && fadv_fd_stat[fd].st_ino == sb.st_ino) {
312 +               if (fadv_fd_pos[fd] < pos - FADV_BUFFER_SIZE) {
313 +                       if (sync) {
314 +                               /* If the file is not flushed to disk before calling fadvise,
315 +                                * then the Cache will not be freed and the advise gets ignored
316 +                                * this does give a severe hit on performance. If only there
317 +                                * was a way to mark cache so that it gets release once the data
318 +                                * is written to disk. */
319 +                               fdatasync(fd);
320 +                       }
321 +                       posix_fadvise64(fd, 0, pos, POSIX_FADV_DONTNEED);
322 +                       fadv_fd_pos[fd] = pos;
323 +               }
324 +       } else {
325 +               fadv_fd_stat[fd].st_dev = sb.st_dev;
326 +               fadv_fd_stat[fd].st_ino = sb.st_ino;
327 +               fadv_fd_pos[fd] = 0;
328 +       }
329 +}
330 +
331 +ssize_t fadv_write(int fd, const void *buf, size_t count)
332 +{
333 +       int ret = write(fd, buf, count);
334 +       if (drop_cache)
335 +               fadv_drop(fd, 1);
336 +       return ret;
337 +}
338 +
339 +ssize_t fadv_read(int fd, void *buf, size_t count)
340 +{
341 +       int ret = read(fd, buf, count);
342 +       if (drop_cache)
343 +               fadv_drop(fd, 0);
344 +       return ret;
345 +}
346 +
347 +void fadv_close_all(void)
348 +{
349 +       while (fadv_close_ring_size > 0){
350 +               fdatasync(fadv_close_ring[fadv_close_ring_tail]);
351 +               posix_fadvise64(fadv_close_ring[fadv_close_ring_tail], 0, 0,POSIX_FADV_DONTNEED);
352 +               fadv_close_ring_size--;
353 +               close(fadv_close_ring[fadv_close_ring_tail]);
354 +               fadv_close_ring_tail = (fadv_close_ring_tail + 1) % fadv_max_fd;
355 +               fadv_close_buffer_size = 0;
356 +       }
357 +}
358 +
359 +int fadv_close(int fd)
360 +{
361 +       if (drop_cache) {
362 +               /* If the file is not flushed to disk before calling fadvise,
363 +                * then the Cache will not be freed and the advise gets ignored
364 +                * this does give a severe hit on performance. So instead of doing
365 +                * it right away, we save us a copy of the filehandle and do it
366 +                * some time before we are out of filehandles. This speeds
367 +                * up operation for small files massively. It is directly
368 +                * related to the number of spare file handles you have. */
369 +               int newfd = dup(fd);
370 +               int pos = lseek(fd, 0, SEEK_CUR);
371 +               fadv_fd_init_func();
372 +               fadv_close_buffer_size += pos - fadv_fd_pos[fd];
373 +               fadv_close_ring[fadv_close_ring_head] = newfd;
374 +               fadv_close_ring_head = (fadv_close_ring_head + 1) % fadv_max_fd;
375 +               fadv_close_ring_size ++;
376 +               if (fadv_close_ring_size == fadv_max_fd || fadv_close_buffer_size > 1024*1024 ){
377 +                       /* it seems fastest to drop things 'in groups' */
378 +                       fadv_close_all();
379 +               }
380 +       }
381 +       return close(fd);
382 +}
383 +
384 +#define close(fd) fadv_close(fd)
385 +#define read(fd,buf,len) fadv_read(fd,buf,len)
386 +#define write(fd,buf,len) fadv_write(fd,buf,len)
387 +#endif
388 +
389  /* Set a fd into nonblocking mode. */
390  void set_nonblocking(int fd)
391  {