Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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);	\
	})
	})


+22 −10
Original line number Original line Diff line number Diff line
@@ -34,9 +34,8 @@
/* 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);
@@ -44,9 +43,13 @@ void put_page(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)
		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;