diff options
| author | Andrew Morton <akpm@zip.com.au> | 2002-08-19 06:04:46 -0700 |
|---|---|---|
| committer | Oleg Drokin <green@angband.namesys.com> | 2002-08-19 06:04:46 -0700 |
| commit | 9bdedfceabd0892444c6581997a34278e41fd80d (patch) | |
| tree | c111be3da6844647d32fda62d6e0d4b335b4c6aa | |
| parent | ac31cf7092e7e8a1d1f6057ad3550482dc50bd4c (diff) | |
[PATCH] Fix a race between __page_cache_release() and shrink_cache()
__page_cache_release() needs to recheck the page count inside the LRU
lock, because shrink_cache() may have found the page on the LRU and
incremented its refcount again.
Which is carefully documented over __pagevec_release(). Duh.
| -rw-r--r-- | mm/swap.c | 17 |
1 files changed, 10 insertions, 7 deletions
diff --git a/mm/swap.c b/mm/swap.c index 10e6d4a3683b..1d9eba6744e8 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -81,15 +81,18 @@ void __page_cache_release(struct page *page) unsigned long flags; spin_lock_irqsave(&_pagemap_lru_lock, flags); - if (!TestClearPageLRU(page)) - BUG(); - if (PageActive(page)) - del_page_from_active_list(page); - else - del_page_from_inactive_list(page); + if (TestClearPageLRU(page)) { + if (PageActive(page)) + del_page_from_active_list(page); + else + del_page_from_inactive_list(page); + } + if (page_count(page) != 0) + page = NULL; spin_unlock_irqrestore(&_pagemap_lru_lock, flags); } - __free_pages_ok(page, 0); + if (page) + __free_pages_ok(page, 0); } /* |
