Re: [PATCH bpf-next v3 4/5] bpf, x86: make sure allocation in arch_bpf_trampoline_size() is writable

Jiri Olsa <[email protected]>
Newsgroups org.kernel.vger.bpf,org.kernel.vger.linux-kernel
Message-ID <aoIhPvP8yM-3f60Y@krava>
On Fri, Aug 14, 2026 at 04:53:32PM +0200, Jiri Olsa wrote:
> On Thu, Jul 16, 2026 at 10:51:38AM +0300, Mike Rapoport (Microsoft) wrote:
> > arch_bpf_trampoline_size() allocates a buffer to get actual size required
> > for a trampoline.
> > 
> > This buffer must be in the module address space because
> > __arch_prepare_bpf_trampoline() calculates  rel32 offsets relatively to
> > that buffer.
> > 
> > In preparation for enabling ROX mode for EXECMEM_BPF make sure that the
> > allocated memory is writable.
> > 
> > Add bpf_jit_alloc_exec_rw() wrapper for execmem_alloc_rw() and use it for > buffer allocation in arch_bpf_trampoline_size().
> > 
> > Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
> > ---
> >  arch/x86/net/bpf_jit_comp.c | 5 ++---
> >  include/linux/filter.h      | 1 +
> >  kernel/bpf/core.c           | 5 +++++
> >  3 files changed, 8 insertions(+), 3 deletions(-)
> > 
> > diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> > index de7515ea1bea..b2feec81e231 100644
> > --- a/arch/x86/net/bpf_jit_comp.c
> > +++ b/arch/x86/net/bpf_jit_comp.c
> > @@ -3703,13 +3703,12 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
> >  	int ret;
> >  
> >  	/* Allocate a temporary buffer for __arch_prepare_bpf_trampoline().
> > -	 * This will NOT cause fragmentation in direct map, as we do not
> > -	 * call set_memory_*() on this buffer.
> >  	 *
> >  	 * We cannot use kvmalloc here, because we need image to be in
> >  	 * module memory range.
> > +	 * Since it must be writable use bpf_jit_alloc_exec_rw().
> >  	 */
> > -	image = bpf_jit_alloc_exec(PAGE_SIZE);
> > +	image = bpf_jit_alloc_exec_rw(PAGE_SIZE);
> 
> hi,
> this change (this particular patch plus possibly others in this set) is
> causing tracing_multi attachment bench slowdown
> 
> the benchmark allocates huge number of trampolines and I'm seeing extra
> arch_bpf_trampoline_size code paths in the attached perf profile 
> 
> I'm not that familiar with the allocator, but following hack makes the
> benchmark ok again:
> 
> 	diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> 	index 6a94370a2448..bbff3c9c6681 100644
> 	--- a/kernel/bpf/core.c
> 	+++ b/kernel/bpf/core.c
> 	@@ -1130,7 +1130,7 @@ void *bpf_jit_alloc_exec(unsigned long size)
> 	 
> 	 void *bpf_jit_alloc_exec_rw(unsigned long size)
> 	 {
> 	-	return execmem_alloc_rw(EXECMEM_BPF, size);
> 	+	return execmem_alloc(EXECMEM_MODULE_DATA, size);
> 	 }
> 	 
> 	 void bpf_jit_free_exec(void *addr)
> 
> 
> I still need to do more checks, but I'm wondering if we could actually fix
> this by not allocating image data in arch_bpf_trampoline_size at all..
> and just teach __arch_prepare_bpf_trampoline to survive NULL image data
> and just return the size in such case

fyi doing ^^^ is not so straigh forward as I hoped for, but if we just allocate
dummy page at init and used that in arch_bpf_trampoline_size we get some speedup

I'll rebase that on top of your fix and send it out later


with your fix:
	serial_test_tracing_multi_bench_attach: found 55077 functions
	serial_test_tracing_multi_bench_attach: attached in   1.470s
	serial_test_tracing_multi_bench_attach: detached in   0.249s


with change below:
	serial_test_tracing_multi_bench_attach: found 55079 functions
	serial_test_tracing_multi_bench_attach: attached in   0.754s
	serial_test_tracing_multi_bench_attach: detached in   0.247s

thanks,
jirka


---
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 1a9fb530adc3..3e8ba33944c9 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -9,6 +9,7 @@
 #include <linux/filter.h>
 #include <linux/if_vlan.h>
 #include <linux/bitfield.h>
+#include <linux/init.h>
 #include <linux/bpf.h>
 #include <linux/bpf_verifier.h>
 #include <linux/memory.h>
@@ -23,6 +24,19 @@
 
 static bool all_callee_regs_used[4] = {true, true, true, true};
 
+/*
+ * Reuse a writable image in the BPF execmem range for size calculation.
+ * Its contents do not affect size calculation.
+ */
+static void *trampoline_size_image;
+
+static int __init init_trampoline_size_image(void)
+{
+	trampoline_size_image = bpf_jit_alloc_exec_rw(PAGE_SIZE);
+	return trampoline_size_image ? 0 : -ENOMEM;
+}
+late_initcall(init_trampoline_size_image);
+
 static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 {
 	if (len == 1)
@@ -3811,23 +3825,11 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
 			     struct bpf_tramp_nodes *tnodes, void *func_addr)
 {
 	struct bpf_tramp_image im;
-	void *image;
-	int ret;
-
-	/* Allocate a temporary buffer for __arch_prepare_bpf_trampoline().
-	 *
-	 * We cannot use kvmalloc here, because we need image to be in
-	 * module memory range.
-	 * Since it must be writable use bpf_jit_alloc_exec_rw().
-	 */
-	image = bpf_jit_alloc_exec_rw(PAGE_SIZE);
-	if (!image)
-		return -ENOMEM;
 
-	ret = __arch_prepare_bpf_trampoline(&im, image, image + PAGE_SIZE, image,
-					    m, flags, tnodes, func_addr);
-	bpf_jit_free_exec(image);
-	return ret;
+	return __arch_prepare_bpf_trampoline(&im, trampoline_size_image,
+					     trampoline_size_image + PAGE_SIZE,
+					     trampoline_size_image, m, flags,
+					     tnodes, func_addr);
 }
 
 static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image, u8 *buf)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.