Skip to content

Commit fc8ff39

Browse files
amir73ilMiklos Szeredi
authored andcommitted
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: Amir Goldstein <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 4435025 commit fc8ff39

File tree

4 files changed

+78
-31
lines changed

4 files changed

+78
-31
lines changed

fs/fuse/dir.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
615615
FUSE_ARGS(args);
616616
struct fuse_forget_link *forget;
617617
struct fuse_create_in inarg;
618-
struct fuse_open_out outopen;
618+
struct fuse_open_out *outopenp;
619619
struct fuse_entry_out outentry;
620620
struct fuse_inode *fi;
621621
struct fuse_file *ff;
@@ -659,8 +659,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
659659
args.out_numargs = 2;
660660
args.out_args[0].size = sizeof(outentry);
661661
args.out_args[0].value = &outentry;
662-
args.out_args[1].size = sizeof(outopen);
663-
args.out_args[1].value = &outopen;
662+
/* Store outarg for fuse_finish_open() */
663+
outopenp = &ff->args->open_outarg;
664+
args.out_args[1].size = sizeof(*outopenp);
665+
args.out_args[1].value = outopenp;
664666

665667
err = get_create_ext(&args, dir, entry, mode);
666668
if (err)
@@ -676,9 +678,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
676678
fuse_invalid_attr(&outentry.attr))
677679
goto out_free_ff;
678680

679-
ff->fh = outopen.fh;
681+
ff->fh = outopenp->fh;
680682
ff->nodeid = outentry.nodeid;
681-
ff->open_flags = outopen.open_flags;
683+
ff->open_flags = outopenp->open_flags;
682684
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
683685
&outentry.attr, ATTR_TIMEOUT(&outentry), 0);
684686
if (!inode) {

fs/fuse/file.c

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,6 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
5050
return fuse_simple_request(fm, &args);
5151
}
5252

