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>
d_iput: no no yes no
d_dname: no no no no
d_automount: no no yes no
d_iput: no no yes no
d_dname: no no no no
d_automount: no no yes no
+d_manage: no no yes (ref-walk) maybe
--------------------------- inode_operations ---------------------------
prototypes:
--------------------------- inode_operations ---------------------------
prototypes:
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
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
held by the caller and the function should not initiate any mounts or
unmounts that it will then wait for.
held by the caller and the function should not initiate any mounts or
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
dentry being transited from.
This function is only used if DCACHE_MANAGE_TRANSIT is set on the
dentry being transited from.
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_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,
const struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
-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);
+ /* 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);
/* Wait for pending expires */
do_expire_wait(dentry);
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
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;
}
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
struct inode **inode, bool reverse_transit)
{
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;
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;
mounted = __lookup_mnt(path->mnt, path->dentry, 1);
if (!mounted)
break;
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
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;
}
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
- int (*d_manage)(struct dentry *, bool);
+ int (*d_manage)(struct dentry *, bool, bool);
} ____cacheline_aligned;
/*
} ____cacheline_aligned;
/*