Re: [RFC bpf-next] bpf: Create shadow variables for struct_ops in skeletons.

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

 



On Wed, Jan 24, 2024 at 2:41 PM <thinker.li@xxxxxxxxx> wrote:
>
> From: Kui-Feng Lee <thinker.li@xxxxxxxxx>
>
> Create shadow variables for the fields of struct_ops maps in a skeleton
> with the same name as the respective fields. For instance, if struct
> bpf_testmod_ops has a "data" field as following, you can access or modify
> its value through a shadow variable also named "data" in the skeleton.
>
>   SEC(".struct_ops.link")
>   struct bpf_testmod_ops testmod_1 = {
>       .data = 0x1,
>   };
>
> Then, you can change the value in the following manner as shown in the code
> below.
>
>   skel->st_ops_vars.testmod_1.data = 13;
>
> It is helpful to configure a struct_ops map during the execution of a
> program. For instance, you can include a flag in a struct_ops type to
> modify the way the kernel handles an instance. By implementing this
> feature, a user space program can alter the flag's value prior to loading
> an instance into the kernel.
>
> The code generator for skeletons will produce code that copies values to
> shadow variables from the internal data buffer when a skeleton is
> opened. It will also copy values from shadow variables back to the internal
> data buffer before a skeleton is loaded into the kernel.
>
> The code generator will calculate the offset of a field and generate code
> that copies the value of the field from or to the shadow variable to or
> from the struct_ops internal data, with an offset relative to the beginning
> of the internal data. For instance, if the "data" field in struct
> bpf_testmod_ops is situated 16 bytes from the beginning of the struct, the
> address for the "data" field of struct bpf_testmod_ops will be the starting
> address plus 16.
>
> The offset is calculated during code generation, so it is always in line
> with the internal data in the skeleton. Even if the user space programs and
> the BPF program were not compiled together, any differences in versions
> would not impact correctness. This means that even if the BPF program and
> the user space program reference different versions of the "struct
> bpf_testmod_ops" and have different offsets for "data", these offsets
> computed by the code generator would still function correctly.
>
> The user space programs can only modify values by using these shadow
> variables when the skeleton is open, but before being loaded. Once the
> skeleton is loaded, the value is copied to the kernel, and any future
> changes only affect the shadow variables in the user space memory and do
> not update the kernel space. The shadow variables are not initialized
> before opening a skeleton, so you cannot update values through them before
> opening.
>
> For function pointers (operators), you can change/replace their values with
> other BPF programs. For example, the test case in test_struct_ops_module.c
> points .test_2 to test_3() while it was pointed to test_2() by assigning a
> new value to the shadow variable.
>
>   skel->st_ops_vars.testmod_1.test_2 = skel->progs.test_3;
>
> However, you need to turn off autoload for test_2() since it is not
> assigned to any struct_ops map anymore. Or, it will fails to load test_2().
>
>  bpf_program__set_autoload(skel->progs.test_2, false);
>
> You can define more struct_ops programs than necessary. However, you need
> to turn autoload off for unused ones.

Overall I like the idea, it seems like a pretty natural and powerful
interface. Few things I'd do a bit differently:

- less code gen in the skeleton. It feels like it's better to teach
libbpf to allow getting/setting intial struct_ops map "image" using
standard bpf_map__initial_value() and bpf_map__set_initial_value().
That fits with other global data maps.

- I think all struct ops maps should be in skel->struct_ops.<name>,
not struct_ops_vars. I'd probably also combine struct_ops.link and
no-link struct ops in one section for simplicity

- getting underlying struct_ops BTF type should be possible to do
through bpf_map__btf_value_type_id(), no need for struct_ops-specific
one

- you have a bunch of specific logic to dump INT/ENUM/PTR, I wonder if
we can just reuse libbpf's BTF dumping API to dump everything? Though
for prog pointers we'd want to rewrite them to `struct bpf_program *`
field, not sure yet how hard it is.

The above is brief, so please ask questions where it's not clear what
I propose, thanks!

[...]





[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