Skip to content

Commit

Permalink
autofs4: allow RCU-walk to walk through autofs4
Browse files Browse the repository at this point in the history
This series teaches autofs about RCU-walk so that we don't drop straight
into REF-walk when we hit an autofs directory, and so that we avoid
spinlocks as much as possible when performing an RCU-walk.

This is needed so that the benefits of the recent NFS support for
RCU-walk are fully available when NFS filesystems are automounted.

Patches have been carefully reviewed and tested both with test suites
and in production - thanks a lot to Ian Kent for his support there.

This patch (of 6):

Any attempt to look up a pathname that passes though an autofs4 mount is
currently forced out of RCU-walk into REF-walk.

This can significantly hurt performance of many-thread work loads on
many-core systems, especially if the automounted filesystem supports
RCU-walk but doesn't get to benefit from it.

So if autofs4_d_manage is called with rcu_walk set, only fail with -ECHILD
if it is necessary to wait longer than a spinlock.

Signed-off-by: NeilBrown <[email protected]>
Reviewed-by: Ian Kent <[email protected]>
Tested-by: Ian Kent <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
neilbrown authored and torvalds committed Oct 14, 2014
1 parent 8a27334 commit 23bfc2a
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 18 deletions.
2 changes: 1 addition & 1 deletion fs/autofs4/autofs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ void autofs4_free_ino(struct autofs_info *);

/* Expiration */
int is_autofs4_dentry(struct dentry *);
int autofs4_expire_wait(struct dentry *dentry);
int autofs4_expire_wait(struct dentry *dentry, int rcu_walk);
int autofs4_expire_run(struct super_block *, struct vfsmount *,
struct autofs_sb_info *,
struct autofs_packet_expire __user *);
Expand Down
2 changes: 1 addition & 1 deletion fs/autofs4/dev-ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ static int autofs_dev_ioctl_requester(struct file *fp,
ino = autofs4_dentry_ino(path.dentry);
if (ino) {
err = 0;
autofs4_expire_wait(path.dentry);
autofs4_expire_wait(path.dentry, 0);
spin_lock(&sbi->fs_lock);
param->requester.uid = from_kuid_munged(current_user_ns(), ino->uid);
param->requester.gid = from_kgid_munged(current_user_ns(), ino->gid);
Expand Down
4 changes: 3 additions & 1 deletion fs/autofs4/expire.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
return expired;
}

int autofs4_expire_wait(struct dentry *dentry)
int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
Expand All @@ -471,6 +471,8 @@ int autofs4_expire_wait(struct dentry *dentry)
spin_lock(&sbi->fs_lock);
if (ino->flags & AUTOFS_INF_EXPIRING) {
spin_unlock(&sbi->fs_lock);
if (rcu_walk)
return -ECHILD;

DPRINTK("waiting for expire %p name=%.*s",
dentry, dentry->d_name.len, dentry->d_name.name);
Expand Down
44 changes: 29 additions & 15 deletions fs/autofs4/root.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
return NULL;
}

static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
static struct dentry *autofs4_lookup_expiring(struct dentry *dentry,
bool rcu_walk)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct dentry *parent = dentry->d_parent;
Expand All @@ -229,6 +230,11 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
struct dentry *expiring;
struct qstr *qstr;

if (rcu_walk) {
spin_unlock(&sbi->lookup_lock);
return ERR_PTR(-ECHILD);
}

ino = list_entry(p, struct autofs_info, expiring);
expiring = ino->dentry;

Expand Down Expand Up @@ -264,13 +270,15 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
return NULL;
}

static int autofs4_mount_wait(struct dentry *dentry)
static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
int status = 0;

if (ino->flags & AUTOFS_INF_PENDING) {
if (rcu_walk)
return -ECHILD;
DPRINTK("waiting for mount name=%.*s",
dentry->d_name.len, dentry->d_name.name);
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
Expand All @@ -280,20 +288,22 @@ static int autofs4_mount_wait(struct dentry *dentry)
return status;
}

static int do_expire_wait(struct dentry *dentry)
static int do_expire_wait(struct dentry *dentry, bool rcu_walk)
{
struct dentry *expiring;

expiring = autofs4_lookup_expiring(dentry);
expiring = autofs4_lookup_expiring(dentry, rcu_walk);
if (IS_ERR(expiring))
return PTR_ERR(expiring);
if (!expiring)
return autofs4_expire_wait(dentry);
return autofs4_expire_wait(dentry, rcu_walk);
else {
/*
* If we are racing with expire the request might not
* be quite complete, but the directory has been removed
* so it must have been successful, just wait for it.
*/
autofs4_expire_wait(expiring);
autofs4_expire_wait(expiring, 0);
autofs4_del_expiring(expiring);
dput(expiring);
}
Expand Down Expand Up @@ -345,15 +355,15 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
* and the directory was removed, so just go ahead and try
* the mount.
*/
status = do_expire_wait(dentry);
status = do_expire_wait(dentry, 0);
if (status && status != -EAGAIN)
return NULL;

/* Callback to the daemon to perform the mount or wait */
spin_lock(&sbi->fs_lock);
if (ino->flags & AUTOFS_INF_PENDING) {
spin_unlock(&sbi->fs_lock);
status = autofs4_mount_wait(dentry);
status = autofs4_mount_wait(dentry, 0);
if (status)
return ERR_PTR(status);
goto done;
Expand Down Expand Up @@ -394,7 +404,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
}
ino->flags |= AUTOFS_INF_PENDING;
spin_unlock(&sbi->fs_lock);
status = autofs4_mount_wait(dentry);
status = autofs4_mount_wait(dentry, 0);
spin_lock(&sbi->fs_lock);
ino->flags &= ~AUTOFS_INF_PENDING;
if (status) {
Expand Down Expand Up @@ -430,21 +440,25 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
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);
if (do_expire_wait(dentry, rcu_walk) == -ECHILD)
return -ECHILD;

/*
* This dentry may be under construction so wait on mount
* completion.
*/
status = autofs4_mount_wait(dentry);
status = autofs4_mount_wait(dentry, rcu_walk);
if (status)
return status;

if (rcu_walk)
/* it is always safe to return 0 as the worst that
* will happen is we retry in REF-walk mode.
* Better than always taking a lock.
*/
return 0;

spin_lock(&sbi->fs_lock);
/*
* If the dentry has been selected for expire while we slept
Expand Down

0 comments on commit 23bfc2a

Please sign in to comment.