diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 21 |
1 files changed, 12 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index b4d2e28eef5b..a8f89765d602 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -446,24 +446,27 @@ struct dentry *dget_parent(struct dentry *dentry) | |||
446 | struct dentry *ret; | 446 | struct dentry *ret; |
447 | 447 | ||
448 | repeat: | 448 | repeat: |
449 | spin_lock(&dentry->d_lock); | 449 | /* |
450 | * Don't need rcu_dereference because we re-check it was correct under | ||
451 | * the lock. | ||
452 | */ | ||
453 | rcu_read_lock(); | ||
450 | ret = dentry->d_parent; | 454 | ret = dentry->d_parent; |
451 | if (!ret) | 455 | if (!ret) { |
452 | goto out; | 456 | rcu_read_unlock(); |
453 | if (dentry == ret) { | ||
454 | ret->d_count++; | ||
455 | goto out; | 457 | goto out; |
456 | } | 458 | } |
457 | if (!spin_trylock(&ret->d_lock)) { | 459 | spin_lock(&ret->d_lock); |
458 | spin_unlock(&dentry->d_lock); | 460 | if (unlikely(ret != dentry->d_parent)) { |
459 | cpu_relax(); | 461 | spin_unlock(&ret->d_lock); |
462 | rcu_read_unlock(); | ||
460 | goto repeat; | 463 | goto repeat; |
461 | } | 464 | } |
465 | rcu_read_unlock(); | ||
462 | BUG_ON(!ret->d_count); | 466 | BUG_ON(!ret->d_count); |
463 | ret->d_count++; | 467 | ret->d_count++; |
464 | spin_unlock(&ret->d_lock); | 468 | spin_unlock(&ret->d_lock); |
465 | out: | 469 | out: |
466 | spin_unlock(&dentry->d_lock); | ||
467 | return ret; | 470 | return ret; |
468 | } | 471 | } |
469 | EXPORT_SYMBOL(dget_parent); | 472 | EXPORT_SYMBOL(dget_parent); |