aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2006-02-07 15:58:52 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-07 19:12:33 -0500
commit8519fb30e438f8088b71a94a7d5a660a814d3872 (patch)
tree8d29a7b1fa33e5f65e649d363cfa10e83c4fab97
parent99f6d61bda82d09b2d94414d413d39f66a0b7da2 (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.h2
-rw-r--r--mm/swap.c32
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
diff --git a/mm/swap.c b/mm/swap.c
index bc2442a7b0ee..76247424dea1 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -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? */
35int page_cluster; 35int page_cluster;
36 36
37void put_page(struct page *page) 37static 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
48void 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}
52EXPORT_SYMBOL(put_page); 55EXPORT_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