Merge tag 'fuse-update-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/mszered...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Jan 2022 21:30:58 +0000 (13:30 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Jan 2022 21:30:58 +0000 (13:30 -0800)
Pull fuse updates from Miklos Szeredi:

 - Fix a regression introduced in 5.15

 - Extend the size of the FUSE_INIT request to accommodate for more
   flags. There's a slight possibility of a regression for obscure fuse
   servers; if this happens, then more complexity will need to be added
   to the protocol

 - Allow the DAX property to be controlled by the server on a per-inode
   basis in virtiofs

 - Allow sending security context to the server when creating a file or
   directory

* tag 'fuse-update-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  Documentation/filesystem/dax: DAX on virtiofs
  fuse: mark inode DONT_CACHE when per inode DAX hint changes
  fuse: negotiate per inode DAX in FUSE_INIT
  fuse: enable per inode DAX
  fuse: support per inode DAX in fuse protocol
  fuse: make DAX mount option a tri-state
  fuse: add fuse_should_enable_dax() helper
  fuse: Pass correct lend value to filemap_write_and_wait_range()
  fuse: send security context of inode on file
  fuse: extend init flags

Documentation/filesystems/dax.rst
fs/fuse/dax.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/fuse/virtio_fs.c
include/uapi/linux/fuse.h

index 9a1b8fd..e3b3042 100644 (file)
@@ -23,8 +23,8 @@ on it as usual.  The `DAX` code currently only supports files with a block
 size equal to your kernel's `PAGE_SIZE`, so you may need to specify a block
 size when creating the filesystem.
 
-Currently 3 filesystems support `DAX`: ext2, ext4 and xfs.  Enabling `DAX` on them
-is different.
+Currently 4 filesystems support `DAX`: ext2, ext4, xfs and virtiofs.
+Enabling `DAX` on them is different.
 
 Enabling DAX on ext2
 --------------------
@@ -168,6 +168,22 @@ if the underlying media does not support dax and/or the filesystem is
 overridden with a mount option.
 
 
+Enabling DAX on virtiofs
+----------------------------
+The semantic of DAX on virtiofs is basically equal to that on ext4 and xfs,
+except that when '-o dax=inode' is specified, virtiofs client derives the hint
+whether DAX shall be enabled or not from virtiofs server through FUSE protocol,
+rather than the persistent `FS_XFLAG_DAX` flag. That is, whether DAX shall be
+enabled or not is completely determined by virtiofs server, while virtiofs
+server itself may deploy various algorithm making this decision, e.g. depending
+on the persistent `FS_XFLAG_DAX` flag on the host.
+
+It is still supported to set or clear persistent `FS_XFLAG_DAX` flag inside
+guest, but it is not guaranteed that DAX will be enabled or disabled for
+corresponding file then. Users inside guest still need to call statx(2) and
+check the statx flag `STATX_ATTR_DAX` to see if DAX is enabled for this file.
+
+
 Implementation Tips for Block Driver Writers
 --------------------------------------------
 
index 713818d..182b24a 100644 (file)
@@ -1279,11 +1279,14 @@ out_err:
        return ret;
 }
 
-int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev)
+int fuse_dax_conn_alloc(struct fuse_conn *fc, enum fuse_dax_mode dax_mode,
+                       struct dax_device *dax_dev)
 {
        struct fuse_conn_dax *fcd;
        int err;
 
+       fc->dax_mode = dax_mode;
+
        if (!dax_dev)
                return 0;
 
@@ -1327,17 +1330,46 @@ static const struct address_space_operations fuse_dax_file_aops  = {
        .invalidatepage = noop_invalidatepage,
 };
 
-void fuse_dax_inode_init(struct inode *inode)
+static bool fuse_should_enable_dax(struct inode *inode, unsigned int flags)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
+       enum fuse_dax_mode dax_mode = fc->dax_mode;
+
+       if (dax_mode == FUSE_DAX_NEVER)
+               return false;
 
+       /*
+        * fc->dax may be NULL in 'inode' mode when filesystem device doesn't
+        * support DAX, in which case it will silently fallback to 'never' mode.
+        */
        if (!fc->dax)
+               return false;
+
+       if (dax_mode == FUSE_DAX_ALWAYS)
+               return true;
+
+       /* dax_mode is FUSE_DAX_INODE* */
+       return fc->inode_dax && (flags & FUSE_ATTR_DAX);
+}
+
+void fuse_dax_inode_init(struct inode *inode, unsigned int flags)
+{
+       if (!fuse_should_enable_dax(inode, flags))
                return;
 
        inode->i_flags |= S_DAX;
        inode->i_data.a_ops = &fuse_dax_file_aops;
 }
 
