* [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin @ 2025-01-01 20:37 Emil Tsalapatis 2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis 2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis 0 siblings, 2 replies; 9+ messages in thread From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw) To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis From: Emil Tsalapatis (Meta) <emil@etsalapatis.com> In BPF programs, kfunc calls while holding a lock are not allowed because kfuncs may sleep by default. The exception to this rule are the functions in special_kfunc_list, which are guaranteed to not sleep. The bpf_iter_num_* functions used by the bpf_for and bpf_repeat macros make no function calls themselves, and as such are guaranteed to not sleep. Add them to special_kfunc_list to allow them within BPF spinlock critical sections. Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> Emil Tsalapatis (2): bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock selftests/bpf: test bpf_for within spin lock section kernel/bpf/verifier.c | 20 +++++++++++++- .../selftests/bpf/progs/verifier_spin_lock.c | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) -- 2.47.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis @ 2025-01-01 20:37 ` Emil Tsalapatis 2025-01-02 18:02 ` Eduard Zingerman 2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis 1 sibling, 1 reply; 9+ messages in thread From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw) To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list, and allow the calls even while holding a spin lock. Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> --- kernel/bpf/verifier.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d77abb87ffb1..f209021914b1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11690,6 +11690,9 @@ enum special_kfunc_type { KF_bpf_get_kmem_cache, KF_bpf_local_irq_save, KF_bpf_local_irq_restore, + KF_bpf_iter_num_new, + KF_bpf_iter_num_next, + KF_bpf_iter_num_destroy, }; BTF_SET_START(special_kfunc_set) @@ -11765,6 +11768,9 @@ BTF_ID_UNUSED BTF_ID(func, bpf_get_kmem_cache) BTF_ID(func, bpf_local_irq_save) BTF_ID(func, bpf_local_irq_restore) +BTF_ID(func, bpf_iter_num_new) +BTF_ID(func, bpf_iter_num_next) +BTF_ID(func, bpf_iter_num_destroy) static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) { @@ -12151,12 +12157,24 @@ static bool is_bpf_rbtree_api_kfunc(u32 btf_id) btf_id == special_kfunc_list[KF_bpf_rbtree_first]; } +static bool is_bpf_loop_api_kfunc(u32 btf_id) +{ + return btf_id == special_kfunc_list[KF_bpf_iter_num_new] || + btf_id == special_kfunc_list[KF_bpf_iter_num_next] || + btf_id == special_kfunc_list[KF_bpf_iter_num_destroy]; +} + static bool is_bpf_graph_api_kfunc(u32 btf_id) { return is_bpf_list_api_kfunc(btf_id) || is_bpf_rbtree_api_kfunc(btf_id) || btf_id == special_kfunc_list[KF_bpf_refcount_acquire_impl]; } +static bool kfunc_spin_allowed(u32 btf_id) +{ + return is_bpf_graph_api_kfunc(btf_id) || is_bpf_loop_api_kfunc(btf_id); +} + static bool is_sync_callback_calling_kfunc(u32 btf_id) { return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl]; @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env) if (env->cur_state->active_locks) { if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && - (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { + (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) { verbose(env, "function calls are not allowed while holding a lock\n"); return -EINVAL; } -- 2.47.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis @ 2025-01-02 18:02 ` Eduard Zingerman 2025-01-04 19:25 ` Emil Tsalapatis 0 siblings, 1 reply; 9+ messages in thread From: Eduard Zingerman @ 2025-01-02 18:02 UTC (permalink / raw) To: Emil Tsalapatis, bpf; +Cc: ast, daniel, andrii On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote: > Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list, > and allow the calls even while holding a spin lock. > > Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> > --- Reviewed-by: Eduard Zingerman <eddyz87@gmail.com> [...] > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env) > if (env->cur_state->active_locks) { > if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || > (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && > - (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { > + (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) { > verbose(env, "function calls are not allowed while holding a lock\n"); > return -EINVAL; > } Nit: technically, 'bpf_loop' is a helper function independent of iter_num API. I suggest to change the name to is_bpf_iter_num_api_kfunc. Also, if we decide that loops are ok with spin locks, the condition above should be adjusted to allow calls to bpf_loop, e.g. to make the following test work: --- 8< ------------------------------------- static int loop_cb(__u64 index, void *ctx) { return 0; } SEC("tc") __success __failure_unpriv __msg_unpriv("") __retval(0) int bpf_loop_inside_locked_region2(void) { const int zero = 0; struct val *val; val = bpf_map_lookup_elem(&map_spin_lock, &zero); if (!val) return -1; bpf_spin_lock(&val->l); bpf_loop(10, loop_cb, NULL, 0); bpf_spin_unlock(&val->l); return 0; } ------------------------------------- >8 --- ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-02 18:02 ` Eduard Zingerman @ 2025-01-04 19:25 ` Emil Tsalapatis 2025-01-05 0:11 ` Eduard Zingerman 0 siblings, 1 reply; 9+ messages in thread From: Emil Tsalapatis @ 2025-01-04 19:25 UTC (permalink / raw) To: Eduard Zingerman; +Cc: bpf, ast, daniel, andrii On Thu, Jan 2, 2025 at 1:02 PM Eduard Zingerman <eddyz87@gmail.com> wrote: > > On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote: > > Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list, > > and allow the calls even while holding a spin lock. > > > > Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> > > --- > > Reviewed-by: Eduard Zingerman <eddyz87@gmail.com> > > [...] > > > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env) > > if (env->cur_state->active_locks) { > > if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || > > (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && > > - (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { > > + (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) { > > verbose(env, "function calls are not allowed while holding a lock\n"); > > return -EINVAL; > > } > > > Nit: technically, 'bpf_loop' is a helper function independent of iter_num API. > I suggest to change the name to is_bpf_iter_num_api_kfunc. > Also, if we decide that loops are ok with spin locks, > the condition above should be adjusted to allow calls to bpf_loop, > e.g. to make the following test work: > (Sorry for the duplicate, accidentally didn't send the email in plaintext) Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops AFAICT we would need to ensure the callback cannot sleep, which would need extra checks/changes to the verifier compared to bpf_for. IMO we can deal with it in a separate patch if we think allowing it is a good idea. > --- 8< ------------------------------------- > static int loop_cb(__u64 index, void *ctx) > { > return 0; > } > > SEC("tc") > __success __failure_unpriv __msg_unpriv("") > __retval(0) > int bpf_loop_inside_locked_region2(void) > { > const int zero = 0; > struct val *val; > > val = bpf_map_lookup_elem(&map_spin_lock, &zero); > if (!val) > return -1; > > bpf_spin_lock(&val->l); > bpf_loop(10, loop_cb, NULL, 0); > bpf_spin_unlock(&val->l); > > return 0; > } > ------------------------------------- >8 --- > > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-04 19:25 ` Emil Tsalapatis @ 2025-01-05 0:11 ` Eduard Zingerman 2025-01-06 14:56 ` Emil Tsalapatis 0 siblings, 1 reply; 9+ messages in thread From: Eduard Zingerman @ 2025-01-05 0:11 UTC (permalink / raw) To: Emil Tsalapatis; +Cc: bpf, ast, daniel, andrii On Sat, 2025-01-04 at 14:25 -0500, Emil Tsalapatis wrote: [...] > > > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env) > > > if (env->cur_state->active_locks) { > > > if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || > > > (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && > > > - (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { > > > + (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) { > > > verbose(env, "function calls are not allowed while holding a lock\n"); > > > return -EINVAL; > > > } > > > > > > Nit: technically, 'bpf_loop' is a helper function independent of iter_num API. > > I suggest to change the name to is_bpf_iter_num_api_kfunc. > > Also, if we decide that loops are ok with spin locks, > > the condition above should be adjusted to allow calls to bpf_loop, > > e.g. to make the following test work: > > > > (Sorry for the duplicate, accidentally didn't send the email in plaintext) > > Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops > AFAICT we would need to ensure the callback cannot sleep, > which would need extra checks/changes to the verifier compared to > bpf_for. IMO we can deal with it in a separate patch if we think > allowing it is a good idea. Not really, callbacks are verified "in-line". When a function call to a function calling synchronous callback is verified, verifier steps into callback body some number of times. If a sleeping call would be discovered during callback function verification, verifier would see that spin lock is currently taken and report error. So, this is really just a check for particular helper call. [...] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-05 0:11 ` Eduard Zingerman @ 2025-01-06 14:56 ` Emil Tsalapatis 2025-01-06 18:59 ` Alexei Starovoitov 0 siblings, 1 reply; 9+ messages in thread From: Emil Tsalapatis @ 2025-01-06 14:56 UTC (permalink / raw) To: Eduard Zingerman; +Cc: bpf, ast, daniel, andrii I see, thank you for the feedback. in that case I will send another version that handles bpf_loop. On Sat, Jan 4, 2025 at 7:11 PM Eduard Zingerman <eddyz87@gmail.com> wrote: > > On Sat, 2025-01-04 at 14:25 -0500, Emil Tsalapatis wrote: > > [...] > > > > > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env) > > > > if (env->cur_state->active_locks) { > > > > if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || > > > > (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && > > > > - (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { > > > > + (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) { > > > > verbose(env, "function calls are not allowed while holding a lock\n"); > > > > return -EINVAL; > > > > } > > > > > > > > > Nit: technically, 'bpf_loop' is a helper function independent of iter_num API. > > > I suggest to change the name to is_bpf_iter_num_api_kfunc. > > > Also, if we decide that loops are ok with spin locks, > > > the condition above should be adjusted to allow calls to bpf_loop, > > > e.g. to make the following test work: > > > > > > > (Sorry for the duplicate, accidentally didn't send the email in plaintext) > > > > Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops > > AFAICT we would need to ensure the callback cannot sleep, > > which would need extra checks/changes to the verifier compared to > > bpf_for. IMO we can deal with it in a separate patch if we think > > allowing it is a good idea. > > Not really, callbacks are verified "in-line". When a function call to > a function calling synchronous callback is verified, verifier steps > into callback body some number of times. If a sleeping call would > be discovered during callback function verification, verifier would > see that spin lock is currently taken and report error. So, this is > really just a check for particular helper call. > > [...] > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock 2025-01-06 14:56 ` Emil Tsalapatis @ 2025-01-06 18:59 ` Alexei Starovoitov 0 siblings, 0 replies; 9+ messages in thread From: Alexei Starovoitov @ 2025-01-06 18:59 UTC (permalink / raw) To: Emil Tsalapatis Cc: Eduard Zingerman, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko On Mon, Jan 6, 2025 at 7:04 AM Emil Tsalapatis <emil@etsalapatis.com> wrote: > > I see, thank you for the feedback. in that case I will send another > version that handles bpf_loop. I think that can be a separate follow up. I'll apply the v2 as-is. Also pls don't top post. This mailing list preferes inline replies. ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section 2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis 2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis @ 2025-01-01 20:37 ` Emil Tsalapatis 2025-01-02 18:05 ` Eduard Zingerman 1 sibling, 1 reply; 9+ messages in thread From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw) To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis Add a selftest to ensure BPF for loops within critical sections are accepted by the verifier. Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> --- .../selftests/bpf/progs/verifier_spin_lock.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_spin_lock.c b/tools/testing/selftests/bpf/progs/verifier_spin_lock.c index 25599eac9a70..d9d7b05cf6d2 100644 --- a/tools/testing/selftests/bpf/progs/verifier_spin_lock.c +++ b/tools/testing/selftests/bpf/progs/verifier_spin_lock.c @@ -530,4 +530,30 @@ l1_%=: exit; \ : __clobber_all); } +SEC("tc") +__description("spin_lock: loop within a locked region") +__success __failure_unpriv __msg_unpriv("") +__retval(0) +int bpf_loop_inside_locked_region(void) +{ + const int zero = 0; + struct val *val; + int i, j = 0; + + val = bpf_map_lookup_elem(&map_spin_lock, &zero); + if (!val) + return -1; + + bpf_spin_lock(&val->l); + bpf_for(i, 0, 10) { + j++; + /* Silence "unused variable" warnings. */ + if (j == 10) + break; + } + bpf_spin_unlock(&val->l); + + return 0; +} + char _license[] SEC("license") = "GPL"; -- 2.47.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section 2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis @ 2025-01-02 18:05 ` Eduard Zingerman 0 siblings, 0 replies; 9+ messages in thread From: Eduard Zingerman @ 2025-01-02 18:05 UTC (permalink / raw) To: Emil Tsalapatis, bpf; +Cc: ast, daniel, andrii On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote: > Add a selftest to ensure BPF for loops within critical sections are > accepted by the verifier. > > Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com> > --- Acked-by: Eduard Zingerman <eddyz87@gmail.com> [...] ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-01-06 18:59 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis 2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis 2025-01-02 18:02 ` Eduard Zingerman 2025-01-04 19:25 ` Emil Tsalapatis 2025-01-05 0:11 ` Eduard Zingerman 2025-01-06 14:56 ` Emil Tsalapatis 2025-01-06 18:59 ` Alexei Starovoitov 2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis 2025-01-02 18:05 ` Eduard Zingerman
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox