Commit 8519fb30e438f8088b71a94a7d5a660a814d3872

Authored by Nick Piggin
Committed by Linus Torvalds
1 parent 99f6d61bda

[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>

Showing 2 changed files with 23 additions and 11 deletions Side-by-side Diff

... ... @@ -303,7 +303,7 @@
303 303 */
304 304 #define put_page_testzero(p) \
305 305 ({ \
306   - BUG_ON(page_count(p) == 0); \
  306 + BUG_ON(atomic_read(&(p)->_count) == -1);\
307 307 atomic_add_negative(-1, &(p)->_count); \
308 308 })
309 309  
... ... @@ -34,19 +34,22 @@
34 34 /* How many pages do we try to swap or page in/out together? */
35 35 int page_cluster;
36 36  
37   -void put_page(struct page *page)
  37 +static void put_compound_page(struct page *page)
38 38 {
39   - if (unlikely(PageCompound(page))) {
40   - page = (struct page *)page_private(page);
41   - if (put_page_testzero(page)) {
42   - void (*dtor)(struct page *page);
  39 + page = (struct page *)page_private(page);
  40 + if (put_page_testzero(page)) {
  41 + void (*dtor)(struct page *page);
43 42  
44   - dtor = (void (*)(struct page *))page[1].mapping;
45   - (*dtor)(page);
46   - }
47   - return;
  43 + dtor = (void (*)(struct page *))page[1].mapping;
  44 + (*dtor)(page);
48 45 }
49   - if (put_page_testzero(page))
  46 +}
  47 +
  48 +void put_page(struct page *page)
  49 +{
  50 + if (unlikely(PageCompound(page)))
  51 + put_compound_page(page);
  52 + else if (put_page_testzero(page))
50 53 __page_cache_release(page);
51 54 }
52 55 EXPORT_SYMBOL(put_page);
... ... @@ -243,6 +246,15 @@
243 246 for (i = 0; i < nr; i++) {
244 247 struct page *page = pages[i];
245 248 struct zone *pagezone;
  249 +
  250 + if (unlikely(PageCompound(page))) {
  251 + if (zone) {
  252 + spin_unlock_irq(&zone->lru_lock);
  253 + zone = NULL;
  254 + }
  255 + put_compound_page(page);
  256 + continue;
  257 + }
246 258  
247 259 if (!put_page_testzero(page))
248 260 continue;