From: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Date: Fri, 22 Jul 2011 08:44:51 -0700 Subject: [PATCH 1/2] VFS: Cut down inode->i_op->xyz accesses in path walking One of the biggest remaining unnecessary costs in path walking is the pointer chasing in inode operations. We already avoided the dentry->d_op derferences with the DCACHE_OP_xyz flags, this just starts doing the same thing for the i_op->xyz cases. Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> --- fs/dcache.c | 6 ++++++ fs/namei.c | 25 +++++++++++++------------ include/linux/dcache.h | 12 +++++++++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index fbdcbca40725..2dacddb7b101 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1374,6 +1374,12 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) if (unlikely(IS_AUTOMOUNT(inode))) dentry->d_flags |= DCACHE_NEED_AUTOMOUNT; list_add(&dentry->d_alias, &inode->i_dentry); + if (unlikely(inode->i_op->lookup)) + dentry->d_flags |= DCACHE_OP_LOOKUP; + if (unlikely(inode->i_op->permission)) + dentry->d_flags |= DCACHE_OP_PERMISSION; + if (unlikely(inode->i_op->follow_link)) + dentry->d_flags |= DCACHE_OP_FOLLOW_LINK; } dentry->d_inode = inode; dentry_rcuwalk_barrier(dentry); diff --git a/fs/namei.c b/fs/namei.c index 14ab8d3f2f0c..02b680e0e816 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -577,12 +577,12 @@ static int complete_walk(struct nameidata *nd) * short-cut DAC fails, then call ->permission() to do more * complete permission check. */ -static inline int exec_permission(struct inode *inode, unsigned int flags) +static inline int exec_permission(struct dentry *dentry, struct inode *inode, unsigned int flags) { int ret; struct user_namespace *ns = inode_userns(inode); - if (inode->i_op->permission) { + if (dentry->d_flags & DCACHE_OP_PERMISSION) { ret = inode->i_op->permission(inode, MAY_EXEC, flags); } else { ret = acl_permission_check(inode, MAY_EXEC, flags, @@ -1234,13 +1234,13 @@ retry: static inline int may_lookup(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { - int err = exec_permission(nd->inode, IPERM_FLAG_RCU); + int err = exec_permission(nd->path.dentry, nd->inode, IPERM_FLAG_RCU); if (err != -ECHILD) return err; if (unlazy_walk(nd, NULL)) return -ECHILD; } - return exec_permission(nd->inode, 0); + return exec_permission(nd->path.dentry, nd->inode, 0); } static inline int handle_dots(struct nameidata *nd, int type) @@ -1290,7 +1290,7 @@ static inline int walk_component(struct nameidata *nd, struct path *path, terminate_walk(nd); return -ENOENT; } - if (unlikely(inode->i_op->follow_link) && follow) { + if (unlikely(path->dentry->d_flags & DCACHE_OP_FOLLOW_LINK) && follow) { if (nd->flags & LOOKUP_RCU) { if (unlikely(unlazy_walk(nd, path->dentry))) { terminate_walk(nd); @@ -1425,7 +1425,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) return err; } err = -ENOTDIR; - if (!nd->inode->i_op->lookup) + if (!(nd->path.dentry->d_flags & DCACHE_OP_LOOKUP)) break; continue; /* here ends the main loop */ @@ -1452,11 +1452,12 @@ static int path_init(int dfd, const char *name, unsigned int flags, nd->flags = flags | LOOKUP_JUMPED; nd->depth = 0; if (flags & LOOKUP_ROOT) { - struct inode *inode = nd->root.dentry->d_inode; + struct dentry *dentry = nd->root.dentry; + struct inode *inode = dentry->d_inode; if (*name) { - if (!inode->i_op->lookup) + if (!(dentry->d_flags & DCACHE_OP_LOOKUP)) return -ENOTDIR; - retval = inode_permission(inode, MAY_EXEC); + retval = exec_permission(dentry, inode, 0); if (retval) return retval; } @@ -1599,7 +1600,7 @@ static int path_lookupat(int dfd, const char *name, err = complete_walk(nd); if (!err && nd->flags & LOOKUP_DIRECTORY) { - if (!nd->inode->i_op->lookup) { + if (!(nd->path.dentry->d_flags & DCACHE_OP_LOOKUP)) { path_put(&nd->path); err = -ENOTDIR; } @@ -1672,7 +1673,7 @@ static struct dentry *__lookup_hash(struct qstr *name, struct dentry *dentry; int err; - err = exec_permission(inode, 0); + err = exec_permission(base, inode, 0); if (err) return ERR_PTR(err); @@ -2099,7 +2100,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, error = -ENOTDIR; if (nd->flags & LOOKUP_DIRECTORY) { - if (!nd->inode->i_op->lookup) + if (!(nd->path.dentry->d_flags & DCACHE_OP_LOOKUP)) goto exit; } audit_inode(pathname, nd->path.dentry); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 19d90a55541d..8cd1a3d5b320 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -208,14 +208,20 @@ struct dentry_operations { #define DCACHE_CANT_MOUNT 0x0100 #define DCACHE_GENOCIDE 0x0200 +/* Avoid 'dentry->d_op->op' dereference chain */ #define DCACHE_OP_HASH 0x1000 #define DCACHE_OP_COMPARE 0x2000 #define DCACHE_OP_REVALIDATE 0x4000 #define DCACHE_OP_DELETE 0x8000 -#define DCACHE_MOUNTED 0x10000 /* is a mountpoint */ -#define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */ -#define DCACHE_MANAGE_TRANSIT 0x40000 /* manage transit from this dirent */ +/* Avoid 'inode->i_op->op' dereference chain */ +#define DCACHE_OP_LOOKUP 0x10000 +#define DCACHE_OP_PERMISSION 0x20000 +#define DCACHE_OP_FOLLOW_LINK 0x40000 + +#define DCACHE_MOUNTED 0x100000 /* is a mountpoint */ +#define DCACHE_NEED_AUTOMOUNT 0x200000 /* handle automount on this dir */ +#define DCACHE_MANAGE_TRANSIT 0x400000 /* manage transit from this dirent */ #define DCACHE_MANAGED_DENTRY \ (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT) -- 1.7.6.233.gd79bc.dirty -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html