+void fuse_dax_dontcache(struct inode *inode, unsigned int flags)
+{
+       struct fuse_conn *fc = get_fuse_conn(inode);
+
+       if (fuse_is_inode_dax_mode(fc->dax_mode) &&
+           ((bool) IS_DAX(inode) != (bool) (flags & FUSE_ATTR_DAX)))
+               d_mark_dontcache(inode);
+}
+
 bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment)
 {
        if (fc->dax && (map_alignment > FUSE_DAX_SHIFT)) {
index 0654bfe..656e921 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/xattr.h>
 #include <linux/iversion.h>
 #include <linux/posix_acl.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
 {
@@ -456,6 +459,62 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        return ERR_PTR(err);
 }
 
+static int get_security_context(struct dentry *entry, umode_t mode,
+                               void **security_ctx, u32 *security_ctxlen)
+{
+       struct fuse_secctx *fctx;
+       struct fuse_secctx_header *header;
+       void *ctx = NULL, *ptr;
+       u32 ctxlen, total_len = sizeof(*header);
+       int err, nr_ctx = 0;
+       const char *name;
+       size_t namelen;
+
+       err = security_dentry_init_security(entry, mode, &entry->d_name,
+                                           &name, &ctx, &ctxlen);
+       if (err) {
+               if (err != -EOPNOTSUPP)
+                       goto out_err;
+               /* No LSM is supporting this security hook. Ignore error */
+               ctxlen = 0;
+               ctx = NULL;
+       }
+
+       if (ctxlen) {
+               nr_ctx = 1;
+               namelen = strlen(name) + 1;
+               err = -EIO;
+               if (WARN_ON(namelen > XATTR_NAME_MAX + 1 || ctxlen > S32_MAX))
+                       goto out_err;
+               total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen + ctxlen);
+       }
+
+       err = -ENOMEM;
+       header = ptr = kzalloc(total_len, GFP_KERNEL);
+       if (!ptr)
+               goto out_err;
+
+       header->nr_secctx = nr_ctx;
+       header->size = total_len;
+       ptr += sizeof(*header);
+       if (nr_ctx) {
+               fctx = ptr;
+               fctx->size = ctxlen;
+               ptr += sizeof(*fctx);
+
+               strcpy(ptr, name);
+               ptr += namelen;
+
+               memcpy(ptr, ctx, ctxlen);
+       }
+       *security_ctxlen = total_len;
+       *security_ctx = header;
+       err = 0;
+out_err:
+       kfree(ctx);
+       return err;
+}
+
 /*
  * Atomic create+open operation
  *
@@ -476,6 +535,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        struct fuse_entry_out outentry;
        struct fuse_inode *fi;
        struct fuse_file *ff;
+       void *security_ctx = NULL;
+       u32 security_ctxlen;
 
        /* Userspace expects S_IFREG in create mode */
        BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,7 +578,20 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        args.out_args[0].value = &outentry;
        args.out_args[1].size = sizeof(outopen);
        args.out_args[1].value = &outopen;
+
+       if (fm->fc->init_security) {
+               err = get_security_context(entry, mode, &security_ctx,
+                                          &security_ctxlen);
+               if (err)
+                       goto out_put_forget_req;
+
+               args.in_numargs = 3;
+               args.in_args[2].size = security_ctxlen;
+               args.in_args[2].value = security_ctx;
+       }
+
        err = fuse_simple_request(fm, &args);
+       kfree(security_ctx);
        if (err)
                goto out_free_ff;
 
@@ -620,6 +694,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
        struct dentry *d;
        int err;
        struct fuse_forget_link *forget;
+       void *security_ctx = NULL;
+       u32 security_ctxlen;
 
        if (fuse_is_bad(dir))
                return -EIO;
@@ -633,7 +709,22 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
        args->out_numargs = 1;
        args->out_args[0].size = sizeof(outarg);
        args->out_args[0].value = &outarg;
+
+       if (fm->fc->init_security && args->opcode != FUSE_LINK) {
+               err = get_security_context(entry, mode, &security_ctx,
+                                          &security_ctxlen);
+               if (err)
+                       goto out_put_forget_req;
+
+               BUG_ON(args->in_numargs != 2);
+
+               args->in_numargs = 3;
+               args->in_args[2].size = security_ctxlen;
+               args->in_args[2].value = security_ctx;
+       }
+
        err = fuse_simple_request(fm, args);
+       kfree(security_ctx);
        if (err)
                goto out_put_forget_req;
 
index 9d6c5f6..8290944 100644 (file)
@@ -2910,7 +2910,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 
 static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end)
 {
-       int err = filemap_write_and_wait_range(inode->i_mapping, start, -1);
+       int err = filemap_write_and_wait_range(inode->i_mapping, start, LLONG_MAX);
 
        if (!err)
                fuse_sync_writes(inode);
@@ -3169,7 +3169,7 @@ static const struct address_space_operations fuse_file_aops  = {
        .write_end      = fuse_write_end,
 };
 
-void fuse_init_file_inode(struct inode *inode)
+void fuse_init_file_inode(struct inode *inode, unsigned int flags)
 {
        struct fuse_inode *fi = get_fuse_inode(inode);
 
@@ -3183,5 +3183,5 @@ void fuse_init_file_inode(struct inode *inode)
        fi->writepages = RB_ROOT;
 
        if (IS_ENABLED(CONFIG_FUSE_DAX))
-               fuse_dax_inode_init(inode);
+               fuse_dax_inode_init(inode, flags);
 }
index 198637b..e8e59fb 100644 (file)
@@ -480,6 +480,18 @@ struct fuse_dev {
        struct list_head entry;
 };
 
+enum fuse_dax_mode {
+       FUSE_DAX_INODE_DEFAULT, /* default */
+       FUSE_DAX_ALWAYS,        /* "-o dax=always" */
+       FUSE_DAX_NEVER,         /* "-o dax=never" */
+       FUSE_DAX_INODE_USER,    /* "-o dax=inode" */
+};
+
+static inline bool fuse_is_inode_dax_mode(enum fuse_dax_mode mode)
+{
+       return mode == FUSE_DAX_INODE_DEFAULT || mode == FUSE_DAX_INODE_USER;
+}
+
 struct fuse_fs_context {
        int fd;
        struct file *file;
@@ -497,7 +509,7 @@ struct fuse_fs_context {
        bool no_control:1;
        bool no_force_umount:1;
        bool legacy_opts_show:1;
-       bool dax:1;
+       enum fuse_dax_mode dax_mode;
        unsigned int max_read;
        unsigned int blksize;
        const char *subtype;
@@ -765,6 +777,12 @@ struct fuse_conn {
        /* Propagate syncfs() to server */
        unsigned int sync_fs:1;
 
+       /* Initialize security xattrs when creating a new inode */
+       unsigned int init_security:1;
+
+       /* Does the filesystem support per inode DAX? */
+       unsigned int inode_dax:1;
+
        /** The number of requests waiting for completion */
        atomic_t num_waiting;
 
@@ -802,6 +820,9 @@ struct fuse_conn {
        struct list_head devices;
 
 #ifdef CONFIG_FUSE_DAX
+       /* Dax mode */
+       enum fuse_dax_mode dax_mode;
+
        /* Dax specific conn data, non-NULL if DAX is enabled */
        struct fuse_conn_dax *dax;
 #endif
@@ -1007,7 +1028,7 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
 /**
  * Initialize file operations on a regular file
  */
-void fuse_init_file_inode(struct inode *inode);
+void fuse_init_file_inode(struct inode *inode, unsigned int flags);
 
 /**
  * Initialize inode operations on regular files and special files
@@ -1269,11 +1290,13 @@ ssize_t fuse_dax_read_iter(struct kiocb *iocb, struct iov_iter *to);
 ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from);
 int fuse_dax_mmap(struct file *file, struct vm_area_struct *vma);
 int fuse_dax_break_layouts(struct inode *inode, u64 dmap_start, u64 dmap_end);
-int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev);
+int fuse_dax_conn_alloc(struct fuse_conn *fc, enum fuse_dax_mode mode,
+                       struct dax_device *dax_dev);
 void fuse_dax_conn_free(struct fuse_conn *fc);
 bool fuse_dax_inode_alloc(struct super_block *sb, struct fuse_inode *fi);
-void fuse_dax_inode_init(struct inode *inode);
+void fuse_dax_inode_init(struct inode *inode, unsigned int flags);
 void fuse_dax_inode_cleanup(struct inode *inode);
+void fuse_dax_dontcache(struct inode *inode, unsigned int flags);
 bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment);
 void fuse_dax_cancel_work(struct fuse_conn *fc);
 
index 8b89e3b..ee846ce 100644 (file)
@@ -301,6 +301,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
                if (inval)
                        invalidate_inode_pages2(inode->i_mapping);
        }
+
+       if (IS_ENABLED(CONFIG_FUSE_DAX))
+               fuse_dax_dontcache(inode, attr->flags);
 }
 
 static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
@@ -313,7 +316,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
        inode->i_ctime.tv_nsec = attr->ctimensec;
        if (S_ISREG(inode->i_mode)) {
                fuse_init_common(inode);
-               fuse_init_file_inode(inode);
+               fuse_init_file_inode(inode, attr->flags);
        } else if (S_ISDIR(inode->i_mode))
                fuse_init_dir(inode);
        else if (S_ISLNK(inode->i_mode))
@@ -767,8 +770,12 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
                        seq_printf(m, ",blksize=%lu", sb->s_blocksize);
        }
 #ifdef CONFIG_FUSE_DAX
-       if (fc->dax)
-               seq_puts(m, ",dax");
+       if (fc->dax_mode == FUSE_DAX_ALWAYS)
+               seq_puts(m, ",dax=always");
+       else if (fc->dax_mode == FUSE_DAX_NEVER)
+               seq_puts(m, ",dax=never");
+       else if (fc->dax_mode == FUSE_DAX_INODE_USER)
+               seq_puts(m, ",dax=inode");
 #endif
 
        return 0;
@@ -1109,73 +1116,80 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
                process_init_limits(fc, arg);
 
                if (arg->minor >= 6) {
+                       u64 flags = arg->flags | (u64) arg->flags2 << 32;
+
                        ra_pages = arg->max_readahead / PAGE_SIZE;
-                       if (arg->flags & FUSE_ASYNC_READ)
+                       if (flags & FUSE_ASYNC_READ)
                                fc->async_read = 1;
-                       if (!(arg->flags & FUSE_POSIX_LOCKS))
+                       if (!(flags & FUSE_POSIX_LOCKS))
                                fc->no_lock = 1;
                        if (arg->minor >= 17) {
-                               if (!(arg->flags & FUSE_FLOCK_LOCKS))
+                               if (!(flags & FUSE_FLOCK_LOCKS))
                                        fc->no_flock = 1;
                        } else {
-                               if (!(arg->flags & FUSE_POSIX_LOCKS))
+                               if (!(flags & FUSE_POSIX_LOCKS))
                                        fc->no_flock = 1;
                        }
-                       if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+                       if (flags & FUSE_ATOMIC_O_TRUNC)
                                fc->atomic_o_trunc = 1;
                        if (arg->minor >= 9) {
                                /* LOOKUP has dependency on proto version */
-                               if (arg->flags & FUSE_EXPORT_SUPPORT)
+                               if (flags & FUSE_EXPORT_SUPPORT)
                                        fc->export_support = 1;
                        }
-                       if (arg->flags & FUSE_BIG_WRITES)
+                       if (flags & FUSE_BIG_WRITES)
                                fc->big_writes = 1;
-                       if (arg->flags & FUSE_DONT_MASK)
+                       if (flags & FUSE_DONT_MASK)
                                fc->dont_mask = 1;
-                       if (arg->flags & FUSE_AUTO_INVAL_DATA)
+                       if (flags & FUSE_AUTO_INVAL_DATA)
                                fc->auto_inval_data = 1;
-                       else if (arg->flags & FUSE_EXPLICIT_INVAL_DATA)
+                       else if (flags & FUSE_EXPLICIT_INVAL_DATA)
                                fc->explicit_inval_data = 1;
-                       if (arg->flags & FUSE_DO_READDIRPLUS) {
+                       if (flags & FUSE_DO_READDIRPLUS) {
                                fc->do_readdirplus = 1;
-                               if (arg->flags & FUSE_READDIRPLUS_AUTO)
+                               if (flags & FUSE_READDIRPLUS_AUTO)
                                        fc->readdirplus_auto = 1;
                        }
-                       if (arg->flags & FUSE_ASYNC_DIO)
+                       if (flags & FUSE_ASYNC_DIO)
                                fc->async_dio = 1;
-                       if (arg->flags & FUSE_WRITEBACK_CACHE)
+                       if (flags & FUSE_WRITEBACK_CACHE)
                                fc->writeback_cache = 1;
-                       if (arg->flags & FUSE_PARALLEL_DIROPS)
+                       if (flags & FUSE_PARALLEL_DIROPS)
                                fc->parallel_dirops = 1;
-                       if (arg->flags & FUSE_HANDLE_KILLPRIV)
+                       if (flags & FUSE_HANDLE_KILLPRIV)
                                fc->handle_killpriv = 1;
                        if (arg->time_gran && arg->time_gran <= 1000000000)
                                fm->sb->s_time_gran = arg->time_gran;
-                       if ((arg->flags & FUSE_POSIX_ACL)) {
+                       if ((flags & FUSE_POSIX_ACL)) {
                                fc->default_permissions = 1;
                                fc->posix_acl = 1;
                                fm->sb->s_xattr = fuse_acl_xattr_handlers;
                        }
-                       if (arg->flags & FUSE_CACHE_SYMLINKS)
+                       if (flags & FUSE_CACHE_SYMLINKS)
                                fc->cache_symlinks = 1;
-                       if (arg->flags & FUSE_ABORT_ERROR)
+                       if (flags & FUSE_ABORT_ERROR)
                                fc->abort_err = 1;
-                       if (arg->flags & FUSE_MAX_PAGES) {
+                       if (flags & FUSE_MAX_PAGES) {
                                fc->max_pages =
                                        min_t(unsigned int, fc->max_pages_limit,
                                        max_t(unsigned int, arg->max_pages, 1));
                        }
-                       if (IS_ENABLED(CONFIG_FUSE_DAX) &&
-                           arg->flags & FUSE_MAP_ALIGNMENT &&
-                           !fuse_dax_check_alignment(fc, arg->map_alignment)) {
-                               ok = false;
+                       if (IS_ENABLED(CONFIG_FUSE_DAX)) {
+                               if (flags & FUSE_MAP_ALIGNMENT &&
+                                   !fuse_dax_check_alignment(fc, arg->map_alignment)) {
+                                       ok = false;
+                               }
+                               if (flags & FUSE_HAS_INODE_DAX)
+                                       fc->inode_dax = 1;
                        }
-                       if (arg->flags & FUSE_HANDLE_KILLPRIV_V2) {
+                       if (flags & FUSE_HANDLE_KILLPRIV_V2) {
                                fc->handle_killpriv_v2 = 1;
                                fm->sb->s_flags |= SB_NOSEC;
                        }
-                       if (arg->flags & FUSE_SETXATTR_EXT)
+                       if (flags & FUSE_SETXATTR_EXT)
                                fc->setxattr_ext = 1;
+                       if (flags & FUSE_SECURITY_CTX)
+                               fc->init_security = 1;
                } else {
                        ra_pages = fc->max_read / PAGE_SIZE;
                        fc->no_lock = 1;
@@ -1203,13 +1217,14 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
 void fuse_send_init(struct fuse_mount *fm)
 {
        struct fuse_init_args *ia;
+       u64 flags;
 
        ia = kzalloc(sizeof(*ia), GFP_KERNEL | __GFP_NOFAIL);
 
        ia->in.major = FUSE_KERNEL_VERSION;
        ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
        ia->in.max_readahead = fm->sb->s_bdi->ra_pages * PAGE_SIZE;
-       ia->in.flags |=
+       flags =
                FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
                FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
                FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
@@ -1219,13 +1234,19 @@ void fuse_send_init(struct fuse_mount *fm)
                FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
                FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
                FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
-               FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
+               FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
+               FUSE_SECURITY_CTX;
 #ifdef CONFIG_FUSE_DAX
        if (fm->fc->dax)
-               ia->in.flags |= FUSE_MAP_ALIGNMENT;
+               flags |= FUSE_MAP_ALIGNMENT;
+       if (fuse_is_inode_dax_mode(fm->fc->dax_mode))
+               flags |= FUSE_HAS_INODE_DAX;
 #endif
        if (fm->fc->auto_submounts)
-               ia->in.flags |= FUSE_SUBMOUNTS;
+               flags |= FUSE_SUBMOUNTS;
+
+       ia->in.flags = flags;
+       ia->in.flags2 = flags >> 32;
 
        ia->args.opcode = FUSE_INIT;
        ia->args.in_numargs = 1;
@@ -1514,7 +1535,7 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
        sb->s_subtype = ctx->subtype;
        ctx->subtype = NULL;
        if (IS_ENABLED(CONFIG_FUSE_DAX)) {
-               err = fuse_dax_conn_alloc(fc, ctx->dax_dev);
+               err = fuse_dax_conn_alloc(fc, ctx->dax_mode, ctx->dax_dev);
                if (err)
                        goto err;
        }
index 4cfa4bc..e54dc06 100644 (file)
@@ -88,12 +88,21 @@ struct virtio_fs_req_work {
 static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
                                 struct fuse_req *req, bool in_flight);
 
+static const struct constant_table dax_param_enums[] = {
+       {"always",      FUSE_DAX_ALWAYS },
+       {"never",       FUSE_DAX_NEVER },
+       {"inode",       FUSE_DAX_INODE_USER },
+       {}
+};
+
 enum {
        OPT_DAX,
+       OPT_DAX_ENUM,
 };
 
 static const struct fs_parameter_spec virtio_fs_parameters[] = {
        fsparam_flag("dax", OPT_DAX),
+       fsparam_enum("dax", OPT_DAX_ENUM, dax_param_enums),
        {}
 };
 
@@ -110,7 +119,10 @@ static int virtio_fs_parse_param(struct fs_context *fsc,
 
        switch (opt) {
        case OPT_DAX:
-               ctx->dax = 1;
+               ctx->dax_mode = FUSE_DAX_ALWAYS;
+               break;
+       case OPT_DAX_ENUM:
+               ctx->dax_mode = result.uint_32;
                break;
        default:
                return -EINVAL;
@@ -1326,8 +1338,8 @@ static int virtio_fs_fill_super(struct super_block *sb, struct fs_context *fsc)
 
        /* virtiofs allocates and installs its own fuse devices */
        ctx->fudptr = NULL;
-       if (ctx->dax) {
-               if (!fs->dax_dev) {
+       if (ctx->dax_mode != FUSE_DAX_NEVER) {
+               if (ctx->dax_mode == FUSE_DAX_ALWAYS && !fs->dax_dev) {
                        err = -EINVAL;
                        pr_err("virtio-fs: dax can't be enabled as filesystem"
                               " device does not support it.\n");
index a1dc3ee..d6ccee9 100644 (file)
  *
  *  7.35
  *  - add FOPEN_NOFLUSH
+ *
+ *  7.36
+ *  - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
+ *  - add flags2 to fuse_init_in and fuse_init_out
+ *  - add FUSE_SECURITY_CTX init flag
+ *  - add security context to create, mkdir, symlink, and mknod requests
+ *  - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
  */
 
 #ifndef _LINUX_FUSE_H
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 35
+#define FUSE_KERNEL_MINOR_VERSION 36
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -341,6 +348,11 @@ struct fuse_file_lock {
  *                     write/truncate sgid is killed only if file has group
  *                     execute permission. (Same as Linux VFS behavior).
  * FUSE_SETXATTR_EXT:  Server supports extended struct fuse_setxattr_in
+ * FUSE_INIT_EXT: extended fuse_init_in request
+ * FUSE_INIT_RESERVED: reserved, do not use
+ * FUSE_SECURITY_CTX:  add security context to create, mkdir, symlink, and
+ *                     mknod
+ * FUSE_HAS_INODE_DAX:  use per inode DAX
  */
 #define FUSE_ASYNC_READ                (1 << 0)
 #define FUSE_POSIX_LOCKS       (1 << 1)
@@ -372,6 +384,11 @@ struct fuse_file_lock {
 #define FUSE_SUBMOUNTS         (1 << 27)
 #define FUSE_HANDLE_KILLPRIV_V2        (1 << 28)
 #define FUSE_SETXATTR_EXT      (1 << 29)
+#define FUSE_INIT_EXT          (1 << 30)
+#define FUSE_INIT_RESERVED     (1 << 31)
+/* bits 32..63 get shifted down 32 bits into the flags2 field */
+#define FUSE_SECURITY_CTX      (1ULL << 32)
+#define FUSE_HAS_INODE_DAX     (1ULL << 33)
 
 /**
  * CUSE INIT request/reply flags
@@ -454,8 +471,10 @@ struct fuse_file_lock {
  * fuse_attr flags
  *
  * FUSE_ATTR_SUBMOUNT: Object is a submount root
+ * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
  */
 #define FUSE_ATTR_SUBMOUNT      (1 << 0)
+#define FUSE_ATTR_DAX          (1 << 1)
 
 /**
  * Open flags
@@ -741,6 +760,8 @@ struct fuse_init_in {
        uint32_t        minor;
        uint32_t        max_readahead;
        uint32_t        flags;
+       uint32_t        flags2;
+       uint32_t        unused[11];
 };
 
 #define FUSE_COMPAT_INIT_OUT_SIZE 8
@@ -757,7 +778,8 @@ struct fuse_init_out {
        uint32_t        time_gran;
        uint16_t        max_pages;
        uint16_t        map_alignment;
-       uint32_t        unused[8];
+       uint32_t        flags2;
+       uint32_t        unused[7];
 };
 
 #define CUSE_INIT_INFO_MAX 4096
@@ -865,9 +887,12 @@ struct fuse_dirent {
        char name[];
 };
 
-#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
-#define FUSE_DIRENT_ALIGN(x) \
+/* Align variable length records to 64bit boundary */
+#define FUSE_REC_ALIGN(x) \
        (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
 #define FUSE_DIRENT_SIZE(d) \
        FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
 
@@ -984,4 +1009,26 @@ struct fuse_syncfs_in {
        uint64_t        padding;
 };
 
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ */
+struct fuse_secctx {
+       uint32_t        size;
+       uint32_t        padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+       uint32_t        size;
+       uint32_t        nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */