aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-06-12 11:24:46 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-06-12 11:24:46 -0400
commitea01a18494b3d7a91b2f1f2a6a5aaef4741bc294 (patch)
tree58b50373b64652941796b5b7e03416dd632f4ef8 /fs
parentba65dc5ef16f82fba77869cecf7a7d515f61446b (diff)
autofs races
* make autofs4_expire_indirect() skip the dentries being in process of expiry * do *not* mess with list_move(); making sure that dentry with AUTOFS_INF_EXPIRING are not picked for expiry is enough. * do not remove NO_RCU when we set EXPIRING, don't bother with smp_mb() there. Clear it at the same time we clear EXPIRING. Makes a bunch of tests simpler. * rename NO_RCU to WANT_EXPIRE, which is what it really is. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/autofs4/autofs_i.h8
-rw-r--r--fs/autofs4/expire.c27
-rw-r--r--fs/autofs4/root.c2
3 files changed, 15 insertions, 22 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index f0d268b97d19..a439548de785 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -70,9 +70,13 @@ struct autofs_info {
70}; 70};
71 71
72#define AUTOFS_INF_EXPIRING (1<<0) /* dentry in the process of expiring */ 72#define AUTOFS_INF_EXPIRING (1<<0) /* dentry in the process of expiring */
73#define AUTOFS_INF_NO_RCU (1<<1) /* the dentry is being considered 73#define AUTOFS_INF_WANT_EXPIRE (1<<1) /* the dentry is being considered
74 * for expiry, so RCU_walk is 74 * for expiry, so RCU_walk is
75 * not permitted 75 * not permitted. If it progresses to
76 * actual expiry attempt, the flag is
77 * not cleared when EXPIRING is set -
78 * in that case it gets cleared only
79 * when it comes to clearing EXPIRING.
76 */ 80 */
77#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */ 81#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
78 82
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 9510d8d2e9cd..b493909e7492 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -316,19 +316,17 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
316 if (ino->flags & AUTOFS_INF_PENDING) 316 if (ino->flags & AUTOFS_INF_PENDING)
317 goto out; 317 goto out;
318 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { 318 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
319 ino->flags |= AUTOFS_INF_NO_RCU; 319 ino->flags |= AUTOFS_INF_WANT_EXPIRE;
320 spin_unlock(&sbi->fs_lock); 320 spin_unlock(&sbi->fs_lock);
321 synchronize_rcu(); 321 synchronize_rcu();
322 spin_lock(&sbi->fs_lock); 322 spin_lock(&sbi->fs_lock);
323 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { 323 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
324 ino->flags |= AUTOFS_INF_EXPIRING; 324 ino->flags |= AUTOFS_INF_EXPIRING;
325 smp_mb();
326 ino->flags &= ~AUTOFS_INF_NO_RCU;
327 init_completion(&ino->expire_complete); 325 init_completion(&ino->expire_complete);
328 spin_unlock(&sbi->fs_lock); 326 spin_unlock(&sbi->fs_lock);
329 return root; 327 return root;
330 } 328 }
331 ino->flags &= ~AUTOFS_INF_NO_RCU; 329 ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
332 } 330 }
333out: 331out:
334 spin_unlock(&sbi->fs_lock); 332 spin_unlock(&sbi->fs_lock);
@@ -446,7 +444,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
446 while ((dentry = get_next_positive_subdir(dentry, root))) { 444 while ((dentry = get_next_positive_subdir(dentry, root))) {
447 spin_lock(&sbi->fs_lock); 445 spin_lock(&sbi->fs_lock);
448 ino = autofs4_dentry_ino(dentry); 446 ino = autofs4_dentry_ino(dentry);
449 if (ino->flags & AUTOFS_INF_NO_RCU) 447 if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
450 expired = NULL; 448 expired = NULL;
451 else 449 else
452 expired = should_expire(dentry, mnt, timeout, how); 450 expired = should_expire(dentry, mnt, timeout, how);
@@ -455,7 +453,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
455 continue; 453 continue;
456 } 454 }
457 ino = autofs4_dentry_ino(expired); 455 ino = autofs4_dentry_ino(expired);
458 ino->flags |= AUTOFS_INF_NO_RCU; 456 ino->flags |= AUTOFS_INF_WANT_EXPIRE;
459 spin_unlock(&sbi->fs_lock); 457 spin_unlock(&sbi->fs_lock);
460 synchronize_rcu(); 458 synchronize_rcu();
461 spin_lock(&sbi->fs_lock); 459 spin_lock(&sbi->fs_lock);
@@ -465,7 +463,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
465 goto found; 463 goto found;
466 } 464 }
467 465
468 ino->flags &= ~AUTOFS_INF_NO_RCU; 466 ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
469 if (expired != dentry) 467 if (expired != dentry)
470 dput(expired); 468 dput(expired);
471 spin_unlock(&sbi->fs_lock); 469 spin_unlock(&sbi->fs_lock);
@@ -475,17 +473,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
475found: 473found:
476 pr_debug("returning %p %pd\n", expired, expired); 474 pr_debug("returning %p %pd\n", expired, expired);
477 ino->flags |= AUTOFS_INF_EXPIRING; 475 ino->flags |= AUTOFS_INF_EXPIRING;
478 smp_mb();
479 ino->flags &= ~AUTOFS_INF_NO_RCU;
480 init_completion(&ino->expire_complete); 476 init_completion(&ino->expire_complete);
481 spin_unlock(&sbi->fs_lock); 477 spin_unlock(&sbi->fs_lock);
482 spin_lock(&sbi->lookup_lock);
483 spin_lock(&expired->d_parent->d_lock);
484 spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
485 list_move(&expired->d_parent->d_subdirs, &expired->d_child);
486 spin_unlock(&expired->d_lock);
487 spin_unlock(&expired->d_parent->d_lock);
488 spin_unlock(&sbi->lookup_lock);
489 return expired; 478 return expired;
490} 479}
491 480
@@ -496,7 +485,7 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
496 int status; 485 int status;
497 486
498 /* Block on any pending expire */ 487 /* Block on any pending expire */
499 if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU))) 488 if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
500 return 0; 489 return 0;
501 if (rcu_walk) 490 if (rcu_walk)
502 return -ECHILD; 491 return -ECHILD;
@@ -554,7 +543,7 @@ int autofs4_expire_run(struct super_block *sb,
554 ino = autofs4_dentry_ino(dentry); 543 ino = autofs4_dentry_ino(dentry);
555 /* avoid rapid-fire expire attempts if expiry fails */ 544 /* avoid rapid-fire expire attempts if expiry fails */
556 ino->last_used = now; 545 ino->last_used = now;
557 ino->flags &= ~AUTOFS_INF_EXPIRING; 546 ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
558 complete_all(&ino->expire_complete); 547 complete_all(&ino->expire_complete);
559 spin_unlock(&sbi->fs_lock); 548 spin_unlock(&sbi->fs_lock);
560 549
@@ -583,7 +572,7 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
583 spin_lock(&sbi->fs_lock); 572 spin_lock(&sbi->fs_lock);
584 /* avoid rapid-fire expire attempts if expiry fails */ 573 /* avoid rapid-fire expire attempts if expiry fails */
585 ino->last_used = now; 574 ino->last_used = now;
586 ino->flags &= ~AUTOFS_INF_EXPIRING; 575 ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
587 complete_all(&ino->expire_complete); 576 complete_all(&ino->expire_complete);
588 spin_unlock(&sbi->fs_lock); 577 spin_unlock(&sbi->fs_lock);
589 dput(dentry); 578 dput(dentry);
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 78bd80298528..3767f6641af1 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -458,7 +458,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
458 */ 458 */
459 struct inode *inode; 459 struct inode *inode;
460 460
461 if (ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)) 461 if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
462 return 0; 462 return 0;
463 if (d_mountpoint(dentry)) 463 if (d_mountpoint(dentry))
464 return 0; 464 return 0;