History

fuse: enable caching of symlinks

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).

Dan Schatzberg committed 8 years ago

Commit 5571f1e1 parent 9a2eb244 files · +92 −26Browse files

Unified · Split

4 files
  1. fs/fuse/dir.c +83 −25
  2. fs/fuse/fuse_i.h +3 −0
  3. fs/fuse/inode.c +3 −1
  4. include/uapi/linux/fuse.h +3 −0
fs/fuse/dir.c+83−25
@@ -1160,38 +1160,78 @@ static int fuse_permission(struct inode *inode, int mask) · 1159 unmodified lines
1160 return err;1160 return err;
1161}1161}
1162 1162
1163static const char *fuse_get_link(struct dentry *dentry,1163static int fuse_readlink_page(struct inode *inode, struct page *page)
1164 struct inode *inode,
1165 struct delayed_call *done)
1166{1164{
1167 struct fuse_conn *fc = get_fuse_conn(inode);1165 struct fuse_conn *fc = get_fuse_conn(inode);
1168 FUSE_ARGS(args);1166 struct fuse_req *req;
1169 char *link;1167 int err;
1170 ssize_t ret;
1171 1168
1172 if (!dentry)1169 req = fuse_get_req(fc, 1);
1173 return ERR_PTR(-ECHILD);1170 if (IS_ERR(req))
1171 return PTR_ERR(req);
1172
1173 req->out.page_zeroing = 1;
1174 req->out.argpages = 1;
1175 req->num_pages = 1;
1176 req->pages[0] = page;
1177 req->page_descs[0].length = PAGE_SIZE - 1;
1178 req->in.h.opcode = FUSE_READLINK;
1179 req->in.h.nodeid = get_node_id(inode);
1180 req->out.argvar = 1;
1181 req->out.numargs = 1;
1182 req->out.args[0].size = PAGE_SIZE - 1;
1183 fuse_request_send(fc, req);
1184 err = req->out.h.error;
1174 1185
1175 link = kmalloc(PAGE_SIZE, GFP_KERNEL);1186 if (!err) {
1176 if (!link)1187 char *link = page_address(page);
1177 return ERR_PTR(-ENOMEM);1188 size_t len = req->out.args[0].size;
1178 1189
1179 args.in.h.opcode = FUSE_READLINK;1190 BUG_ON(len >= PAGE_SIZE);
1180 args.in.h.nodeid = get_node_id(inode);1191 link[len] = '\0';
1181 args.out.argvar = 1;
1182 args.out.numargs = 1;
1183 args.out.args[0].size = PAGE_SIZE - 1;
1184 args.out.args[0].value = link;
1185 ret = fuse_simple_request(fc, &args);
1186 if (ret < 0) {
1187 kfree(link);
1188 link = ERR_PTR(ret);
1189 } else {
1190 link[ret] = '\0';
1191 set_delayed_call(done, kfree_link, link);
1192 }1192 }
1193
1194 fuse_put_request(fc, req);
1193 fuse_invalidate_atime(inode);1195 fuse_invalidate_atime(inode);
1194 return link;1196
1197 return err;
1198}
1199
1200static const char *fuse_get_link(struct dentry *dentry, struct inode *inode,
1201 struct delayed_call *callback)
1202{
1203 struct fuse_conn *fc = get_fuse_conn(inode);
1204 struct page *page;
1205 int err;
1206
1207 err = -EIO;
1208 if (is_bad_inode(inode))
1209 goto out_err;
1210
1211 if (fc->cache_symlinks)
1212 return page_get_link(dentry, inode, callback);
1213
1214 err = -ECHILD;
1215 if (!dentry)
1216 goto out_err;
1217
1218 page = alloc_page(GFP_KERNEL);
1219 err = -ENOMEM;
1220 if (!page)
1221 goto out_err;
1222
1223 err = fuse_readlink_page(inode, page);
1224 if (err) {
1225 __free_page(page);
1226 goto out_err;
1227 }
1228
1229 set_delayed_call(callback, page_put_link, page);
1230
1231 return page_address(page);
1232
1233out_err:
1234 return ERR_PTR(err);
1195}1235}
1196 1236
1197static int fuse_dir_open(struct inode *inode, struct file *file)1237static int fuse_dir_open(struct inode *inode, struct file *file)
@@ -1644,7 +1684,25 @@ void fuse_init_dir(struct inode *inode) · 446 unmodified lines
1644 fi->rdc.version = 0;1684 fi->rdc.version = 0;
1645}1685}
1646 1686
1687static int fuse_symlink_readpage(struct file *null, struct page *page)
1688{
1689 int err = fuse_readlink_page(page->mapping->host, page);
1690
1691 if (!err)
1692 SetPageUptodate(page);
1693
1694 unlock_page(page);
1695
1696 return err;
1697}
1698
1699static const struct address_space_operations fuse_symlink_aops = {
1700 .readpage = fuse_symlink_readpage,
1701};
1702
1647void fuse_init_symlink(struct inode *inode)1703void fuse_init_symlink(struct inode *inode)
1648{1704{
1649 inode->i_op = &fuse_symlink_inode_operations;1705 inode->i_op = &fuse_symlink_inode_operations;
1706 inode->i_data.a_ops = &fuse_symlink_aops;
1707 inode_nohighmem(inode);
1650}1708}
fs/fuse/fuse_i.h+3−0
@@ -613,6 +613,9 @@ struct fuse_conn { · 612 unmodified lines
613 /** handle fs handles killing suid/sgid/cap on write/chown/trunc */613 /** handle fs handles killing suid/sgid/cap on write/chown/trunc */
614 unsigned handle_killpriv:1;614 unsigned handle_killpriv:1;
615 615
616 /** cache READLINK responses in page cache */
617 unsigned cache_symlinks:1;
618
616 /*619 /*
617 * The following bitfields are only for optimization purposes620 * The following bitfields are only for optimization purposes
618 * and hence races in setting them will not cause malfunction621 * and hence races in setting them will not cause malfunction
fs/fuse/inode.c+3−1
@@ -928,6 +928,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) · 927 unmodified lines
928 fc->posix_acl = 1;928 fc->posix_acl = 1;
929 fc->sb->s_xattr = fuse_acl_xattr_handlers;929 fc->sb->s_xattr = fuse_acl_xattr_handlers;
930 }930 }
931 if (arg->flags & FUSE_CACHE_SYMLINKS)
932 fc->cache_symlinks = 1;
931 if (arg->flags & FUSE_ABORT_ERROR)933 if (arg->flags & FUSE_ABORT_ERROR)
932 fc->abort_err = 1;934 fc->abort_err = 1;
933 if (arg->flags & FUSE_MAX_PAGES) {935 if (arg->flags & FUSE_MAX_PAGES) {
@@ -966,7 +968,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) · 32 unmodified lines
966 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |968 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
967 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |969 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
968 FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |970 FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
969 FUSE_ABORT_ERROR | FUSE_MAX_PAGES;971 FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS;
970 req->in.h.opcode = FUSE_INIT;972 req->in.h.opcode = FUSE_INIT;
971 req->in.numargs = 1;973 req->in.numargs = 1;
972 req->in.args[0].size = sizeof(*arg);974 req->in.args[0].size = sizeof(*arg);
include/uapi/linux/fuse.h+3−0
@@ -121,6 +121,7 @@ · 120 unmodified lines
121 * - add FUSE_COPY_FILE_RANGE121 * - add FUSE_COPY_FILE_RANGE
122 * - add FOPEN_CACHE_DIR122 * - add FOPEN_CACHE_DIR
123 * - add FUSE_MAX_PAGES, add max_pages to init_out123 * - add FUSE_MAX_PAGES, add max_pages to init_out
124 * - add FUSE_CACHE_SYMLINKS
124 */125 */
125 126
126#ifndef _LINUX_FUSE_H127#ifndef _LINUX_FUSE_H
@@ -257,6 +258,7 @@ struct fuse_file_lock { · 130 unmodified lines
257 * FUSE_POSIX_ACL: filesystem supports posix acls258 * FUSE_POSIX_ACL: filesystem supports posix acls
258 * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED259 * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
259 * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages260 * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
261 * FUSE_CACHE_SYMLINKS: cache READLINK responses
260 */262 */
261#define FUSE_ASYNC_READ (1 << 0)263#define FUSE_ASYNC_READ (1 << 0)
262#define FUSE_POSIX_LOCKS (1 << 1)264#define FUSE_POSIX_LOCKS (1 << 1)
@@ -281,6 +283,7 @@ struct fuse_file_lock { · 18 unmodified lines
281#define FUSE_POSIX_ACL (1 << 20)283#define FUSE_POSIX_ACL (1 << 20)
282#define FUSE_ABORT_ERROR (1 << 21)284#define FUSE_ABORT_ERROR (1 << 21)
283#define FUSE_MAX_PAGES (1 << 22)285#define FUSE_MAX_PAGES (1 << 22)
286#define FUSE_CACHE_SYMLINKS (1 << 23)
284 287
285/**288/**
286 * CUSE INIT request/reply flags289 * CUSE INIT request/reply flags

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t