Skip to content

Commit

Permalink
Merge tag 'fuse-update-5.17' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/mszeredi/fuse

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
  • Loading branch information
torvalds committed Jan 12, 2022
2 parents 1fb38c9 + 073c3ab commit 8975f89
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 52 deletions.
20 changes: 18 additions & 2 deletions Documentation/filesystems/dax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------
Expand Down Expand Up @@ -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
--------------------------------------------

Expand Down
36 changes: 34 additions & 2 deletions fs/fuse/dax.c
Original file line number Diff line number Diff line change
Expand Up @@ -1279,11 +1279,14 @@ static int fuse_dax_mem_range_init(struct fuse_conn_dax *fcd)
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;

Expand Down Expand Up @@ -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)) {
Expand Down
91 changes: 91 additions & 0 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
*
Expand All @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions fs/fuse/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
31 changes: 27 additions & 4 deletions fs/fuse/fuse_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
Loading

0 comments on commit 8975f89

Please sign in to comment.