From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: bot+bpf-ci@kernel.org, bpf@vger.kernel.org
Cc: emil@etsalapatis.com, ast@kernel.org, daniel@iogearbox.net,
andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com,
martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev,
jolsa@kernel.org, ihor.solodrai@linux.dev,
linux-kernel@vger.kernel.org, martin.lau@kernel.org,
clm@meta.com
Subject: Re: [PATCH bpf-next v4 2/4] bpf: arena: allocate the fault-in page outside the lock
Date: Fri, 21 Aug 2026 18:31:55 +0800 [thread overview]
Message-ID: <44f56d87-14eb-4fb8-bade-09c94aa0952b@linux.dev> (raw)
In-Reply-To: <b951553281d4e1491647aad4c4c0a8fac03d6b78ecac351fd216888eaae6ce72@mail.kernel.org>
On 8/21/26 1:59 PM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
>> index 7b6847200b43..50dac9ba0f5e 100644
>> --- a/kernel/bpf/arena.c
>> +++ b/kernel/bpf/arena.c
> [ ... ]
>
>> @@ -489,59 +490,108 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
>> kbase = bpf_arena_get_kern_vm_start(arena);
>> kaddr = kbase + (u32)(vmf->address);
>>
>> + page = vmalloc_to_page((void *)kaddr);
>> + if (!page && !(arena->map.map_flags & BPF_F_SEGV_ON_FAULT)) {
>> + /*
>> + * We run in process context here, so preallocate the page
>> + * outside the lock with an explicitly sleepable allocator. It
>> + * can then go through reclaim (both memcg and global) and the
>> + * OOM path, the way do_anonymous_page() does; under
>> + * arena->spinlock only the non-blocking allocator is available,
>> + * which never reclaims. That also decides the return value:
>> + * VM_FAULT_OOM below is only meaningful if the OOM machinery was
>> + * actually engaged, which the non-blocking allocator never does.
>> + */
>> + bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
>> + new_page = bpf_map_alloc_page_sleepable(map);
>> + bpf_map_memcg_exit(old_memcg, new_memcg);
>> + if (!new_page)
>> + return VM_FAULT_OOM;
>> + }
> Can this return VM_FAULT_OOM when the allocation is charged to a foreign
> memcg?
>
> The preallocation charges the arena creator's memcg, not current's:
>
> bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
> /* set_active_memcg(bpf_map_get_memcg(map)) */
>
> new_page = bpf_map_alloc_page_sleepable(map);
> /* GFP_KERNEL|__GFP_ZERO|__GFP_ACCOUNT|__GFP_NOWARN */
>
> For an order-0 GFP_KERNEL allocation the only realistic NULL is a memcg
> charge rejection. mem_cgroup_oom() calls out_of_memory() with oom_control
> pointing at the arena owner's memcg, so the OOM killer can only select
> victims inside that cgroup.
>
> VM_FAULT_OOM reaches pagefault_out_of_memory() (mm/oom_kill.c:1180-1193),
> which only resolves current->memcg_in_oom; otherwise it prints the
> rate-limited "Huh VM_FAULT_OOM leaked out to the #PF handler. Retrying PF"
> message and returns. Because the over-limit memcg is foreign, killing
> tasks there never makes fatal_signal_pending(current) true, so
> try_charge_memcg()'s force-charge escape hatch never fires.
>
> If the owner memcg's charge is dominated by memory its tasks cannot free
> (for example the arena's own pages, which are only freed by the bpf program
> or by map destruction and are unreclaimable), or if that cgroup has no
> OOM-eligible tasks left, mem_cgroup_oom() returns false, try_charge_memcg()
> returns -ENOMEM, and the instruction is retried in a loop.
>
> This creates an unbounded fault/retry livelock burning CPU in the faulting
> task plus dmesg spam, where the pre-image returned a bounded
> VM_FAULT_SIGSEGV. Before this patch the in-lock allocator never engaged
> reclaim or the OOM killer, so a user touching an arena page could not kill
> anything. Now any task that can mmap the arena (the fd can be passed via
> SCM_RIGHTS or a bpffs pin, so the faulting task need not be in the owner's
> cgroup at all) can drive the memcg OOM killer in the owner's cgroup one
> task at a time simply by faulting in pages.
Thanks, you're right — this is a real bug, and I could reproduce it.
The root cause is simply that we return VM_FAULT_OOM.
That return value does nothing useful for arena: all reclaim and OOM
handling (both memcg and global) already happens inside
bpf_map_alloc_page_sleepable().
The fix is to return VM_FAULT_SIGBUS instead of VM_FAULT_OOM.
next prev parent reply other threads:[~2026-08-21 10:32 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 5:01 [PATCH bpf-next v4 0/4] bpf: arena: handle memory.max on fault-in with reclaim/OOM Jiayuan Chen
2026-08-21 5:06 ` [PATCH bpf-next v4 1/4] bpf: Add a sleepable page allocator for map memory Jiayuan Chen
2026-08-21 5:06 ` [PATCH bpf-next v4 2/4] bpf: arena: allocate the fault-in page outside the lock Jiayuan Chen
2026-08-21 5:59 ` bot+bpf-ci
2026-08-21 10:31 ` Jiayuan Chen [this message]
2026-08-21 5:06 ` [PATCH bpf-next v4 3/4] selftests/bpf: Add read_cgroup_file() to cgroup_helpers Jiayuan Chen
2026-08-21 5:06 ` [PATCH bpf-next v4 4/4] selftests/bpf: Add a test for arena fault-in under memory.max Jiayuan Chen
2026-08-21 5:59 ` bot+bpf-ci
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=44f56d87-14eb-4fb8-bade-09c94aa0952b@linux.dev \
--to=jiayuan.chen@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bot+bpf-ci@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=clm@meta.com \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=song@kernel.org \
--cc=yonghong.song@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