]> granicus.if.org Git - zfs/commitdiff
Increase Linux pipe buffer size on 'zfs receive'
authorRichard Yao <richard.yao@clusterhq.com>
Wed, 11 Mar 2015 18:24:46 +0000 (14:24 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 20 Mar 2015 17:03:34 +0000 (10:03 -0700)
I noticed when reviewing documentation that it is possible for user
space to use fctnl(fd, F_SETPIPE_SZ, (unsigned long) size) to change
the kernel pipe buffer size on Linux to increase the pipe size up to
the value specified in /proc/sys/fs/pipe-max-size. There are users using
mbuffer to improve zfs recv performance when piping over the network, so
it seems advantageous to integrate such functionality directly into the
zfs recv tool. This avoids the addition of two buffers and two copies
(one for the buffer mbuffer adds and another for the additional pipe),
so it should be more efficient.

This could have been made configurable and/or this could have changed
the value back to the original after we were done with the file
descriptor, but I do not see a strong case for doing either, so I
went with a simple implementation.

Signed-off-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #1161

lib/libzfs/libzfs_sendrecv.c

index e3572914c997e45c2a9edc4e3400356a2ca04afc..70870f766fbfd96187ee6a36311ed8ad15755891 100644 (file)
@@ -43,6 +43,7 @@
 #include <sys/mnttab.h>
 #include <sys/avl.h>
 #include <sys/debug.h>
+#include <sys/stat.h>
 #include <stddef.h>
 #include <pthread.h>
 #include <umem.h>
@@ -3297,6 +3298,48 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
        int err;
        int cleanup_fd;
        uint64_t action_handle = 0;
+       struct stat sb;
+
+       /*
+        * The only way fstat can fail is if we do not have a valid file
+        * descriptor.
+        */
+       if (fstat(infd, &sb) == -1) {
+               perror("fstat");
+               return (-2);
+       }
+
+#ifdef __linux__
+#ifndef F_SETPIPE_SZ
+#define        F_SETPIPE_SZ (F_SETLEASE + 7)
+#endif /* F_SETPIPE_SZ */
+
+#ifndef F_GETPIPE_SZ
+#define        F_GETPIPE_SZ (F_GETLEASE + 7)
+#endif /* F_GETPIPE_SZ */
+
+       /*
+        * It is not uncommon for gigabytes to be processed in zfs receive.
+        * Speculatively increase the buffer size via Linux-specific fcntl()
+        * call.
+        */
+       if (S_ISFIFO(sb.st_mode)) {
+               FILE *procf = fopen("/proc/sys/fs/pipe-max-size", "r");
+
+               if (procf != NULL) {
+                       unsigned long max_psize;
+                       long cur_psize;
+                       if (fscanf(procf, "%lu", &max_psize) > 0) {
+                               cur_psize = fcntl(infd, F_GETPIPE_SZ);
+                               if (cur_psize > 0 &&
+                                   max_psize > (unsigned long) cur_psize)
+                                       (void) fcntl(infd, F_SETPIPE_SZ,
+                                           max_psize);
+                       }
+                       fclose(procf);
+               }
+       }
+#endif /* __linux__ */
 
        cleanup_fd = open(ZFS_DEV, O_RDWR);
        VERIFY(cleanup_fd >= 0);