Skip to content

Commit e6f2dd0

Browse files
jkoong-fbAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf: Add bpf_loop helper
This patch adds the kernel-side and API changes for a new helper function, bpf_loop: long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags); where long (*callback_fn)(u32 index, void *ctx); bpf_loop invokes the "callback_fn" **nr_loops** times or until the callback_fn returns 1. The callback_fn can only return 0 or 1, and this is enforced by the verifier. The callback_fn index is zero-indexed. A few things to please note: ~ The "u64 flags" parameter is currently unused but is included in case a future use case for it arises. ~ In the kernel-side implementation of bpf_loop (kernel/bpf/bpf_iter.c), bpf_callback_t is used as the callback function cast. ~ A program can have nested bpf_loop calls but the program must still adhere to the verifier constraint of its stack depth (the stack depth cannot exceed MAX_BPF_STACK)) ~ Recursive callback_fns do not pass the verifier, due to the call stack for these being too deep. ~ The next patch will include the tests and benchmark Signed-off-by: Joanne Koong <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 88691e9 commit e6f2dd0

File tree

6 files changed

+142
-34
lines changed

6 files changed

+142
-34
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,6 +2164,7 @@ extern const struct bpf_func_proto bpf_sk_setsockopt_proto;
21642164
extern const struct bpf_func_proto bpf_sk_getsockopt_proto;
21652165
extern const struct bpf_func_proto bpf_kallsyms_lookup_name_proto;
21662166
extern const struct bpf_func_proto bpf_find_vma_proto;
2167+
extern const struct bpf_func_proto bpf_loop_proto;
21672168

21682169
const struct bpf_func_proto *tracing_prog_func_proto(
21692170
enum bpf_func_id func_id, const struct bpf_prog *prog);

include/uapi/linux/bpf.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4957,6 +4957,30 @@ union bpf_attr {
49574957
* **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*.
49584958
* **-EBUSY** if failed to try lock mmap_lock.
49594959
* **-EINVAL** for invalid **flags**.
4960+
*
4961+
* long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
4962+
* Description
4963+
* For **nr_loops**, call **callback_fn** function
4964+
* with **callback_ctx** as the context parameter.
4965+
* The **callback_fn** should be a static function and
4966+
* the **callback_ctx** should be a pointer to the stack.
4967+
* The **flags** is used to control certain aspects of the helper.
4968+
* Currently, the **flags** must be 0. Currently, nr_loops is
4969+
* limited to 1 << 23 (~8 million) loops.
4970+
*
4971+
* long (\*callback_fn)(u32 index, void \*ctx);
4972+
*
4973+
* where **index** is the current index in the loop. The index
4974+
* is zero-indexed.
4975+
*
4976+
* If **callback_fn** returns 0, the helper will continue to the next
4977+
* loop. If return value is 1, the helper will skip the rest of
4978+
* the loops and return. Other return values are not used now,
4979+
* and will be rejected by the verifier.
4980+
*
4981+
* Return
4982+
* The number of loops performed, **-EINVAL** for invalid **flags**,
4983+
* **-E2BIG** if **nr_loops** exceeds the maximum number of loops.
49604984
*/
49614985
#define __BPF_FUNC_MAPPER(FN) \
49624986
FN(unspec), \
@@ -5140,6 +5164,7 @@ union bpf_attr {
51405164
FN(skc_to_unix_sock), \
51415165
FN(kallsyms_lookup_name), \
51425166
FN(find_vma), \
5167+
FN(loop), \
51435168
/* */
51445169

51455170
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

kernel/bpf/bpf_iter.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,3 +714,38 @@ const struct bpf_func_proto bpf_for_each_map_elem_proto = {
714714
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
715715
.arg4_type = ARG_ANYTHING,
716716
};
717+
718+
/* maximum number of loops */
719+
#define MAX_LOOPS BIT(23)
720+
721+
BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,
722+
u64, flags)
723+
{
724+
bpf_callback_t callback = (bpf_callback_t)callback_fn;
725+
u64 ret;
726+
u32 i;
727+
728+
if (flags)
729+
return -EINVAL;
730+
if (nr_loops > MAX_LOOPS)
731+
return -E2BIG;
732+
733+
for (i = 0; i < nr_loops; i++) {
734+
ret = callback((u64)i, (u64)(long)callback_ctx, 0, 0, 0);
735+
/* return value: 0 - continue, 1 - stop and return */
736+
if (ret)
737+
return i + 1;
738+
}
739+
740+
return i;
741+
}
742+
743+
const struct bpf_func_proto bpf_loop_proto = {
744+
.func = bpf_loop,
745+
.gpl_only = false,
746+
.ret_type = RET_INTEGER,
747+
.arg1_type = ARG_ANYTHING,
748+
.arg2_type = ARG_PTR_TO_FUNC,
749+
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
750+
.arg4_type = ARG_ANYTHING,
751+
};

kernel/bpf/helpers.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
13781378
return &bpf_ringbuf_query_proto;
13791379
case BPF_FUNC_for_each_map_elem:
13801380
return &bpf_for_each_map_elem_proto;
1381+
case BPF_FUNC_loop:
1382+
return &bpf_loop_proto;
13811383
default:
13821384
break;
13831385
}

kernel/bpf/verifier.c

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6085,6 +6085,27 @@ static int set_map_elem_callback_state(struct bpf_verifier_env *env,
60856085
return 0;
60866086
}
60876087

