From: Baolin Wang <baolin.wang@linux.alibaba.com>
To: Qinyun Tan <qinyuntan@linux.alibaba.com>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@suse.com>,
Roman Gushchin <roman.gushchin@linux.dev>,
Shakeel Butt <shakeel.butt@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
David Hildenbrand <david@kernel.org>,
Lorenzo Stoakes <ljs@kernel.org>, Zi Yan <ziy@nvidia.com>,
Xunlei Pang <xlpang@linux.alibaba.com>,
"Liam R . Howlett" <liam@infradead.org>,
Nico Pache <nico.pache@linux.dev>,
Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
Barry Song <baohua@kernel.org>, Lance Yang <lance.yang@linux.dev>,
Usama Arif <usama.arif@linux.dev>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Chris Down <chris@chrisdown.name>,
Chuanhua Han <hanchuanhua@oppo.com>,
Kairui Song <kasong@tencent.com>,
linux-mm@kvack.org, cgroups@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH 1/2] mm: memcg: settle memory.high debt after THP faults with non-blocking gfp
Date: Fri, 4 Sep 2026 14:50:30 +0800 [thread overview]
Message-ID: <de28b6a7-7f51-4344-8139-1ec412604062@linux.alibaba.com> (raw)
In-Reply-To: <20260904035407.4098627-2-qinyuntan@linux.alibaba.com>
On 9/4/26 11:54 AM, Qinyun Tan wrote:
> Anonymous THP faults happening in a kernel loop that does not return
> to userspace -- the populate loop of a single mlock() call, or any
> GUP-driven population -- can drive a memcg's usage from memory.high
> all the way up to memory.max with zero reclaim and zero penalty
> sleep.
>
> This defeats the containment memory.high is supposed to provide:
> above high, the documented promise is that "the processes of the
> cgroup are throttled and put under heavy reclaim pressure", and
> userspace OOM handlers (oomd, Kubernetes) rely on the high..max
> buffer as their reaction window. Only after hitting memory.max does
> the non-blocking charge fail, THP fall back to 4K, and
> folio_prealloc()'s GFP_KERNEL charge finally restore throttling --
> by which point the entire buffer has been consumed.
>
> memory.high is enforced at two points after a charge succeeds:
>
> 1. from resume_user_mode_work() on return to userspace, requested
> via set_notify_resume();
> 2. synchronously in try_charge_memcg() for large overcharges, added
> by commit c9afe31ec443 ("memcg: synchronously enforce memory.high
> for large overcharges"), gated on gfpflags_allow_blocking().
>
> A populate loop does not return to userspace between faults, so gate
> 1 never runs. Gate 2 is defeated by the charge gfp: since
> commit 3b3636924dfe ("mm, memcg: sync allocation and memcg charge
> gfp flags for THP"), the THP fault path passes the allocation gfp
> from vma_thp_gfp_mask() to mem_cgroup_charge(). With defrag=defer
> that gfp is GFP_TRANSHUGE_LIGHT | __GFP_KSWAPD_RECLAIM; with the
> default defrag=madvise and no MADV_HUGEPAGE it is plain
> GFP_TRANSHUGE_LIGHT. Neither allows blocking. This is the right
> policy for the physical allocation -- a THP is not worth direct
> compaction, fall back to 4K instead -- but try_charge_memcg() also
> interprets it as "this context cannot sleep" and skips the
> synchronous enforcement, even
> though fault context sleeps just fine (it holds the mmap or per-VMA
> read lock).
>
> Fix this in the fault paths, which know their context can sleep:
> after a successful THP/mTHP charge, settle any accrued over-high
> debt via mem_cgroup_handle_over_high(GFP_KERNEL). This reuses the
> existing throttling machinery (reclaim + calculate_high_delay()
> penalty sleep) and is a no-op read of
> current->memcg_nr_pages_over_high when there is no debt.
This doesn't convince me. If the user sets defrag=defer, it means large
folio allocations should not block, so using gfpflags_allow_blocking()
to decide whether to call mem_cgroup_handle_over_high() is reasonable.
If you call mem_cgroup_handle_over_high() directly in the allocation
functions, it would definitely increase allocation latency. This is not
what we expect when setting defrag=defer.
On the other hand, I wonder if briefly exceeding memory.high is really a
problem in the real products. If this is only to fix the
test_memcg_high_sync selftest below, which is full of magic numbers, I
don't think it makes much sense.
> Deliberately not changed:
>
> - The charge gfp itself is kept coupled to the allocation gfp, so the
> fail-fast behaviour at memory.max (charge fails -> fall back to 4K
> instead of reclaiming or OOMing for a THP) that the coupling was
> introduced for is fully preserved.
>
> - try_charge_memcg() is not touched: gfpflags_allow_blocking() is the
> only signal it has, and it must stay conservative for callers that
> genuinely cannot sleep.
>
> The pre-existing selftest test_memcg_high_sync, added alongside the
> synchronous enforcement by commit 6323ec54b450 ("selftests: memcg:
> test high limit for single entry allocation"), readily reproduces
> this: it mlocks 200M against memory.high=30M and memory.max=140M
> with swap disabled, and expects high events with no max events. On
> systems with transparent_hugepage/enabled=always it fails without
> this patch -- the population bursts through to memory.max -- and
> passes with it.
>
> Fixes: c9afe31ec443 ("memcg: synchronously enforce memory.high for large overcharges")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Qinyun Tan <qinyuntan@linux.alibaba.com>
> ---
>
> Note for stable backports: mem_cgroup_handle_over_high() only gained
> its gfp_mask argument in v6.6, from commit 9ea9cb00a82b ("mm:
> memcontrol: fix GFP_NOFS recursion in memory.high enforcement"); on
> older kernels the call sites take no argument.
>
> mm/huge_memory.c | 8 ++++++++
> mm/memory.c | 2 ++
> 2 files changed, 10 insertions(+)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index ced400f72d43..543ba4a74dc3 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -1329,6 +1329,14 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,
> return NULL;
> }
>
> + /*
> + * The charge gfp encodes THP allocation policy and may not allow
> + * blocking, which makes try_charge skip its synchronous memory.high
> + * throttling. Fault context can sleep, so settle any over-high debt
> + * here instead of letting usage grow unthrottled up to memory.max.
> + */
> + mem_cgroup_handle_over_high(GFP_KERNEL);
> +
> if (folio_memcg_alloc_deferred(folio)) {
> folio_put(folio);
> count_vm_event(THP_FAULT_FALLBACK);
> diff --git a/mm/memory.c b/mm/memory.c
> index 8b0c2c735d3d..24cbf2a26905 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -5362,6 +5362,8 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)
> folio_put(folio);
> goto next;
> }
> + /* Same reasoning as in vma_alloc_anon_folio_pmd(). */
> + mem_cgroup_handle_over_high(GFP_KERNEL);
> if (order > 1 && folio_memcg_alloc_deferred(folio)) {
> folio_put(folio);
> goto fallback;
next prev parent reply other threads:[~2026-09-04 6:50 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 3:54 [PATCH 0/2] mm: memcg: settle memory.high debt for non-blocking THP charges Qinyun Tan
2026-09-04 3:54 ` [PATCH 1/2] mm: memcg: settle memory.high debt after THP faults with non-blocking gfp Qinyun Tan
2026-09-04 6:50 ` Baolin Wang [this message]
2026-09-04 9:05 ` Qinyun Tan
2026-09-04 15:10 ` Zi Yan
2026-09-04 3:54 ` [PATCH 2/2] mm: memcg: settle memory.high debt after large folio swapin Qinyun Tan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=de28b6a7-7f51-4344-8139-1ec412604062@linux.alibaba.com \
--to=baolin.wang@linux.alibaba.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=cgroups@vger.kernel.org \
--cc=chris@chrisdown.name \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=hanchuanhua@oppo.com \
--cc=hannes@cmpxchg.org \
--cc=kasong@tencent.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=muchun.song@linux.dev \
--cc=nico.pache@linux.dev \
--cc=qinyuntan@linux.alibaba.com \
--cc=roman.gushchin@linux.dev \
--cc=rppt@kernel.org \
--cc=ryan.roberts@arm.com \
--cc=shakeel.butt@linux.dev \
--cc=stable@vger.kernel.org \
--cc=surenb@google.com \
--cc=usama.arif@linux.dev \
--cc=vbabka@kernel.org \
--cc=xlpang@linux.alibaba.com \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®