aboutsummaryrefslogtreecommitdiffstats
path: root/fs/autofs4/expire.c
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2012-08-05 21:37:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-08-16 14:58:28 -0400
commita45440f05e9ebc26f2a375df911823fdef5b5281 (patch)
tree68f36bc8cd8a8a32a41c6a4f3ee349ec29c9de80 /fs/autofs4/expire.c
parent63ca5f1d17a4eecaec3149982223fe89e55a568f (diff)
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>
Diffstat (limited to 'fs/autofs4/expire.c')
-rw-r--r--fs/autofs4/expire.c31
1 files changed, 13 insertions, 18 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 1feb68ecef95..8c0e56d92938 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -94,25 +94,21 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
94{ 94{
95 struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); 95 struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb);
96 struct list_head *next; 96 struct list_head *next;
97 struct dentry *p, *q; 97 struct dentry *q;
98 98
99 spin_lock(&sbi->lookup_lock); 99 spin_lock(&sbi->lookup_lock);
100 spin_lock(&root->d_lock);
100 101
101 if (prev == NULL) { 102 if (prev)
102 spin_lock(&root->d_lock); 103 next = prev->d_u.d_child.next;
104 else {
103 prev = dget_dlock(root); 105 prev = dget_dlock(root);
104 next = prev->d_subdirs.next; 106 next = prev->d_subdirs.next;
105 p = prev;
106 goto start;
107 } 107 }
108 108
109 p = prev; 109cont:
110 spin_lock(&p->d_lock);
111again:
112 next = p->d_u.d_child.next;
113start:
114 if (next == &root->d_subdirs) { 110 if (next == &root->d_subdirs) {
115 spin_unlock(&p->d_lock); 111 spin_unlock(&root->d_lock);
116 spin_unlock(&sbi->lookup_lock); 112 spin_unlock(&sbi->lookup_lock);
117 dput(prev); 113 dput(prev);
118 return NULL; 114 return NULL;
@@ -121,16 +117,15 @@ start:
121 q = list_entry(next, struct dentry, d_u.d_child); 117 q = list_entry(next, struct dentry, d_u.d_child);
122 118
123 spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); 119 spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
124 /* Negative dentry - try next */ 120 /* Already gone or negative dentry (under construction) - try next */
125 if (!simple_positive(q)) { 121 if (q->d_count == 0 || !simple_positive(q)) {
126 spin_unlock(&p->d_lock); 122 spin_unlock(&q->d_lock);
127 lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_); 123 next = q->d_u.d_child.next;
128 p = q; 124 goto cont;
129 goto again;
130 } 125 }
131 dget_dlock(q); 126 dget_dlock(q);
132 spin_unlock(&q->d_lock); 127 spin_unlock(&q->d_lock);
133 spin_unlock(&p->d_lock); 128 spin_unlock(&root->d_lock);
134 spin_unlock(&sbi->lookup_lock); 129 spin_unlock(&sbi->lookup_lock);
135 130
136 dput(prev); 131 dput(prev);