diff options
author | NeilBrown <neilb@suse.de> | 2014-10-13 18:52:20 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-13 20:18:16 -0400 |
commit | 4d885f90e3784df77945101d8ec32dc798a49862 (patch) | |
tree | a034e761a43d14bb24f27e98a0cd3f568bbf7732 | |
parent | 6ece08e6187a62dd5bdf7c49539980571922c551 (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.h | 4 | ||||
-rw-r--r-- | fs/autofs4/expire.c | 46 |
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 | ||
84 | struct autofs_wait_queue { | 88 | struct 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 | } |
329 | out: | 338 | out: |
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, | |||
455 | found: | 481 | found: |
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); |