Linux Kernel Selftest development
 help / color / mirror / Atom feed
From: Shakeel Butt <shakeel.butt@linux.dev>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>,
	 Hui Zhu <hui.zhu@linux.dev>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	 JP Kobryn <inwardvessel@gmail.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	 Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	 Ihor Solodrai <ihor.solodrai@linux.dev>,
	Alexei Starovoitov <ast@kernel.org>,
	 Daniel Borkmann <daniel@iogearbox.net>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	 Song Liu <song@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	 Jiri Olsa <jolsa@kernel.org>,
	Emil Tsalapatis <emil@etsalapatis.com>,
	 Shuah Khan <shuah@kernel.org>, Barry Song <baohua@kernel.org>,
	 Geliang Tang <geliang@kernel.org>,
	linux-kernel@vger.kernel.org, bpf@vger.kernel.org,
	 linux-mm@kvack.org, linux-kselftest@vger.kernel.org,
	Hui Zhu <zhuhui@kylinos.cn>
Subject: Re: [PATCH bpf-next v4 1/2] mm/bpf: Add bpf_proactive_reclaim kfuncs
Date: Fri, 21 Aug 2026 20:19:45 -0700	[thread overview]
Message-ID: <aojJoHl2uSe1IsfA@linux.dev> (raw)
In-Reply-To: <DKUVJN7NQMAI.2JXLSBOGJTI1W@gmail.com>

