Commit ab90911f authored by David Howells's avatar David Howells Committed by Al Viro

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: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 87556ef1
...@@ -32,7 +32,7 @@ d_release: no no yes no ...@@ -32,7 +32,7 @@ d_release: no no yes no
d_iput: no no yes no d_iput: no no yes no
d_dname: no no no no d_dname: no no no no
d_automount: no no yes no d_automount: no no yes no
d_manage: no no yes no d_manage: no no yes (ref-walk) maybe
--------------------------- inode_operations --------------------------- --------------------------- inode_operations ---------------------------
prototypes: prototypes:
......
...@@ -865,7 +865,7 @@ struct dentry_operations { ...@@ -865,7 +865,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool, bool);
}; };
d_revalidate: called when the VFS needs to revalidate a dentry. This d_revalidate: called when the VFS needs to revalidate a dentry. This
...@@ -960,6 +960,11 @@ struct dentry_operations { ...@@ -960,6 +960,11 @@ struct dentry_operations {
held by the caller and the function should not initiate any mounts or held by the caller and the function should not initiate any mounts or
unmounts that it will then wait for. unmounts that it will then wait for.
If the 'rcu_walk' parameter is true, then the caller is doing a
pathwalk in RCU-walk mode. Sleeping is not permitted in this mode,
and the caller can be asked to leave it and call again by returing
-ECHILD.
This function is only used if DCACHE_MANAGE_TRANSIT is set on the This function is only used if DCACHE_MANAGE_TRANSIT is set on the
dentry being transited from. dentry being transited from.
......
...@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); ...@@ -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 int autofs4_dir_open(struct inode *inode, struct file *file);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
static struct vfsmount *autofs4_d_automount(struct path *); 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 = { const struct file_operations autofs4_root_operations = {
.open = dcache_dir_open, .open = dcache_dir_open,
...@@ -450,7 +450,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) ...@@ -450,7 +450,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
return NULL; 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); struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
...@@ -464,6 +464,10 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here) ...@@ -464,6 +464,10 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
return 0; return 0;
} }
/* We need to sleep, so we need pathwalk to be in ref-mode */
if (rcu_walk)
return -ECHILD;
/* Wait for pending expires */ /* Wait for pending expires */
do_expire_wait(dentry); do_expire_wait(dentry);
......
...@@ -987,7 +987,8 @@ static int follow_managed(struct path *path, unsigned flags) ...@@ -987,7 +987,8 @@ static int follow_managed(struct path *path, unsigned flags)
if (managed & DCACHE_MANAGE_TRANSIT) { if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op); BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage); 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) if (ret < 0)
return ret == -EISDIR ? 0 : ret; return ret == -EISDIR ? 0 : ret;
} }
...@@ -1048,13 +1049,12 @@ int follow_down_one(struct path *path) ...@@ -1048,13 +1049,12 @@ int follow_down_one(struct path *path)
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
struct inode **inode, bool reverse_transit) struct inode **inode, bool reverse_transit)
{ {
unsigned abort_mask =
reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT;
while (d_mountpoint(path->dentry)) { while (d_mountpoint(path->dentry)) {
struct vfsmount *mounted; struct vfsmount *mounted;
if (path->dentry->d_flags & abort_mask) if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
return true; !reverse_transit &&
path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
return false;
mounted = __lookup_mnt(path->mnt, path->dentry, 1); mounted = __lookup_mnt(path->mnt, path->dentry, 1);
if (!mounted) if (!mounted)
break; break;
...@@ -1132,7 +1132,8 @@ int follow_down(struct path *path, bool mounting_here) ...@@ -1132,7 +1132,8 @@ int follow_down(struct path *path, bool mounting_here)
if (managed & DCACHE_MANAGE_TRANSIT) { if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op); BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage); 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) if (ret < 0)
return ret == -EISDIR ? 0 : ret; return ret == -EISDIR ? 0 : ret;
} }
......
...@@ -168,7 +168,7 @@ struct dentry_operations { ...@@ -168,7 +168,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool, bool);
} ____cacheline_aligned; } ____cacheline_aligned;
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment