fuse: support clients that don't implement 'open'
authorAndrew Gallagher <agallagher@fb.com>
Tue, 5 Nov 2013 15:05:52 +0000 (16:05 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Wed, 22 Jan 2014 18:36:59 +0000 (19:36 +0100)
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>
fs/fuse/file.c
fs/fuse/fuse_i.h

index d53af8f..74f6ca5 100644 (file)
@@ -127,7 +127,15 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
        if (atomic_dec_and_test(&ff->count)) {
                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;
                        fuse_request_send(ff->fc, req);
                        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,
                 bool isdir)
 {
-       struct fuse_open_out outarg;
        struct fuse_file *ff;
-       int err;
        int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
 
        ff = fuse_file_alloc(fc);
        if (!ff)
                return -ENOMEM;
 
-       err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
-       if (err) {
-               fuse_file_free(ff);
-               return err;
+       ff->fh = 0;
+       ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */
+       if (!fc->no_open || isdir) {
+               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)
-               outarg.open_flags &= ~FOPEN_DIRECT_IO;
+               ff->open_flags &= ~FOPEN_DIRECT_IO;
 
-       ff->fh = outarg.fh;
        ff->nodeid = nodeid;
-       ff->open_flags = outarg.open_flags;
        file->private_data = fuse_file_get(ff);
 
        return 0;
index dc44b9e..2da5db2 100644 (file)
@@ -485,6 +485,9 @@ struct fuse_conn {
         * 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? */
        unsigned no_fsync:1;