On Fri, Aug 21, 2026 at 09:38:31PM +0200, Kumar Kartikeya Dwivedi wrote:
> On Fri Aug 21, 2026 at 9:12 PM CEST, Andrii Nakryiko wrote:
> > On Wed, Aug 19, 2026 at 11:12 PM Hui Zhu <hui.zhu@linux.dev> wrote:
> >>
> >> From: Hui Zhu <zhuhui@kylinos.cn>
> >>
> >> Expose memcg proactive reclaim to sleepable BPF programs:
> >> unsigned long bpf_proactive_reclaim(memcg, size);
> >> unsigned long bpf_proactive_reclaim_swappiness(memcg, size, swappiness);
> >>
> >> They perform one reclaim pass on @memcg, like a write to memory.reclaim:
> >> swap is allowed, and the anon/file balance follows the cgroup's
> >> swappiness or an explicit override in [MIN_SWAPPINESS, MAX_SWAPPINESS]
> >> plus SWAPPINESS_ANON_ONLY. Both go through a shared helper,
> >> bpf_proactive_reclaim_pages(), which guards against reclaim recursion
> >> and calls try_to_free_mem_cgroup_pages() with GFP_KERNEL and
> >> MEMCG_RECLAIM_MAY_SWAP | MEMCG_RECLAIM_PROACTIVE, the same parameters
> >> user_proactive_reclaim() uses, and unlike memory.reclaim they do not
> >> retry until @size is reached.
> >>
> >> Reclaim must not recurse: try_to_free_mem_cgroup_pages() overwrites
> >> current->reclaim_state on entry and NULLs it on exit, so a nested call
> >> from an in-flight reclaim would corrupt the outer reclaim state (e.g.
> >> MGLRU dereferences current->reclaim_state->mm_walk). Both kfuncs
> >> therefore refuse to reclaim when PF_MEMALLOC is set or
> >> current->reclaim_state is non-NULL. The latter check also closes the
> >> window in try_to_free_mem_cgroup_pages() where reclaim_state is already
> >> installed but PF_MEMALLOC is not: only a tracepoint call sits in
> >> between, and while a sleepable BPF program cannot attach to the
> >> tracepoint itself, it can attach to the generated trace iterator
> >> function (__traceiter_mm_vmscan_memcg_reclaim_begin) via fentry.
> >>
> >> The kfuncs take @size in bytes; the return value is normalized to bytes
> >> as well, matching the byte-based unit of bpf_mem_cgroup_usage() and
> >> bpf_mem_cgroup_page_state(), so callers can mix them without manual
> >> page/byte conversions.
> >>
> >> An out-of-range @swappiness is reported with (unsigned long)-1 instead
> >> of 0, following the convention of bpf_mem_cgroup_vm_events() and
> >> bpf_mem_cgroup_page_state(), as 0 cannot be told apart from a
> >> successful pass that reclaimed nothing.
> >>
> >> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
> >> ---
> >>  mm/bpf_memcontrol.c | 118 ++++++++++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 118 insertions(+)
> >>
> >> diff --git a/mm/bpf_memcontrol.c b/mm/bpf_memcontrol.c
> >> index 716df49d7647..dc51868b3acf 100644
> >> --- a/mm/bpf_memcontrol.c
> >> +++ b/mm/bpf_memcontrol.c
> >> @@ -6,6 +6,7 @@
> >>   */
> >>
> >>  #include <linux/memcontrol.h>
> >> +#include <linux/swap.h>
> >>  #include <linux/bpf.h>
> >>
> >>  __bpf_kfunc_start_defs();
> >> @@ -159,6 +160,120 @@ __bpf_kfunc void bpf_mem_cgroup_flush_stats(struct mem_cgroup *memcg)
> >>         mem_cgroup_flush_stats(memcg);
> >>  }
> >>
> >> +/*
> >> + * Reclaim must not recurse. try_to_free_mem_cgroup_pages() unconditionally
> >> + * overwrites current->reclaim_state on entry and resets it to NULL on exit.
> >> + * So invoking it from an in-flight reclaim would clobber the outer reclaim
> >> + * state and corrupt its accounting.
> >> + *
> >> + * The guards are PF_MEMALLOC and current->reclaim_state. Every reclaim
> >> + * entry point marks the current task with PF_MEMALLOC for the whole
> >> + * reclaim window: try_to_free_mem_cgroup_pages() and __perform_reclaim()
> >> + * do so via memalloc_noreclaim_save(), and kswapd keeps it set for its
> >> + * entire lifetime. A hook inside the reclaim path (shrink_node,
> >> + * shrink_slab, ...) executes in the context of the reclaiming task, where
> >> + * current->flags already carries the flag. The page allocator, the memcg
> >> + * charging path and node_reclaim() rely on the same flag to avoid reclaim
> >> + * recursion.
> >> + *
> >> + * reclaim_state is checked in addition because it is set slightly before
> >> + * PF_MEMALLOC in try_to_free_mem_cgroup_pages(), with only a tracepoint
> >> + * call in between. A sleepable BPF program cannot attach to the tracepoint
> >> + * itself, but it can attach to the generated trace iterator function
> >> + * (__traceiter_mm_vmscan_memcg_reclaim_begin) via fentry, so PF_MEMALLOC
> >> + * alone would leave that window open.
> >> + *
> >> + * Also, PF_MEMALLOC is set in some non-reclaim contexts (e.g. direct compaction
> >> + * and vmalloc), where the kfunc conservatively refuses to reclaim as well.
> >> + */
> >> +static bool bpf_in_reclaim_context(void)
> >> +{
> >> +       return (current->flags & PF_MEMALLOC) || current->reclaim_state;
> >> +}
> >> +
> >> +/*
> >> + * Shared implementation of the proactive reclaim kfuncs: performs one
> >> + * reclaim pass on @memcg with @nr_pages as the goal, allowing swap, and
> >> + * @swappiness as the anon/file balance override (NULL to follow the
> >> + * cgroup's own swappiness setting). Returns the reclaimed amount in
> >> + * bytes, keeping the byte-based unit of the kfuncs' @size argument.
> >> + */
> >> +static unsigned long
> >> +bpf_proactive_reclaim_pages(struct mem_cgroup *memcg, unsigned long nr_pages,
> >> +                           int *swappiness)
> >> +{
> >> +       unsigned long nr_reclaimed;
> >> +
> >> +       if (!nr_pages || unlikely(bpf_in_reclaim_context()))
> >> +               return 0;
> >> +
> >> +       nr_reclaimed = try_to_free_mem_cgroup_pages(memcg, nr_pages, GFP_KERNEL,
> >> +                                                   MEMCG_RECLAIM_MAY_SWAP |
> >> +                                                   MEMCG_RECLAIM_PROACTIVE,
> >> +                                                   swappiness);
> >> +
> >> +       return nr_reclaimed * PAGE_SIZE;
> >> +}
> >> +
> >> +/**
> >> + * bpf_proactive_reclaim - proactively reclaim memory from a memory
> >> + *                         cgroup
> >> + * @memcg: the target memory cgroup to reclaim from
> >> + * @size:  the amount of memory to reclaim, in bytes
> >> + *
> >> + * Trigger one proactive reclaim pass on @memcg, similar to a write to
> >> + * the memory.reclaim cgroup file: pages are reclaimed according to the
> >> + * cgroup's own swappiness setting and swap is allowed. Note that,
> >> + * unlike memory.reclaim, this does not retry until @size is reached;
> >> + * callers can invoke it again if needed.
> >> + *
> >> + * The reclaim runs with GFP_KERNEL, so this function must not be called
> >> + * from a context that holds a filesystem lock (e.g. an LSM hook invoked
> >> + * with inode_lock held): the reclaim path may enter filesystem shrinkers
> >> + * and deadlock trying to reacquire the lock. Contexts that set
> >> + * PF_MEMALLOC_NOFS/NOIO are handled by the gfp context inheritance.
> >> + *
> >> + * Return:
> >> + *   The amount of memory actually reclaimed, in bytes (rounded to full
> >> + *   pages), or 0 if @size is smaller than a page or the calling task is
> >> + *   already in a reclaim/freeing context (PF_MEMALLOC).
> >> + */
> >> +__bpf_kfunc unsigned long bpf_proactive_reclaim(struct mem_cgroup *memcg,
> >> +                                               unsigned long size)
> >> +{
> >> +       return bpf_proactive_reclaim_pages(memcg, size / PAGE_SIZE, NULL);
> >> +}
> >> +
> >> +/**
> >> + * bpf_proactive_reclaim_swappiness - proactively reclaim memory from a
> >> + *                                    memory cgroup with an explicit
> >> + *                                    swappiness
> >> + * @memcg:      the target memory cgroup to reclaim from
> >> + * @size:       the amount of memory to reclaim, in bytes
> >> + * @swappiness: swappiness override for this reclaim pass
> >> + *
> >> + * Same as bpf_proactive_reclaim(), except that the anon/file reclaim
> >> + * balance is controlled by @swappiness instead of the cgroup's
> >> + * swappiness setting. Valid values are [MIN_SWAPPINESS, MAX_SWAPPINESS]
> >> + * and SWAPPINESS_ANON_ONLY, which restricts reclaim to anon folios.
> >> + *
> >> + * Return:
> >> + *   The amount of memory actually reclaimed, in bytes (rounded to full
> >> + *   pages), (unsigned long)-1 if @swappiness is out of range, or 0 if
> >> + *   @size is smaller than a page or the calling task is already in a
> >> + *   reclaim/freeing context (PF_MEMALLOC).
> >> + */
> >> +__bpf_kfunc unsigned long
> >> +bpf_proactive_reclaim_swappiness(struct mem_cgroup *memcg, unsigned long size,
> >> +                                int swappiness)
> >> +{
> >> +       if (swappiness < MIN_SWAPPINESS || swappiness > SWAPPINESS_ANON_ONLY)
> >> +               return (unsigned long)-1;
> >> +
> >> +       return bpf_proactive_reclaim_pages(memcg, size / PAGE_SIZE,
> >> +                                          &swappiness);
> >> +}
> >
> > I haven't followed previous discussion, so I apologize if this was
> > discussed, but if not, isn't it a bit an overkill to have second
> > variant just to provide optional swappiness? Valid range of swappinees
> > seems to be non-negative [0, 200], that special ANON is 201, so why
> > can't we defined that <0 swappiness just means no swappiness was
> > provided and get away with just one kfunc?
> >
> 
> I think my understanding of Shakeel's suggestion was that we only add one
> bpf_proactive_reclaim() for now, if swappiness parameter is necessary we can
> introduce the second API later. But given it seems Hui wants to add both, I
> think it would make sense to do what Andrii said and just introduce one kfunc
> with the swappiness parameter now.
> 
> But Hui, please wait for Shakeel to comment before respinning again.

