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
11601160 return err;
11611161 }
11621162
1163−static const char *fuse_get_link(struct dentry *dentry,
1164− struct inode *inode,
1165− struct delayed_call *done)
1163+static int fuse_readlink_page(struct inode *inode, struct page *page)
11661164 {
11671165 struct fuse_conn *fc = get_fuse_conn(inode);
1168− FUSE_ARGS(args);
1169− char *link;
1170− ssize_t ret;
1166+ struct fuse_req *req;
1167+ int err;
11711168
1172− if (!dentry)
1173− return ERR_PTR(-ECHILD);
1169+ req = fuse_get_req(fc, 1);
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;
11741185
1175− link = kmalloc(PAGE_SIZE, GFP_KERNEL);
1176− if (!link)
1177− return ERR_PTR(-ENOMEM);
1186+ if (!err) {
1187+ char *link = page_address(page);
1188+ size_t len = req->out.args[0].size;
11781189
1179− args.in.h.opcode = FUSE_READLINK;
1180− args.in.h.nodeid = get_node_id(inode);
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);
1190+ BUG_ON(len >= PAGE_SIZE);
1191+ link[len] = '\0';
11921192 }
1193+
1194+ fuse_put_request(fc, req);
11931195 fuse_invalidate_atime(inode);
1194− return link;
1196+
1197+ return err;
1198+}
1199+
1200+static 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+
1233+out_err:
1234+ return ERR_PTR(err);
11951235 }
11961236
11971237 static int fuse_dir_open(struct inode *inode, struct file *file)
@@ -1644,7 +1684,25 @@ void fuse_init_dir(struct inode *inode) · 446 unmodified lines
16441684 fi->rdc.version = 0;
16451685 }
16461686
1687+static 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+
1699+static const struct address_space_operations fuse_symlink_aops = {
1700+ .readpage = fuse_symlink_readpage,
1701+};
1702+
16471703 void fuse_init_symlink(struct inode *inode)
16481704 {
16491705 inode->i_op = &fuse_symlink_inode_operations;
1706+ inode->i_data.a_ops = &fuse_symlink_aops;
1707+ inode_nohighmem(inode);
16501708 }
fs/fuse/fuse_i.h+3−0
@@ -613,6 +613,9 @@ struct fuse_conn { · 612 unmodified lines
613613 /** handle fs handles killing suid/sgid/cap on write/chown/trunc */
614614 unsigned handle_killpriv:1;
615615
616+ /** cache READLINK responses in page cache */
617+ unsigned cache_symlinks:1;
618+
616619 /*
617620 * The following bitfields are only for optimization purposes
618621 * 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
928928 fc->posix_acl = 1;
929929 fc->sb->s_xattr = fuse_acl_xattr_handlers;
930930 }
931+ if (arg->flags & FUSE_CACHE_SYMLINKS)
932+ fc->cache_symlinks = 1;
931933 if (arg->flags & FUSE_ABORT_ERROR)
932934 fc->abort_err = 1;
933935 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
966968 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
967969 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
968970 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;
970972 req->in.h.opcode = FUSE_INIT;
971973 req->in.numargs = 1;
972974 req->in.args[0].size = sizeof(*arg);
include/uapi/linux/fuse.h+3−0
@@ -121,6 +121,7 @@ · 120 unmodified lines
121121 * - add FUSE_COPY_FILE_RANGE
122122 * - add FOPEN_CACHE_DIR
123123 * - add FUSE_MAX_PAGES, add max_pages to init_out
124+ * - add FUSE_CACHE_SYMLINKS
124125 */
125126
126127 #ifndef _LINUX_FUSE_H
@@ -257,6 +258,7 @@ struct fuse_file_lock { · 130 unmodified lines
257258 * FUSE_POSIX_ACL: filesystem supports posix acls
258259 * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
259260 * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
261+ * FUSE_CACHE_SYMLINKS: cache READLINK responses
260262 */
261263 #define FUSE_ASYNC_READ (1 << 0)
262264 #define FUSE_POSIX_LOCKS (1 << 1)
@@ -281,6 +283,7 @@ struct fuse_file_lock { · 18 unmodified lines
281283 #define FUSE_POSIX_ACL (1 << 20)
282284 #define FUSE_ABORT_ERROR (1 << 21)
283285 #define FUSE_MAX_PAGES (1 << 22)
286+#define FUSE_CACHE_SYMLINKS (1 << 23)
284287
285288 /**
286289 * 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