fuse_lowlevel: Add max_pages support (#384)
authorscosu <mpargmann@allfex.org>
Thu, 13 Jun 2019 11:59:10 +0000 (13:59 +0200)
committerNikolaus Rath <Nikolaus@rath.org>
Thu, 13 Jun 2019 11:59:10 +0000 (12:59 +0100)
Starting with kernel version 4.20 fuse supports a new property
'max_pages' which is the maximum number of pages that can be used per
request. This can be set via an argument during initialization.
This new property allows writes to be larger than 128k.

This patch sets the property if the matching capability is set
(FUSE_MAX_PAGES). It will also set max_write to 1MiB. Filesystems have
the possibility to decrease this size by setting max_write to a smaller
size. The max_pages and bufsize fields are adjusted accordingly.

Cc: Constantine Shulyupin <const@MakeLinux.com>
Signed-off-by: Markus Pargmann <scosu@quobyte.com>
ChangeLog.rst
lib/fuse_i.h
lib/fuse_lowlevel.c

index 2885775948843b0861b8d2c5b55706591e2a2a9c..abbd3ee1434efc14968855085b54b1313f87ffb7 100644 (file)
@@ -4,6 +4,12 @@ Unreleased Changes
 * Added a new example (passthrough_hp). The functionality is similar
   to passthrough_ll, but the implementation focuses on performance and
   correctness rather than simplicity.
+* Added support for fuse kernel feature `max_pages` which allows to increase
+  the maximum number of pages that can be used per request. This feature was
+  introduced in kernel 4.20. `max_pages` is set based on the value in
+  `max_write`. By default `max_write` will be 1MiB now for kernels that support
+  `max_pages`. If you want smaller buffers or writes you have to set
+  `max_write` manually.
 
 libfuse 3.5.0 (2019-04-16)
 ==========================
index cf3555168234e10d463c29ab6d04c4997d5f4bc1..d38b630ac5fc94c52cc69db469e5349e7311a499 100644 (file)
@@ -131,3 +131,9 @@ struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *o
 int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
 
+#define FUSE_MAX_MAX_PAGES 256
+#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
+
+/* room needed in buffer to accommodate header */
+#define FUSE_BUFFER_HEADER_SIZE 0x1000
+
index c96e211045b7a3a999109025f5ed1c3ac2fcdf47..3684b8b309d3e07d89f613dffc951d1824fb7cce 100644 (file)
@@ -1909,6 +1909,14 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                        se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
                if (arg->flags & FUSE_NO_OPENDIR_SUPPORT)
                        se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
+               if (!(arg->flags & FUSE_MAX_PAGES)) {
+                       size_t max_bufsize =
+                               FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
+                               + FUSE_BUFFER_HEADER_SIZE;
+                       if (bufsize > max_bufsize) {
+                               bufsize = max_bufsize;
+                       }
+               }
        } else {
                se->conn.max_readahead = 0;
        }
@@ -1955,10 +1963,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                        bufsize);
                bufsize = FUSE_MIN_READ_BUFFER;
        }
+       se->bufsize = bufsize;
 
-       bufsize -= 4096;
-       if (bufsize < se->conn.max_write)
-               se->conn.max_write = bufsize;
+       if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE)
+               se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE;
 
        se->got_init = 1;
        if (se->op.init)
@@ -1985,6 +1993,14 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                return;
        }
 
+       if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) {
+               se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
+       }
+       if (arg->flags & FUSE_MAX_PAGES) {
+               outarg.flags |= FUSE_MAX_PAGES;
+               outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
+       }
+
        /* Always enable big writes, this is superseded
           by the max_write option */
        outarg.flags |= FUSE_BIG_WRITES;
@@ -2807,11 +2823,6 @@ restart:
        return res;
 }
 
-#define KERNEL_BUF_PAGES 32
-
-/* room needed in buffer to accommodate header */
-#define HEADER_SIZE 0x1000
-
 struct fuse_session *fuse_session_new(struct fuse_args *args,
                                      const struct fuse_lowlevel_ops *op,
                                      size_t op_size, void *userdata)
@@ -2872,7 +2883,8 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
        if (se->debug)
                fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
 
-       se->bufsize = KERNEL_BUF_PAGES * getpagesize() + HEADER_SIZE;
+       se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() +
+               FUSE_BUFFER_HEADER_SIZE;
 
        list_init_req(&se->list);
        list_init_req(&se->interrupts);