From 34286d6662308d82aed891852d04c7c3a2649b16 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 7 Jan 2011 17:49:57 +1100 Subject: [PATCH] fs: rcu-walk aware d_revalidate method Require filesystems be aware of .d_revalidate being called in rcu-walk mode (nd->flags & LOOKUP_RCU). For now do a simple push down, returning -ECHILD from all implementations. Signed-off-by: Nick Piggin --- Documentation/filesystems/Locking | 18 ++++---- Documentation/filesystems/path-lookup.txt | 5 +-- Documentation/filesystems/porting | 20 +++++++++ Documentation/filesystems/vfs.txt | 9 ++++ drivers/staging/autofs/root.c | 5 ++- drivers/staging/smbfs/dir.c | 16 +++++-- fs/afs/dir.c | 4 ++ fs/autofs4/root.c | 13 ++++-- fs/ceph/dir.c | 7 ++- fs/cifs/dir.c | 3 ++ fs/coda/dir.c | 7 ++- fs/ecryptfs/dentry.c | 9 +++- fs/fat/namei_vfat.c | 6 +++ fs/fuse/dir.c | 6 ++- fs/gfs2/dentry.c | 17 +++++-- fs/hfs/sysdep.c | 7 ++- fs/jfs/namei.c | 2 + fs/namei.c | 54 +++++++++++++++-------- fs/ncpfs/dir.c | 4 ++ fs/ncpfs/inode.c | 1 + fs/nfs/dir.c | 8 +++- fs/ocfs2/dcache.c | 10 ++++- fs/proc/base.c | 33 +++++++++++--- fs/proc/proc_sysctl.c | 3 ++ fs/reiserfs/xattr.c | 2 + fs/sysfs/dir.c | 6 ++- include/linux/dcache.h | 1 - 27 files changed, 215 insertions(+), 61 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index bdad6414dfa0..e90ffe61eb65 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -9,7 +9,7 @@ be able to use diff(1). --------------------------- dentry_operations -------------------------- prototypes: - int (*d_revalidate)(struct dentry *, int); + int (*d_revalidate)(struct dentry *, struct nameidata *); int (*d_hash)(const struct dentry *, const struct inode *, struct qstr *); int (*d_compare)(const struct dentry *, const struct inode *, @@ -21,14 +21,14 @@ prototypes: char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); locking rules: - rename_lock ->d_lock may block -d_revalidate: no no yes -d_hash no no no -d_compare: yes no no -d_delete: no yes no -d_release: no no yes -d_iput: no no yes -d_dname: no no no + rename_lock ->d_lock may block rcu-walk +d_revalidate: no no yes (ref-walk) maybe +d_hash no no no maybe +d_compare: yes no no maybe +d_delete: no yes no no +d_release: no no yes no +d_iput: no no yes no +d_dname: no no no no --------------------------- inode_operations --------------------------- prototypes: diff --git a/Documentation/filesystems/path-lookup.txt b/Documentation/filesystems/path-lookup.txt index 09b2878724a1..8789d1810bed 100644 --- a/Documentation/filesystems/path-lookup.txt +++ b/Documentation/filesystems/path-lookup.txt @@ -317,11 +317,10 @@ The detailed design for rcu-walk is like this: The cases where rcu-walk cannot continue are: * NULL dentry (ie. any uncached path element) * parent with d_inode->i_op->permission or ACLs -* dentries with d_revalidate * Following links -In future patches, permission checks and d_revalidate become rcu-walk aware. It -may be possible eventually to make following links rcu-walk aware. +In future patches, permission checks become rcu-walk aware. It may be possible +eventually to make following links rcu-walk aware. Uncached path elements will always require dropping to ref-walk mode, at the very least because i_mutex needs to be grabbed, and objects allocated. diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index ccf0ce7866b9..cd9756a2709d 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -360,3 +360,23 @@ i_dentry to be reinitialized before it is freed, so an: INIT_LIST_HEAD(&inode->i_dentry); must be done in the RCU callback. + +-- +[recommended] + vfs now tries to do path walking in "rcu-walk mode", which avoids +atomic operations and scalability hazards on dentries and inodes (see +Documentation/filesystems/path-walk.txt). d_hash and d_compare changes (above) +are examples of the changes required to support this. For more complex +filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so +no changes are required to the filesystem. However, this is costly and loses +the benefits of rcu-walk mode. We will begin to add filesystem callbacks that +are rcu-walk aware, shown below. Filesystems should take advantage of this +where possible. + +-- +[mandatory] + d_revalidate is a callback that is made on every path element (if +the filesystem provides it), which requires dropping out of rcu-walk mode. This +may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be +returned if the filesystem cannot handle rcu-walk. See +Documentation/filesystems/vfs.txt for more details. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 69b10ff5ec81..c936b4912383 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -863,6 +863,15 @@ struct dentry_operations { dcache. Most filesystems leave this as NULL, because all their dentries in the dcache are valid + d_revalidate may be called in rcu-walk mode (nd->flags & LOOKUP_RCU). + If in rcu-walk mode, the filesystem must revalidate the dentry without + blocking or storing to the dentry, d_parent and d_inode should not be + used without care (because they can go NULL), instead nd->inode should + be used. + + If a situation is encountered that rcu-walk cannot handle, return + -ECHILD and it will be called again in ref-walk mode. + d_hash: called when the VFS adds a dentry to the hash table. The first dentry passed to d_hash is the parent directory that the name is to be hashed into. The inode is the dentry's inode. diff --git a/drivers/staging/autofs/root.c b/drivers/staging/autofs/root.c index b09adb57971f..bf0e9755da67 100644 --- a/drivers/staging/autofs/root.c +++ b/drivers/staging/autofs/root.c @@ -154,13 +154,16 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd) +static int autofs_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode * dir; struct autofs_sb_info *sbi; struct autofs_dir_ent *ent; int res; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + lock_kernel(); dir = dentry->d_parent->d_inode; sbi = autofs_sbi(dir->i_sb); diff --git a/drivers/staging/smbfs/dir.c b/drivers/staging/smbfs/dir.c index 78f09412740c..dd612f50749f 100644 --- a/drivers/staging/smbfs/dir.c +++ b/drivers/staging/smbfs/dir.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "smb_fs.h" #include "smb_mount.h" @@ -301,13 +302,20 @@ static const struct dentry_operations smbfs_dentry_operations_case = * This is the callback when the dcache has a lookup hit. */ static int -smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) +smb_lookup_validate(struct dentry *dentry, struct nameidata *nd) { - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode * inode = dentry->d_inode; - unsigned long age = jiffies - dentry->d_time; + struct smb_sb_info *server; + struct inode *inode; + unsigned long age; int valid; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + server = server_from_dentry(dentry); + inode = dentry->d_inode; + age = jiffies - dentry->d_time; + /* * The default validation is based on dentry age: * we believe in dentries for a few seconds. (But each diff --git a/fs/afs/dir.c b/fs/afs/dir.c index b8bb7e7148d0..34a3263d60a4 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -607,6 +608,9 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) void *dir_version; int ret; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + vnode = AFS_FS_I(dentry->d_inode); if (dentry->d_inode) diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index bfe3f2eb684d..651e4ef563b1 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -315,12 +315,19 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) */ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); + struct inode *dir; + struct autofs_sb_info *sbi; + int oz_mode; int flags = nd ? nd->flags : 0; int status = 1; + if (flags & LOOKUP_RCU) + return -ECHILD; + + dir = dentry->d_parent->d_inode; + sbi = autofs4_sbi(dir->i_sb); + oz_mode = autofs4_oz_mode(sbi); + /* Pending dentry */ spin_lock(&sbi->fs_lock); if (autofs4_ispending(dentry)) { diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index cc01cf826769..fa7ca04ee816 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -990,7 +990,12 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry) */ static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *dir = dentry->d_parent->d_inode; + struct inode *dir; + + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + dir = dentry->d_parent->d_inode; dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e3b10ca6d453..db2a58c00f7b 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -656,6 +656,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, static int cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + if (direntry->d_inode) { if (cifs_revalidate_dentry(direntry)) return 0; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index aa40c811f8d2..619a8303766e 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -541,9 +542,13 @@ static int coda_venus_readdir(struct file *coda_file, void *buf, /* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) { - struct inode *inode = de->d_inode; + struct inode *inode; struct coda_inode_info *cii; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = de->d_inode; if (!inode || coda_isroot(inode)) goto out; if (is_bad_inode(inode)) diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c index 906e803f7f79..6fc4f319b550 100644 --- a/fs/ecryptfs/dentry.c +++ b/fs/ecryptfs/dentry.c @@ -44,12 +44,17 @@ */ static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct dentry *dentry_save; struct vfsmount *vfsmount_save; int rc = 1; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; dentry_save = nd->path.dentry; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 3be5ed7d859f..e3ffc5e12332 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -43,6 +43,9 @@ static int vfat_revalidate_shortname(struct dentry *dentry) static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + /* This is not negative dentry. Always valid. */ if (dentry->d_inode) return 1; @@ -51,6 +54,9 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + /* * This is not negative dentry. Always valid. * diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c9a8a426a395..07f4b5e675fc 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -156,8 +156,12 @@ u64 fuse_get_attr_version(struct fuse_conn *fc) */ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) { - struct inode *inode = entry->d_inode; + struct inode *inode; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = entry->d_inode; if (inode && is_bad_inode(inode)) return 0; else if (fuse_dentry_time(entry) < get_jiffies_64()) { diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index 50497f65763b..4a456338b873 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "gfs2.h" @@ -34,15 +35,23 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { - struct dentry *parent = dget_parent(dentry); - struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); - struct gfs2_inode *dip = GFS2_I(parent->d_inode); - struct inode *inode = dentry->d_inode; + struct dentry *parent; + struct gfs2_sbd *sdp; + struct gfs2_inode *dip; + struct inode *inode; struct gfs2_holder d_gh; struct gfs2_inode *ip = NULL; int error; int had_lock = 0; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + parent = dget_parent(dentry); + sdp = GFS2_SB(parent->d_inode); + dip = GFS2_I(parent->d_inode); + inode = dentry->d_inode; + if (inode) { if (is_bad_inode(inode)) goto invalid; diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index 7478f5c219aa..19cf291eb91f 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c @@ -8,15 +8,20 @@ * This file contains the code to do various system dependent things. */ +#include #include "hfs_fs.h" /* dentry case-handling: just lowercase everything */ static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; + struct inode *inode; int diff; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = dentry->d_inode; if(!inode) return 1; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index a151cbdec626..4414e3a42264 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1608,6 +1608,8 @@ static int jfs_ci_compare(const struct dentry *parent, static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; /* * This is not negative dentry. Always valid. * diff --git a/fs/namei.c b/fs/namei.c index 90bd2873e117..6e275363e89d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -563,10 +563,26 @@ void release_open_intent(struct nameidata *nd) fput(nd->intent.open.file); } +static int d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int status; + + status = dentry->d_op->d_revalidate(dentry, nd); + if (status == -ECHILD) { + if (nameidata_dentry_drop_rcu(nd, dentry)) + return status; + status = dentry->d_op->d_revalidate(dentry, nd); + } + + return status; +} + static inline struct dentry * do_revalidate(struct dentry *dentry, struct nameidata *nd) { - int status = dentry->d_op->d_revalidate(dentry, nd); + int status; + + status = d_revalidate(dentry, nd); if (unlikely(status <= 0)) { /* * The dentry failed validation. @@ -574,14 +590,20 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd) * the dentry otherwise d_revalidate is asking us * to return a fail status. */ - if (!status) { + if (status < 0) { + /* If we're in rcu-walk, we don't have a ref */ + if (!(nd->flags & LOOKUP_RCU)) + dput(dentry); + dentry = ERR_PTR(status); + + } else { + /* Don't d_invalidate in rcu-walk mode */ + if (nameidata_dentry_drop_rcu_maybe(nd, dentry)) + return ERR_PTR(-ECHILD); if (!d_invalidate(dentry)) { dput(dentry); dentry = NULL; } - } else { - dput(dentry); - dentry = ERR_PTR(status); } } return dentry; @@ -626,7 +648,7 @@ force_reval_path(struct path *path, struct nameidata *nd) if (!need_reval_dot(dentry)) return 0; - status = dentry->d_op->d_revalidate(dentry, nd); + status = d_revalidate(dentry, nd); if (status > 0) return 0; @@ -1039,12 +1061,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, return -ECHILD; nd->seq = seq; - if (dentry->d_flags & DCACHE_OP_REVALIDATE) { - /* We commonly drop rcu-walk here */ - if (nameidata_dentry_drop_rcu(nd, dentry)) - return -ECHILD; + if (dentry->d_flags & DCACHE_OP_REVALIDATE) goto need_revalidate; - } path->mnt = mnt; path->dentry = dentry; __follow_mount_rcu(nd, path, inode); @@ -1292,12 +1310,11 @@ static int link_path_walk(const char *name, struct nameidata *nd) * We may need to check the cached dentry for staleness. */ if (need_reval_dot(nd->path.dentry)) { - if (nameidata_drop_rcu_maybe(nd)) - return -ECHILD; - err = -ESTALE; /* Note: we do not d_invalidate() */ - if (!nd->path.dentry->d_op->d_revalidate( - nd->path.dentry, nd)) + err = d_revalidate(nd->path.dentry, nd); + if (!err) + err = -ESTALE; + if (err < 0) break; } return_base: @@ -2080,10 +2097,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path, dir = nd->path.dentry; case LAST_DOT: if (need_reval_dot(dir)) { - if (!dir->d_op->d_revalidate(dir, nd)) { + error = d_revalidate(nd->path.dentry, nd); + if (!error) error = -ESTALE; + if (error < 0) goto exit; - } } /* fallthrough */ case LAST_ROOT: diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 4b9cbb28d7fa..28f136d4aaec 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -308,6 +309,9 @@ ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd) int res, val = 0, len; __u8 __name[NCP_MAXPATHLEN + 1]; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + parent = dget_parent(dentry); dir = parent->d_inode; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 0c75a5f3cafd..9531c052d7a4 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -29,6 +29,7 @@ #include #include #include +#include #include diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 37e0a8bb077e..58beace14b19 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -938,7 +938,8 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) * component of the path. * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. */ -static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask) +static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, + unsigned int mask) { if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) return 0; @@ -1018,7 +1019,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, * If the parent directory is seen to have changed, we throw out the * cached dentry and do a new lookup. */ -static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) +static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode *dir; struct inode *inode; @@ -1027,6 +1028,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) struct nfs_fattr *fattr = NULL; int error; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + parent = dget_parent(dentry); dir = parent->d_inode; nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 4d54c60ceee4..0310b16a7238 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -52,9 +52,15 @@ void ocfs2_dentry_attach_gen(struct dentry *dentry) static int ocfs2_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; + struct inode *inode; int ret = 0; /* if all else fails, just return false */ - struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb); + struct ocfs2_super *osb; + + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = dentry->d_inode; + osb = OCFS2_SB(dentry->d_sb); mlog_entry("(0x%p, '%.*s')\n", dentry, dentry->d_name.len, dentry->d_name.name); diff --git a/fs/proc/base.c b/fs/proc/base.c index 85f0a80912aa..dc5b2fcadc3b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1719,10 +1719,16 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat */ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; - struct task_struct *task = get_proc_task(inode); + struct inode *inode; + struct task_struct *task; const struct cred *cred; + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = dentry->d_inode; + task = get_proc_task(inode); + if (task) { if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || task_dumpable(task)) { @@ -1888,12 +1894,19 @@ static int proc_fd_link(struct inode *inode, struct path *path) static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; - struct task_struct *task = get_proc_task(inode); - int fd = proc_fd(inode); + struct inode *inode; + struct task_struct *task; + int fd; struct files_struct *files; const struct cred *cred; + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = dentry->d_inode; + task = get_proc_task(inode); + fd = proc_fd(inode); + if (task) { files = get_files_struct(task); if (files) { @@ -2563,8 +2576,14 @@ static const struct pid_entry proc_base_stuff[] = { */ static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; - struct task_struct *task = get_proc_task(inode); + struct inode *inode; + struct task_struct *task; + + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + inode = dentry->d_inode; + task = get_proc_task(inode); if (task) { put_task_struct(task); return 1; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 35efd85a4d32..c9097f43b425 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "internal.h" static const struct dentry_operations proc_sys_dentry_operations; @@ -389,6 +390,8 @@ static const struct inode_operations proc_sys_dir_operations = { static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; return !PROC_I(dentry->d_inode)->sysctl->unregistering; } diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index e0f0d7ea10a1..9ea22a56cdf1 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -972,6 +972,8 @@ int reiserfs_permission(struct inode *inode, int mask) static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd) { + if (nd->flags & LOOKUP_RCU) + return -ECHILD; return -EPERM; } diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 3e076caa8daf..ea9120a830d8 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -239,9 +239,13 @@ static int sysfs_dentry_delete(const struct dentry *dentry) static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_dirent *sd; int is_dir; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + sd = dentry->d_fsdata; mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index b1aeda077258..8b2064d02928 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -190,7 +190,6 @@ struct dentry_operations { #define DCACHE_OP_REVALIDATE 0x4000 #define DCACHE_OP_DELETE 0x8000 - extern spinlock_t dcache_inode_lock; extern seqlock_t rename_lock;