aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2014-10-13 18:52:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-13 20:18:16 -0400
commit4d885f90e3784df77945101d8ec32dc798a49862 (patch)
treea034e761a43d14bb24f27e98a0cd3f568bbf7732
parent6ece08e6187a62dd5bdf7c49539980571922c551 (diff)
autofs4: avoid taking fs_lock during rcu-walk
->fs_lock protects AUTOFS_INF_EXPIRING. We need to be sure that once the flag is set, no new references beneath the dentry are taken. So rcu-walk currently needs to take fs_lock before checking the flag. This hurts performance. Change the expiry to a two-stage process. First set AUTOFS_INF_NO_RCU which forces any path walk into ref-walk mode, then drop the lock and call synchronize_rcu(). Once that returns we can be sure no rcu-walk is active beneath the dentry and we can check reference counts again. Now during an RCU-walk we can test AUTOFS_INF_EXPIRING without taking the lock as along as we test AUTOFS_INF_NO_RCU too. If either are set, we must abort the RCU-walk If neither are set, we know that refcounts will be tested again after we finish the RCU-walk so we are safe to continue. ->fs_lock is still taken in d_manage() to check for a non-trap directory. That will be resolved in the next patch. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Ian Kent <raven@themaw.net> Tested-by: Ian Kent <raven@themaw.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/autofs4/autofs_i.h4
-rw-r--r--fs/autofs4/expire.c46
2 files changed, 42 insertions, 8 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 2f1032f12d91..8e98cf954bab 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -79,6 +79,10 @@ struct autofs_info {
79}; 79};
80 80
81#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ 81#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
82#define AUTOFS_INF_NO_RCU (1<<1) /* the dentry is being considered
83 * for expiry, so RCU_walk is
84 * not permitted
85 */
82#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */ 86#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
83 87
84struct autofs_wait_queue { 88struct autofs_wait_queue {
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index af141fc8cfe6..683a5b9ce22a 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -321,10 +321,19 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
321 if (ino->flags & AUTOFS_INF_PENDING) 321 if (ino->flags & AUTOFS_INF_PENDING)
322 goto out; 322 goto out;
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_NO_RCU;
325 init_completion(&ino->expire_complete);
326 spin_unlock(&sbi->fs_lock); 325 spin_unlock(&sbi->fs_lock);
327 return root; 326 synchronize_rcu();
327 spin_lock(&sbi->fs_lock);
328 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
329 ino->flags |= AUTOFS_INF_EXPIRING;
330 smp_mb();
331 ino->flags &= ~AUTOFS_INF_NO_RCU;
332 init_completion(&ino->expire_complete);
333 spin_unlock(&sbi->fs_lock);
334 return root;
335 }
336 ino->flags &= ~AUTOFS_INF_NO_RCU;
328 } 337 }
329out: 338out:
330 spin_unlock(&sbi->fs_lock); 339 spin_unlock(&sbi->fs_lock);
@@ -442,12 +451,29 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
442 dentry = NULL; 451 dentry = NULL;
443 while ((dentry = get_next_positive_subdir(dentry, root))) { 452 while ((dentry = get_next_positive_subdir(dentry, root))) {
444 spin_lock(&sbi->fs_lock); 453 spin_lock(&sbi->fs_lock);
445 expired = should_expire(dentry, mnt, timeout, how); 454 ino = autofs4_dentry_ino(dentry);
446 if (expired) { 455 if (ino->flags & AUTOFS_INF_NO_RCU)
456 expired = NULL;
457 else
458 expired = should_expire(dentry, mnt, timeout, how);
459 if (!expired) {
460 spin_unlock(&sbi->fs_lock);
461 continue;
462 }
463 ino = autofs4_dentry_ino(expired);
464 ino->flags |= AUTOFS_INF_NO_RCU;
465 spin_unlock(&sbi->fs_lock);
466 synchronize_rcu();
467 spin_lock(&sbi->fs_lock);
468 if (should_expire(expired, mnt, timeout, how)) {
447 if (expired != dentry) 469 if (expired != dentry)
448 dput(dentry); 470 dput(dentry);
449 goto found; 471 goto found;
450 } 472 }
473
474 ino->flags &= ~AUTOFS_INF_NO_RCU;
475 if (expired != dentry)
476 dput(expired);
451 spin_unlock(&sbi->fs_lock); 477 spin_unlock(&sbi->fs_lock);
452 } 478 }
453 return NULL; 479 return NULL;
@@ -455,8 +481,9 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
455found: 481found:
456 DPRINTK("returning %p %.*s", 482 DPRINTK("returning %p %.*s",
457 expired, (int)expired->d_name.len, expired->d_name.name); 483 expired, (int)expired->d_name.len, expired->d_name.name);
458 ino = autofs4_dentry_ino(expired);
459 ino->flags |= AUTOFS_INF_EXPIRING; 484 ino->flags |= AUTOFS_INF_EXPIRING;
485 smp_mb();
486 ino->flags &= ~AUTOFS_INF_NO_RCU;
460 init_completion(&ino->expire_complete); 487 init_completion(&ino->expire_complete);
461 spin_unlock(&sbi->fs_lock); 488 spin_unlock(&sbi->fs_lock);
462 spin_lock(&sbi->lookup_lock); 489 spin_lock(&sbi->lookup_lock);
@@ -476,11 +503,14 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
476 int status; 503 int status;
477 504
478 /* Block on any pending expire */ 505 /* Block on any pending expire */
506 if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)))
507 return 0;
508 if (rcu_walk)
509 return -ECHILD;
510
479 spin_lock(&sbi->fs_lock); 511 spin_lock(&sbi->fs_lock);
480 if (ino->flags & AUTOFS_INF_EXPIRING) { 512 if (ino->flags & AUTOFS_INF_EXPIRING) {
481 spin_unlock(&sbi->fs_lock); 513 spin_unlock(&sbi->fs_lock);
482 if (rcu_walk)
483 return -ECHILD;
484 514
485 DPRINTK("waiting for expire %p name=%.*s", 515 DPRINTK("waiting for expire %p name=%.*s",
486 dentry, dentry->d_name.len, dentry->d_name.name); 516 dentry, dentry->d_name.len, dentry->d_name.name);