From: Audra Mitchell <audra@redhat.com>
To: david@kernel.org
Cc: jocolema@redhat.com, raquini@redhat.com,
Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@kernel.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Shakeel Butt <shakeel.butt@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
Andrew Morton <akpm@linux-foundation.org>,
cgroups@vger.kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org
Subject: [PATCH] Fix unbounded loop within try_charge_memcg
Date: Thu, 6 Aug 2026 14:24:28 -0400 [thread overview]
Message-ID: <20260806182429.1841095-1-audra@redhat.com> (raw)
Originally nr_retries was actually nr_oom_retries and we used it to track (and
limit) the number of times we entered the mem_cgroup_oom path and then attempted
a retry. The purpose of nr_retries counter changed with the introduction of
9b1306192d33 ("mm: memcontrol: retry reclaim for oom-disabled and __GFP_NOFAIL
charges") so that the oom-disabled and __GFP_NOFAIL charges would also continue
to retry within the desired nr_retries threshold. Later d977aa939fca
("mm, memcg: unify reclaim retry limits with page allocator") changed the
nr_retries counter from 5 to 16.
As the function has evolved we now have multiple paths that have a goto retry
path and we have lost the original purpose of the nr_retries counter, allowing
us to take a goto retry path an unbounded number of times.
Fix the unbounded retries by nesting the code in a loop and decrementing the
nr_retries counter correctly.
Signed-off-by: Audra Mitchell <audra@redhat.com>
---
mm/memcontrol.c | 153 ++++++++++++++++++++++++------------------------
1 file changed, 76 insertions(+), 77 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6dc4888a90f3..781bcced5848 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2607,98 +2607,97 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned long pflags;
bool allow_spinning = gfpflags_allow_spinning(gfp_mask);
-retry:
- if (consume_stock(memcg, nr_pages))
- return 0;
+ for (; nr_retries >= 0; nr_retries--) {
- if (!allow_spinning)
- /* Avoid the refill and flush of the older stock */
- batch = nr_pages;
+ if (consume_stock(memcg, nr_pages))
+ return 0;
- reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
- if (!do_memsw_account() ||
- page_counter_try_charge(&memcg->memsw, batch, &counter)) {
- if (page_counter_try_charge(&memcg->memory, batch, &counter))
- goto done_restock;
- if (do_memsw_account())
- page_counter_uncharge(&memcg->memsw, batch);
- mem_over_limit = mem_cgroup_from_counter(counter, memory);
- } else {
- mem_over_limit = mem_cgroup_from_counter(counter, memsw);
- reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
- }
+ if (!allow_spinning)
+ /* Avoid the refill and flush of the older stock */
+ batch = nr_pages;
- if (batch > nr_pages) {
- batch = nr_pages;
- goto retry;
- }
+ reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
+ if (!do_memsw_account() ||
+ page_counter_try_charge(&memcg->memsw, batch, &counter)) {
+ if (page_counter_try_charge(&memcg->memory, batch, &counter))
+ goto done_restock;
+ if (do_memsw_account())
+ page_counter_uncharge(&memcg->memsw, batch);
+ mem_over_limit = mem_cgroup_from_counter(counter, memory);
+ } else {
+ mem_over_limit = mem_cgroup_from_counter(counter, memsw);
+ reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
+ }
- /*
- * Prevent unbounded recursion when reclaim operations need to
- * allocate memory. This might exceed the limits temporarily,
- * but we prefer facilitating memory reclaim and getting back
- * under the limit over triggering OOM kills in these cases.
- */
- if (unlikely(current->flags & PF_MEMALLOC))
- goto force;
+ if (batch > nr_pages) {
+ batch = nr_pages;
+ continue;
+ }
- if (unlikely(task_in_memcg_oom(current)))
- goto nomem;
+ /*
+ * Prevent unbounded recursion when reclaim operations need to
+ * allocate memory. This might exceed the limits temporarily,
+ * but we prefer facilitating memory reclaim and getting back
+ * under the limit over triggering OOM kills in these cases.
+ */
+ if (unlikely(current->flags & PF_MEMALLOC))
+ goto force;
- if (!gfpflags_allow_blocking(gfp_mask))
- goto nomem;
+ if (unlikely(task_in_memcg_oom(current)))
+ goto nomem;
- __memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning);
- raised_max_event = true;
+ if (!gfpflags_allow_blocking(gfp_mask))
+ goto nomem;
- psi_memstall_enter(&pflags);
- nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
- gfp_mask, reclaim_options, NULL);
- psi_memstall_leave(&pflags);
+ __memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning);
+ raised_max_event = true;
- if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
- goto retry;
+ psi_memstall_enter(&pflags);
+ nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
+ gfp_mask, reclaim_options, NULL);
+ psi_memstall_leave(&pflags);
- if (!drained) {
- drain_all_stock(mem_over_limit);
- drained = true;
- goto retry;
- }
+ if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
+ continue;
- if (gfp_mask & __GFP_NORETRY)
- goto nomem;
- /*
- * Even though the limit is exceeded at this point, reclaim
- * may have been able to free some pages. Retry the charge
- * before killing the task.
- *
- * Only for regular pages, though: huge pages are rather
- * unlikely to succeed so close to the limit, and we fall back
- * to regular pages anyway in case of failure.
- */
- if (nr_reclaimed && nr_pages <= (1 << PAGE_ALLOC_COSTLY_ORDER))
- goto retry;
+ if (!drained) {
+ drain_all_stock(mem_over_limit);
+ drained = true;
+ continue;
+ }
- if (nr_retries--)
- goto retry;
+ if (gfp_mask & __GFP_NORETRY)
+ goto nomem;
+ /*
+ * Even though the limit is exceeded at this point, reclaim
+ * may have been able to free some pages. Retry the charge
+ * before killing the task.
+ *
+ * Only for regular pages, though: huge pages are rather
+ * unlikely to succeed so close to the limit, and we fall back
+ * to regular pages anyway in case of failure.
+ */
+ if (nr_reclaimed && nr_pages <= (1 << PAGE_ALLOC_COSTLY_ORDER))
+ continue;
- if (gfp_mask & __GFP_RETRY_MAYFAIL)
- goto nomem;
+ if (gfp_mask & __GFP_RETRY_MAYFAIL)
+ goto nomem;
- /* Avoid endless loop for tasks bypassed by the oom killer */
- if (passed_oom && task_is_dying())
- goto nomem;
+ /* Avoid endless loop for tasks bypassed by the oom killer */
+ if (passed_oom && task_is_dying())
+ goto nomem;
- /*
- * keep retrying as long as the memcg oom killer is able to make
- * a forward progress or bypass the charge if the oom killer
- * couldn't make any progress.
- */
- if (mem_cgroup_oom(mem_over_limit, gfp_mask,
- get_order(nr_pages * PAGE_SIZE))) {
- passed_oom = true;
- nr_retries = MAX_RECLAIM_RETRIES;
- goto retry;
+ /*
+ * keep retrying as long as the memcg oom killer is able to make
+ * a forward progress or bypass the charge if the oom killer
+ * couldn't make any progress.
+ */
+ if (mem_cgroup_oom(mem_over_limit, gfp_mask,
+ get_order(nr_pages * PAGE_SIZE))) {
+ passed_oom = true;
+ nr_retries = MAX_RECLAIM_RETRIES;
+ continue;
+ }
}
nomem:
/*
--
2.52.0
next reply other threads:[~2026-08-06 18:24 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 18:24 Audra Mitchell [this message]
2026-08-07 9:17 ` [PATCH] Fix unbounded loop within try_charge_memcg Michal Hocko
[not found] <20260806151004.1825320-1-audra@redhat.com>
[not found] ` <anWSHc04JJBJ1VbS@tiehlicka>
2026-08-07 15:40 ` Audra Mitchell
2026-08-07 17:28 ` Shakeel Butt
2026-08-07 18:47 ` Michal Hocko
2026-08-07 19:10 ` Shakeel Butt
2026-08-07 19:13 ` Audra Mitchell
2026-08-07 20:07 ` Michal Hocko
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=20260806182429.1841095-1-audra@redhat.com \
--to=audra@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=jocolema@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=raquini@redhat.com \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
/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