[PATCH] mm/page_isolation: separate the pageblock isolation function

From: Joonsoo Kim
Date: Mon Oct 23 2017 - 22:38:22 EST


There are two users who use pageblock isolation function,
alloc_contig_range() and memory hotplug. Each one has different purpose
on isolation so they should be treated separately. For example,
alloc_contig_range() doesn't require that all pages on the pageblock are
movable because it could just needs part of pages on the pageblock.
But, memory hotplug does since memory offline works for pageblock unit
or more. Currently, they are distiniguished by migratetype of
the target pageblock but it causes a problem on memory hotplug
so it's better to separate the function completely at this moment.

Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@xxxxxxx>
---
include/linux/page-isolation.h | 3 +-
mm/page_alloc.c | 9 +++--
mm/page_isolation.c | 76 +++++++++++++++++++++++++++++-------------
3 files changed, 57 insertions(+), 31 deletions(-)

diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h
index d4cd201..614dc00 100644
--- a/include/linux/page-isolation.h
+++ b/include/linux/page-isolation.h
@@ -29,8 +29,7 @@ static inline bool is_migrate_isolate(int migratetype)
}
#endif

-bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
- bool skip_hwpoisoned_pages);
+bool has_unremovable_pages(struct zone *zone, struct page *page, int count);
void set_pageblock_migratetype(struct page *page, int migratetype);
int move_freepages_block(struct zone *zone, struct page *page,
int migratetype, int *num_movable);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 1008c58..04f3b36 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -7373,7 +7373,7 @@ void *__init alloc_large_system_hash(const char *tablename,
}

/*
- * This function checks whether pageblock includes unmovable pages or not.
+ * This function checks whether pageblock includes unremovable pages or not.
* If @count is not zero, it is okay to include less @count unmovable pages
*
* PageLRU check without isolation or lru_lock could race so that
@@ -7381,8 +7381,7 @@ void *__init alloc_large_system_hash(const char *tablename,
* check without lock_page also may miss some movable non-lru pages at
* race condition. So you can't expect this function should be exact.
*/
-bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
- bool skip_hwpoisoned_pages)
+bool has_unremovable_pages(struct zone *zone, struct page *page, int count)
{
unsigned long pfn, iter, found;
int mt;
@@ -7432,7 +7431,7 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
* The HWPoisoned page may be not in buddy system, and
* page_count() is not 0.
*/
- if (skip_hwpoisoned_pages && PageHWPoison(page))
+ if (PageHWPoison(page))
continue;

if (__PageMovable(page))
@@ -7479,7 +7478,7 @@ bool is_pageblock_removable_nolock(struct page *page)
if (!zone_spans_pfn(zone, pfn))
return false;

- return !has_unmovable_pages(zone, page, 0, true);
+ return !has_unremovable_pages(zone, page, 0);
}

#if (defined(CONFIG_MEMORY_ISOLATION) && defined(CONFIG_COMPACTION)) || defined(CONFIG_CMA)
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index 757410d..1650e01 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -14,8 +14,38 @@
#define CREATE_TRACE_POINTS
#include <trace/events/page_isolation.h>

-static int set_migratetype_isolate(struct page *page,
- bool skip_hwpoisoned_pages)
+/* Should hold the zone lock */
+static void __set_migratetype_isolate(struct zone *zone,
+ struct page *page, int mt)
+{
+ unsigned long nr_pages;
+
+ set_pageblock_migratetype(page, MIGRATE_ISOLATE);
+ zone->nr_isolate_pageblock++;
+ nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE, NULL);
+ __mod_zone_freepage_state(zone, -nr_pages, mt);
+}
+
+static int isolate_pageblock(struct page *page, int mt)
+{
+ struct zone *zone = page_zone(page);
+ unsigned long flags;
+
+ spin_lock_irqsave(&zone->lock, flags);
+ if (get_pageblock_migratetype(page) != mt) {
+ spin_unlock_irqrestore(&zone->lock, flags);
+ return -EBUSY;
+ }
+
+ __set_migratetype_isolate(zone, page, mt);
+ spin_unlock_irqrestore(&zone->lock, flags);
+
+ drain_all_pages(zone);
+
+ return 0;
+}
+
+static int isolate_pageblock_for_offline(struct page *page)
{
struct zone *zone;
unsigned long flags, pfn;
@@ -46,33 +76,22 @@ static int set_migratetype_isolate(struct page *page,
notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
notifier_ret = notifier_to_errno(notifier_ret);
if (notifier_ret)
- goto out;
+ goto err;
/*
* FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
* We just check MOVABLE pages.
*/
- if (!has_unmovable_pages(zone, page, arg.pages_found,
- skip_hwpoisoned_pages))
- ret = 0;
-
/*
* immobile means "not-on-lru" pages. If immobile is larger than
* removable-by-driver pages reported by notifier, we'll fail.
*/
+ if (has_unremovable_pages(zone, page, arg.pages_found))
+ goto err;

-out:
- if (!ret) {
- unsigned long nr_pages;
- int migratetype = get_pageblock_migratetype(page);
-
- set_pageblock_migratetype(page, MIGRATE_ISOLATE);
- zone->nr_isolate_pageblock++;
- nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE,
- NULL);
-
- __mod_zone_freepage_state(zone, -nr_pages, migratetype);
- }
+ __set_migratetype_isolate(zone, page, get_pageblock_migratetype(page));
+ ret = 0;

+err:
spin_unlock_irqrestore(&zone->lock, flags);
if (!ret)
drain_all_pages(zone);
@@ -159,6 +178,7 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages)
* @start_pfn: The lower PFN of the range to be isolated.
* @end_pfn: The upper PFN of the range to be isolated.
* @migratetype: migrate type to set in error recovery.
+ * @for_memory_offline: The purpose of the isolation
*
* Making page-allocation-type to be MIGRATE_ISOLATE means free pages in
* the range will never be allocated. Any free pages and pages freed in the
@@ -168,7 +188,7 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages)
* Returns 0 on success and -EBUSY if any part of range cannot be isolated.
*/
int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
- unsigned migratetype, bool skip_hwpoisoned_pages)
+ unsigned int migratetype, bool for_memory_offline)
{
unsigned long pfn;
unsigned long undo_pfn;
@@ -181,11 +201,19 @@ int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
pfn < end_pfn;
pfn += pageblock_nr_pages) {
page = __first_valid_page(pfn, pageblock_nr_pages);
- if (page &&
- set_migratetype_isolate(page, skip_hwpoisoned_pages)) {
- undo_pfn = pfn;
- goto undo;
+ if (!page)
+ continue;
+
+ if (for_memory_offline) {
+ if (!isolate_pageblock_for_offline(page))
+ continue;
+ } else {
+ if (!isolate_pageblock(page, migratetype))
+ continue;
}
+
+ undo_pfn = pfn;
+ goto undo;
}
return 0;
undo:
--
2.7.4