[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: Nick Piggin <npiggin@suse.de> Acked-by: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
99f6d61bda
commit
8519fb30e4
2 changed files with 24 additions and 12 deletions
|
@ -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
mm/swap.c
34
mm/swap.c
|
@ -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;
|
||||||
|
|
||||||
|
static void put_compound_page(struct page *page)
|
||||||
|
{
|
||||||
|
page = (struct page *)page_private(page);
|
||||||
|
if (put_page_testzero(page)) {
|
||||||
|
void (*dtor)(struct page *page);
|
||||||
|
|
||||||
|
dtor = (void (*)(struct page *))page[1].mapping;
|
||||||
|
(*dtor)(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void put_page(struct page *page)
|
void put_page(struct page *page)
|
||||||
{
|
{
|
||||||
if (unlikely(PageCompound(page))) {
|
if (unlikely(PageCompound(page)))
|
||||||
page = (struct page *)page_private(page);
|
put_compound_page(page);
|
||||||
if (put_page_testzero(page)) {
|
else if (put_page_testzero(page))
|
||||||
void (*dtor)(struct page *page);
|
|
||||||
|
|
||||||
dtor = (void (*)(struct page *))page[1].mapping;
|
|
||||||
(*dtor)(page);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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)
|
||||||
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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue