fuse: support clients that don't implement 'open'
open/release operations require userspace transitions to keep track of the open count and to perform any FS-specific setup. However, for some purely read-only FSs which don't need to perform any setup at open/release time, we can avoid the performance overhead of calling into userspace for open/release calls. This patch adds the necessary support to the fuse kernel modules to prevent open/release operations from hitting in userspace. When the client returns ENOSYS, we avoid sending the subsequent release to userspace, and also remember this so that future opens also don't trigger a userspace operation. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
parent
451418fc92
commit
7678ac5061
2 changed files with 30 additions and 10 deletions
|
@ -127,7 +127,15 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
|
||||||
if (atomic_dec_and_test(&ff->count)) {
|
if (atomic_dec_and_test(&ff->count)) {
|
||||||
struct fuse_req *req = ff->reserved_req;
|
struct fuse_req *req = ff->reserved_req;
|
||||||
|
|
||||||
if (sync) {
|
if (ff->fc->no_open) {
|
||||||
|
/*
|
||||||
|
* Drop the release request when client does not
|
||||||
|
* implement 'open'
|
||||||
|
*/
|
||||||
|
req->background = 0;
|
||||||
|
path_put(&req->misc.release.path);
|
||||||
|
fuse_put_request(ff->fc, req);
|
||||||
|
} else if (sync) {
|
||||||
req->background = 0;
|
req->background = 0;
|
||||||
fuse_request_send(ff->fc, req);
|
fuse_request_send(ff->fc, req);
|
||||||
path_put(&req->misc.release.path);
|
path_put(&req->misc.release.path);
|
||||||
|
@ -144,27 +152,36 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
|
||||||
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
||||||
bool isdir)
|
bool isdir)
|
||||||
{
|
{
|
||||||
struct fuse_open_out outarg;
|
|
||||||
struct fuse_file *ff;
|
struct fuse_file *ff;
|
||||||
int err;
|
|
||||||
int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
|
int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
|
||||||
|
|
||||||
ff = fuse_file_alloc(fc);
|
ff = fuse_file_alloc(fc);
|
||||||
if (!ff)
|
if (!ff)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
|
ff->fh = 0;
|
||||||
if (err) {
|
ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */
|
||||||
fuse_file_free(ff);
|
if (!fc->no_open || isdir) {
|
||||||
return err;
|
struct fuse_open_out outarg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
|
||||||
|
if (!err) {
|
||||||
|
ff->fh = outarg.fh;
|
||||||
|
ff->open_flags = outarg.open_flags;
|
||||||
|
|
||||||
|
} else if (err != -ENOSYS || isdir) {
|
||||||
|
fuse_file_free(ff);
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
fc->no_open = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isdir)
|
if (isdir)
|
||||||
outarg.open_flags &= ~FOPEN_DIRECT_IO;
|
ff->open_flags &= ~FOPEN_DIRECT_IO;
|
||||||
|
|
||||||
ff->fh = outarg.fh;
|
|
||||||
ff->nodeid = nodeid;
|
ff->nodeid = nodeid;
|
||||||
ff->open_flags = outarg.open_flags;
|
|
||||||
file->private_data = fuse_file_get(ff);
|
file->private_data = fuse_file_get(ff);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -485,6 +485,9 @@ struct fuse_conn {
|
||||||
* and hence races in setting them will not cause malfunction
|
* and hence races in setting them will not cause malfunction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** Is open/release not implemented by fs? */
|
||||||
|
unsigned no_open:1;
|
||||||
|
|
||||||
/** Is fsync not implemented by fs? */
|
/** Is fsync not implemented by fs? */
|
||||||
unsigned no_fsync:1;
|
unsigned no_fsync:1;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue