On Mon 29-02-16 22:02:13, Michal Hocko wrote: > Andrew, > could you queue this one as well, please? This is more a band aid than a > real solution which I will be working on as soon as I am able to > reproduce the issue but the patch should help to some degree at least.
Joonsoo wasn't very happy about this approach so let me try a different way. What do you think about the following? Hugh, Sergey does it help for your load? I have tested it with the Hugh's load and there was no major difference from the previous testing so at least nothing has blown up as I am not able to reproduce the issue here. Other changes in the compaction are still needed but I would like to not depend on them right now. --- >From 0974f127e8eb7fe53e65f3a8b398db57effe9755 Mon Sep 17 00:00:00 2001 From: Michal Hocko <mho...@suse.com> Date: Mon, 7 Mar 2016 15:30:37 +0100 Subject: [PATCH] mm, oom: protect !costly allocations some more should_reclaim_retry will give up retries for higher order allocations if none of the eligible zones has any requested or higher order pages available even if we pass the watermak check for order-0. This is done because there is no guarantee that the reclaimable and currently free pages will form the required order. This can, however, lead to situations were the high-order request (e.g. order-2 required for the stack allocation during fork) will trigger OOM too early - e.g. after the first reclaim/compaction round. Such a system would have to be highly fragmented and there is no guarantee further reclaim/compaction attempts would help but at least make sure that the compaction was active before we go OOM and keep retrying even if should_reclaim_retry tells us to oom if the last compaction round was either inactive (deferred, skipped or bailed out early due to contention) or it told us to continue. Additionally define COMPACT_NONE which reflects cases where the compaction is completely disabled. Signed-off-by: Michal Hocko <mho...@suse.com> --- include/linux/compaction.h | 2 ++ mm/page_alloc.c | 41 ++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 4cd4ddf64cc7..a4cec4a03f7d 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -1,6 +1,8 @@ #ifndef _LINUX_COMPACTION_H #define _LINUX_COMPACTION_H +/* compaction disabled */ +#define COMPACT_NONE -1 /* Return values for compact_zone() and try_to_compact_pages() */ /* compaction didn't start as it was deferred due to past failures */ #define COMPACT_DEFERRED 0 diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 269a04f20927..f89e3cbfdf90 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2819,28 +2819,22 @@ static struct page * __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac, enum migrate_mode mode, int *contended_compaction, - bool *deferred_compaction) + unsigned long *compact_result) { - unsigned long compact_result; struct page *page; - if (!order) + if (!order) { + *compact_result = COMPACT_NONE; return NULL; + } current->flags |= PF_MEMALLOC; - compact_result = try_to_compact_pages(gfp_mask, order, alloc_flags, ac, + *compact_result = try_to_compact_pages(gfp_mask, order, alloc_flags, ac, mode, contended_compaction); current->flags &= ~PF_MEMALLOC; - switch (compact_result) { - case COMPACT_DEFERRED: - *deferred_compaction = true; - /* fall-through */ - case COMPACT_SKIPPED: + if (*compact_result <= COMPACT_SKIPPED) return NULL; - default: - break; - } /* * At least in one zone compaction wasn't deferred or skipped, so let's @@ -2875,8 +2869,9 @@ static inline struct page * __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac, enum migrate_mode mode, int *contended_compaction, - bool *deferred_compaction) + unsigned long *compact_result) { + *compact_result = COMPACT_NONE; return NULL; } #endif /* CONFIG_COMPACTION */ @@ -3118,7 +3113,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, int alloc_flags; unsigned long did_some_progress; enum migrate_mode migration_mode = MIGRATE_ASYNC; - bool deferred_compaction = false; + unsigned long compact_result; int contended_compaction = COMPACT_CONTENDED_NONE; int no_progress_loops = 0; @@ -3227,7 +3222,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, migration_mode, &contended_compaction, - &deferred_compaction); + &compact_result); if (page) goto got_pg; @@ -3240,7 +3235,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, * to heavily disrupt the system, so we fail the allocation * instead of entering direct reclaim. */ - if (deferred_compaction) + if (compact_result == COMPACT_DEFERRED) goto nopage; /* @@ -3294,6 +3289,18 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, did_some_progress > 0, no_progress_loops)) goto retry; + /* + * !costly allocations are really important and we have to make sure + * the compaction wasn't deferred or didn't bail out early due to locks + * contention before we go OOM. + */ + if (order && order <= PAGE_ALLOC_COSTLY_ORDER) { + if (compact_result <= COMPACT_CONTINUE) + goto retry; + if (contended_compaction > COMPACT_CONTENDED_NONE) + goto retry; + } + /* Reclaim has failed us, start killing things */ page = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress); if (page) @@ -3314,7 +3321,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, migration_mode, &contended_compaction, - &deferred_compaction); + &compact_result); if (page) goto got_pg; nopage: -- 2.7.0 -- Michal Hocko SUSE Labs