summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morton <akpm@zip.com.au>2002-08-19 06:04:46 -0700
committerOleg Drokin <green@angband.namesys.com>2002-08-19 06:04:46 -0700
commit9bdedfceabd0892444c6581997a34278e41fd80d (patch)
treec111be3da6844647d32fda62d6e0d4b335b4c6aa
parentac31cf7092e7e8a1d1f6057ad3550482dc50bd4c (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.c17
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);
}
/*