From 565008042b759835d51703f1da9b335dc0404546 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 12 Sep 2019 13:40:46 +0200 Subject: [PATCH] mm, compaction: distinguish when compaction is impossible --- include/linux/compaction.h | 7 ++++++- include/trace/events/mmflags.h | 1 + mm/compaction.c | 16 +++++++++++++-- mm/vmscan.c | 36 ++++++++++++++++++++++++---------- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 9569e7c786d3..6e624f482a08 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -17,8 +17,13 @@ enum compact_priority { }; /* Return values for compact_zone() and try_to_compact_pages() */ -/* When adding new states, please adjust include/trace/events/compaction.h */ +/* When adding new states, please adjust include/trace/events/mmflags.h */ enum compact_result { + /* + * The zone is too small to provide the requested allocation even if + * fully freed (i.e. ZONE_DMA for THP allocation due to lowmem reserves) + */ + COMPACT_IMPOSSIBLE, /* For more detailed tracepoint output - internal to compaction */ COMPACT_NOT_SUITABLE_ZONE, /* diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h index a1675d43777e..557dad69a9db 100644 --- a/include/trace/events/mmflags.h +++ b/include/trace/events/mmflags.h @@ -170,6 +170,7 @@ IF_HAVE_VM_SOFTDIRTY(VM_SOFTDIRTY, "softdirty" ) \ #ifdef CONFIG_COMPACTION #define COMPACTION_STATUS \ + EM( COMPACT_IMPOSSIBLE, "impossible") \ EM( COMPACT_SKIPPED, "skipped") \ EM( COMPACT_DEFERRED, "deferred") \ EM( COMPACT_CONTINUE, "continue") \ diff --git a/mm/compaction.c b/mm/compaction.c index 9e1b9acb116b..50a3dd2e2b6e 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1948,6 +1948,7 @@ static enum compact_result compact_finished(struct compact_control *cc) /* * compaction_suitable: Is this suitable to run compaction on this zone now? * Returns + * COMPACT_IMPOSSIBLE If the allocation would fail even with all pages free * COMPACT_SKIPPED - If there are too few free pages for compaction * COMPACT_SUCCESS - If the allocation would succeed without compaction * COMPACT_CONTINUE - If compaction should run now @@ -1971,6 +1972,16 @@ static enum compact_result __compaction_suitable(struct zone *zone, int order, alloc_flags)) return COMPACT_SUCCESS; + /* + * If the allocation would not succeed even with a fully free zone + * due to e.g. lowmem reserves, indicate that compaction can't possibly + * help and it would be pointless to reclaim. + */ + watermark += 1UL << order; + if (!__zone_watermark_ok(zone, 0, watermark, classzone_idx, + alloc_flags, zone_managed_pages(zone))) + return COMPACT_IMPOSSIBLE; + /* * Watermarks for order-0 must be met for compaction to be able to * isolate free pages for migration targets. This means that the @@ -2058,7 +2069,7 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order, available += zone_page_state_snapshot(zone, NR_FREE_PAGES); compact_result = __compaction_suitable(zone, order, alloc_flags, ac_classzone_idx(ac), available); - if (compact_result != COMPACT_SKIPPED) + if (compact_result > COMPACT_SKIPPED) return true; } @@ -2079,7 +2090,8 @@ compact_zone(struct compact_control *cc, struct capture_control *capc) ret = compaction_suitable(cc->zone, cc->order, cc->alloc_flags, cc->classzone_idx); /* Compaction is likely to fail */ - if (ret == COMPACT_SUCCESS || ret == COMPACT_SKIPPED) + if (ret == COMPACT_SUCCESS || ret == COMPACT_SKIPPED + || ret == COMPACT_IMPOSSIBLE) return ret; /* huh, compaction_suitable is returning something unexpected */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 910e02c793ff..20ba471a8454 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2778,11 +2778,12 @@ static bool shrink_node(pg_data_t *pgdat, struct scan_control *sc) } /* - * Returns true if compaction should go ahead for a costly-order request, or - * the allocation would already succeed without compaction. Return false if we - * should reclaim first. + * Returns 1 if compaction should go ahead for a costly-order request, or the + * allocation would already succeed without compaction. Return 0 if we should + * reclaim first. Return -1 when compaction can't help at all due to zone being + * too small, which means there's no point in reclaim nor compaction. */ -static inline bool compaction_ready(struct zone *zone, struct scan_control *sc) +static inline int compaction_ready(struct zone *zone, struct scan_control *sc) { unsigned long watermark; enum compact_result suitable; @@ -2790,10 +2791,16 @@ static inline bool compaction_ready(struct zone *zone, struct scan_control *sc) suitable = compaction_suitable(zone, sc->order, 0, sc->reclaim_idx); if (suitable == COMPACT_SUCCESS) /* Allocation should succeed already. Don't reclaim. */ - return true; + return 1; if (suitable == COMPACT_SKIPPED) /* Compaction cannot yet proceed. Do reclaim. */ - return false; + return 0; + if (suitable == COMPACT_IMPOSSIBLE) + /* + * Compaction can't possibly help. So don't reclaim, but keep + * checking other zones. + */ + return -1; /* * Compaction is already possible, but it takes time to run and there @@ -2839,6 +2846,7 @@ static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc) for_each_zone_zonelist_nodemask(zone, z, zonelist, sc->reclaim_idx, sc->nodemask) { + int compact_ready; /* * Take care memory controller reclaiming has small influence * to global LRU. @@ -2858,10 +2866,18 @@ static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc) * page allocations. */ if (IS_ENABLED(CONFIG_COMPACTION) && - sc->order > PAGE_ALLOC_COSTLY_ORDER && - compaction_ready(zone, sc)) { - sc->compaction_ready = true; - continue; + sc->order > PAGE_ALLOC_COSTLY_ORDER) { + compact_ready = compaction_ready(zone, sc); + if (compact_ready == 1) { + sc->compaction_ready = true; + continue; + } else if (compact_ready == -1) { + /* + * In this zone, neither reclaim nor + * compaction can help. + */ + continue; + } } /* -- 2.23.0