Skip to content

Commit a45440f

Browse files
raven-autorvalds
authored andcommitted
autofs4 - fix get_next_positive_subdir()
Following a report of a crash during an automount expire I found that the locking in fs/autofs4/expire.c:get_next_positive_subdir() was wrong. Not only is the locking wrong but the function is more complex than it needs to be. The function is meant to calculate (and dget) the next entry in the list of directories contained in the root of an autofs mount point (an autofs indirect mount to be precise). The main problem was that the d_lock of the owner of the list was not being taken when walking the list, which lead to list corruption under load. The only other lock that needs to be taken is against the next dentry candidate so it can be checked for usability. Signed-off-by: Ian Kent <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 63ca5f1 commit a45440f

File tree

1 file changed

+13
-18
lines changed

1 file changed

+13
-18
lines changed

fs/autofs4/expire.c

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,25 +94,21 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
9494
{
9595
struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb);
9696
struct list_head *next;
97-
struct dentry *p, *q;
97+
struct dentry *q;
9898

9999
spin_lock(&sbi->lookup_lock);
100+
spin_lock(&root->d_lock);
100101

101-
if (prev == NULL) {
102-
spin_lock(&root->d_lock);
102+
if (prev)
103+
next = prev->d_u.d_child.next;
104+
else {
103105
prev = dget_dlock(root);
104106
next = prev->d_subdirs.next;
105-
p = prev;
106-
goto start;
107107
}
108108

109-
p = prev;
110-
spin_lock(&p->d_lock);
111-
again:
112-
next = p->d_u.d_child.next;
113-
start:
109+
cont:
114110
if (next == &root->d_subdirs) {
115-
spin_unlock(&p->d_lock);
111+
spin_unlock(&root->d_lock);
116112
spin_unlock(&sbi->lookup_lock);
117113
dput(prev);
118114
return NULL;
@@ -121,16 +117,15 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
121117
q = list_entry(next, struct dentry, d_u.d_child);
122118

123119
spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
124-
/* Negative dentry - try next */
125-
if (!simple_positive(q)) {
126-
spin_unlock(&p->d_lock);
127-
lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_);
128-
p = q;
129-
goto again;
120+
/* Already gone or negative dentry (under construction) - try next */
121+
if (q->d_count == 0 || !simple_positive(q)) {
122+
spin_unlock(&q->d_lock);
123+
next = q->d_u.d_child.next;
124+
goto cont;
130125
}
131126
dget_dlock(q);
132127
spin_unlock(&q->d_lock);
133-
spin_unlock(&p->d_lock);
128+
spin_unlock(&root->d_lock);
134129
spin_unlock(&sbi->lookup_lock);
135130

136131
dput(prev);

0 commit comments

Comments
 (0)