From 3017a895474a6b36aa385eb27fd156f72d5360f2 Mon Sep 17 00:00:00 2001 From: Anagh Kumar Baranwal <6824881+darthShadow@users.noreply.github.com> Date: Mon, 10 Feb 2020 10:24:33 +0530 Subject: [PATCH] Added support for Max Pages, which was introduced from Linux Kernel 4.20 Signed-off-by: Anagh Kumar Baranwal <6824881+darthShadow@users.noreply.github.com> --- fuse.go | 18 +++++++++++++++++- fuse_kernel.go | 4 ++++ fuse_linux.go | 4 +++- options.go | 9 +++++++++ options_darwin.go | 4 ++++ options_freebsd.go | 4 ++++ options_linux.go | 12 ++++++++++++ 7 files changed, 53 insertions(+), 2 deletions(-) diff --git a/fuse.go b/fuse.go index b774a25c..6340df4b 100644 --- a/fuse.go +++ b/fuse.go @@ -156,7 +156,8 @@ func (e *MountpointDoesNotExistError) Error() string { // progress. func Mount(dir string, options ...MountOption) (*Conn, error) { conf := mountConfig{ - options: make(map[string]string), + options: make(map[string]string), + maxPages: 32, } for _, option := range options { if err := option(&conf); err != nil { @@ -238,10 +239,20 @@ func initMount(c *Conn, conf *mountConfig) error { MaxWrite: maxWrite, Flags: InitBigWrites | conf.initFlags, } + + setMaxPages(r, s, conf) + r.Respond(s) return nil } +func setMaxPages(request *InitRequest, response *InitResponse, conf *mountConfig) { + if (request.Flags & InitMaxPages) == InitMaxPages { + response.Flags |= InitMaxPages + response.MaxPages = conf.maxPages + } +} + // A Request represents a single FUSE request received from the kernel. // Use a type switch to determine the specific kind. // A request of unrecognized type will have concrete type *Header. @@ -1249,6 +1260,9 @@ type InitResponse struct { // Maximum size of a single write operation. // Linux enforces a minimum of 4 KiB. MaxWrite uint32 + // Maximum number of pages in a single write operation. + // Linux enforces a minimum of 32. + MaxPages uint16 } func (r *InitResponse) String() string { @@ -1264,12 +1278,14 @@ func (r *InitRequest) Respond(resp *InitResponse) { out.MaxReadahead = resp.MaxReadahead out.Flags = uint32(resp.Flags) out.MaxWrite = resp.MaxWrite + out.MaxPages = resp.MaxPages // MaxWrite larger than our receive buffer would just lead to // errors on large writes. if out.MaxWrite > maxWrite { out.MaxWrite = maxWrite } + r.respond(buf) } diff --git a/fuse_kernel.go b/fuse_kernel.go index 416d32aa..68fc123a 100644 --- a/fuse_kernel.go +++ b/fuse_kernel.go @@ -274,6 +274,8 @@ const ( InitWritebackCache InitFlags = 1 << 16 InitNoOpenSupport InitFlags = 1 << 17 + InitMaxPages InitFlags = 1 << 22 // Linux only + InitCaseSensitive InitFlags = 1 << 29 // OS X only InitVolRename InitFlags = 1 << 30 // OS X only InitXtimes InitFlags = 1 << 31 // OS X only @@ -708,6 +710,8 @@ type initOut struct { Flags uint32 Unused uint32 MaxWrite uint32 + _ uint32 // Unused, refers to TimeGran + MaxPages uint16 } type interruptIn struct { diff --git a/fuse_linux.go b/fuse_linux.go index 5fb96f9a..a1e414b1 100644 --- a/fuse_linux.go +++ b/fuse_linux.go @@ -4,4 +4,6 @@ package fuse // // Linux 4.2.0 has been observed to cap this value at 128kB // (FUSE_MAX_PAGES_PER_REQ=32, 4kB pages). -const maxWrite = 128 * 1024 +// From Linux 4.20, the cap has been increased to 1MiB +// (FUSE_MAX_PAGES_PER_REQ=256, 4kB pages). +const maxWrite = 1 * 1024 * 1024 // 1 MiB diff --git a/options.go b/options.go index f09ffd4e..4bb7483c 100644 --- a/options.go +++ b/options.go @@ -16,6 +16,7 @@ type mountConfig struct { maxReadahead uint32 initFlags InitFlags osxfuseLocations []OSXFUSEPaths + maxPages uint16 } func escapeComma(s string) string { @@ -294,3 +295,11 @@ func AllowNonEmptyMount() MountOption { return nil } } + +// EnableMaxPages enables the configuration of the maximum number of pages +// in the request & response from the kernel. +// +// Linux only. Others ignore this option. +func MaxPages(count uint16) MountOption { + return maxPages(count) +} diff --git a/options_darwin.go b/options_darwin.go index a85e64cb..f82faa58 100644 --- a/options_darwin.go +++ b/options_darwin.go @@ -38,3 +38,7 @@ func noBrowse(conf *mountConfig) error { conf.options["nobrowse"] = "" return nil } + +func maxPages(count uint16) MountOption { + return dummyOption +} diff --git a/options_freebsd.go b/options_freebsd.go index 2c956e7f..e62f9c6f 100644 --- a/options_freebsd.go +++ b/options_freebsd.go @@ -30,3 +30,7 @@ func exclCreate(conf *mountConfig) error { func noBrowse(conf *mountConfig) error { return nil } + +func maxPages(count uint16) MountOption { + return dummyOption +} diff --git a/options_linux.go b/options_linux.go index 2c925b18..510cc8c7 100644 --- a/options_linux.go +++ b/options_linux.go @@ -27,3 +27,15 @@ func exclCreate(conf *mountConfig) error { func noBrowse(conf *mountConfig) error { return nil } + +func maxPages(count uint16) MountOption { + return func(conf *mountConfig) error { + if count < 32 { + count = 32 // The minimum allowed by the kernel + } else if count > 256 { + count = 256 // The maximum allowed by the kernel + } + conf.maxPages = count + return nil + } +}