aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Yao <[email protected]>2015-03-11 14:24:46 -0400
committerBrian Behlendorf <[email protected]>2015-03-20 10:03:34 -0700
commit5c3f61eb498e8124858b1369096bf64b86a938e7 (patch)
tree44b88b4b2549d8e4fb1fd3fd39ab5cf5a00999fd
parentb1a3e93217e6e474e86345010469994c066cf875 (diff)
Increase Linux pipe buffer size on 'zfs receive'
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 <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Issue #1161
-rw-r--r--lib/libzfs/libzfs_sendrecv.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index e3572914c..70870f766 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -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);