FUSE: Adapt readdirplus to application usage patterns
authorFeng Shuo <steve.shuo.feng@gmail.com>
Tue, 15 Jan 2013 03:23:28 +0000 (11:23 +0800)
committerMiklos Szeredi <mszeredi@suse.cz>
Thu, 31 Jan 2013 16:08:11 +0000 (17:08 +0100)
Use the same adaptive readdirplus mechanism as NFS:

http://permalink.gmane.org/gmane.linux.nfs/49299

If the user space implementation wants to disable readdirplus
temporarily, it could just return ENOTSUPP. Then kernel will
recall it with readdir.

Signed-off-by: Feng Shuo <steve.shuo.feng@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
fs/fuse/dir.c
fs/fuse/fuse_i.h
fs/fuse/inode.c

index aa0b6ade0e68121938fc7e56fe96d39dd84083c0..dc5e64893375ffcf2caa1dba652d0ac0fb66863c 100644 (file)
 #include <linux/namei.h>
 #include <linux/slab.h>
 
+static bool fuse_use_readdirplus(struct inode *dir, struct file *filp)
+{
+       struct fuse_conn *fc = get_fuse_conn(dir);
+       struct fuse_inode *fi = get_fuse_inode(dir);
+
+       if (!fc->do_readdirplus)
+               return false;
+       if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
+               return true;
+       if (filp->f_pos == 0)
+               return true;
+       return false;
+}
+
+static void fuse_advise_use_readdirplus(struct inode *dir)
+{
+       struct fuse_inode *fi = get_fuse_inode(dir);
+
+       set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state);
+}
+
 #if BITS_PER_LONG >= 64
 static inline void fuse_dentry_settime(struct dentry *entry, u64 time)
 {
@@ -219,6 +240,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                                       attr_version);
                fuse_change_entry_timeout(entry, &outarg);
        }
+       fuse_advise_use_readdirplus(inode);
        return 1;
 }
 
@@ -355,6 +377,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        else
                fuse_invalidate_entry_cache(entry);
 
+       fuse_advise_use_readdirplus(dir);
        return newent;
 
  out_iput:
@@ -1290,7 +1313,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
 
 static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
 {
-       int err;
+       int plus, err;
        size_t nbytes;
        struct page *page;
        struct inode *inode = file->f_path.dentry->d_inode;
@@ -1310,11 +1333,13 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
                fuse_put_request(fc, req);
                return -ENOMEM;
        }
+
+       plus = fuse_use_readdirplus(inode, file);
        req->out.argpages = 1;
        req->num_pages = 1;
        req->pages[0] = page;
        req->page_descs[0].length = PAGE_SIZE;
-       if (fc->do_readdirplus) {
+       if (plus) {
                attr_version = fuse_get_attr_version(fc);
                fuse_read_fill(req, file, file->f_pos, PAGE_SIZE,
                               FUSE_READDIRPLUS);
@@ -1327,7 +1352,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
-               if (fc->do_readdirplus) {
+               if (plus) {
                        err = parse_dirplusfile(page_address(page), nbytes,
                                                file, dstbuf, filldir,
                                                attr_version);
index af51c146a9aee4b69273aa599c3473dacfefc41e..fc55dd33c1e27ba9da68124defdcf02ef48bf335 100644 (file)
@@ -106,6 +106,15 @@ struct fuse_inode {
 
        /** List of writepage requestst (pending or sent) */
        struct list_head writepages;
+
+       /** Miscellaneous bits describing inode state */
+       unsigned long state;
+};
+
+/** FUSE inode state bits */
+enum {
+       /** Advise readdirplus  */
+       FUSE_I_ADVISE_RDPLUS,
 };
 
 struct fuse_conn;
index 79b70deb7cd6dba0ca53f542929fc9f93c0462f4..9876a87255fe28b138bdd277c256581682d9dc93 100644 (file)
@@ -92,6 +92,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
        fi->attr_version = 0;
        fi->writectr = 0;
        fi->orig_ino = 0;
+       fi->state = 0;
        INIT_LIST_HEAD(&fi->write_files);
        INIT_LIST_HEAD(&fi->queued_writes);
        INIT_LIST_HEAD(&fi->writepages);