6088+
static int set_loop_callback_state(struct bpf_verifier_env *env,
6089+
struct bpf_func_state *caller,
6090+
struct bpf_func_state *callee,
6091+
int insn_idx)
6092+
{
6093+
/* bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx,
6094+
* u64 flags);
6095+
* callback_fn(u32 index, void *callback_ctx);
6096+
*/
6097+
callee->regs[BPF_REG_1].type = SCALAR_VALUE;
6098+
callee->regs[BPF_REG_2] = caller->regs[BPF_REG_3];
6099+
6100+
/* unused */
6101+
__mark_reg_not_init(env, &callee->regs[BPF_REG_3]);
6102+
__mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
6103+
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
6104+
6105+
callee->in_callback_fn = true;
6106+
return 0;
6107+
}
6108+
60886109
static int set_timer_callback_state(struct bpf_verifier_env *env,
60896110
struct bpf_func_state *caller,
60906111
struct bpf_func_state *callee,
@@ -6458,13 +6479,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
64586479
return err;
64596480
}
64606481

6461-
if (func_id == BPF_FUNC_tail_call) {
6462-
err = check_reference_leak(env);
6463-
if (err) {
6464-
verbose(env, "tail_call would lead to reference leak\n");
6465-
return err;
6466-
}
6467-
} else if (is_release_function(func_id)) {
6482+
if (is_release_function(func_id)) {
64686483
err = release_reference(env, meta.ref_obj_id);
64696484
if (err) {
64706485
verbose(env, "func %s#%d reference has not been acquired before\n",
@@ -6475,42 +6490,47 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
64756490

64766491
regs = cur_regs(env);
64776492

6478-
/* check that flags argument in get_local_storage(map, flags) is 0,
6479-
* this is required because get_local_storage() can't return an error.
6480-
*/
6481-
if (func_id == BPF_FUNC_get_local_storage &&
6482-
!register_is_null(&regs[BPF_REG_2])) {
6483-
verbose(env, "get_local_storage() doesn't support non-zero flags\n");
6484-
return -EINVAL;
6485-
}
6486-
6487-
if (func_id == BPF_FUNC_for_each_map_elem) {
6493+
switch (func_id) {
6494+
case BPF_FUNC_tail_call:
6495+
err = check_reference_leak(env);
6496+
if (err) {
6497+
verbose(env, "tail_call would lead to reference leak\n");
6498+
return err;
6499+
}
6500+
break;
6501+
case BPF_FUNC_get_local_storage:
6502+
/* check that flags argument in get_local_storage(map, flags) is 0,
6503+
* this is required because get_local_storage() can't return an error.
6504+
*/
6505+
if (!register_is_null(&regs[BPF_REG_2])) {
6506+
verbose(env, "get_local_storage() doesn't support non-zero flags\n");
6507+
return -EINVAL;
6508+
}
6509+
break;
6510+
case BPF_FUNC_for_each_map_elem:
64886511
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
64896512
set_map_elem_callback_state);
6490-
if (err < 0)
6491-
return -EINVAL;
6492-
}
6493-
6494-
if (func_id == BPF_FUNC_timer_set_callback) {
6513+
break;
6514+
case BPF_FUNC_timer_set_callback:
64956515
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
64966516
set_timer_callback_state);
6497-
if (err < 0)
6498-
return -EINVAL;
6499-
}
6500-
6501-
if (func_id == BPF_FUNC_find_vma) {
6517+
break;
6518+
case BPF_FUNC_find_vma:
65026519
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
65036520
set_find_vma_callback_state);
6504-
if (err < 0)
6505-
return -EINVAL;
6506-
}
6507-
6508-
if (func_id == BPF_FUNC_snprintf) {
6521+
break;
6522+
case BPF_FUNC_snprintf:
65096523
err = check_bpf_snprintf_call(env, regs);
6510-
if (err < 0)
6511-
return err;
6524+
break;
6525+
case BPF_FUNC_loop:
6526+
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
6527+
set_loop_callback_state);
6528+
break;
65126529
}
65136530

6531+
if (err)
6532+
return err;
6533+
65146534
/* reset caller saved regs */
65156535
for (i = 0; i < CALLER_SAVED_REGS; i++) {
65166536
mark_reg_not_init(env, regs, caller_saved[i]);

tools/include/uapi/linux/bpf.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4957,6 +4957,30 @@ union bpf_attr {
49574957
* **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*.
49584958
* **-EBUSY** if failed to try lock mmap_lock.
49594959
* **-EINVAL** for invalid **flags**.
4960+
*
4961+
* long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
4962+
* Description
4963+
* For **nr_loops**, call **callback_fn** function
4964+
* with **callback_ctx** as the context parameter.
4965+
* The **callback_fn** should be a static function and
4966+
* the **callback_ctx** should be a pointer to the stack.
4967+
* The **flags** is used to control certain aspects of the helper.
4968+
* Currently, the **flags** must be 0. Currently, nr_loops is
4969+
* limited to 1 << 23 (~8 million) loops.
4970+
*
4971+
* long (\*callback_fn)(u32 index, void \*ctx);
4972+
*
4973+
* where **index** is the current index in the loop. The index
4974+
* is zero-indexed.
4975+
*
4976+
* If **callback_fn** returns 0, the helper will continue to the next
4977+
* loop. If return value is 1, the helper will skip the rest of
4978+
* the loops and return. Other return values are not used now,
4979+
* and will be rejected by the verifier.
4980+
*
4981+
* Return
4982+
* The number of loops performed, **-EINVAL** for invalid **flags**,
4983+
* **-E2BIG** if **nr_loops** exceeds the maximum number of loops.
49604984
*/
49614985
#define __BPF_FUNC_MAPPER(FN) \
49624986
FN(unspec), \
@@ -5140,6 +5164,7 @@ union bpf_attr {
51405164
FN(skc_to_unix_sock), \
51415165
FN(kallsyms_lookup_name), \
51425166
FN(find_vma), \
5167+
FN(loop), \
51435168
/* */
51445169

51455170
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

0 commit comments

Comments
 (0)