summaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2006-03-22 00:08:03 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-22 07:53:57 -0800
commit7c8ee9a86340db686cd4314e9944dc9b6111bda9 (patch)
tree80638e1658556b4fd7c0b92d571aaac854245bd3 /mm
parentf205b2fe62d321403525065a4cb31b6bff1bbe53 (diff)
downloadlinux-7c8ee9a86340db686cd4314e9944dc9b6111bda9.tar.gz
linux-7c8ee9a86340db686cd4314e9944dc9b6111bda9.tar.bz2
linux-7c8ee9a86340db686cd4314e9944dc9b6111bda9.zip
[PATCH] mm: simplify vmscan vs release refcounting
The VM has an interesting race where a page refcount can drop to zero, but it is still on the LRU lists for a short time. This was solved by testing a 0->1 refcount transition when picking up pages from the LRU, and dropping the refcount in that case. Instead, use atomic_add_unless to ensure we never pick up a 0 refcount page from the LRU, thus a 0 refcount page will never have its refcount elevated until it is allocated again. Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/vmscan.c25
1 files changed, 11 insertions, 14 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 8e477b1a4838..e21bab4deda6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1083,29 +1083,26 @@ static int isolate_lru_pages(int nr_to_scan, struct list_head *src,
int scan = 0;
while (scan++ < nr_to_scan && !list_empty(src)) {
+ struct list_head *target;
page = lru_to_page(src);
prefetchw_prev_lru_page(page, src, flags);
BUG_ON(!PageLRU(page));
list_del(&page->lru);
- if (unlikely(get_page_testone(page))) {
+ target = src;
+ if (likely(get_page_unless_zero(page))) {
/*
- * It is being freed elsewhere
+ * Be careful not to clear PageLRU until after we're
+ * sure the page is not being freed elsewhere -- the
+ * page release code relies on it.
*/
- __put_page(page);
- list_add(&page->lru, src);
- continue;
- }
+ ClearPageLRU(page);
+ target = dst;
+ nr_taken++;
+ } /* else it is being freed elsewhere */
- /*
- * Be careful not to clear PageLRU until after we're sure
- * the page is not being freed elsewhere -- the page release
- * code relies on it.
- */
- ClearPageLRU(page);
- list_add(&page->lru, dst);
- nr_taken++;
+ list_add(&page->lru, target);
}
*scanned = scan;