summaryrefslogtreecommitdiffstats
path: root/mm/compaction.c
diff options
context:
space:
mode:
authorVlastimil Babka <vbabka@suse.cz>2014-10-09 18:27:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-09 22:25:53 -0400
commit53853e2d2bfb748a8b5aa2fd1de15699266865e0 (patch)
treedd09605e9cd9a4329afc274faffae1c15e81f150 /mm/compaction.c
parent8b1645685acf3c7e0b93611fb4b328ef45c47e92 (diff)
mm, compaction: defer each zone individually instead of preferred zone
When direct sync compaction is often unsuccessful, it may become deferred for some time to avoid further useless attempts, both sync and async. Successful high-order allocations un-defer compaction, while further unsuccessful compaction attempts prolong the compaction deferred period. Currently the checking and setting deferred status is performed only on the preferred zone of the allocation that invoked direct compaction. But compaction itself is attempted on all eligible zones in the zonelist, so the behavior is suboptimal and may lead both to scenarios where 1) compaction is attempted uselessly, or 2) where it's not attempted despite good chances of succeeding, as shown on the examples below: 1) A direct compaction with Normal preferred zone failed and set deferred compaction for the Normal zone. Another unrelated direct compaction with DMA32 as preferred zone will attempt to compact DMA32 zone even though the first compaction attempt also included DMA32 zone. In another scenario, compaction with Normal preferred zone failed to compact Normal zone, but succeeded in the DMA32 zone, so it will not defer compaction. In the next attempt, it will try Normal zone which will fail again, instead of skipping Normal zone and trying DMA32 directly. 2) Kswapd will balance DMA32 zone and reset defer status based on watermarks looking good. A direct compaction with preferred Normal zone will skip compaction of all zones including DMA32 because Normal was still deferred. The allocation might have succeeded in DMA32, but won't. This patch makes compaction deferring work on individual zone basis instead of preferred zone. For each zone, it checks compaction_deferred() to decide if the zone should be skipped. If watermarks fail after compacting the zone, defer_compaction() is called. The zone where watermarks passed can still be deferred when the allocation attempt is unsuccessful. When allocation is successful, compaction_defer_reset() is called for the zone containing the allocated page. This approach should approximate calling defer_compaction() only on zones where compaction was attempted and did not yield allocated page. There might be corner cases but that is inevitable as long as the decision to stop compacting dues not guarantee that a page will be allocated. Due to a new COMPACT_DEFERRED return value, some functions relying implicitly on COMPACT_SKIPPED = 0 had to be updated, with comments made more accurate. The did_some_progress output parameter of __alloc_pages_direct_compact() is removed completely, as the caller actually does not use it after compaction sets it - it is only considered when direct reclaim sets it. During testing on a two-node machine with a single very small Normal zone on node 1, this patch has improved success rates in stress-highalloc mmtests benchmark. The success here were previously made worse by commit 3a025760fc15 ("mm: page_alloc: spill to remote nodes before waking kswapd") as kswapd was no longer resetting often enough the deferred compaction for the Normal zone, and DMA32 zones on both nodes were thus not considered for compaction. On different machine, success rates were improved with __GFP_NO_KSWAPD allocations. [akpm@linux-foundation.org: fix CONFIG_COMPACTION=n build] Signed-off-by: Vlastimil Babka <vbabka@suse.cz> Acked-by: Minchan Kim <minchan@kernel.org> Reviewed-by: Zhang Yanfei <zhangyanfei@cn.fujitsu.com> Acked-by: Mel Gorman <mgorman@suse.de> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Michal Nazarewicz <mina86@mina86.com> Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Christoph Lameter <cl@linux.com> Cc: Rik van Riel <riel@redhat.com> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/compaction.c')
-rw-r--r--mm/compaction.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index 21bf292b642a..1c7195d42e83 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1125,27 +1125,26 @@ int sysctl_extfrag_threshold = 500;
1125 * @nodemask: The allowed nodes to allocate from 1125 * @nodemask: The allowed nodes to allocate from
1126 * @mode: The migration mode for async, sync light, or sync migration 1126 * @mode: The migration mode for async, sync light, or sync migration
1127 * @contended: Return value that is true if compaction was aborted due to lock contention 1127 * @contended: Return value that is true if compaction was aborted due to lock contention
1128 * @page: Optionally capture a free page of the requested order during compaction 1128 * @candidate_zone: Return the zone where we think allocation should succeed
1129 * 1129 *
1130 * This is the main entry point for direct page compaction. 1130 * This is the main entry point for direct page compaction.
1131 */ 1131 */
1132unsigned long try_to_compact_pages(struct zonelist *zonelist, 1132unsigned long try_to_compact_pages(struct zonelist *zonelist,
1133 int order, gfp_t gfp_mask, nodemask_t *nodemask, 1133 int order, gfp_t gfp_mask, nodemask_t *nodemask,
1134 enum migrate_mode mode, bool *contended) 1134 enum migrate_mode mode, bool *contended,
1135 struct zone **candidate_zone)
1135{ 1136{
1136 enum zone_type high_zoneidx = gfp_zone(gfp_mask); 1137 enum zone_type high_zoneidx = gfp_zone(gfp_mask);
1137 int may_enter_fs = gfp_mask & __GFP_FS; 1138 int may_enter_fs = gfp_mask & __GFP_FS;
1138 int may_perform_io = gfp_mask & __GFP_IO; 1139 int may_perform_io = gfp_mask & __GFP_IO;
1139 struct zoneref *z; 1140 struct zoneref *z;
1140 struct zone *zone; 1141 struct zone *zone;
1141 int rc = COMPACT_SKIPPED; 1142 int rc = COMPACT_DEFERRED;
1142 int alloc_flags = 0; 1143 int alloc_flags = 0;
1143 1144
1144 /* Check if the GFP flags allow compaction */ 1145 /* Check if the GFP flags allow compaction */
1145 if (!order || !may_enter_fs || !may_perform_io) 1146 if (!order || !may_enter_fs || !may_perform_io)
1146 return rc; 1147 return COMPACT_SKIPPED;
1147
1148 count_compact_event(COMPACTSTALL);
1149 1148
1150#ifdef CONFIG_CMA 1149#ifdef CONFIG_CMA
1151 if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) 1150 if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
@@ -1156,14 +1155,33 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
1156 nodemask) { 1155 nodemask) {
1157 int status; 1156 int status;
1158 1157
1158 if (compaction_deferred(zone, order))
1159 continue;
1160
1159 status = compact_zone_order(zone, order, gfp_mask, mode, 1161 status = compact_zone_order(zone, order, gfp_mask, mode,
1160 contended); 1162 contended);
1161 rc = max(status, rc); 1163 rc = max(status, rc);
1162 1164
1163 /* If a normal allocation would succeed, stop compacting */ 1165 /* If a normal allocation would succeed, stop compacting */
1164 if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0, 1166 if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0,
1165 alloc_flags)) 1167 alloc_flags)) {
1168 *candidate_zone = zone;
1169 /*
1170 * We think the allocation will succeed in this zone,
1171 * but it is not certain, hence the false. The caller
1172 * will repeat this with true if allocation indeed
1173 * succeeds in this zone.
1174 */
1175 compaction_defer_reset(zone, order, false);
1166 break; 1176 break;
1177 } else if (mode != MIGRATE_ASYNC) {
1178 /*
1179 * We think that allocation won't succeed in this zone
1180 * so we defer compaction there. If it ends up
1181 * succeeding after all, it will be reset.
1182 */
1183 defer_compaction(zone, order);
1184 }
1167 } 1185 }
1168 1186
1169 return rc; 1187 return rc;