mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
@ 2026-09-16 18:10 Usama Arif
  2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
  2026-09-16 20:00 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 7+ messages in thread
From: Usama Arif @ 2026-09-16 18:10 UTC (permalink / raw)
  To: ast, andrii, bpf, daniel, eddyz87, emil, ihor.solodrai, jolsa,
	justinstitt, linux-kernel, llvm, martin.lau, memxor, morbo,
	nathan, ndesaulniers, song, yonghong.song, yatsenko, kernel-team
  Cc: Usama Arif

bpf_local_storage_update() takes the map bucket lock while holding
local_storage->lock. bpf_selem_unlink_map() does the same; its only
caller holds local_storage->lock. The outer lock is acquired with
raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
sites.

Using raw_res_spin_lock_irqsave() for the nested lock saves the already
disabled IRQ state and issues another IRQ disable. The matching unlock
tests that saved state before leaving interrupts disabled. On x86-64,
this adds a pushfq/popq/cli sequence and a test/branch around an
unreachable sti to each acquisition.

Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
preemption nesting, memory ordering and resilient-lock bookkeeping. The
outer unlock remains responsible for restoring the caller's IRQ state.

In the tested clang x86-64 build, this removes five executed instructions
from each uncontended nested acquisition. It also shrinks
bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
from 1030 to 992 bytes. The affected paths are updates that add or replace
an element in existing owner storage and successful unlinks.

Document the owner-lock requirement of bpf_selem_unlink_map() and assert
that interrupts are disabled.

Signed-off-by: Usama Arif <usama.arif@linux.dev>
---
 kernel/bpf/bpf_local_storage.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c
index 6fc6a4b672b55..4642da062f0f8 100644
--- a/kernel/bpf/bpf_local_storage.c
+++ b/kernel/bpf/bpf_local_storage.c
@@ -240,24 +240,26 @@ void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
 	hlist_add_head_rcu(&selem->snode, &local_storage->list);
 }
 
+/* Must be called with the owning local_storage->lock held. */
 static int bpf_selem_unlink_map(struct bpf_local_storage_elem *selem)
 {
 	struct bpf_local_storage *local_storage;
 	struct bpf_local_storage_map *smap;
 	struct bpf_local_storage_map_bucket *b;
-	unsigned long flags;
 	int err;
 
+	lockdep_assert_irqs_disabled();
+
 	local_storage = rcu_dereference_check(selem->local_storage,
 					      bpf_rcu_lock_held());
 	smap = rcu_dereference_check(SDATA(selem)->smap, bpf_rcu_lock_held());
 	b = select_bucket(smap, local_storage);
-	err = raw_res_spin_lock_irqsave(&b->lock, flags);
+	err = raw_res_spin_lock(&b->lock);
 	if (err)
 		return err;
 
 	hlist_del_init_rcu(&selem->map_node);
-	raw_res_spin_unlock_irqrestore(&b->lock, flags);
+	raw_res_spin_unlock(&b->lock);
 
 	return 0;
 }
@@ -552,7 +554,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
 	struct bpf_local_storage *local_storage;
 	struct bpf_local_storage_map_bucket *b;
 	HLIST_HEAD(old_selem_free_list);
-	unsigned long flags, b_flags;
+	unsigned long flags;
 	int err;
 
 	/* BPF_EXIST and BPF_NOEXIST cannot be both set */
@@ -637,7 +639,8 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
 
 	b = select_bucket(smap, local_storage);
 
-	err = raw_res_spin_lock_irqsave(&b->lock, b_flags);
+	/* local_storage->lock is held, so IRQs are already disabled. */
+	err = raw_res_spin_lock(&b->lock);
 	if (err)
 		goto unlock;
 
@@ -655,7 +658,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
 						&old_selem_free_list);
 	}
 
-	raw_res_spin_unlock_irqrestore(&b->lock, b_flags);
+	raw_res_spin_unlock(&b->lock);
 unlock:
 	raw_res_spin_unlock_irqrestore(&local_storage->lock, flags);
 free_selem:
