diff options
Diffstat (limited to 'fs/autofs4/expire.c')
-rw-r--r-- | fs/autofs4/expire.c | 141 |
1 files changed, 77 insertions, 64 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index a796c9417fb1..cc1d01365905 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -91,24 +91,64 @@ done: | |||
91 | } | 91 | } |
92 | 92 | ||
93 | /* | 93 | /* |
94 | * Calculate next entry in top down tree traversal. | 94 | * Calculate and dget next entry in top down tree traversal. |
95 | * From next_mnt in namespace.c - elegant. | ||
96 | */ | 95 | */ |
97 | static struct dentry *next_dentry(struct dentry *p, struct dentry *root) | 96 | static struct dentry *get_next_positive_dentry(struct dentry *prev, |
97 | struct dentry *root) | ||
98 | { | 98 | { |
99 | struct list_head *next = p->d_subdirs.next; | 99 | struct list_head *next; |
100 | struct dentry *p, *ret; | ||
101 | |||
102 | if (prev == NULL) | ||
103 | return dget(prev); | ||
100 | 104 | ||
105 | spin_lock(&autofs4_lock); | ||
106 | relock: | ||
107 | p = prev; | ||
108 | spin_lock(&p->d_lock); | ||
109 | again: | ||
110 | next = p->d_subdirs.next; | ||
101 | if (next == &p->d_subdirs) { | 111 | if (next == &p->d_subdirs) { |
102 | while (1) { | 112 | while (1) { |
103 | if (p == root) | 113 | struct dentry *parent; |
114 | |||
115 | if (p == root) { | ||
116 | spin_unlock(&p->d_lock); | ||
117 | spin_unlock(&autofs4_lock); | ||
118 | dput(prev); | ||
104 | return NULL; | 119 | return NULL; |
120 | } | ||
121 | |||
122 | parent = p->d_parent; | ||
123 | if (!spin_trylock(&parent->d_lock)) { | ||
124 | spin_unlock(&p->d_lock); | ||
125 | cpu_relax(); | ||
126 | goto relock; | ||
127 | } | ||
128 | spin_unlock(&p->d_lock); | ||
105 | next = p->d_u.d_child.next; | 129 | next = p->d_u.d_child.next; |
106 | if (next != &p->d_parent->d_subdirs) | 130 | p = parent; |
131 | if (next != &parent->d_subdirs) | ||
107 | break; | 132 | break; |
108 | p = p->d_parent; | ||
109 | } | 133 | } |
110 | } | 134 | } |
111 | return list_entry(next, struct dentry, d_u.d_child); | 135 | ret = list_entry(next, struct dentry, d_u.d_child); |
136 | |||
137 | spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); | ||
138 | /* Negative dentry - try next */ | ||
139 | if (!simple_positive(ret)) { | ||
140 | spin_unlock(&ret->d_lock); | ||
141 | p = ret; | ||
142 | goto again; | ||
143 | } | ||
144 | dget_dlock(ret); | ||
145 | spin_unlock(&ret->d_lock); | ||
146 | spin_unlock(&p->d_lock); | ||
147 | spin_unlock(&autofs4_lock); | ||
148 | |||
149 | dput(prev); | ||
150 | |||
151 | return ret; | ||
112 | } | 152 | } |
113 | 153 | ||
114 | /* | 154 | /* |
@@ -158,18 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
158 | if (!simple_positive(top)) | 198 | if (!simple_positive(top)) |
159 | return 1; | 199 | return 1; |
160 | 200 | ||
161 | spin_lock(&dcache_lock); | 201 | p = NULL; |
162 | for (p = top; p; p = next_dentry(p, top)) { | 202 | while ((p = get_next_positive_dentry(p, top))) { |
163 | /* Negative dentry - give up */ | ||
164 | if (!simple_positive(p)) | ||
165 | continue; | ||
166 | |||
167 | DPRINTK("dentry %p %.*s", | 203 | DPRINTK("dentry %p %.*s", |
168 | p, (int) p->d_name.len, p->d_name.name); | 204 | p, (int) p->d_name.len, p->d_name.name); |
169 | 205 | ||
170 | p = dget(p); | ||
171 | spin_unlock(&dcache_lock); | ||
172 | |||
173 | /* | 206 | /* |
174 | * Is someone visiting anywhere in the subtree ? | 207 | * Is someone visiting anywhere in the subtree ? |
175 | * If there's no mount we need to check the usage | 208 | * If there's no mount we need to check the usage |
@@ -198,16 +231,13 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
198 | else | 231 | else |
199 | ino_count++; | 232 | ino_count++; |
200 | 233 | ||
201 | if (atomic_read(&p->d_count) > ino_count) { | 234 | if (p->d_count > ino_count) { |
202 | top_ino->last_used = jiffies; | 235 | top_ino->last_used = jiffies; |
203 | dput(p); | 236 | dput(p); |
204 | return 1; | 237 | return 1; |
205 | } | 238 | } |
206 | } | 239 | } |
207 | dput(p); | ||
208 | spin_lock(&dcache_lock); | ||
209 | } | 240 | } |
210 | spin_unlock(&dcache_lock); | ||
211 | 241 | ||
212 | /* Timeout of a tree mount is ultimately determined by its top dentry */ | 242 | /* Timeout of a tree mount is ultimately determined by its top dentry */ |
213 | if (!autofs4_can_expire(top, timeout, do_now)) | 243 | if (!autofs4_can_expire(top, timeout, do_now)) |
@@ -226,32 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |||
226 | DPRINTK("parent %p %.*s", | 256 | DPRINTK("parent %p %.*s", |
227 | parent, (int)parent->d_name.len, parent->d_name.name); | 257 | parent, (int)parent->d_name.len, parent->d_name.name); |
228 | 258 | ||
229 | spin_lock(&dcache_lock); | 259 | p = NULL; |
230 | for (p = parent; p; p = next_dentry(p, parent)) { | 260 | while ((p = get_next_positive_dentry(p, parent))) { |
231 | /* Negative dentry - give up */ | ||
232 | if (!simple_positive(p)) | ||
233 | continue; | ||
234 | |||
235 | DPRINTK("dentry %p %.*s", | 261 | DPRINTK("dentry %p %.*s", |
236 | p, (int) p->d_name.len, p->d_name.name); | 262 | p, (int) p->d_name.len, p->d_name.name); |
237 | 263 | ||
238 | p = dget(p); | ||
239 | spin_unlock(&dcache_lock); | ||
240 | |||
241 | if (d_mountpoint(p)) { | 264 | if (d_mountpoint(p)) { |
242 | /* Can we umount this guy */ | 265 | /* Can we umount this guy */ |
243 | if (autofs4_mount_busy(mnt, p)) | 266 | if (autofs4_mount_busy(mnt, p)) |
244 | goto cont; | 267 | continue; |
245 | 268 | ||
246 | /* Can we expire this guy */ | 269 | /* Can we expire this guy */ |
247 | if (autofs4_can_expire(p, timeout, do_now)) | 270 | if (autofs4_can_expire(p, timeout, do_now)) |
248 | return p; | 271 | return p; |
249 | } | 272 | } |
250 | cont: | ||
251 | dput(p); | ||
252 | spin_lock(&dcache_lock); | ||
253 | } | 273 | } |
254 | spin_unlock(&dcache_lock); | ||
255 | return NULL; | 274 | return NULL; |
256 | } | 275 | } |
257 | 276 | ||
@@ -276,7 +295,9 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, | |||
276 | struct autofs_info *ino = autofs4_dentry_ino(root); | 295 | struct autofs_info *ino = autofs4_dentry_ino(root); |
277 | if (d_mountpoint(root)) { | 296 | if (d_mountpoint(root)) { |
278 | ino->flags |= AUTOFS_INF_MOUNTPOINT; | 297 | ino->flags |= AUTOFS_INF_MOUNTPOINT; |
279 | root->d_mounted--; | 298 | spin_lock(&root->d_lock); |
299 | root->d_flags &= ~DCACHE_MOUNTED; | ||
300 | spin_unlock(&root->d_lock); | ||
280 | } | 301 | } |
281 | ino->flags |= AUTOFS_INF_EXPIRING; | 302 | ino->flags |= AUTOFS_INF_EXPIRING; |
282 | init_completion(&ino->expire_complete); | 303 | init_completion(&ino->expire_complete); |
@@ -302,8 +323,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
302 | { | 323 | { |
303 | unsigned long timeout; | 324 | unsigned long timeout; |
304 | struct dentry *root = sb->s_root; | 325 | struct dentry *root = sb->s_root; |
326 | struct dentry *dentry; | ||
305 | struct dentry *expired = NULL; | 327 | struct dentry *expired = NULL; |
306 | struct list_head *next; | ||
307 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | 328 | int do_now = how & AUTOFS_EXP_IMMEDIATE; |
308 | int exp_leaves = how & AUTOFS_EXP_LEAVES; | 329 | int exp_leaves = how & AUTOFS_EXP_LEAVES; |
309 | struct autofs_info *ino; | 330 | struct autofs_info *ino; |
@@ -315,23 +336,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
315 | now = jiffies; | 336 | now = jiffies; |
316 | timeout = sbi->exp_timeout; | 337 | timeout = sbi->exp_timeout; |
317 | 338 | ||
318 | spin_lock(&dcache_lock); | 339 | dentry = NULL; |
319 | next = root->d_subdirs.next; | 340 | while ((dentry = get_next_positive_dentry(dentry, root))) { |
320 | |||
321 | /* On exit from the loop expire is set to a dgot dentry | ||
322 | * to expire or it's NULL */ | ||
323 | while ( next != &root->d_subdirs ) { | ||
324 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); | ||
325 | |||
326 | /* Negative dentry - give up */ | ||
327 | if (!simple_positive(dentry)) { | ||
328 | next = next->next; | ||
329 | continue; | ||
330 | } | ||
331 | |||
332 | dentry = dget(dentry); | ||
333 | spin_unlock(&dcache_lock); | ||
334 | |||
335 | spin_lock(&sbi->fs_lock); | 341 | spin_lock(&sbi->fs_lock); |
336 | ino = autofs4_dentry_ino(dentry); | 342 | ino = autofs4_dentry_ino(dentry); |
337 | 343 | ||
@@ -347,7 +353,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
347 | 353 | ||
348 | /* Path walk currently on this dentry? */ | 354 | /* Path walk currently on this dentry? */ |
349 | ino_count = atomic_read(&ino->count) + 2; | 355 | ino_count = atomic_read(&ino->count) + 2; |
350 | if (atomic_read(&dentry->d_count) > ino_count) | 356 | if (dentry->d_count > ino_count) |
351 | goto next; | 357 | goto next; |
352 | 358 | ||
353 | /* Can we umount this guy */ | 359 | /* Can we umount this guy */ |
@@ -369,7 +375,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
369 | if (!exp_leaves) { | 375 | if (!exp_leaves) { |
370 | /* Path walk currently on this dentry? */ | 376 | /* Path walk currently on this dentry? */ |
371 | ino_count = atomic_read(&ino->count) + 1; | 377 | ino_count = atomic_read(&ino->count) + 1; |
372 | if (atomic_read(&dentry->d_count) > ino_count) | 378 | if (dentry->d_count > ino_count) |
373 | goto next; | 379 | goto next; |
374 | 380 | ||
375 | if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { | 381 | if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { |
@@ -383,7 +389,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
383 | } else { | 389 | } else { |
384 | /* Path walk currently on this dentry? */ | 390 | /* Path walk currently on this dentry? */ |
385 | ino_count = atomic_read(&ino->count) + 1; | 391 | ino_count = atomic_read(&ino->count) + 1; |
386 | if (atomic_read(&dentry->d_count) > ino_count) | 392 | if (dentry->d_count > ino_count) |
387 | goto next; | 393 | goto next; |
388 | 394 | ||
389 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); | 395 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); |
@@ -394,11 +400,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
394 | } | 400 | } |
395 | next: | 401 | next: |
396 | spin_unlock(&sbi->fs_lock); | 402 | spin_unlock(&sbi->fs_lock); |
397 | dput(dentry); | ||
398 | spin_lock(&dcache_lock); | ||
399 | next = next->next; | ||
400 | } | 403 | } |
401 | spin_unlock(&dcache_lock); | ||
402 | return NULL; | 404 | return NULL; |
403 | 405 | ||
404 | found: | 406 | found: |
@@ -408,9 +410,13 @@ found: | |||
408 | ino->flags |= AUTOFS_INF_EXPIRING; | 410 | ino->flags |= AUTOFS_INF_EXPIRING; |
409 | init_completion(&ino->expire_complete); | 411 | init_completion(&ino->expire_complete); |
410 | spin_unlock(&sbi->fs_lock); | 412 | spin_unlock(&sbi->fs_lock); |
411 | spin_lock(&dcache_lock); | 413 | spin_lock(&autofs4_lock); |
414 | spin_lock(&expired->d_parent->d_lock); | ||
415 | spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); | ||
412 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 416 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
413 | spin_unlock(&dcache_lock); | 417 | spin_unlock(&expired->d_lock); |
418 | spin_unlock(&expired->d_parent->d_lock); | ||
419 | spin_unlock(&autofs4_lock); | ||
414 | return expired; | 420 | return expired; |
415 | } | 421 | } |
416 | 422 | ||
@@ -499,7 +505,14 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
499 | 505 | ||
500 | spin_lock(&sbi->fs_lock); | 506 | spin_lock(&sbi->fs_lock); |
501 | if (ino->flags & AUTOFS_INF_MOUNTPOINT) { | 507 | if (ino->flags & AUTOFS_INF_MOUNTPOINT) { |
502 | sb->s_root->d_mounted++; | 508 | spin_lock(&sb->s_root->d_lock); |
509 | /* | ||
510 | * If we haven't been expired away, then reset | ||
511 | * mounted status. | ||
512 | */ | ||
513 | if (mnt->mnt_parent != mnt) | ||
514 | sb->s_root->d_flags |= DCACHE_MOUNTED; | ||
515 | spin_unlock(&sb->s_root->d_lock); | ||
503 | ino->flags &= ~AUTOFS_INF_MOUNTPOINT; | 516 | ino->flags &= ~AUTOFS_INF_MOUNTPOINT; |
504 | } | 517 | } |
505 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 518 | ino->flags &= ~AUTOFS_INF_EXPIRING; |