diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h index 280ae96dc4c300d29418ececeb5e9b91acf27e2e..cad3f2dc0aff4c894842b38e0eddaa1981ad0bf7 100644 --- a/include/linux/page-isolation.h +++ b/include/linux/page-isolation.h @@ -33,8 +33,8 @@ static inline bool is_migrate_isolate(int migratetype) #define SKIP_HWPOISON 0x1 #define REPORT_FAILURE 0x2 -bool has_unmovable_pages(struct zone *zone, struct page *page, int count, - int migratetype, int flags); +struct page *has_unmovable_pages(struct zone *zone, struct page *page, + int count, int migratetype, int flags); 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/debug.c b/mm/debug.c index 2da184b16bce0f088cef07c02b0ce640c8cc9c7d..30c80c2029bfb8ac8266815cf999476d9652bb75 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -44,6 +44,13 @@ const struct trace_print_flags vmaflag_names[] = { void __dump_page(struct page *page, const char *reason) { bool page_poisoned = PagePoisoned(page); + /* + * Accessing the pageblock without the zone lock. It could change to + * "isolate" again in the meantime, but since we are just dumping the + * state for debugging, it should be fine to accept a bit of + * inaccuracy here due to racing. + */ + bool page_cma = is_migrate_cma_page(page); int mapcount; /* @@ -71,7 +78,8 @@ void __dump_page(struct page *page, const char *reason) pr_cont("\n"); BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1); - pr_emerg("flags: %#lx(%pGp)\n", page->flags, &page->flags); + pr_emerg("flags: %#lx(%pGp)%s\n", page->flags, &page->flags, + page_cma ? " CMA" : ""); hex_only: print_hex_dump(KERN_ALERT, "raw: ", DUMP_PREFIX_NONE, 32, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index cede3ebe73539f4abc54ad5537647f7e9c7c9ebf..a998aa179bfa67f61a75d3390b0255fbc86dfea3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -8138,14 +8138,18 @@ void *__init alloc_large_system_hash(const char *tablename, * MIGRATE_MOVABLE block might include unmovable pages. And __PageMovable * 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. + * + * Returns a page without holding a reference. If the caller wants to + * dereference that page (e.g., dumping), it has to make sure that that it + * cannot get removed (e.g., via memory unplug) concurrently. + * */ -bool has_unmovable_pages(struct zone *zone, struct page *page, int count, - int migratetype, int flags) +struct page *has_unmovable_pages(struct zone *zone, struct page *page, + int count, int migratetype, int flags) { unsigned long found; unsigned long iter = 0; unsigned long pfn = page_to_pfn(page); - const char *reason = "unmovable page"; /* * TODO we could make this much more efficient by not checking every @@ -8162,9 +8166,8 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count, * so consider them movable here. */ if (is_migrate_cma(migratetype)) - return false; + return NULL; - reason = "CMA page"; goto unmovable; } @@ -8244,12 +8247,10 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count, if (found > count) goto unmovable; } - return false; + return NULL; unmovable: WARN_ON_ONCE(zone_idx(zone) == ZONE_MOVABLE); - if (flags & REPORT_FAILURE) - dump_page(pfn_to_page(pfn + iter), reason); - return true; + return pfn_to_page(pfn + iter); } #if (defined(CONFIG_MEMORY_ISOLATION) && defined(CONFIG_COMPACTION)) || defined(CONFIG_CMA) @@ -8553,10 +8554,6 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn) BUG_ON(page_count(page)); BUG_ON(!PageBuddy(page)); order = page_order(page); -#ifdef CONFIG_DEBUG_VM - pr_info("remove from free list %lx %d %lx\n", - pfn, 1 << order, end_pfn); -#endif list_del(&page->lru); rmv_page_order(page); zone->free_area[order].nr_free--; diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 4639ef78fb151133ead6431b8fa3cb7d297fe728..49b50cef01015a6a3a0e8454c7b47392e958357b 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -17,6 +17,7 @@ static int set_migratetype_isolate(struct page *page, int migratetype, int isol_flags) { + struct page *unmovable = NULL; struct zone *zone; unsigned long flags, pfn; struct memory_isolate_notify arg; @@ -59,8 +60,9 @@ static int set_migratetype_isolate(struct page *page, int migratetype, int isol_ * 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, migratetype, - isol_flags)) + unmovable = has_unmovable_pages(zone, page, arg.pages_found, + migratetype, isol_flags); + if (!unmovable) ret = 0; /* @@ -84,6 +86,13 @@ static int set_migratetype_isolate(struct page *page, int migratetype, int isol_ spin_unlock_irqrestore(&zone->lock, flags); if (!ret) drain_all_pages(zone); + else if ((isol_flags & REPORT_FAILURE) && unmovable) + /* + * printk() with zone->lock held will guarantee to trigger a + * lockdep splat, so defer it here. + */ + dump_page(unmovable, "unmovable page"); + return ret; }