Commit fc8ff397 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

fuse: prepare for opening file in passthrough mode

In preparation for opening file in passthrough mode, store the
fuse_open_out argument in ff->args to be passed into fuse_file_io_open()
with the optional backing_id member.

This will be used for setting up passthrough to backing file on open
reply with FOPEN_PASSTHROUGH flag and a valid backing_id.

Opening a file in passthrough mode may fail for several reasons, such as
missing capability, conflicting open flags or inode in caching mode.
Return EIO from fuse_file_io_open() in those cases.

The combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO is allowed -
it mean that read/write operations will go directly to the server,
but mmap will be done to the backing file.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 44350256
...@@ -615,7 +615,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -615,7 +615,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
FUSE_ARGS(args); FUSE_ARGS(args);
struct fuse_forget_link *forget; struct fuse_forget_link *forget;
struct fuse_create_in inarg; struct fuse_create_in inarg;
struct fuse_open_out outopen; struct fuse_open_out *outopenp;
struct fuse_entry_out outentry; struct fuse_entry_out outentry;
struct fuse_inode *fi; struct fuse_inode *fi;
struct fuse_file *ff; struct fuse_file *ff;
...@@ -659,8 +659,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -659,8 +659,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
args.out_numargs = 2; args.out_numargs = 2;
args.out_args[0].size = sizeof(outentry); args.out_args[0].size = sizeof(outentry);
args.out_args[0].value = &outentry; args.out_args[0].value = &outentry;
args.out_args[1].size = sizeof(outopen); /* Store outarg for fuse_finish_open() */
args.out_args[1].value = &outopen; outopenp = &ff->args->open_outarg;
args.out_args[1].size = sizeof(*outopenp);
args.out_args[1].value = outopenp;
err = get_create_ext(&args, dir, entry, mode); err = get_create_ext(&args, dir, entry, mode);
if (err) if (err)
...@@ -676,9 +678,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -676,9 +678,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
fuse_invalid_attr(&outentry.attr)) fuse_invalid_attr(&outentry.attr))
goto out_free_ff; goto out_free_ff;
ff->fh = outopen.fh; ff->fh = outopenp->fh;
ff->nodeid = outentry.nodeid; ff->nodeid = outentry.nodeid;
ff->open_flags = outopen.open_flags; ff->open_flags = outopenp->open_flags;
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
&outentry.attr, ATTR_TIMEOUT(&outentry), 0); &outentry.attr, ATTR_TIMEOUT(&outentry), 0);
if (!inode) { if (!inode) {
......
...@@ -50,12 +50,6 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, ...@@ -50,12 +50,6 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
return fuse_simple_request(fm, &args); return fuse_simple_request(fm, &args);
} }
struct fuse_release_args {
struct fuse_args args;
struct fuse_release_in inarg;
struct inode *inode;
};
struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
{ {
struct fuse_file *ff; struct fuse_file *ff;
...@@ -66,9 +60,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) ...@@ -66,9 +60,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
ff->fm = fm; ff->fm = fm;
if (release) { if (release) {
ff->release_args = kzalloc(sizeof(*ff->release_args), ff->args = kzalloc(sizeof(*ff->args), GFP_KERNEL_ACCOUNT);
GFP_KERNEL_ACCOUNT); if (!ff->args) {
if (!ff->release_args) {
kfree(ff); kfree(ff);
return NULL; return NULL;
} }
...@@ -87,7 +80,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) ...@@ -87,7 +80,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
void fuse_file_free(struct fuse_file *ff) void fuse_file_free(struct fuse_file *ff)
{ {
kfree(ff->release_args); kfree(ff->args);
mutex_destroy(&ff->readdir.lock); mutex_destroy(&ff->readdir.lock);
kfree(ff); kfree(ff);
} }
...@@ -110,7 +103,7 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args, ...@@ -110,7 +103,7 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args,
static void fuse_file_put(struct fuse_file *ff, bool sync) static void fuse_file_put(struct fuse_file *ff, bool sync)
{ {
if (refcount_dec_and_test(&ff->count)) { if (refcount_dec_and_test(&ff->count)) {
struct fuse_release_args *ra = ff->release_args; struct fuse_release_args *ra = &ff->args->release_args;
struct fuse_args *args = (ra ? &ra->args : NULL); struct fuse_args *args = (ra ? &ra->args : NULL);
if (ra && ra->inode) if (ra && ra->inode)
...@@ -147,20 +140,21 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, ...@@ -147,20 +140,21 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
/* Default for no-open */ /* Default for no-open */
ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0);
if (open) { if (open) {
struct fuse_open_out outarg; /* Store outarg for fuse_finish_open() */
struct fuse_open_out *outargp = &ff->args->open_outarg;
int err; int err;
err = fuse_send_open(fm, nodeid, open_flags, opcode, &outarg); err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);
if (!err) { if (!err) {
ff->fh = outarg.fh; ff->fh = outargp->fh;
ff->open_flags = outarg.open_flags; ff->open_flags = outargp->open_flags;
} else if (err != -ENOSYS) { } else if (err != -ENOSYS) {
fuse_file_free(ff); fuse_file_free(ff);
return ERR_PTR(err); return ERR_PTR(err);
} else { } else {
/* No release needed */ /* No release needed */
kfree(ff->release_args); kfree(ff->args);
ff->release_args = NULL; ff->args = NULL;
if (isdir) if (isdir)
fc->no_opendir = 1; fc->no_opendir = 1;
else else
...@@ -299,7 +293,7 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, ...@@ -299,7 +293,7 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
unsigned int flags, int opcode, bool sync) unsigned int flags, int opcode, bool sync)
{ {
struct fuse_conn *fc = ff->fm->fc; struct fuse_conn *fc = ff->fm->fc;
struct fuse_release_args *ra = ff->release_args; struct fuse_release_args *ra = &ff->args->release_args;
/* Inode is NULL on error path of fuse_create_open() */ /* Inode is NULL on error path of fuse_create_open() */
if (likely(fi)) { if (likely(fi)) {
...@@ -317,6 +311,8 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, ...@@ -317,6 +311,8 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
if (!ra) if (!ra)
return; return;
/* ff->args was used for open outarg */
memset(ff->args, 0, sizeof(*ff->args));
ra->inarg.fh = ff->fh; ra->inarg.fh = ff->fh;
ra->inarg.flags = flags; ra->inarg.flags = flags;
ra->args.in_numargs = 1; ra->args.in_numargs = 1;
...@@ -339,7 +335,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff, ...@@ -339,7 +335,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
unsigned int open_flags, fl_owner_t id, bool isdir) unsigned int open_flags, fl_owner_t id, bool isdir)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_release_args *ra = ff->release_args; struct fuse_release_args *ra = &ff->args->release_args;
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
fuse_prepare_release(fi, ff, open_flags, opcode, false); fuse_prepare_release(fi, ff, open_flags, opcode, false);
......
...@@ -213,15 +213,15 @@ enum { ...@@ -213,15 +213,15 @@ enum {
struct fuse_conn; struct fuse_conn;
struct fuse_mount; struct fuse_mount;
struct fuse_release_args; union fuse_file_args;
/** FUSE specific file data */ /** FUSE specific file data */
struct fuse_file { struct fuse_file {
/** Fuse connection for this file */ /** Fuse connection for this file */
struct fuse_mount *fm; struct fuse_mount *fm;
/* Argument space reserved for release */ /* Argument space reserved for open/release */
struct fuse_release_args *release_args; union fuse_file_args *args;
/** Kernel file handle guaranteed to be unique */ /** Kernel file handle guaranteed to be unique */
u64 kh; u64 kh;
...@@ -320,6 +320,19 @@ struct fuse_args_pages { ...@@ -320,6 +320,19 @@ struct fuse_args_pages {
unsigned int num_pages; unsigned int num_pages;
}; };
struct fuse_release_args {
struct fuse_args args;
struct fuse_release_in inarg;
struct inode *inode;
};
union fuse_file_args {
/* Used during open() */
struct fuse_open_out open_outarg;
/* Used during release() */
struct fuse_release_args release_args;
};
#define FUSE_ARGS(args) struct fuse_args args = {} #define FUSE_ARGS(args) struct fuse_args args = {}
/** The request IO state (for asynchronous processing) */ /** The request IO state (for asynchronous processing) */
......
...@@ -31,7 +31,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff) ...@@ -31,7 +31,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
/* There are no io modes if server does not implement open */ /* There are no io modes if server does not implement open */
if (!ff->release_args) if (!ff->args)
return 0; return 0;
spin_lock(&fi->lock); spin_lock(&fi->lock);
...@@ -103,6 +103,37 @@ void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff) ...@@ -103,6 +103,37 @@ void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
} }
/*
* Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
* A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
* operations go directly to the server, but mmap is done on the backing file.
* FOPEN_PASSTHROUGH mode should not co-exist with any users of the fuse inode
* page cache, so FOPEN_KEEP_CACHE is a strange and undesired combination.
*/
#define FOPEN_PASSTHROUGH_MASK \
(FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES | \
FOPEN_NOFLUSH)
static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = get_fuse_conn(inode);
int err;
/* Check allowed conditions for file open in passthrough mode */
if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough ||
(ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
return -EINVAL;
/* TODO: implement backing file open */
return -EOPNOTSUPP;
/* First passthrough file open denies caching inode io mode */
err = fuse_file_uncached_io_start(inode, ff);
return err;
}
/* Request access to submit new io to inode via open file */ /* Request access to submit new io to inode via open file */
int fuse_file_io_open(struct file *file, struct inode *inode) int fuse_file_io_open(struct file *file, struct inode *inode)
{ {
...@@ -113,7 +144,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode) ...@@ -113,7 +144,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
* io modes are not relevant with DAX and with server that does not * io modes are not relevant with DAX and with server that does not
* implement open. * implement open.
*/ */
if (FUSE_IS_DAX(inode) || !ff->release_args) if (FUSE_IS_DAX(inode) || !ff->args)
return 0; return 0;
/* /*
...@@ -123,16 +154,21 @@ int fuse_file_io_open(struct file *file, struct inode *inode) ...@@ -123,16 +154,21 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
ff->open_flags &= ~FOPEN_PARALLEL_DIRECT_WRITES; ff->open_flags &= ~FOPEN_PARALLEL_DIRECT_WRITES;
/* /*
* First passthrough file open denies caching inode io mode.
* First caching file open enters caching inode io mode. * First caching file open enters caching inode io mode.
* *
* Note that if user opens a file open with O_DIRECT, but server did * Note that if user opens a file open with O_DIRECT, but server did
* not specify FOPEN_DIRECT_IO, a later fcntl() could remove O_DIRECT, * not specify FOPEN_DIRECT_IO, a later fcntl() could remove O_DIRECT,
* so we put the inode in caching mode to prevent parallel dio. * so we put the inode in caching mode to prevent parallel dio.
*/ */
if (ff->open_flags & FOPEN_DIRECT_IO) if ((ff->open_flags & FOPEN_DIRECT_IO) &&
!(ff->open_flags & FOPEN_PASSTHROUGH))
return 0; return 0;
err = fuse_file_cached_io_start(inode, ff); if (ff->open_flags & FOPEN_PASSTHROUGH)
err = fuse_file_passthrough_open(inode, file);
else
err = fuse_file_cached_io_start(inode, ff);
if (err) if (err)
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment