Currently, when cache=loose is enabled, file reads are cached in the page cache, but symlink reads are not. This patch allows the results of p9_client_readlink() to be stored in the page cache, eliminating the need for repeated 9P transactions on subsequent symlink accesses. This change improves performance for workloads that involve frequent symlink resolution. Signed-off-by: Remi Pommarel <repk@xxxxxxxxxxxx> --- fs/9p/v9fs.h | 1 + fs/9p/vfs_inode.c | 7 +++- fs/9p/vfs_inode_dotl.c | 94 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 8c5fa0f7ba71..f48a85b610e3 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -186,6 +186,7 @@ extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct super_block *sb, int new); extern const struct inode_operations v9fs_dir_inode_operations_dotl; extern const struct inode_operations v9fs_file_inode_operations_dotl; +extern const struct address_space_operations v9fs_symlink_aops_dotl; extern const struct inode_operations v9fs_symlink_inode_operations_dotl; extern const struct netfs_request_ops v9fs_req_ops; extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 89eeb2b3ba43..362e39b3e7eb 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -302,10 +302,13 @@ int v9fs_init_inode(struct v9fs_session_info *v9ses, goto error; } - if (v9fs_proto_dotl(v9ses)) + if (v9fs_proto_dotl(v9ses)) { inode->i_op = &v9fs_symlink_inode_operations_dotl; - else + inode->i_mapping->a_ops = &v9fs_symlink_aops_dotl; + inode_nohighmem(inode); + } else { inode->i_op = &v9fs_symlink_inode_operations; + } break; case S_IFDIR: diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 5b5fda617b80..2e2119be1d49 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -126,8 +126,10 @@ static struct inode *v9fs_qid_iget_dotl(struct super_block *sb, goto error; v9fs_stat2inode_dotl(st, inode, 0); - v9fs_set_netfs_context(inode); - v9fs_cache_inode_get_cookie(inode); + if (inode->i_mapping->a_ops == &v9fs_addr_operations) { + v9fs_set_netfs_context(inode); + v9fs_cache_inode_get_cookie(inode); + } retval = v9fs_get_acl(inode, fid); if (retval) goto error; @@ -858,24 +860,23 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir, } /** - * v9fs_vfs_get_link_dotl - follow a symlink path + * v9fs_vfs_get_link_nocache_dotl - Resolve a symlink directly. + * + * To be used when symlink caching is not enabled. + * * @dentry: dentry for symlink * @inode: inode for symlink * @done: destructor for return value */ - static const char * -v9fs_vfs_get_link_dotl(struct dentry *dentry, - struct inode *inode, - struct delayed_call *done) +v9fs_vfs_get_link_nocache_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct p9_fid *fid; char *target; int retval; - if (!dentry) - return ERR_PTR(-ECHILD); - p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); fid = v9fs_fid_lookup(dentry); @@ -889,6 +890,75 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry, return target; } +/** + * v9fs_symlink_read_folio_dotl - Fetch a symlink path and store it in buffer + * cache. + * @file: file associated to symlink (NULL) + * @folio: folio where to store the symlink resolve result + */ +static int v9fs_symlink_read_folio_dotl(struct file *file, + struct folio *folio) +{ + struct inode *inode = folio->mapping->host; + struct dentry *dentry; + struct p9_fid *fid; + char *target; + size_t len; + int ret = -EIO; + + /* Does not expect symlink inode to have a fid as it as not been + * opened> + */ + dentry = d_find_alias(inode); + if (!dentry) + goto out; + + fid = v9fs_fid_lookup(dentry); + dput(dentry); + if (IS_ERR(fid)) { + ret = PTR_ERR(fid); + goto out; + } + + ret = p9_client_readlink(fid, &target); + p9_fid_put(fid); + if (ret) + goto out; + + len = strnlen(target, PAGE_SIZE - 1); + target[len] = '\0'; + memcpy_to_folio(folio, 0, target, len); + kfree(target); + + ret = 0; +out: + folio_end_read(folio, ret == 0); + return ret; +} + +/** + * v9fs_vfs_get_link_dotl - follow a symlink path + * @dentry: dentry for symlink + * @inode: inode for symlink + * @done: destructor for return value + */ +static const char * +v9fs_vfs_get_link_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) +{ + struct v9fs_session_info *v9ses; + + if (!dentry) + return ERR_PTR(-ECHILD); + + v9ses = v9fs_inode2v9ses(inode); + if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) + return page_get_link(dentry, inode, done); + + return v9fs_vfs_get_link_nocache_dotl(dentry, inode, done); +} + int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) { struct p9_stat_dotl *st; @@ -945,6 +1015,10 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { .set_acl = v9fs_iop_set_acl, }; +const struct address_space_operations v9fs_symlink_aops_dotl = { + .read_folio = v9fs_symlink_read_folio_dotl, +}; + const struct inode_operations v9fs_symlink_inode_operations_dotl = { .get_link = v9fs_vfs_get_link_dotl, .getattr = v9fs_vfs_getattr_dotl, -- 2.50.1