autofs: use dentry flags to block walks during expire
Somewhere along the way the autofs expire operation has changed to hold a spin lock over expired dentry selection. The autofs indirect mount expired dentry selection is complicated and quite lengthy so it isn't appropriate to hold a spin lock over the operation. Commit47be61845c
("fs/dcache.c: avoid soft-lockup in dput()") added a might_sleep() to dput() causing a WARN_ONCE() about this usage to be issued. But the spin lock doesn't need to be held over this check, the autofs dentry info. flags are enough to block walks into dentrys during the expire. I've left the direct mount expire as it is (for now) because it is much simpler and quicker than the indirect mount expire and adding spin lock release and re-aquires would do nothing more than add overhead. Fixes:47be61845c
("fs/dcache.c: avoid soft-lockup in dput()") Link: http://lkml.kernel.org/r/20160912014017.1773.73060.stgit@pluto.themaw.net Signed-off-by: Ian Kent <raven@themaw.net> Reported-by: Takashi Iwai <tiwai@suse.de> Tested-by: Takashi Iwai <tiwai@suse.de> Cc: Takashi Iwai <tiwai@suse.de> Cc: NeilBrown <neilb@suse.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
08eeb3061e
commit
7cbdb4a286
@ -417,6 +417,7 @@ static struct dentry *should_expire(struct dentry *dentry,
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find an eligible tree to time-out
|
* Find an eligible tree to time-out
|
||||||
* A tree is eligible if :-
|
* A tree is eligible if :-
|
||||||
@ -432,6 +433,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
|||||||
struct dentry *root = sb->s_root;
|
struct dentry *root = sb->s_root;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct dentry *expired;
|
struct dentry *expired;
|
||||||
|
struct dentry *found;
|
||||||
struct autofs_info *ino;
|
struct autofs_info *ino;
|
||||||
|
|
||||||
if (!root)
|
if (!root)
|
||||||
@ -442,31 +444,46 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
|||||||
|
|
||||||
dentry = NULL;
|
dentry = NULL;
|
||||||
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
||||||
|
int flags = how;
|
||||||
|
|
||||||
spin_lock(&sbi->fs_lock);
|
spin_lock(&sbi->fs_lock);
|
||||||
ino = autofs4_dentry_ino(dentry);
|
ino = autofs4_dentry_ino(dentry);
|
||||||
if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
|
if (ino->flags & AUTOFS_INF_WANT_EXPIRE) {
|
||||||
expired = NULL;
|
|
||||||
else
|
|
||||||
expired = should_expire(dentry, mnt, timeout, how);
|
|
||||||
if (!expired) {
|
|
||||||
spin_unlock(&sbi->fs_lock);
|
spin_unlock(&sbi->fs_lock);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
spin_unlock(&sbi->fs_lock);
|
||||||
|
|
||||||
|
expired = should_expire(dentry, mnt, timeout, flags);
|
||||||
|
if (!expired)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
spin_lock(&sbi->fs_lock);
|
||||||
ino = autofs4_dentry_ino(expired);
|
ino = autofs4_dentry_ino(expired);
|
||||||
ino->flags |= AUTOFS_INF_WANT_EXPIRE;
|
ino->flags |= AUTOFS_INF_WANT_EXPIRE;
|
||||||
spin_unlock(&sbi->fs_lock);
|
spin_unlock(&sbi->fs_lock);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
spin_lock(&sbi->fs_lock);
|
|
||||||
if (should_expire(expired, mnt, timeout, how)) {
|
/* Make sure a reference is not taken on found if
|
||||||
|
* things have changed.
|
||||||
|
*/
|
||||||
|
flags &= ~AUTOFS_EXP_LEAVES;
|
||||||
|
found = should_expire(expired, mnt, timeout, how);
|
||||||
|
if (!found || found != expired)
|
||||||
|
/* Something has changed, continue */
|
||||||
|
goto next;
|
||||||
|
|
||||||
if (expired != dentry)
|
if (expired != dentry)
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
spin_lock(&sbi->fs_lock);
|
||||||
|
goto found;
|
||||||
|
next:
|
||||||
|
spin_lock(&sbi->fs_lock);
|
||||||
ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
|
ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
|
||||||
|
spin_unlock(&sbi->fs_lock);
|
||||||
if (expired != dentry)
|
if (expired != dentry)
|
||||||
dput(expired);
|
dput(expired);
|
||||||
spin_unlock(&sbi->fs_lock);
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -483,6 +500,7 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
|
|||||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||||
int status;
|
int status;
|
||||||
|
int state;
|
||||||
|
|
||||||
/* Block on any pending expire */
|
/* Block on any pending expire */
|
||||||
if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
|
if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
|
||||||
@ -490,8 +508,19 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
|
|||||||
if (rcu_walk)
|
if (rcu_walk)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
|
retry:
|
||||||
spin_lock(&sbi->fs_lock);
|
spin_lock(&sbi->fs_lock);
|
||||||
if (ino->flags & AUTOFS_INF_EXPIRING) {
|
state = ino->flags & (AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING);
|
||||||
|
if (state == AUTOFS_INF_WANT_EXPIRE) {
|
||||||
|
spin_unlock(&sbi->fs_lock);
|
||||||
|
/*
|
||||||
|
* Possibly being selected for expire, wait until
|
||||||
|
* it's selected or not.
|
||||||
|
*/
|
||||||
|
schedule_timeout_uninterruptible(HZ/10);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
if (state & AUTOFS_INF_EXPIRING) {
|
||||||
spin_unlock(&sbi->fs_lock);
|
spin_unlock(&sbi->fs_lock);
|
||||||
|
|
||||||
pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
|
pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
|
||||||
|
Loading…
Reference in New Issue
Block a user