Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <raven@themaw.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Ian Kent and committed by
Linus Torvalds
a45440f0 63ca5f1d

+13 -18
+13 -18
fs/autofs4/expire.c
··· 94 94 { 95 95 struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); 96 96 struct list_head *next; 97 - struct dentry *p, *q; 97 + struct dentry *q; 98 98 99 99 spin_lock(&sbi->lookup_lock); 100 + spin_lock(&root->d_lock); 100 101 101 - if (prev == NULL) { 102 - spin_lock(&root->d_lock); 102 + if (prev) 103 + next = prev->d_u.d_child.next; 104 + else { 103 105 prev = dget_dlock(root); 104 106 next = prev->d_subdirs.next; 105 - p = prev; 106 - goto start; 107 107 } 108 108 109 - p = prev; 110 - spin_lock(&p->d_lock); 111 - again: 112 - next = p->d_u.d_child.next; 113 - start: 109 + cont: 114 110 if (next == &root->d_subdirs) { 115 - spin_unlock(&p->d_lock); 111 + spin_unlock(&root->d_lock); 116 112 spin_unlock(&sbi->lookup_lock); 117 113 dput(prev); 118 114 return NULL; ··· 117 121 q = list_entry(next, struct dentry, d_u.d_child); 118 122 119 123 spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); 120 - /* Negative dentry - try next */ 121 - if (!simple_positive(q)) { 122 - spin_unlock(&p->d_lock); 123 - lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_); 124 - p = q; 125 - goto again; 124 + /* Already gone or negative dentry (under construction) - try next */ 125 + if (q->d_count == 0 || !simple_positive(q)) { 126 + spin_unlock(&q->d_lock); 127 + next = q->d_u.d_child.next; 128 + goto cont; 126 129 } 127 130 dget_dlock(q); 128 131 spin_unlock(&q->d_lock); 129 - spin_unlock(&p->d_lock); 132 + spin_unlock(&root->d_lock); 130 133 spin_unlock(&sbi->lookup_lock); 131 134 132 135 dput(prev);