Re: [PATCH] fuse: enable caching of symlinks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Sep 27, 2018 at 3:52 PM, Dan Schatzberg <dschatzberg@xxxxxx> wrote:
> FUSE file reads are cached in the page cache, but symlink reads are
> not. This patch enables FUSE READLINK operations to be cached which
> can improve performance of some FUSE workloads.
>
> In particular, I'm working on a FUSE filesystem for access to source
> code and discovered that about a 10% improvement to build times is
> achieved with this patch (there are a lot of symlinks in the source
> tree).
>
> Please provide feedback, I'm looking to flesh this out more.

Need to think about how/when to invalidate the cached symlink
contents.  I think treating it like metadata (i.e. look at
attr_timeout for validity) would be the simplest.

Thanks,
Miklos

>
> Signed-off-by: Dan Schatzberg <dschatzberg@xxxxxx>
> ---
>  fs/fuse/dir.c | 70 +++++++++++++++++++++++++++++++--------------------
>  1 file changed, 43 insertions(+), 27 deletions(-)
>
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index 0979609d6eba..3c0a81ef5bca 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -1398,38 +1398,46 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
>         return err;
>  }
>
> -static const char *fuse_get_link(struct dentry *dentry,
> -                                struct inode *inode,
> -                                struct delayed_call *done)
> +static int fuse_symlink_readpage(struct file *file, struct page *page)
>  {
> +       struct inode *inode = page->mapping->host;
>         struct fuse_conn *fc = get_fuse_conn(inode);
> -       FUSE_ARGS(args);
> -       char *link;
> -       ssize_t ret;
> -
> -       if (!dentry)
> -               return ERR_PTR(-ECHILD);
> +       struct fuse_req *req;
> +       int err;
> +       size_t num_read;
>
> -       link = kmalloc(PAGE_SIZE, GFP_KERNEL);
> -       if (!link)
> -               return ERR_PTR(-ENOMEM);
> +       err = -EIO;
> +       if (is_bad_inode(inode))
> +               goto out;
>
> -       args.in.h.opcode = FUSE_READLINK;
> -       args.in.h.nodeid = get_node_id(inode);
> -       args.out.argvar = 1;
> -       args.out.numargs = 1;
> -       args.out.args[0].size = PAGE_SIZE - 1;
> -       args.out.args[0].value = link;
> -       ret = fuse_simple_request(fc, &args);
> -       if (ret < 0) {
> -               kfree(link);
> -               link = ERR_PTR(ret);
> -       } else {
> -               link[ret] = '\0';
> -               set_delayed_call(done, kfree_link, link);
> +       req = fuse_get_req(fc, 1);
> +       if (IS_ERR(req)) {
> +               err = PTR_ERR(req);
> +               goto out;
>         }
> +
> +       req->out.page_zeroing = 1;
> +       req->out.argpages = 1;
> +       req->num_pages = 1;
> +       req->pages[0] = page;
> +       req->page_descs[0].length = PAGE_SIZE - 1;
> +       req->in.h.opcode = FUSE_READLINK;
> +       req->in.h.nodeid = get_node_id(inode);
> +       req->out.argvar = 1;
> +       req->out.numargs = 1;
> +       req->out.args[0].size = PAGE_SIZE - 1;
> +       fuse_request_send(fc, req);
> +       num_read = req->out.args[0].size;
> +       err = req->out.h.error;
> +
> +       if (!err)
> +               SetPageUptodate(page);
> +
> +       fuse_put_request(fc, req);
>         fuse_invalidate_atime(inode);
> -       return link;
> +out:
> +       unlock_page(page);
> +       return err;
>  }
>
>  static int fuse_dir_open(struct inode *inode, struct file *file)
> @@ -1855,7 +1863,7 @@ static const struct inode_operations fuse_common_inode_operations = {
>
>  static const struct inode_operations fuse_symlink_inode_operations = {
>         .setattr        = fuse_setattr,
> -       .get_link       = fuse_get_link,
> +       .get_link       = page_get_link,
>         .getattr        = fuse_getattr,
>         .listxattr      = fuse_listxattr,
>  };
> @@ -1871,7 +1879,15 @@ void fuse_init_dir(struct inode *inode)
>         inode->i_fop = &fuse_dir_operations;
>  }
>
> +static const struct address_space_operations fuse_symlink_aops = {
> +       .readpage       = fuse_symlink_readpage,
> +};
> +
>  void fuse_init_symlink(struct inode *inode)
>  {
>         inode->i_op = &fuse_symlink_inode_operations;
> +       inode->i_data.a_ops = &fuse_symlink_aops;
> +       mapping_set_gfp_mask(inode->i_mapping,
> +                       mapping_gfp_constraint(inode->i_mapping,
> +                                       ~(__GFP_FS | __GFP_HIGHMEM)));
>  }
> --
> 2.17.1
>



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux