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 >