Re: [QUESTION] Check bpf_loop support on kernels < 5.13

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

 



Thank you for the detailed explanation!
One alternative could be to use two different eBPF programs, one with
bpf_loop and the other with a simple loop, but I was looking for
possible alternatives. I tried using a const variable.
eBPF side:
```
volatile const int has_bpf_loop = 0;

static int loop_cb(int i, void *ctx) {
  return 0;
}

SEC("raw_tp")
int test_prog(void *ctx) {
  if (has_bpf_loop == 1) {
    bpf_loop(10, loop_cb, NULL, 0);
  } else {
    for (int i = 0; i < 10; ++i)
      loop_cb(i, NULL);
  }
  return 0;
}
```

Userspace side:
```
...
  if (libbpf_probe_bpf_helper(BPF_PROG_TYPE_RAW_TRACEPOINT,
BPF_FUNC_loop, NULL) == 1) {
    skel->rodata->has_bpf_loop = 1;
  } else {
    skel->rodata->has_bpf_loop = 0;
  }
...
```

But I ended up with the same error as before
```
libbpf: prog 'test_prog': BPF program load failed: Invalid argument
libbpf: prog 'test_prog': -- BEGIN PROG LOAD LOG --
number of funcs in func_info doesn't match number of subprogs
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0
peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'test_prog': failed to load: -22
```

I will try to dig a little bit more into it but I'm not sure we can
avoid using 2 different ebpf programs. Even if we were able to
overcome this error I suspect that the verifier will reject the
program when we try to load into r2 the pointer to `loop_cp` as you
correctly highlighted.

On Fri, 3 Jan 2025 at 21:31, Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
>
> On Fri, 2025-01-03 at 12:03 +0100, andrea terzolo wrote:
> > Hi folks! I would like to check with you if the verifier failure I'm
> > facing is expected. The verifier rejects the following eBPF program on
> > kernel 5.10.232.
> >
> > ```
> > static long loop_fn(uint32_t index, void *ctx) {
> >   bpf_printk("handle_exit\n");
> >   return 0;
> > }
> >
> > SEC("tp/raw_syscalls/sys_enter")
> > int test(void *ctx) {
> >   if (bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_loop)) {
> >     bpf_printk("loop\n");
> >     bpf_loop(12, loop_fn, NULL, 0);
> >   } else {
> >     bpf_printk("skip loop\n");
> >   }
> >   return 0;
> > }
> > ```
> >
> > With this error:
> >
> > ```
> > libbpf: prog 'test': BPF program load failed: Invalid argument
> > libbpf: prog 'test': -- BEGIN PROG LOAD LOG --
> > number of funcs in func_info doesn't match number of subprogs
> > processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0
> > peak_states 0 mark_read 0
> > -- END PROG LOAD LOG --
> > libbpf: prog 'test': failed to load: -22
> > ```
> >
> > This sounds like a valid use case. I would like to use bpf_loop if
> > supported by the running kernel otherwise I can fall back to a simple
> > loop. This issue goes away on kernel 5.13 with the introduction of
> > PTR_TO_FUNC [0]. Is there a way I can use CO-RE features to avoid this
> > issue? I would expect the verifier to prune the dead code inside the
> > `if` but the error seems to be triggered before the control flow
> > analysis.
> >
> > [0]: https://github.com/torvalds/linux/commit/69c087ba6225b574afb6e505b72cb75242a3d844
>
> bpf_loop was introduced by commit [1] and released as a part of 5.17.
>
> The error you see is indeed caused by the lack of PTR_TO_FUNC register
> type in an old kernel. In your program the call to bpf_loop would look
> like below in the assembly:
>
>   ...
>   r2 = loop_fn  ;; here function pointer is taken
>   ...
>   call bpf_loop
>
> Before main verification pass verifier.c:add_subprog_and_kfunc()
> discovers subprogram entries by looking at function calls and function
> pointer assignments and compares it to function information provided
> via bpf_attr->func_info. The kernel that does not know about
> PTR_TO_FUNC would not find the loop_fn entry, hence the error message
> about mismatch.
>
> Additionally, verifier.c:check_cfg() looks for parts of the program
> that can't be reached by jump and call instructions. For this purpose
> pointers to functions are treated as function calls. The kernel that
> does not know about PTR_TO_FUNC it would seem that loop_fn is unreachable,
> this would cause another error message.
>
> Even if you add a dummy call to loop_fn verifier would most likely
> reject the program at 'r2 = loop_fn'.
>
> The approach libbpf uses to detect running kernel features is based on
> programs accept/reject status [2]. E.g. your program could be simplified to:
>
>   static int loop_fn(int i, void *c) { return 0; }
>
>   SEC("tp/raw_syscalls/sys_enter")
>   int test(void *ctx) {
>     bpf_loop(1, loop_fn, NULL, 0);
>     return 0;
>   }
>
> And checked if load is successful.
>
> [1] e6f2dd0f8067 ("bpf: Add bpf_loop helper")
> [2] see <kernel>/tools/lib/bpf/features.c
>




[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