Skip to content
This repository was archived by the owner on Dec 20, 2023. It is now read-only.

Commit 117aad1

Browse files
aquinitorvalds
authored andcommitted
mm: avoid reinserting isolated balloon pages into LRU lists
Isolated balloon pages can wrongly end up in LRU lists when migrate_pages() finishes its round without draining all the isolated page list. The same issue can happen when reclaim_clean_pages_from_list() tries to reclaim pages from an isolated page list, before migration, in the CMA path. Such balloon page leak opens a race window against LRU lists shrinkers that leads us to the following kernel panic: BUG: unable to handle kernel NULL pointer dereference at 0000000000000028 IP: [<ffffffff810c2625>] shrink_page_list+0x24e/0x897 PGD 3cda2067 PUD 3d713067 PMD 0 Oops: 0000 [#1] SMP CPU: 0 PID: 340 Comm: kswapd0 Not tainted 3.12.0-rc1-22626-g4367597 torvalds#87 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 RIP: shrink_page_list+0x24e/0x897 RSP: 0000:ffff88003da499b8 EFLAGS: 00010286 RAX: 0000000000000000 RBX: ffff88003e82bd60 RCX: 00000000000657d5 RDX: 0000000000000000 RSI: 000000000000031f RDI: ffff88003e82bd40 RBP: ffff88003da49ab0 R08: 0000000000000001 R09: 0000000081121a45 R10: ffffffff81121a45 R11: ffff88003c4a9a28 R12: ffff88003e82bd40 R13: ffff88003da0e800 R14: 0000000000000001 R15: ffff88003da49d58 FS: 0000000000000000(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000067d9000 CR3: 000000003ace5000 CR4: 00000000000407b0 Call Trace: shrink_inactive_list+0x240/0x3de shrink_lruvec+0x3e0/0x566 __shrink_zone+0x94/0x178 shrink_zone+0x3a/0x82 balance_pgdat+0x32a/0x4c2 kswapd+0x2f0/0x372 kthread+0xa2/0xaa ret_from_fork+0x7c/0xb0 Code: 80 7d 8f 01 48 83 95 68 ff ff ff 00 4c 89 e7 e8 5a 7b 00 00 48 85 c0 49 89 c5 75 08 80 7d 8f 00 74 3e eb 31 48 8b 80 18 01 00 00 <48> 8b 74 0d 48 8b 78 30 be 02 00 00 00 ff d2 eb RIP [<ffffffff810c2625>] shrink_page_list+0x24e/0x897 RSP <ffff88003da499b8> CR2: 0000000000000028 ---[ end trace 703d2451af6ffbfd ]--- Kernel panic - not syncing: Fatal exception This patch fixes the issue, by assuring the proper tests are made at putback_movable_pages() & reclaim_clean_pages_from_list() to avoid isolated balloon pages being wrongly reinserted in LRU lists. [[email protected]: clarify awkward comment text] Signed-off-by: Rafael Aquini <[email protected]> Reported-by: Luiz Capitulino <[email protected]> Tested-by: Luiz Capitulino <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 0772dac commit 117aad1

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

Diff for: include/linux/balloon_compaction.h

+25
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,26 @@ static inline bool balloon_page_movable(struct page *page)
158158
return false;
159159
}
160160

161+
/*
162+
* isolated_balloon_page - identify an isolated balloon page on private
163+
* compaction/migration page lists.
164+
*
165+
* After a compaction thread isolates a balloon page for migration, it raises
166+
* the page refcount to prevent concurrent compaction threads from re-isolating
167+
* the same page. For that reason putback_movable_pages(), or other routines
168+
* that need to identify isolated balloon pages on private pagelists, cannot
169+
* rely on balloon_page_movable() to accomplish the task.
170+
*/
171+
static inline bool isolated_balloon_page(struct page *page)
172+
{
173+
/* Already isolated balloon pages, by default, have a raised refcount */
174+
if (page_flags_cleared(page) && !page_mapped(page) &&
175+
page_count(page) >= 2)
176+
return __is_movable_balloon_page(page);
177+
178+
return false;
179+
}
180+
161181
/*
162182
* balloon_page_insert - insert a page into the balloon's page list and make
163183
* the page->mapping assignment accordingly.
@@ -243,6 +263,11 @@ static inline bool balloon_page_movable(struct page *page)
243263
return false;
244264
}
245265

266+
static inline bool isolated_balloon_page(struct page *page)
267+
{
268+
return false;
269+
}
270+
246271
static inline bool balloon_page_isolate(struct page *page)
247272
{
248273
return false;

Diff for: mm/migrate.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ void putback_movable_pages(struct list_head *l)
107107
list_del(&page->lru);
108108
dec_zone_page_state(page, NR_ISOLATED_ANON +
109109
page_is_file_cache(page));
110-
if (unlikely(balloon_page_movable(page)))
110+
if (unlikely(isolated_balloon_page(page)))
111111
balloon_page_putback(page);
112112
else
113113
putback_lru_page(page);

Diff for: mm/vmscan.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <asm/div64.h>
4949

5050
#include <linux/swapops.h>
51+
#include <linux/balloon_compaction.h>
5152

5253
#include "internal.h"
5354

@@ -1113,7 +1114,8 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone,
11131114
LIST_HEAD(clean_pages);
11141115

11151116
list_for_each_entry_safe(page, next, page_list, lru) {
1116-
if (page_is_file_cache(page) && !PageDirty(page)) {
1117+
if (page_is_file_cache(page) && !PageDirty(page) &&
1118+
!isolated_balloon_page(page)) {
11171119
ClearPageActive(page);
11181120
list_move(&page->lru, &clean_pages);
11191121
}

0 commit comments

Comments
 (0)