From: Ian Kent Date: Thu, 24 Mar 2011 17:51:20 +0000 (+0800) Subject: autofs4 - fix autofs4_expire_indirect() traversal X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=d4a85e35d1465da055264407d8395e84483084e6;p=deliverable%2Flinux.git autofs4 - fix autofs4_expire_indirect() traversal The vfs-scale changes changed the traversal used in autofs4_expire_indirect() from a list to a depth first tree traversal which isn't right. Signed-off-by: Ian Kent Signed-off-by: Al Viro --- diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index c403abcc725b..bc482e07b925 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -86,6 +86,56 @@ done: return status; } +/* + * Calculate and dget next entry in the subdirs list under root. + */ +static struct dentry *get_next_positive_subdir(struct dentry *prev, + struct dentry *root) +{ + struct list_head *next; + struct dentry *p, *q; + + spin_lock(&autofs4_lock); + + if (prev == NULL) { + spin_lock(&root->d_lock); + prev = dget_dlock(root); + next = prev->d_subdirs.next; + p = prev; + goto start; + } + + p = prev; + spin_lock(&p->d_lock); +again: + next = p->d_u.d_child.next; +start: + if (next == &root->d_subdirs) { + spin_unlock(&p->d_lock); + spin_unlock(&autofs4_lock); + dput(prev); + return NULL; + } + + q = list_entry(next, struct dentry, d_u.d_child); + + spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); + /* Negative dentry - try next */ + if (!simple_positive(q)) { + spin_unlock(&p->d_lock); + p = q; + goto again; + } + dget_dlock(q); + spin_unlock(&q->d_lock); + spin_unlock(&p->d_lock); + spin_unlock(&autofs4_lock); + + dput(prev); + + return q; +} + /* * Calculate and dget next entry in top down tree traversal. */ @@ -333,7 +383,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, timeout = sbi->exp_timeout; dentry = NULL; - while ((dentry = get_next_positive_dentry(dentry, root))) { + while ((dentry = get_next_positive_subdir(dentry, root))) { spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); /* No point expiring a pending mount */