Yes let's go with Andri's suggestion.

However Hui, don't respin the series. Let me go through it first. I am not happy
with the amount of text and comments added to the patches.

> 
> >> +
> >>  __bpf_kfunc_end_defs();
> >>
> >>  BTF_KFUNCS_START(bpf_memcontrol_kfuncs)
> >> @@ -172,6 +287,9 @@ BTF_ID_FLAGS(func, bpf_mem_cgroup_usage)
> >>  BTF_ID_FLAGS(func, bpf_mem_cgroup_page_state)
> >>  BTF_ID_FLAGS(func, bpf_mem_cgroup_flush_stats, KF_SLEEPABLE)
> >>
> >> +BTF_ID_FLAGS(func, bpf_proactive_reclaim, KF_SLEEPABLE)
> >> +BTF_ID_FLAGS(func, bpf_proactive_reclaim_swappiness, KF_SLEEPABLE)
> >> +
> >>  BTF_KFUNCS_END(bpf_memcontrol_kfuncs)
> >>
> >>  static const struct btf_kfunc_id_set bpf_memcontrol_kfunc_set = {
> >> --
> >> 2.53.0
> >>
> 

  reply	other threads:[~2026-08-22  3:19 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20  6:12 [PATCH bpf-next v4 0/2] bpf: BPF-driven proactive memcg reclaim Hui Zhu
2026-08-20  6:12 ` [PATCH bpf-next v4 1/2] mm/bpf: Add bpf_proactive_reclaim kfuncs Hui Zhu
2026-08-20  7:05   ` bot+bpf-ci
2026-08-21 19:12   ` Andrii Nakryiko
2026-08-21 19:38     ` Kumar Kartikeya Dwivedi
2026-08-22  3:19       ` Shakeel Butt [this message]
2026-08-20  6:12 ` [PATCH bpf-next v4 2/2] selftests/bpf: add memcg async reclaim test Hui Zhu
2026-08-20  7:05   ` bot+bpf-ci
2026-08-24 20:15 ` [PATCH bpf-next v4 0/2] bpf: BPF-driven proactive memcg reclaim Shakeel Butt

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=aojJoHl2uSe1IsfA@linux.dev \
    --to=shakeel.butt@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=andrii.nakryiko@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=baohua@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=geliang@kernel.org \
    --cc=hui.zhu@linux.dev \
    --cc=ihor.solodrai@linux.dev \
    --cc=inwardvessel@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=roman.gushchin@linux.dev \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    --cc=zhuhui@kylinos.cn \
    /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