diff options
author | Nick Piggin <npiggin@suse.de> | 2006-02-07 15:58:52 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-07 19:12:33 -0500 |
commit | 8519fb30e438f8088b71a94a7d5a660a814d3872 (patch) | |
tree | 8d29a7b1fa33e5f65e649d363cfa10e83c4fab97 | |
parent | 99f6d61bda82d09b2d94414d413d39f66a0b7da2 (diff) |
[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>
-rw-r--r-- | include/linux/mm.h | 2 | ||||
-rw-r--r-- | mm/swap.c | 32 |
2 files changed, 23 insertions, 11 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 85854b867463..75e9f0724997 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -303,7 +303,7 @@ struct page { | |||
303 | */ | 303 | */ |
304 | #define put_page_testzero(p) \ | 304 | #define put_page_testzero(p) \ |
305 | ({ \ | 305 | ({ \ |
306 | BUG_ON(page_count(p) == 0); \ | 306 | BUG_ON(atomic_read(&(p)->_count) == -1);\ |
307 | atomic_add_negative(-1, &(p)->_count); \ | 307 | atomic_add_negative(-1, &(p)->_count); \ |
308 | }) | 308 | }) |
309 | 309 | ||
@@ -34,19 +34,22 @@ | |||
34 | /* How many pages do we try to swap or page in/out together? */ | 34 | /* How many pages do we try to swap or page in/out together? */ |
35 | int page_cluster; | 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))) { | 39 | page = (struct page *)page_private(page); |
40 | page = (struct page *)page_private(page); | 40 | if (put_page_testzero(page)) { |
41 | if (put_page_testzero(page)) { | 41 | void (*dtor)(struct page *page); |
42 | void (*dtor)(struct page *page); | ||
43 | 42 | ||
44 | dtor = (void (*)(struct page *))page[1].mapping; | 43 | dtor = (void (*)(struct page *))page[1].mapping; |
45 | (*dtor)(page); | 44 | (*dtor)(page); |
46 | } | ||
47 | return; | ||
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 | __page_cache_release(page); | 53 | __page_cache_release(page); |
51 | } | 54 | } |
52 | EXPORT_SYMBOL(put_page); | 55 | EXPORT_SYMBOL(put_page); |
@@ -244,6 +247,15 @@ void release_pages(struct page **pages, int nr, int cold) | |||
244 | struct page *page = pages[i]; | 247 | struct page *page = pages[i]; |
245 | struct zone *pagezone; | 248 | struct zone *pagezone; |
246 | 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 | } | ||
258 | |||
247 | if (!put_page_testzero(page)) | 259 | if (!put_page_testzero(page)) |
248 | continue; | 260 | continue; |
249 | 261 | ||