diff options
-rw-r--r-- | fs/dcache.c | 135 |
1 files changed, 61 insertions, 74 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index d1840b30c673..dc0551c9755d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -284,6 +284,40 @@ void d_drop(struct dentry *dentry) | |||
284 | } | 284 | } |
285 | EXPORT_SYMBOL(d_drop); | 285 | EXPORT_SYMBOL(d_drop); |
286 | 286 | ||
287 | /* | ||
288 | * Finish off a dentry we've decided to kill. | ||
289 | * dentry->d_lock must be held, returns with it unlocked. | ||
290 | * If ref is non-zero, then decrement the refcount too. | ||
291 | * Returns dentry requiring refcount drop, or NULL if we're done. | ||
292 | */ | ||
293 | static inline struct dentry *dentry_kill(struct dentry *dentry, int ref) | ||
294 | __releases(dentry->d_lock) | ||
295 | { | ||
296 | struct dentry *parent; | ||
297 | |||
298 | if (!spin_trylock(&dcache_inode_lock)) { | ||
299 | relock: | ||
300 | spin_unlock(&dentry->d_lock); | ||
301 | cpu_relax(); | ||
302 | return dentry; /* try again with same dentry */ | ||
303 | } | ||
304 | if (IS_ROOT(dentry)) | ||
305 | parent = NULL; | ||
306 | else | ||
307 | parent = dentry->d_parent; | ||
308 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
309 | spin_unlock(&dcache_inode_lock); | ||
310 | goto relock; | ||
311 | } | ||
312 | if (ref) | ||
313 | dentry->d_count--; | ||
314 | /* if dentry was on the d_lru list delete it from there */ | ||
315 | dentry_lru_del(dentry); | ||
316 | /* if it was on the hash then remove it */ | ||
317 | __d_drop(dentry); | ||
318 | return d_kill(dentry, parent); | ||
319 | } | ||
320 | |||
287 | /* | 321 | /* |
288 | * This is dput | 322 | * This is dput |
289 | * | 323 | * |
@@ -309,13 +343,9 @@ EXPORT_SYMBOL(d_drop); | |||
309 | * call the dentry unlink method as well as removing it from the queues and | 343 | * call the dentry unlink method as well as removing it from the queues and |
310 | * releasing its resources. If the parent dentries were scheduled for release | 344 | * releasing its resources. If the parent dentries were scheduled for release |
311 | * they too may now get deleted. | 345 | * they too may now get deleted. |
312 | * | ||
313 | * no dcache lock, please. | ||
314 | */ | 346 | */ |
315 | |||
316 | void dput(struct dentry *dentry) | 347 | void dput(struct dentry *dentry) |
317 | { | 348 | { |
318 | struct dentry *parent; | ||
319 | if (!dentry) | 349 | if (!dentry) |
320 | return; | 350 | return; |
321 | 351 | ||
@@ -348,26 +378,7 @@ repeat: | |||
348 | return; | 378 | return; |
349 | 379 | ||
350 | kill_it: | 380 | kill_it: |
351 | if (!spin_trylock(&dcache_inode_lock)) { | 381 | dentry = dentry_kill(dentry, 1); |
352 | relock: | ||
353 | spin_unlock(&dentry->d_lock); | ||
354 | cpu_relax(); | ||
355 | goto repeat; | ||
356 | } | ||
357 | if (IS_ROOT(dentry)) | ||
358 | parent = NULL; | ||
359 | else | ||
360 | parent = dentry->d_parent; | ||
361 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
362 | spin_unlock(&dcache_inode_lock); | ||
363 | goto relock; | ||
364 | } | ||
365 | dentry->d_count--; | ||
366 | /* if dentry was on the d_lru list delete it from there */ | ||
367 | dentry_lru_del(dentry); | ||
368 | /* if it was on the hash (d_delete case), then remove it */ | ||
369 | __d_drop(dentry); | ||
370 | dentry = d_kill(dentry, parent); | ||
371 | if (dentry) | 382 | if (dentry) |
372 | goto repeat; | 383 | goto repeat; |
373 | } | 384 | } |
@@ -563,51 +574,43 @@ restart: | |||
563 | EXPORT_SYMBOL(d_prune_aliases); | 574 | EXPORT_SYMBOL(d_prune_aliases); |
564 | 575 | ||
565 | /* | 576 | /* |
566 | * Throw away a dentry - free the inode, dput the parent. This requires that | 577 | * Try to throw away a dentry - free the inode, dput the parent. |
567 | * the LRU list has already been removed. | 578 | * Requires dentry->d_lock is held, and dentry->d_count == 0. |
579 | * Releases dentry->d_lock. | ||
568 | * | 580 | * |
569 | * Try to prune ancestors as well. This is necessary to prevent | 581 | * This may fail if locks cannot be acquired no problem, just try again. |
570 | * quadratic behavior of shrink_dcache_parent(), but is also expected | ||
571 | * to be beneficial in reducing dentry cache fragmentation. | ||
572 | */ | 582 | */ |
573 | static void prune_one_dentry(struct dentry *dentry, struct dentry *parent) | 583 | static void try_prune_one_dentry(struct dentry *dentry) |
574 | __releases(dentry->d_lock) | 584 | __releases(dentry->d_lock) |
575 | __releases(parent->d_lock) | ||
576 | __releases(dcache_inode_lock) | ||
577 | { | 585 | { |
578 | __d_drop(dentry); | 586 | struct dentry *parent; |
579 | dentry = d_kill(dentry, parent); | ||
580 | 587 | ||
588 | parent = dentry_kill(dentry, 0); | ||
581 | /* | 589 | /* |
582 | * Prune ancestors. | 590 | * If dentry_kill returns NULL, we have nothing more to do. |
591 | * if it returns the same dentry, trylocks failed. In either | ||
592 | * case, just loop again. | ||
593 | * | ||
594 | * Otherwise, we need to prune ancestors too. This is necessary | ||
595 | * to prevent quadratic behavior of shrink_dcache_parent(), but | ||
596 | * is also expected to be beneficial in reducing dentry cache | ||
597 | * fragmentation. | ||
583 | */ | 598 | */ |
599 | if (!parent) | ||
600 | return; | ||
601 | if (parent == dentry) | ||
602 | return; | ||
603 | |||
604 | /* Prune ancestors. */ | ||
605 | dentry = parent; | ||
584 | while (dentry) { | 606 | while (dentry) { |
585 | relock: | ||
586 | spin_lock(&dentry->d_lock); | 607 | spin_lock(&dentry->d_lock); |
587 | if (dentry->d_count > 1) { | 608 | if (dentry->d_count > 1) { |
588 | dentry->d_count--; | 609 | dentry->d_count--; |
589 | spin_unlock(&dentry->d_lock); | 610 | spin_unlock(&dentry->d_lock); |
590 | return; | 611 | return; |
591 | } | 612 | } |
592 | if (!spin_trylock(&dcache_inode_lock)) { | 613 | dentry = dentry_kill(dentry, 1); |
593 | relock2: | ||
594 | spin_unlock(&dentry->d_lock); | ||
595 | cpu_relax(); | ||
596 | goto relock; | ||
597 | } | ||
598 | |||
599 | if (IS_ROOT(dentry)) | ||
600 | parent = NULL; | ||
601 | else | ||
602 | parent = dentry->d_parent; | ||
603 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
604 | spin_unlock(&dcache_inode_lock); | ||
605 | goto relock2; | ||
606 | } | ||
607 | dentry->d_count--; | ||
608 | dentry_lru_del(dentry); | ||
609 | __d_drop(dentry); | ||
610 | dentry = d_kill(dentry, parent); | ||
611 | } | 614 | } |
612 | } | 615 | } |
613 | 616 | ||
@@ -617,8 +620,6 @@ static void shrink_dentry_list(struct list_head *list) | |||
617 | 620 | ||
618 | rcu_read_lock(); | 621 | rcu_read_lock(); |
619 | for (;;) { | 622 | for (;;) { |
620 | struct dentry *parent; | ||
621 | |||
622 | dentry = list_entry_rcu(list->prev, struct dentry, d_lru); | 623 | dentry = list_entry_rcu(list->prev, struct dentry, d_lru); |
623 | if (&dentry->d_lru == list) | 624 | if (&dentry->d_lru == list) |
624 | break; /* empty */ | 625 | break; /* empty */ |
@@ -639,24 +640,10 @@ static void shrink_dentry_list(struct list_head *list) | |||
639 | continue; | 640 | continue; |
640 | } | 641 | } |
641 | 642 | ||
642 | if (!spin_trylock(&dcache_inode_lock)) { | ||
643 | relock: | ||
644 | spin_unlock(&dentry->d_lock); | ||
645 | cpu_relax(); | ||
646 | continue; | ||
647 | } | ||
648 | if (IS_ROOT(dentry)) | ||
649 | parent = NULL; | ||
650 | else | ||
651 | parent = dentry->d_parent; | ||
652 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
653 | spin_unlock(&dcache_inode_lock); | ||
654 | goto relock; | ||
655 | } | ||
656 | dentry_lru_del(dentry); | ||
657 | |||
658 | rcu_read_unlock(); | 643 | rcu_read_unlock(); |
659 | prune_one_dentry(dentry, parent); | 644 | |
645 | try_prune_one_dentry(dentry); | ||
646 | |||
660 | rcu_read_lock(); | 647 | rcu_read_lock(); |
661 | } | 648 | } |
662 | rcu_read_unlock(); | 649 | rcu_read_unlock(); |