Commit 8519fb30 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

[PATCH] mm: compound release fix

Compound pages on SMP systems can now often be freed from pagetables via
the release_pages path.  This uses put_page_testzero which does not handle
compound pages at all.  Releasing constituent pages from process mappings
decrements their count to a large negative number and leaks the reference
at the head page - net result is a memory leak.

The problem was hidden because the debug check in put_page_testzero itself
actually did take compound pages into consideration.

Fix the bug and the debug check.
Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
Acked-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 99f6d61b
...@@ -303,7 +303,7 @@ struct page { ...@@ -303,7 +303,7 @@ struct page {
*/ */
#define put_page_testzero(p) \ #define put_page_testzero(p) \
({ \ ({ \
BUG_ON(page_count(p) == 0); \ BUG_ON(atomic_read(&(p)->_count) == -1);\
atomic_add_negative(-1, &(p)->_count); \ atomic_add_negative(-1, &(p)->_count); \
}) })
......
...@@ -34,19 +34,22 @@ ...@@ -34,19 +34,22 @@
/* How many pages do we try to swap or page in/out together? */ /* How many pages do we try to swap or page in/out together? */
int page_cluster; int page_cluster;
void put_page(struct page *page) static void put_compound_page(struct page *page)
{ {
if (unlikely(PageCompound(page))) { page = (struct page *)page_private(page);
page = (struct page *)page_private(page); if (put_page_testzero(page)) {
if (put_page_testzero(page)) { void (*dtor)(struct page *page);
void (*dtor)(struct page *page);
dtor = (void (*)(struct page *))page[1].mapping; dtor = (void (*)(struct page *))page[1].mapping;
(*dtor)(page); (*dtor)(page);
}
return;
} }
if (put_page_testzero(page)) }
void put_page(struct page *page)
{
if (unlikely(PageCompound(page)))
put_compound_page(page);
else if (put_page_testzero(page))
__page_cache_release(page); __page_cache_release(page);
} }
EXPORT_SYMBOL(put_page); EXPORT_SYMBOL(put_page);
...@@ -244,6 +247,15 @@ void release_pages(struct page **pages, int nr, int cold) ...@@ -244,6 +247,15 @@ void release_pages(struct page **pages, int nr, int cold)
struct page *page = pages[i]; struct page *page = pages[i];
struct zone *pagezone; struct zone *pagezone;
if (unlikely(PageCompound(page))) {
if (zone) {
spin_unlock_irq(&zone->lru_lock);
zone = NULL;
}
put_compound_page(page);
continue;
}
if (!put_page_testzero(page)) if (!put_page_testzero(page))
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment