Commit 8519fb30e438f8088b71a94a7d5a660a814d3872
Committed by
Linus Torvalds
1 parent
99f6d61bda
Exists in
master
and in
4 other branches
[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
include/linux/mm.h
mm/swap.c
... | ... | @@ -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; |