-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 18:10 [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks Usama Arif
@ 2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
  2026-09-16 18:30   ` Amery Hung
                     ` (2 more replies)
  2026-09-16 20:00 ` patchwork-bot+netdevbpf
  1 sibling, 3 replies; 7+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-16 18:22 UTC (permalink / raw)
  To: Usama Arif, ast, andrii, bpf, daniel, eddyz87, emil,
	ihor.solodrai, jolsa, justinstitt, linux-kernel, llvm,
	martin.lau, morbo, nathan, ndesaulniers, song, yonghong.song,
	yatsenko, kernel-team, Amery Hung

+Cc Amery

On Wed Sep 16, 2026 at 8:10 PM CEST, Usama Arif wrote:
> bpf_local_storage_update() takes the map bucket lock while holding
> local_storage->lock. bpf_selem_unlink_map() does the same; its only
> caller holds local_storage->lock. The outer lock is acquired with
> raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
> sites.
>
> Using raw_res_spin_lock_irqsave() for the nested lock saves the already
> disabled IRQ state and issues another IRQ disable. The matching unlock
> tests that saved state before leaving interrupts disabled. On x86-64,
> this adds a pushfq/popq/cli sequence and a test/branch around an
> unreachable sti to each acquisition.
>
> Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
> preemption nesting, memory ordering and resilient-lock bookkeeping. The
> outer unlock remains responsible for restoring the caller's IRQ state.
>
> In the tested clang x86-64 build, this removes five executed instructions
> from each uncontended nested acquisition. It also shrinks
> bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
> from 1030 to 992 bytes. The affected paths are updates that add or replace
> an element in existing owner storage and successful unlinks.
>
> Document the owner-lock requirement of bpf_selem_unlink_map() and assert
> that interrupts are disabled.
>
> Signed-off-by: Usama Arif <usama.arif@linux.dev>
> ---

Makes sense. But did you observe any measurable improvement with this change?

> [...]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
@ 2026-09-16 18:30   ` Amery Hung
  2026-09-16 19:20   ` Usama Arif
  2026-09-16 19:22   ` Usama Arif
  2 siblings, 0 replies; 7+ messages in thread
From: Amery Hung @ 2026-09-16 18:30 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: Usama Arif, ast, andrii, bpf, daniel, eddyz87, emil,
	ihor.solodrai, jolsa, justinstitt, linux-kernel, llvm,
	martin.lau, morbo, nathan, ndesaulniers, song, yonghong.song,
	yatsenko, kernel-team

On Wed, Sep 16, 2026 at 11:22 AM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> +Cc Amery
>
> On Wed Sep 16, 2026 at 8:10 PM CEST, Usama Arif wrote:
> > bpf_local_storage_update() takes the map bucket lock while holding
> > local_storage->lock. bpf_selem_unlink_map() does the same; its only
> > caller holds local_storage->lock. The outer lock is acquired with
> > raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
> > sites.
> >
> > Using raw_res_spin_lock_irqsave() for the nested lock saves the already
> > disabled IRQ state and issues another IRQ disable. The matching unlock
> > tests that saved state before leaving interrupts disabled. On x86-64,
> > this adds a pushfq/popq/cli sequence and a test/branch around an
> > unreachable sti to each acquisition.
> >
> > Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
> > preemption nesting, memory ordering and resilient-lock bookkeeping. The
> > outer unlock remains responsible for restoring the caller's IRQ state.
> >
> > In the tested clang x86-64 build, this removes five executed instructions
> > from each uncontended nested acquisition. It also shrinks
> > bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
> > from 1030 to 992 bytes. The affected paths are updates that add or replace
> > an element in existing owner storage and successful unlinks.
> >
> > Document the owner-lock requirement of bpf_selem_unlink_map() and assert
> > that interrupts are disabled.
> >
> > Signed-off-by: Usama Arif <usama.arif@linux.dev>
> > ---
>
> Makes sense. But did you observe any measurable improvement with this change?
>

Same question, but the change looks right to me.

> > [...]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
  2026-09-16 18:30   ` Amery Hung
@ 2026-09-16 19:20   ` Usama Arif
  2026-09-16 19:22   ` Usama Arif
  2 siblings, 0 replies; 7+ messages in thread
From: Usama Arif @ 2026-09-16 19:20 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi, ast, andrii, bpf, daniel, eddyz87, emil,
	ihor.solodrai, jolsa, justinstitt, linux-kernel, llvm,
	martin.lau, morbo, nathan, ndesaulniers, song, yonghong.song,
	yatsenko, kernel-team, Amery Hung



On 16/09/2026 19:22, Kumar Kartikeya Dwivedi wrote:
> +Cc Amery
> 
> On Wed Sep 16, 2026 at 8:10 PM CEST, Usama Arif wrote:
>> bpf_local_storage_update() takes the map bucket lock while holding
>> local_storage->lock. bpf_selem_unlink_map() does the same; its only
>> caller holds local_storage->lock. The outer lock is acquired with
>> raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
>> sites.
>>
>> Using raw_res_spin_lock_irqsave() for the nested lock saves the already
>> disabled IRQ state and issues another IRQ disable. The matching unlock
>> tests that saved state before leaving interrupts disabled. On x86-64,
>> this adds a pushfq/popq/cli sequence and a test/branch around an
>> unreachable sti to each acquisition.
>>
>> Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
>> preemption nesting, memory ordering and resilient-lock bookkeeping. The
>> outer unlock remains responsible for restoring the caller's IRQ state.
>>
>> In the tested clang x86-64 build, this removes five executed instructions
>> from each uncontended nested acquisition. It also shrinks
>> bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
>> from 1030 to 992 bytes. The affected paths are updates that add or replace
>> an element in existing owner storage and successful unlinks.
>>
>> Document the owner-lock requirement of bpf_selem_unlink_map() and assert
>> that interrupts are disabled.
>>
>> Signed-off-by: Usama Arif <usama.arif@linux.dev>
>> ---
> 
> Makes sense. But did you observe any measurable improvement with this change?

Meta fleet wide profile shows bpf_local_storage_update() as one of the more expensive
bpf functions in the fleet. I saw it in production when profiling a hhvm workload as
well.

The main argument for the patch was reduced number of instructions executed in
this expensive path that I see in disassembly (which is mentioned in the 2nd paragraph)
and better code hygiene as it doesnt make sense to to save irq again. I would imagine
this patch alone wont move the needle in application metrics, but would make this function
cheaper (hopefully :)) fleetwide.


> 
>> [...]


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
  2026-09-16 18:30   ` Amery Hung
  2026-09-16 19:20   ` Usama Arif
@ 2026-09-16 19:22   ` Usama Arif
  2026-09-16 19:29     ` Amery Hung
  2 siblings, 1 reply; 7+ messages in thread
From: Usama Arif @ 2026-09-16 19:22 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi, ast, andrii, bpf, daniel, eddyz87, emil,
	ihor.solodrai, jolsa, justinstitt, linux-kernel, llvm,
	martin.lau, morbo, nathan, ndesaulniers, song, yonghong.song,
	yatsenko, kernel-team, Amery Hung



On 16/09/2026 19:22, Kumar Kartikeya Dwivedi wrote:
> +Cc Amery
> 
> On Wed Sep 16, 2026 at 8:10 PM CEST, Usama Arif wrote:
>> bpf_local_storage_update() takes the map bucket lock while holding
>> local_storage->lock. bpf_selem_unlink_map() does the same; its only
>> caller holds local_storage->lock. The outer lock is acquired with
>> raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
>> sites.
>>
>> Using raw_res_spin_lock_irqsave() for the nested lock saves the already
>> disabled IRQ state and issues another IRQ disable. The matching unlock
>> tests that saved state before leaving interrupts disabled. On x86-64,
>> this adds a pushfq/popq/cli sequence and a test/branch around an
>> unreachable sti to each acquisition.
>>
>> Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
>> preemption nesting, memory ordering and resilient-lock bookkeeping. The
>> outer unlock remains responsible for restoring the caller's IRQ state.
>>
>> In the tested clang x86-64 build, this removes five executed instructions
>> from each uncontended nested acquisition. It also shrinks
>> bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
>> from 1030 to 992 bytes. The affected paths are updates that add or replace
>> an element in existing owner storage and successful unlinks.
>>
>> Document the owner-lock requirement of bpf_selem_unlink_map() and assert
>> that interrupts are disabled.
>>
>> Signed-off-by: Usama Arif <usama.arif@linux.dev>
>> ---
> 
> Makes sense. But did you observe any measurable improvement with this change?

Meta fleet wide profile shows bpf_local_storage_update() as one of the more expensive
bpf functions in the fleet. I saw it in production when profiling a hhvm workload as
well.

The main argument for the patch was reduced number of instructions executed in
this expensive path that I see in disassembly (which is mentioned in the 2nd paragraph)
and better code hygiene as it doesnt make sense to to save irq again. I would imagine
this patch alone wont move the needle in application metrics, but would make this function
cheaper (hopefully :)) fleetwide.


> 
>> [...]


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 19:22   ` Usama Arif
@ 2026-09-16 19:29     ` Amery Hung
  0 siblings, 0 replies; 7+ messages in thread
From: Amery Hung @ 2026-09-16 19:29 UTC (permalink / raw)
  To: Usama Arif
  Cc: Kumar Kartikeya Dwivedi, ast, andrii, bpf, daniel, eddyz87, emil,
	ihor.solodrai, jolsa, justinstitt, linux-kernel, llvm,
	martin.lau, morbo, nathan, ndesaulniers, song, yonghong.song,
	yatsenko, kernel-team

On Wed, Sep 16, 2026 at 12:22 PM Usama Arif <usama.arif@linux.dev> wrote:
>
>
>
> On 16/09/2026 19:22, Kumar Kartikeya Dwivedi wrote:
> > +Cc Amery
> >
> > On Wed Sep 16, 2026 at 8:10 PM CEST, Usama Arif wrote:
> >> bpf_local_storage_update() takes the map bucket lock while holding
> >> local_storage->lock. bpf_selem_unlink_map() does the same; its only
> >> caller holds local_storage->lock. The outer lock is acquired with
> >> raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
> >> sites.
> >>
> >> Using raw_res_spin_lock_irqsave() for the nested lock saves the already
> >> disabled IRQ state and issues another IRQ disable. The matching unlock
> >> tests that saved state before leaving interrupts disabled. On x86-64,
> >> this adds a pushfq/popq/cli sequence and a test/branch around an
> >> unreachable sti to each acquisition.
> >>
> >> Use raw_res_spin_lock() and raw_res_spin_unlock() instead. They retain
> >> preemption nesting, memory ordering and resilient-lock bookkeeping. The
> >> outer unlock remains responsible for restoring the caller's IRQ state.
> >>
> >> In the tested clang x86-64 build, this removes five executed instructions
> >> from each uncontended nested acquisition. It also shrinks
> >> bpf_local_storage_update() from 1732 to 1702 bytes and bpf_selem_unlink()
> >> from 1030 to 992 bytes. The affected paths are updates that add or replace
> >> an element in existing owner storage and successful unlinks.
> >>
> >> Document the owner-lock requirement of bpf_selem_unlink_map() and assert
> >> that interrupts are disabled.
> >>
> >> Signed-off-by: Usama Arif <usama.arif@linux.dev>
> >> ---
> >
> > Makes sense. But did you observe any measurable improvement with this change?
>
> Meta fleet wide profile shows bpf_local_storage_update() as one of the more expensive
> bpf functions in the fleet. I saw it in production when profiling a hhvm workload as
> well.
>
> The main argument for the patch was reduced number of instructions executed in
> this expensive path that I see in disassembly (which is mentioned in the 2nd paragraph)
> and better code hygiene as it doesnt make sense to to save irq again. I would imagine
> this patch alone wont move the needle in application metrics, but would make this function
> cheaper (hopefully :)) fleetwide.
>

Reviewed-by: Amery Hung <ameryhung@gmail.com>

>
> >
> >> [...]
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks
  2026-09-16 18:10 [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks Usama Arif
  2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
@ 2026-09-16 20:00 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 7+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-16 20:00 UTC (permalink / raw)
  To: Usama Arif
  Cc: ast, andrii, bpf, daniel, eddyz87, emil, ihor.solodrai, jolsa,
	justinstitt, linux-kernel, llvm, martin.lau, memxor, morbo,
	nathan, ndesaulniers, song, yonghong.song, yatsenko, kernel-team

Hello:

This patch was applied to bpf/bpf-next.git (master)
by Kumar Kartikeya Dwivedi <memxor@gmail.com>:

On Wed, 16 Sep 2026 11:10:01 -0700 you wrote:
> bpf_local_storage_update() takes the map bucket lock while holding
> local_storage->lock. bpf_selem_unlink_map() does the same; its only
> caller holds local_storage->lock. The outer lock is acquired with
> raw_res_spin_lock_irqsave(), so interrupts are already disabled at both
> sites.
> 
> Using raw_res_spin_lock_irqsave() for the nested lock saves the already
> disabled IRQ state and issues another IRQ disable. The matching unlock
> tests that saved state before leaving interrupts disabled. On x86-64,
> this adds a pushfq/popq/cli sequence and a test/branch around an
> unreachable sti to each acquisition.
> 
> [...]

Here is the summary with links:
  - bpf: local_storage: avoid redundant IRQ save on bucket locks
    https://git.kernel.org/bpf/bpf-next/c/10c4f610b215

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-16 20:01 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-16 18:10 [PATCH] bpf: local_storage: avoid redundant IRQ save on bucket locks Usama Arif
2026-09-16 18:22 ` Kumar Kartikeya Dwivedi
2026-09-16 18:30   ` Amery Hung
2026-09-16 19:20   ` Usama Arif
2026-09-16 19:22   ` Usama Arif
2026-09-16 19:29     ` Amery Hung
2026-09-16 20:00 ` patchwork-bot+netdevbpf

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®