53-
struct fuse_release_args {
54-
struct fuse_args args;
55-
struct fuse_release_in inarg;
56-
struct inode *inode;
57-
};
58-
5953
struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
6054
{
6155
struct fuse_file *ff;
@@ -66,9 +60,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
6660

6761
ff->fm = fm;
6862
if (release) {
69-
ff->release_args = kzalloc(sizeof(*ff->release_args),
70-
GFP_KERNEL_ACCOUNT);
71-
if (!ff->release_args) {
63+
ff->args = kzalloc(sizeof(*ff->args), GFP_KERNEL_ACCOUNT);
64+
if (!ff->args) {
7265
kfree(ff);
7366
return NULL;
7467
}
@@ -87,7 +80,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
8780

8881
void fuse_file_free(struct fuse_file *ff)
8982
{
90-
kfree(ff->release_args);
83+
kfree(ff->args);
9184
mutex_destroy(&ff->readdir.lock);
9285
kfree(ff);
9386
}
@@ -110,7 +103,7 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args,
110103
static void fuse_file_put(struct fuse_file *ff, bool sync)
111104
{
112105
if (refcount_dec_and_test(&ff->count)) {
113-
struct fuse_release_args *ra = ff->release_args;
106+
struct fuse_release_args *ra = &ff->args->release_args;
114107
struct fuse_args *args = (ra ? &ra->args : NULL);
115108

116109
if (ra && ra->inode)
@@ -147,20 +140,21 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
147140
/* Default for no-open */
148141
ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0);
149142
if (open) {
150-
struct fuse_open_out outarg;
143+
/* Store outarg for fuse_finish_open() */
144+
struct fuse_open_out *outargp = &ff->args->open_outarg;
151145
int err;
152146

153-
err = fuse_send_open(fm, nodeid, open_flags, opcode, &outarg);
147+
err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);
154148
if (!err) {
155-
ff->fh = outarg.fh;
156-
ff->open_flags = outarg.open_flags;
149+
ff->fh = outargp->fh;
150+
ff->open_flags = outargp->open_flags;
157151
} else if (err != -ENOSYS) {
158152
fuse_file_free(ff);
159153
return ERR_PTR(err);
160154
} else {
161155
/* No release needed */
162-
kfree(ff->release_args);
163-
ff->release_args = NULL;
156+
kfree(ff->args);
157+
ff->args = NULL;
164158
if (isdir)
165159
fc->no_opendir = 1;
166160
else
@@ -299,7 +293,7 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
299293
unsigned int flags, int opcode, bool sync)
300294
{
301295
struct fuse_conn *fc = ff->fm->fc;
302-
struct fuse_release_args *ra = ff->release_args;
296+
struct fuse_release_args *ra = &ff->args->release_args;
303297

304298
/* Inode is NULL on error path of fuse_create_open() */
305299
if (likely(fi)) {
@@ -317,6 +311,8 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
317311
if (!ra)
318312
return;
319313

314+
/* ff->args was used for open outarg */
315+
memset(ff->args, 0, sizeof(*ff->args));
320316
ra->inarg.fh = ff->fh;
321317
ra->inarg.flags = flags;
322318
ra->args.in_numargs = 1;
@@ -339,7 +335,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
339335
unsigned int open_flags, fl_owner_t id, bool isdir)
340336
{
341337
struct fuse_inode *fi = get_fuse_inode(inode);
342-
struct fuse_release_args *ra = ff->release_args;
338+
struct fuse_release_args *ra = &ff->args->release_args;
343339
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
344340

345341
fuse_prepare_release(fi, ff, open_flags, opcode, false);

fs/fuse/fuse_i.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,15 @@ enum {
213213

214214
struct fuse_conn;
215215
struct fuse_mount;
216-
struct fuse_release_args;
216+
union fuse_file_args;
217217

218218
/** FUSE specific file data */
219219
struct fuse_file {
220220
/** Fuse connection for this file */
221221
struct fuse_mount *fm;
222222

223-
/* Argument space reserved for release */
224-
struct fuse_release_args *release_args;
223+
/* Argument space reserved for open/release */
224+
union fuse_file_args *args;
225225

226226
/** Kernel file handle guaranteed to be unique */
227227
u64 kh;
@@ -320,6 +320,19 @@ struct fuse_args_pages {
320320
unsigned int num_pages;
321321
};
322322

323+
struct fuse_release_args {
324+
struct fuse_args args;
325+
struct fuse_release_in inarg;
326+
struct inode *inode;
327+
};
328+
329+
union fuse_file_args {
330+
/* Used during open() */
331+
struct fuse_open_out open_outarg;
332+
/* Used during release() */
333+
struct fuse_release_args release_args;
334+
};
335+
323336
#define FUSE_ARGS(args) struct fuse_args args = {}
324337

325338
/** The request IO state (for asynchronous processing) */

fs/fuse/iomode.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
3131
struct fuse_inode *fi = get_fuse_inode(inode);
3232

3333
/* There are no io modes if server does not implement open */
34-
if (!ff->release_args)
34+
if (!ff->args)
3535
return 0;
3636

3737
spin_lock(&fi->lock);
@@ -103,6 +103,37 @@ void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
103103
spin_unlock(&fi->lock);
104104
}
105105

106+
/*
107+
* Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
108+
* A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
109+
* operations go directly to the server, but mmap is done on the backing file.
110+
* FOPEN_PASSTHROUGH mode should not co-exist with any users of the fuse inode
111+
* page cache, so FOPEN_KEEP_CACHE is a strange and undesired combination.
112+
*/
113+
#define FOPEN_PASSTHROUGH_MASK \
114+
(FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES | \
115+
FOPEN_NOFLUSH)
116+
117+
static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
118+
{
119+
struct fuse_file *ff = file->private_data;
120+
struct fuse_conn *fc = get_fuse_conn(inode);
121+
int err;
122+
123+
/* Check allowed conditions for file open in passthrough mode */
124+
if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough ||
125+
(ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
126+
return -EINVAL;
127+
128+
/* TODO: implement backing file open */
129+
return -EOPNOTSUPP;
130+
131+
/* First passthrough file open denies caching inode io mode */
132+
err = fuse_file_uncached_io_start(inode, ff);
133+
134+
return err;
135+
}
136+
106137
/* Request access to submit new io to inode via open file */
107138
int fuse_file_io_open(struct file *file, struct inode *inode)
108139
{
@@ -113,7 +144,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
113144
* io modes are not relevant with DAX and with server that does not
114145
* implement open.
115146
*/
116-
if (FUSE_IS_DAX(inode) || !ff->release_args)
147+
if (FUSE_IS_DAX(inode) || !ff->args)
117148
return 0;
118149

119150
/*
@@ -123,16 +154,21 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
123154
ff->open_flags &= ~FOPEN_PARALLEL_DIRECT_WRITES;
124155

125156
/*
157+
* First passthrough file open denies caching inode io mode.
126158
* First caching file open enters caching inode io mode.
127159
*
128160
* Note that if user opens a file open with O_DIRECT, but server did
129161
* not specify FOPEN_DIRECT_IO, a later fcntl() could remove O_DIRECT,
130162
* so we put the inode in caching mode to prevent parallel dio.
131163
*/
132-
if (ff->open_flags & FOPEN_DIRECT_IO)
164+
if ((ff->open_flags & FOPEN_DIRECT_IO) &&
165+
!(ff->open_flags & FOPEN_PASSTHROUGH))
133166
return 0;
134167

135-
err = fuse_file_cached_io_start(inode, ff);
168+
if (ff->open_flags & FOPEN_PASSTHROUGH)
169+
err = fuse_file_passthrough_open(inode, file);
170+
else
171+
err = fuse_file_cached_io_start(inode, ff);
136172
if (err)
137173
goto fail;
138174

0 commit comments

Comments
 (0)