summaryrefslogtreecommitdiffstats
path: root/mm/compaction.c
diff options
context:
space:
mode:
authorMichal Hocko <mhocko@suse.com>2016-05-20 19:57:12 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-05-20 20:58:30 -0400
commit86a294a81f93d6f36d00ec3ff779d36d218f852d (patch)
treea8ac97b74556e1d680f81c48635585d28b9f2eec /mm/compaction.c
parent7854ea6c28c6076050e24773eeb78e2925bd7411 (diff)
mm, oom, compaction: prevent from should_compact_retry looping for ever for costly orders
"mm: consider compaction feedback also for costly allocation" has removed the upper bound for the reclaim/compaction retries based on the number of reclaimed pages for costly orders. While this is desirable the patch did miss a mis interaction between reclaim, compaction and the retry logic. The direct reclaim tries to get zones over min watermark while compaction backs off and returns COMPACT_SKIPPED when all zones are below low watermark + 1<<order gap. If we are getting really close to OOM then __compaction_suitable can keep returning COMPACT_SKIPPED a high order request (e.g. hugetlb order-9) while the reclaim is not able to release enough pages to get us over low watermark. The reclaim is still able to make some progress (usually trashing over few remaining pages) so we are not able to break out from the loop. I have seen this happening with the same test described in "mm: consider compaction feedback also for costly allocation" on a swapless system. The original problem got resolved by "vmscan: consider classzone_idx in compaction_ready" but it shows how things might go wrong when we approach the oom event horizont. The reason why compaction requires being over low rather than min watermark is not clear to me. This check was there essentially since 56de7263fcf3 ("mm: compaction: direct compact when a high-order allocation fails"). It is clearly an implementation detail though and we shouldn't pull it into the generic retry logic while we should be able to cope with such eventuality. The only place in should_compact_retry where we retry without any upper bound is for compaction_withdrawn() case. Introduce compaction_zonelist_suitable function which checks the given zonelist and returns true only if there is at least one zone which would would unblock __compaction_suitable if more memory got reclaimed. In this implementation it checks __compaction_suitable with NR_FREE_PAGES plus part of the reclaimable memory as the target for the watermark check. The reclaimable memory is reduced linearly by the allocation order. The idea is that we do not want to reclaim all the remaining memory for a single allocation request just unblock __compaction_suitable which doesn't guarantee we will make a further progress. The new helper is then used if compaction_withdrawn() feedback was provided so we do not retry if there is no outlook for a further progress. !costly requests shouldn't be affected much - e.g. order-2 pages would require to have at least 64kB on the reclaimable LRUs while order-9 would need at least 32M which should be enough to not lock up. [vbabka@suse.cz: fix classzone_idx vs. high_zoneidx usage in compaction_zonelist_suitable] [akpm@linux-foundation.org: fix it for Mel's mm-page_alloc-remove-field-from-alloc_context.patch] Signed-off-by: Michal Hocko <mhocko@suse.com> Acked-by: Hillf Danton <hillf.zj@alibaba-inc.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: David Rientjes <rientjes@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Joonsoo Kim <js1304@gmail.com> Cc: Mel Gorman <mgorman@techsingularity.net> Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Cc: Vladimir Davydov <vdavydov@virtuozzo.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.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index 4af1577adb5c..d8a20fcf8678 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1318,7 +1318,8 @@ static enum compact_result compact_finished(struct zone *zone,
1318 */ 1318 */
1319static enum compact_result __compaction_suitable(struct zone *zone, int order, 1319static enum compact_result __compaction_suitable(struct zone *zone, int order,
1320 unsigned int alloc_flags, 1320 unsigned int alloc_flags,
1321 int classzone_idx) 1321 int classzone_idx,
1322 unsigned long wmark_target)
1322{ 1323{
1323 int fragindex; 1324 int fragindex;
1324 unsigned long watermark; 1325 unsigned long watermark;
@@ -1341,7 +1342,8 @@ static enum compact_result __compaction_suitable(struct zone *zone, int order,
1341 * allocated and for a short time, the footprint is higher 1342 * allocated and for a short time, the footprint is higher
1342 */ 1343 */
1343 watermark += (2UL << order); 1344 watermark += (2UL << order);
1344 if (!zone_watermark_ok(zone, 0, watermark, classzone_idx, alloc_flags)) 1345 if (!__zone_watermark_ok(zone, 0, watermark, classzone_idx,
1346 alloc_flags, wmark_target))
1345 return COMPACT_SKIPPED; 1347 return COMPACT_SKIPPED;
1346 1348
1347 /* 1349 /*
@@ -1368,7 +1370,8 @@ enum compact_result compaction_suitable(struct zone *zone, int order,
1368{ 1370{
1369 enum compact_result ret; 1371 enum compact_result ret;
1370 1372
1371 ret = __compaction_suitable(zone, order, alloc_flags, classzone_idx); 1373 ret = __compaction_suitable(zone, order, alloc_flags, classzone_idx,
1374 zone_page_state(zone, NR_FREE_PAGES));
1372 trace_mm_compaction_suitable(zone, order, ret); 1375 trace_mm_compaction_suitable(zone, order, ret);
1373 if (ret == COMPACT_NOT_SUITABLE_ZONE) 1376 if (ret == COMPACT_NOT_SUITABLE_ZONE)
1374 ret = COMPACT_SKIPPED; 1377 ret = COMPACT_SKIPPED;
@@ -1376,6 +1379,39 @@ enum compact_result compaction_suitable(struct zone *zone, int order,
1376 return ret; 1379 return ret;
1377} 1380}
1378 1381
1382bool compaction_zonelist_suitable(struct alloc_context *ac, int order,
1383 int alloc_flags)
1384{
1385 struct zone *zone;
1386 struct zoneref *z;
1387
1388 /*
1389 * Make sure at least one zone would pass __compaction_suitable if we continue
1390 * retrying the reclaim.
1391 */
1392 for_each_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
1393 ac->nodemask) {
1394 unsigned long available;
1395 enum compact_result compact_result;
1396
1397 /*
1398 * Do not consider all the reclaimable memory because we do not
1399 * want to trash just for a single high order allocation which
1400 * is even not guaranteed to appear even if __compaction_suitable
1401 * is happy about the watermark check.
1402 */
1403 available = zone_reclaimable_pages(zone) / order;
1404 available += zone_page_state_snapshot(zone, NR_FREE_PAGES);
1405 compact_result = __compaction_suitable(zone, order, alloc_flags,
1406 ac_classzone_idx(ac), available);
1407 if (compact_result != COMPACT_SKIPPED &&
1408 compact_result != COMPACT_NOT_SUITABLE_ZONE)
1409 return true;
1410 }
1411
1412 return false;
1413}
1414
1379static enum compact_result compact_zone(struct zone *zone, struct compact_control *cc) 1415static enum compact_result compact_zone(struct zone *zone, struct compact_control *cc)
1380{ 1416{
1381 enum compact_result ret; 1417 enum compact_result ret;