summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-01-14 18:46:51 +0000
committerAl Viro <viro@zeniv.linux.org.uk>2011-01-15 20:07:47 -0500
commitab90911ff90cdab59b31c045c3f0ae480d14f29d (patch)
tree683450a66eb9dc6bf053e38d63f4740bb53a7b6e /fs
parent87556ef19926e97464e0163a7840140527ae6615 (diff)
Allow d_manage() to be used in RCU-walk mode
Allow d_manage() to be called from pathwalk when it is in RCU-walk mode as well as when it is in Ref-walk mode. This permits __follow_mount_rcu() to call d_manage() directly. d_manage() needs a parameter to indicate that it is in RCU-walk mode as it isn't allowed to sleep if in that mode (but should return -ECHILD instead). autofs4_d_manage() can then be set to retain RCU-walk mode if the daemon accesses it and otherwise request dropping back to ref-walk mode. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/autofs4/root.c8
-rw-r--r--fs/namei.c15
2 files changed, 14 insertions, 9 deletions
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 9194e274f849..dbd95512808c 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
static int autofs4_dir_open(struct inode *inode, struct file *file);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
static struct vfsmount *autofs4_d_automount(struct path *);
-static int autofs4_d_manage(struct dentry *, bool);
+static int autofs4_d_manage(struct dentry *, bool, bool);
const struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
@@ -450,7 +450,7 @@ done:
return NULL;
}
-int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
+int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
@@ -464,6 +464,10 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
return 0;
}
+ /* We need to sleep, so we need pathwalk to be in ref-mode */
+ if (rcu_walk)
+ return -ECHILD;
+
/* Wait for pending expires */
do_expire_wait(dentry);
diff --git a/fs/namei.c b/fs/namei.c
index 373852012713..5c89695ae1e4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -987,7 +987,8 @@ static int follow_managed(struct path *path, unsigned flags)
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
- ret = path->dentry->d_op->d_manage(path->dentry, false);
+ ret = path->dentry->d_op->d_manage(path->dentry,
+ false, false);
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}
@@ -1048,13 +1049,12 @@ int follow_down_one(struct path *path)
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
struct inode **inode, bool reverse_transit)
{
- unsigned abort_mask =
- reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT;
-
while (d_mountpoint(path->dentry)) {
struct vfsmount *mounted;
- if (path->dentry->d_flags & abort_mask)
- return true;
+ if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
+ !reverse_transit &&
+ path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
+ return false;
mounted = __lookup_mnt(path->mnt, path->dentry, 1);
if (!mounted)
break;
@@ -1132,7 +1132,8 @@ int follow_down(struct path *path, bool mounting_here)
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
- ret = path->dentry->d_op->d_manage(path->dentry, mounting_here);
+ ret = path->dentry->d_op->d_manage(
+ path->dentry, mounting_here, false);
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}