Commit d53aea3d authored by David Rientjes's avatar David Rientjes Committed by Linus Torvalds
Browse files

mm, compaction: return failed migration target pages back to freelist

Greg reported that he found isolated free pages were returned back to the
VM rather than the compaction freelist.  This will cause holes behind the
free scanner and cause it to reallocate additional memory if necessary

He detected the problem at runtime seeing that ext4 metadata pages (esp
the ones read by "sbi->s_group_desc[i] = sb_bread(sb, block)") were
constantly visited by compaction calls of migrate_pages().  These pages
had a non-zero b_count which caused fallback_migrate_page() ->
try_to_release_page() -> try_to_free_buffers() to fail.

Memory compaction works by having a "freeing scanner" scan from one end of
a zone which isolates pages as migration targets while another "migrating
scanner" scans from the other end of the same zone which isolates pages
for migration.

When page migration fails for an isolated page, the target page is
returned to the system rather than the freelist built by the freeing
scanner.  This may require the freeing scanner to continue scanning memory
after suitable migration targets have already been returned to the system

This patch returns destination pages to the freeing scanner freelist when
page migration fails.  This prevents unnecessary work done by the freeing
scanner but also encourages memory to be as compacted as possible at the
end of the zone.
Signed-off-by: default avatarDavid Rientjes <>
Reported-by: default avatarGreg Thelen <>
Acked-by: default avatarMel Gorman <>
Acked-by: default avatarVlastimil Babka <>
Reviewed-by: default avatarNaoya Horiguchi <>
Signed-off-by: default avatarAndrew Morton <>
Signed-off-by: default avatarLinus Torvalds <>
parent 68711a74
...@@ -790,23 +790,32 @@ static struct page *compaction_alloc(struct page *migratepage, ...@@ -790,23 +790,32 @@ static struct page *compaction_alloc(struct page *migratepage,
} }
/* /*
* We cannot control nr_migratepages and nr_freepages fully when migration is * This is a migrate-callback that "frees" freepages back to the isolated
* running as migrate_pages() has no knowledge of compact_control. When * freelist. All pages on the freelist are from the same zone, so there is no
* migration is complete, we count the number of pages on the lists by hand. * special handling needed for NUMA.
static void compaction_free(struct page *page, unsigned long data)
struct compact_control *cc = (struct compact_control *)data;
list_add(&page->lru, &cc->freepages);
* We cannot control nr_migratepages fully when migration is running as
* migrate_pages() has no knowledge of of compact_control. When migration is
* complete, we count the number of pages on the list by hand.
*/ */
static void update_nr_listpages(struct compact_control *cc) static void update_nr_listpages(struct compact_control *cc)
{ {
int nr_migratepages = 0; int nr_migratepages = 0;
int nr_freepages = 0;
struct page *page; struct page *page;
list_for_each_entry(page, &cc->migratepages, lru) list_for_each_entry(page, &cc->migratepages, lru)
nr_migratepages++; nr_migratepages++;
list_for_each_entry(page, &cc->freepages, lru)
cc->nr_migratepages = nr_migratepages; cc->nr_migratepages = nr_migratepages;
cc->nr_freepages = nr_freepages;
} }
/* possible outcome of isolate_migratepages */ /* possible outcome of isolate_migratepages */
...@@ -1016,8 +1025,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) ...@@ -1016,8 +1025,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
} }
nr_migrate = cc->nr_migratepages; nr_migrate = cc->nr_migratepages;
err = migrate_pages(&cc->migratepages, compaction_alloc, NULL, err = migrate_pages(&cc->migratepages, compaction_alloc,
(unsigned long)cc, compaction_free, (unsigned long)cc,
update_nr_listpages(cc); update_nr_listpages(cc);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment