Commit 68e3e926 authored by Linus Torvalds's avatar Linus Torvalds

Revert "mm: compaction: handle incorrect MIGRATE_UNMOVABLE type pageblocks"

This reverts commit 5ceb9ce6.

That commit seems to be the cause of the mm compation list corruption
issues that Dave Jones reported.  The locking (or rather, absense
there-of) is dubious, as is the use of the 'page' variable once it has
been found to be outside the pageblock range.

So revert it for now, we can re-visit this for 3.6.  If we even need to:
as Minchan Kim says, "The patch wasn't a bug fix and even test workload
was very theoretical".
Reported-and-tested-by: default avatarDave Jones <davej@redhat.com>
Acked-by: default avatarHugh Dickins <hughd@google.com>
Acked-by: default avatarKOSAKI Motohiro <kosaki.motohiro@gmail.com>
Acked-by: default avatarMinchan Kim <minchan@kernel.org>
Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 752dc185
#ifndef _LINUX_COMPACTION_H #ifndef _LINUX_COMPACTION_H
#define _LINUX_COMPACTION_H #define _LINUX_COMPACTION_H
#include <linux/node.h>
/* Return values for compact_zone() and try_to_compact_pages() */ /* Return values for compact_zone() and try_to_compact_pages() */
/* compaction didn't start as it was not possible or direct reclaim was more suitable */ /* compaction didn't start as it was not possible or direct reclaim was more suitable */
#define COMPACT_SKIPPED 0 #define COMPACT_SKIPPED 0
...@@ -13,23 +11,6 @@ ...@@ -13,23 +11,6 @@
/* The full zone was compacted */ /* The full zone was compacted */
#define COMPACT_COMPLETE 3 #define COMPACT_COMPLETE 3
/*
* compaction supports three modes
*
* COMPACT_ASYNC_MOVABLE uses asynchronous migration and only scans
* MIGRATE_MOVABLE pageblocks as migration sources and targets.
* COMPACT_ASYNC_UNMOVABLE uses asynchronous migration and only scans
* MIGRATE_MOVABLE pageblocks as migration sources.
* MIGRATE_UNMOVABLE pageblocks are scanned as potential migration
* targets and convers them to MIGRATE_MOVABLE if possible
* COMPACT_SYNC uses synchronous migration and scans all pageblocks
*/
enum compact_mode {
COMPACT_ASYNC_MOVABLE,
COMPACT_ASYNC_UNMOVABLE,
COMPACT_SYNC,
};
#ifdef CONFIG_COMPACTION #ifdef CONFIG_COMPACTION
extern int sysctl_compact_memory; extern int sysctl_compact_memory;
extern int sysctl_compaction_handler(struct ctl_table *table, int write, extern int sysctl_compaction_handler(struct ctl_table *table, int write,
......
...@@ -236,7 +236,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, ...@@ -236,7 +236,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
*/ */
while (unlikely(too_many_isolated(zone))) { while (unlikely(too_many_isolated(zone))) {
/* async migration should just abort */ /* async migration should just abort */
if (cc->mode != COMPACT_SYNC) if (!cc->sync)
return 0; return 0;
congestion_wait(BLK_RW_ASYNC, HZ/10); congestion_wait(BLK_RW_ASYNC, HZ/10);
...@@ -304,8 +304,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, ...@@ -304,8 +304,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
* satisfies the allocation * satisfies the allocation
*/ */
pageblock_nr = low_pfn >> pageblock_order; pageblock_nr = low_pfn >> pageblock_order;
if (cc->mode != COMPACT_SYNC && if (!cc->sync && last_pageblock_nr != pageblock_nr &&
last_pageblock_nr != pageblock_nr &&
!migrate_async_suitable(get_pageblock_migratetype(page))) { !migrate_async_suitable(get_pageblock_migratetype(page))) {
low_pfn += pageblock_nr_pages; low_pfn += pageblock_nr_pages;
low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1; low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
...@@ -326,7 +325,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, ...@@ -326,7 +325,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
continue; continue;
} }
if (cc->mode != COMPACT_SYNC) if (!cc->sync)
mode |= ISOLATE_ASYNC_MIGRATE; mode |= ISOLATE_ASYNC_MIGRATE;
lruvec = mem_cgroup_page_lruvec(page, zone); lruvec = mem_cgroup_page_lruvec(page, zone);
...@@ -361,90 +360,27 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, ...@@ -361,90 +360,27 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
#endif /* CONFIG_COMPACTION || CONFIG_CMA */ #endif /* CONFIG_COMPACTION || CONFIG_CMA */
#ifdef CONFIG_COMPACTION #ifdef CONFIG_COMPACTION
/*
* Returns true if MIGRATE_UNMOVABLE pageblock was successfully
* converted to MIGRATE_MOVABLE type, false otherwise.
*/
static bool rescue_unmovable_pageblock(struct page *page)
{
unsigned long pfn, start_pfn, end_pfn;
struct page *start_page, *end_page;
pfn = page_to_pfn(page);
start_pfn = pfn & ~(pageblock_nr_pages - 1);
end_pfn = start_pfn + pageblock_nr_pages;
start_page = pfn_to_page(start_pfn);
end_page = pfn_to_page(end_pfn);
/* Do not deal with pageblocks that overlap zones */
if (page_zone(start_page) != page_zone(end_page))
return false;
for (page = start_page, pfn = start_pfn; page < end_page; pfn++,
page++) {
if (!pfn_valid_within(pfn))
continue;
if (PageBuddy(page)) {
int order = page_order(page);
pfn += (1 << order) - 1;
page += (1 << order) - 1;
continue; /* Returns true if the page is within a block suitable for migration to */
} else if (page_count(page) == 0 || PageLRU(page)) static bool suitable_migration_target(struct page *page)
continue;
return false;
}
set_pageblock_migratetype(page, MIGRATE_MOVABLE);
move_freepages_block(page_zone(page), page, MIGRATE_MOVABLE);
return true;
}
enum smt_result {
GOOD_AS_MIGRATION_TARGET,
FAIL_UNMOVABLE_TARGET,
FAIL_BAD_TARGET,
};
/*
* Returns GOOD_AS_MIGRATION_TARGET if the page is within a block
* suitable for migration to, FAIL_UNMOVABLE_TARGET if the page
* is within a MIGRATE_UNMOVABLE block, FAIL_BAD_TARGET otherwise.
*/
static enum smt_result suitable_migration_target(struct page *page,
struct compact_control *cc)
{ {
int migratetype = get_pageblock_migratetype(page); int migratetype = get_pageblock_migratetype(page);
/* Don't interfere with memory hot-remove or the min_free_kbytes blocks */ /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE) if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
return FAIL_BAD_TARGET; return false;
/* If the page is a large free page, then allow migration */ /* If the page is a large free page, then allow migration */
if (PageBuddy(page) && page_order(page) >= pageblock_order) if (PageBuddy(page) && page_order(page) >= pageblock_order)
return GOOD_AS_MIGRATION_TARGET; return true;
/* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */ /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
if (cc->mode != COMPACT_ASYNC_UNMOVABLE && if (migrate_async_suitable(migratetype))
migrate_async_suitable(migratetype)) return true;
return GOOD_AS_MIGRATION_TARGET;
if (cc->mode == COMPACT_ASYNC_MOVABLE &&
migratetype == MIGRATE_UNMOVABLE)
return FAIL_UNMOVABLE_TARGET;
if (cc->mode != COMPACT_ASYNC_MOVABLE &&
migratetype == MIGRATE_UNMOVABLE &&
rescue_unmovable_pageblock(page))
return GOOD_AS_MIGRATION_TARGET;
/* Otherwise skip the block */ /* Otherwise skip the block */
return FAIL_BAD_TARGET; return false;
} }
/* /*
...@@ -477,13 +413,6 @@ static void isolate_freepages(struct zone *zone, ...@@ -477,13 +413,6 @@ static void isolate_freepages(struct zone *zone,
zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages; zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
/*
* isolate_freepages() may be called more than once during
* compact_zone_order() run and we want only the most recent
* count.
*/
cc->nr_pageblocks_skipped = 0;
/* /*
* Isolate free pages until enough are available to migrate the * Isolate free pages until enough are available to migrate the
* pages on cc->migratepages. We stop searching if the migrate * pages on cc->migratepages. We stop searching if the migrate
...@@ -492,7 +421,6 @@ static void isolate_freepages(struct zone *zone, ...@@ -492,7 +421,6 @@ static void isolate_freepages(struct zone *zone,
for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages; for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages;
pfn -= pageblock_nr_pages) { pfn -= pageblock_nr_pages) {
unsigned long isolated; unsigned long isolated;
enum smt_result ret;
if (!pfn_valid(pfn)) if (!pfn_valid(pfn))
continue; continue;
...@@ -509,12 +437,9 @@ static void isolate_freepages(struct zone *zone, ...@@ -509,12 +437,9 @@ static void isolate_freepages(struct zone *zone,
continue; continue;
/* Check the block is suitable for migration */ /* Check the block is suitable for migration */
ret = suitable_migration_target(page, cc); if (!suitable_migration_target(page))
if (ret != GOOD_AS_MIGRATION_TARGET) {
if (ret == FAIL_UNMOVABLE_TARGET)
cc->nr_pageblocks_skipped++;
continue; continue;
}
/* /*
* Found a block suitable for isolating free pages from. Now * Found a block suitable for isolating free pages from. Now
* we disabled interrupts, double check things are ok and * we disabled interrupts, double check things are ok and
...@@ -523,14 +448,12 @@ static void isolate_freepages(struct zone *zone, ...@@ -523,14 +448,12 @@ static void isolate_freepages(struct zone *zone,
*/ */
isolated = 0; isolated = 0;
spin_lock_irqsave(&zone->lock, flags); spin_lock_irqsave(&zone->lock, flags);
ret = suitable_migration_target(page, cc); if (suitable_migration_target(page)) {
if (ret == GOOD_AS_MIGRATION_TARGET) {
end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
isolated = isolate_freepages_block(pfn, end_pfn, isolated = isolate_freepages_block(pfn, end_pfn,
freelist, false); freelist, false);
nr_freepages += isolated; nr_freepages += isolated;
} else if (ret == FAIL_UNMOVABLE_TARGET) }
cc->nr_pageblocks_skipped++;
spin_unlock_irqrestore(&zone->lock, flags); spin_unlock_irqrestore(&zone->lock, flags);
/* /*
...@@ -762,9 +685,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) ...@@ -762,9 +685,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, err = migrate_pages(&cc->migratepages, compaction_alloc,
(unsigned long)&cc->freepages, false, (unsigned long)cc, false,
(cc->mode == COMPACT_SYNC) ? MIGRATE_SYNC_LIGHT cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC);
: MIGRATE_ASYNC);
update_nr_listpages(cc); update_nr_listpages(cc);
nr_remaining = cc->nr_migratepages; nr_remaining = cc->nr_migratepages;
...@@ -793,8 +715,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) ...@@ -793,8 +715,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
static unsigned long compact_zone_order(struct zone *zone, static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask, int order, gfp_t gfp_mask,
enum compact_mode mode, bool sync)
unsigned long *nr_pageblocks_skipped)
{ {
struct compact_control cc = { struct compact_control cc = {
.nr_freepages = 0, .nr_freepages = 0,
...@@ -802,17 +723,12 @@ static unsigned long compact_zone_order(struct zone *zone, ...@@ -802,17 +723,12 @@ static unsigned long compact_zone_order(struct zone *zone,
.order = order, .order = order,
.migratetype = allocflags_to_migratetype(gfp_mask), .migratetype = allocflags_to_migratetype(gfp_mask),
.zone = zone, .zone = zone,
.mode = mode, .sync = sync,
}; };
unsigned long rc;
INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages); INIT_LIST_HEAD(&cc.migratepages);
rc = compact_zone(zone, &cc); return compact_zone(zone, &cc);
*nr_pageblocks_skipped = cc.nr_pageblocks_skipped;
return rc;
} }
int sysctl_extfrag_threshold = 500; int sysctl_extfrag_threshold = 500;
...@@ -837,8 +753,6 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, ...@@ -837,8 +753,6 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
struct zoneref *z; struct zoneref *z;
struct zone *zone; struct zone *zone;
int rc = COMPACT_SKIPPED; int rc = COMPACT_SKIPPED;
unsigned long nr_pageblocks_skipped;
enum compact_mode mode;
/* /*
* Check whether it is worth even starting compaction. The order check is * Check whether it is worth even starting compaction. The order check is
...@@ -855,22 +769,12 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, ...@@ -855,22 +769,12 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
nodemask) { nodemask) {
int status; int status;
mode = sync ? COMPACT_SYNC : COMPACT_ASYNC_MOVABLE; status = compact_zone_order(zone, order, gfp_mask, sync);
retry:
status = compact_zone_order(zone, order, gfp_mask, mode,
&nr_pageblocks_skipped);
rc = max(status, rc); rc = max(status, rc);
/* If a normal allocation would succeed, stop compacting */ /* If a normal allocation would succeed, stop compacting */
if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0, 0)) if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0, 0))
break; break;
if (rc == COMPACT_COMPLETE && mode == COMPACT_ASYNC_MOVABLE) {
if (nr_pageblocks_skipped) {
mode = COMPACT_ASYNC_UNMOVABLE;
goto retry;
}
}
} }
return rc; return rc;
...@@ -904,7 +808,7 @@ static int __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc) ...@@ -904,7 +808,7 @@ static int __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
if (ok && cc->order > zone->compact_order_failed) if (ok && cc->order > zone->compact_order_failed)
zone->compact_order_failed = cc->order + 1; zone->compact_order_failed = cc->order + 1;
/* Currently async compaction is never deferred. */ /* Currently async compaction is never deferred. */
else if (!ok && cc->mode == COMPACT_SYNC) else if (!ok && cc->sync)
defer_compaction(zone, cc->order); defer_compaction(zone, cc->order);
} }
...@@ -919,7 +823,7 @@ int compact_pgdat(pg_data_t *pgdat, int order) ...@@ -919,7 +823,7 @@ int compact_pgdat(pg_data_t *pgdat, int order)
{ {
struct compact_control cc = { struct compact_control cc = {
.order = order, .order = order,
.mode = COMPACT_ASYNC_MOVABLE, .sync = false,
}; };
return __compact_pgdat(pgdat, &cc); return __compact_pgdat(pgdat, &cc);
...@@ -929,7 +833,7 @@ static int compact_node(int nid) ...@@ -929,7 +833,7 @@ static int compact_node(int nid)
{ {
struct compact_control cc = { struct compact_control cc = {
.order = -1, .order = -1,
.mode = COMPACT_SYNC, .sync = true,
}; };
return __compact_pgdat(NODE_DATA(nid), &cc); return __compact_pgdat(NODE_DATA(nid), &cc);
......
...@@ -94,9 +94,6 @@ extern void putback_lru_page(struct page *page); ...@@ -94,9 +94,6 @@ extern void putback_lru_page(struct page *page);
/* /*
* in mm/page_alloc.c * in mm/page_alloc.c
*/ */
extern void set_pageblock_migratetype(struct page *page, int migratetype);
extern int move_freepages_block(struct zone *zone, struct page *page,
int migratetype);
extern void __free_pages_bootmem(struct page *page, unsigned int order); extern void __free_pages_bootmem(struct page *page, unsigned int order);
extern void prep_compound_page(struct page *page, unsigned long order); extern void prep_compound_page(struct page *page, unsigned long order);
#ifdef CONFIG_MEMORY_FAILURE #ifdef CONFIG_MEMORY_FAILURE
...@@ -104,7 +101,6 @@ extern bool is_free_buddy_page(struct page *page); ...@@ -104,7 +101,6 @@ extern bool is_free_buddy_page(struct page *page);
#endif #endif
#if defined CONFIG_COMPACTION || defined CONFIG_CMA #if defined CONFIG_COMPACTION || defined CONFIG_CMA
#include <linux/compaction.h>
/* /*
* in mm/compaction.c * in mm/compaction.c
...@@ -123,14 +119,11 @@ struct compact_control { ...@@ -123,14 +119,11 @@ struct compact_control {
unsigned long nr_migratepages; /* Number of pages to migrate */ unsigned long nr_migratepages; /* Number of pages to migrate */
unsigned long free_pfn; /* isolate_freepages search base */ unsigned long free_pfn; /* isolate_freepages search base */
unsigned long migrate_pfn; /* isolate_migratepages search base */ unsigned long migrate_pfn; /* isolate_migratepages search base */
enum compact_mode mode; /* Compaction mode */ bool sync; /* Synchronous migration */
int order; /* order a direct compactor needs */ int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */ int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone; struct zone *zone;
/* Number of UNMOVABLE destination pageblocks skipped during scan */
unsigned long nr_pageblocks_skipped;
}; };
unsigned long unsigned long
......
...@@ -219,7 +219,7 @@ EXPORT_SYMBOL(nr_online_nodes); ...@@ -219,7 +219,7 @@ EXPORT_SYMBOL(nr_online_nodes);
int page_group_by_mobility_disabled __read_mostly; int page_group_by_mobility_disabled __read_mostly;
void set_pageblock_migratetype(struct page *page, int migratetype) static void set_pageblock_migratetype(struct page *page, int migratetype)
{ {
if (unlikely(page_group_by_mobility_disabled)) if (unlikely(page_group_by_mobility_disabled))
...@@ -954,7 +954,7 @@ static int move_freepages(struct zone *zone, ...@@ -954,7 +954,7 @@ static int move_freepages(struct zone *zone,
return pages_moved; return pages_moved;
} }
int move_freepages_block(struct zone *zone, struct page *page, static int move_freepages_block(struct zone *zone, struct page *page,
int migratetype) int migratetype)
{ {
unsigned long start_pfn, end_pfn; unsigned long start_pfn, end_pfn;
...@@ -5651,7 +5651,7 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end) ...@@ -5651,7 +5651,7 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
.nr_migratepages = 0, .nr_migratepages = 0,
.order = -1, .order = -1,
.zone = page_zone(pfn_to_page(start)), .zone = page_zone(pfn_to_page(start)),
.mode = COMPACT_SYNC, .sync = true,
}; };
INIT_LIST_HEAD(&cc.migratepages); INIT_LIST_HEAD(&cc.migratepages);
......
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