Re: [PATCH v2 bpf-next 1/4] bpf: Add bpf_loop helper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 23, 2021 at 10:34 AM Joanne Koong <joannekoong@xxxxxx> wrote:
>
> 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 <joannekoong@xxxxxx>
> ---
>  include/linux/bpf.h            |  1 +
>  include/uapi/linux/bpf.h       | 25 +++++++++
>  kernel/bpf/bpf_iter.c          | 35 ++++++++++++
>  kernel/bpf/helpers.c           |  2 +
>  kernel/bpf/verifier.c          | 97 +++++++++++++++++++++-------------
>  tools/include/uapi/linux/bpf.h | 25 +++++++++
>  6 files changed, 148 insertions(+), 37 deletions(-)
>

[...]

> +/* maximum number of loops */
> +#define MAX_LOOPS      BIT(23)
> +
> +BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,
> +          u64, flags)
> +{
> +       bpf_callback_t callback = (bpf_callback_t)callback_fn;
> +       u64 ret;
> +       u32 i;
> +
> +       if (flags || nr_loops > MAX_LOOPS)
> +               return -EINVAL;

nit: it's probably a good idea to return -E2BIG for nr_loops >
MAX_LOOPS? It will be more obvious for unsuspecting users that forgot
to read the documentation carefully :)

> +
> +       for (i = 0; i < nr_loops; i++) {
> +               ret = callback((u64)i, (u64)(long)callback_ctx, 0, 0, 0);
> +               /* return value: 0 - continue, 1 - stop and return */
> +               if (ret) {
> +                       i++;
> +                       break;

nit: could be just return i + 1;

> +               }
> +       }
> +
> +       return i;
> +}
> +

[...]

> +       case BPF_FUNC_get_local_storage:
> +               /* check that flags argument in get_local_storage(map, flags) is 0,
> +                * this is required because get_local_storage() can't return an error.
> +                */
> +               if (!register_is_null(&regs[BPF_REG_2])) {
> +                       verbose(env, "get_local_storage() doesn't support non-zero flags\n");
>                         return -EINVAL;
> -       }
> -
> -       if (func_id == BPF_FUNC_timer_set_callback) {
> +               }
> +               err = 0;

err is guaranteed to be zero, no need to re-assign it

> +               break;
> +       case BPF_FUNC_for_each_map_elem:
>                 err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
> -                                       set_timer_callback_state);
> -               if (err < 0)
> -                       return -EINVAL;
> -       }
> -
> -       if (func_id == BPF_FUNC_find_vma) {
> +                                       set_map_elem_callback_state) ? -EINVAL : 0;

I think it's actually good to propagate the original error code, so
let's not do `? -EINVAL : 0` logic

> +               break;
> +       case BPF_FUNC_timer_set_callback:
>                 err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
> -                                       set_find_vma_callback_state);
> -               if (err < 0)
> -                       return -EINVAL;
> -       }
> -
> -       if (func_id == BPF_FUNC_snprintf) {
> +                                       set_timer_callback_state) ? -EINVAL : 0;
> +               break;
> +       case BPF_FUNC_find_vma:
> +               err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
> +                                       set_find_vma_callback_state) ? -EINVAL : 0;
> +               break;
> +       case BPF_FUNC_snprintf:
>                 err = check_bpf_snprintf_call(env, regs);
> -               if (err < 0)
> -                       return err;
> +               break;
> +       case BPF_FUNC_loop:
> +               err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
> +                                       set_loop_callback_state) ? -EINVAL : 0;
> +               break;
> +       default:
> +               err = 0;

same, err is already zero if no error so far was found

>         }
>
> +       if (err)
> +               return err;
> +

[...]



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux