Re: [PATCH bpf-next 1/2] libbpf: Support static initialization of BPF_MAP_TYPE_PROG_ARRAY

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

 



On Sun, Nov 21, 2021 at 5:55 AM Hengqi Chen <hengqi.chen@xxxxxxxxx> wrote:
>
> Support static initialization of BPF_MAP_TYPE_PROG_ARRAY with a
> syntax similar to map-in-map initialization ([0]):
>
>     SEC("socket")
>     int tailcall_1(void *ctx)
>     {
>         return 0;
>     }
>
>     struct {
>         __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
>         __uint(max_entries, 2);
>         __uint(key_size, sizeof(__u32));
>         __array(values, int (void *));
>     } prog_array_init SEC(".maps") = {
>         .values = {
>             [1] = (void *)&tailcall_1,
>         },
>     };
>
> Here's the relevant part of libbpf debug log showing what's
> going on with prog-array initialization:
>
> libbpf: sec '.relsocket': collecting relocation for section(3) 'socket'
> libbpf: sec '.relsocket': relo #0: insn #2 against 'prog_array_init'
> libbpf: prog 'entry': found map 0 (prog_array_init, sec 4, off 0) for insn #0
> libbpf: .maps relo #0: for 3 value 0 rel->r_offset 32 name 53 ('tailcall_1')
> libbpf: .maps relo #0: map 'prog_array_init' slot [1] points to prog 'tailcall_1'
> libbpf: map 'prog_array_init': created successfully, fd=5
> libbpf: map 'prog_array_init': slot [1] set to prog 'tailcall_1' fd=6
>
>   [0] Closes: https://github.com/libbpf/libbpf/issues/354
>
> Signed-off-by: Hengqi Chen <hengqi.chen@xxxxxxxxx>
> ---
>  tools/lib/bpf/libbpf.c | 146 ++++++++++++++++++++++++++++++++++-------
>  1 file changed, 122 insertions(+), 24 deletions(-)
>

Just a few nits and suggestions below, but it looks great overall, thanks!

[...]

>                         t = skip_mods_and_typedefs(btf, btf_array(t)->type, NULL);
>                         if (!btf_is_ptr(t)) {
> -                               pr_warn("map '%s': map-in-map inner def is of unexpected kind %s.\n",
> -                                       map_name, btf_kind_str(t));
> +                               if (is_map_in_map)
> +                                       pr_warn("map '%s': map-in-map inner def is of unexpected kind %s.\n",
> +                                               map_name, btf_kind_str(t));
> +                               else
> +                                       pr_warn("map '%s': prog-array value def is of unexpected kind %s.\n",

maybe let's do

const char *desc = is_map_in_map ? "map-in-map inner" : "prog-array value";

and use desc in those three pr_warn() messages?

> +                                               map_name, btf_kind_str(t));
>                                 return -EINVAL;
>                         }
>                         t = skip_mods_and_typedefs(btf, t->type, NULL);
> -                       if (!btf_is_struct(t)) {
> +                       if (is_prog_array) {
> +                               if (btf_is_func_proto(t))
> +                                       return 0;

you can't return on success here, there could technically be other
fields after "values". Can you please also invert the condition so
that error handling happens first and then we continue:

if (!btf_is_func_proto(t)) {
    pr_warn(..);
    return -EINVAl;
}
continue;

It's more consistent with the other logic in this function


> +                               pr_warn("map '%s': prog-array value def is of unexpected kind %s.\n",
> +                                               map_name, btf_kind_str(t));
> +                               return -EINVAL;
> +                       }
> +                       if (is_map_in_map && !btf_is_struct(t)) {

well, it can't be anything else, so I guess drop the is_map_in_map check?

>                                 pr_warn("map '%s': map-in-map inner def is of unexpected kind %s.\n",
>                                         map_name, btf_kind_str(t));
>                                 return -EINVAL;
> @@ -4964,12 +4985,16 @@ static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
>         unsigned int i;
>         int fd, err = 0;
>
> +       if (map->def.type == BPF_MAP_TYPE_PROG_ARRAY)
> +               return 0;

let's leave a comment that PROG_ARRAY can only be initialized once all
the programs are loaded, so this will be done later

Better still, it would be good to also rename init_map_slots to
init_map_in_map_slots and do the PROG_ARRAY check outside, in
bpf_object__create_maps(). This creates a nice symmetry with
init_prog_array (should it be named init_prog_array_slots for
consistency?). WDYT?

> +
>         for (i = 0; i < map->init_slots_sz; i++) {
>                 if (!map->init_slots[i])
>                         continue;
>
>                 targ_map = map->init_slots[i];
>                 fd = bpf_map__fd(targ_map);
> +
>                 if (obj->gen_loader) {
>                         pr_warn("// TODO map_update_elem: idx %td key %d value==map_idx %td\n",
>                                 map - obj->maps, i, targ_map - obj->maps);
> @@ -4980,8 +5005,7 @@ static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
>                 if (err) {
>                         err = -errno;
>                         pr_warn("map '%s': failed to initialize slot [%d] to map '%s' fd=%d: %d\n",
> -                               map->name, i, targ_map->name,
> -                               fd, err);
> +                               map->name, i, targ_map->name, fd, err);
>                         return err;
>                 }
>                 pr_debug("map '%s': slot [%d] set to map '%s' fd=%d\n",
> @@ -4994,6 +5018,60 @@ static int init_map_slots(struct bpf_object *obj, struct bpf_map *map)
>         return 0;
>  }
>
> +static int init_prog_array(struct bpf_object *obj, struct bpf_map *map)
> +{
> +       const struct bpf_program *targ_prog;
> +       unsigned int i;
> +       int fd, err = 0;

you always set err, no need to zero-initialize it

> +
> +       for (i = 0; i < map->init_slots_sz; i++) {
> +               if (!map->init_slots[i])
> +                       continue;
> +
> +               targ_prog = map->init_slots[i];
> +               fd = bpf_program__fd(targ_prog);
> +
> +               if (obj->gen_loader) {
> +                       return -ENOTSUP;
> +               } else {
> +                       err = bpf_map_update_elem(map->fd, &i, &fd, 0);
> +               }
> +               if (err) {
> +                       err = -errno;
> +                       pr_warn("map '%s': failed to initialize slot [%d] to prog '%s' fd=%d: %d\n",
> +                               map->name, i, targ_prog->name, fd, err);
> +                       return err;
> +               }
> +               pr_debug("map '%s': slot [%d] set to prog '%s' fd=%d\n",
> +                        map->name, i, targ_prog->name, fd);
> +       }
> +
> +       zfree(&map->init_slots);
> +       map->init_slots_sz = 0;
> +
> +       return 0;
> +}
> +
> +static int bpf_object_init_prog_array(struct bpf_object *obj)

s/prog_array/prog_arrays/

> +{
> +       struct bpf_map *map;
> +       int i, err;
> +
> +       for (i = 0; i < obj->nr_maps; i++) {
> +               map = &obj->maps[i];
> +
> +               if (map->def.type == BPF_MAP_TYPE_PROG_ARRAY &&
> +                   map->init_slots_sz) {

nit: longer single line is fine here (but you can also invert the
condition and continue early to avoid extra level of nestedness)

> +                       err = init_prog_array(obj, map);
> +                       if (err < 0) {
> +                               zclose(map->fd);
> +                               return err;
> +                       }
> +               }
> +       }
> +       return 0;
> +}
> +
>  static int
>  bpf_object__create_maps(struct bpf_object *obj)
>  {
> @@ -6174,7 +6252,9 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
>         int i, j, nrels, new_sz;
>         const struct btf_var_secinfo *vi = NULL;
>         const struct btf_type *sec, *var, *def;
> -       struct bpf_map *map = NULL, *targ_map;
> +       struct bpf_map *map = NULL, *targ_map = NULL;
> +       struct bpf_program *targ_prog = NULL;
> +       bool is_prog_array, is_map_in_map;
>         const struct btf_member *member;
>         const char *name, *mname;
>         unsigned int moff;
> @@ -6203,11 +6283,6 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
>                         return -LIBBPF_ERRNO__FORMAT;
>                 }
>                 name = elf_sym_str(obj, sym->st_name) ?: "<?>";
> -               if (sym->st_shndx != obj->efile.btf_maps_shndx) {
> -                       pr_warn(".maps relo #%d: '%s' isn't a BTF-defined map\n",
> -                               i, name);
> -                       return -LIBBPF_ERRNO__RELOC;
> -               }
>
>                 pr_debug(".maps relo #%d: for %zd value %zd rel->r_offset %zu name %d ('%s')\n",
>                          i, (ssize_t)(rel->r_info >> 32), (size_t)sym->st_value,
> @@ -6229,8 +6304,20 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
>                         return -EINVAL;
>                 }
>
> -               if (!bpf_map_type__is_map_in_map(map->def.type))
> +               is_map_in_map = bpf_map_type__is_map_in_map(map->def.type);
> +               is_prog_array = map->def.type == BPF_MAP_TYPE_PROG_ARRAY;
> +               if (!is_map_in_map && !is_prog_array)
>                         return -EINVAL;
> +               if (is_map_in_map && sym->st_shndx != obj->efile.btf_maps_shndx) {
> +                       pr_warn(".maps relo #%d: '%s' isn't a BTF-defined map\n",
> +                               i, name);
> +                       return -LIBBPF_ERRNO__RELOC;
> +               }
> +               if (is_prog_array && !bpf_object__find_program_by_name(obj, name)) {

let's do an additional check on the program you found with find_program_by_name:

  1. prog->sec_idx == sym->st_shndx
  2. prog->sec_insn_off * 8 == sym->st_value
  3. !prog_is_subprog(obj, prog)

This will make sure you have the correct entry-point BPF program (not
a subprog) and we point to its beginning (no offset into the program).


> +                       pr_warn(".maps relo #%d: '%s' isn't a BPF program\n",

"entry-point" is an important distinction, please mention that. You
can't put sub-programs into PROG_ARRAY.

> +                               i, name);
> +                       return -LIBBPF_ERRNO__RELOC;
> +               }
>                 if (map->def.type == BPF_MAP_TYPE_HASH_OF_MAPS &&
>                     map->def.key_size != sizeof(int)) {
>                         pr_warn(".maps relo #%d: hash-of-maps '%s' should have key size %zu.\n",
> @@ -6238,9 +6325,15 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
>                         return -EINVAL;
>                 }
>
> -               targ_map = bpf_object__find_map_by_name(obj, name);
> -               if (!targ_map)
> -                       return -ESRCH;
> +               if (is_map_in_map) {
> +                       targ_map = bpf_object__find_map_by_name(obj, name);
> +                       if (!targ_map)
> +                               return -ESRCH;
> +               } else {
> +                       targ_prog = bpf_object__find_program_by_name(obj, name);
> +                       if (!targ_prog)
> +                               return -ESRCH;
> +               }
>
>                 var = btf__type_by_id(obj->btf, vi->type);
>                 def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
> @@ -6272,10 +6365,14 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
>                                (new_sz - map->init_slots_sz) * host_ptr_sz);
>                         map->init_slots_sz = new_sz;
>                 }
> -               map->init_slots[moff] = targ_map;
> +               map->init_slots[moff] = is_map_in_map ? (void *)targ_map : (void *)targ_prog;
>
> -               pr_debug(".maps relo #%d: map '%s' slot [%d] points to map '%s'\n",
> -                        i, map->name, moff, name);
> +               if (is_map_in_map)
> +                       pr_debug(".maps relo #%d: map '%s' slot [%d] points to map '%s'\n",
> +                                i, map->name, moff, name);
> +               else
> +                       pr_debug(".maps relo #%d: map '%s' slot [%d] points to prog '%s'\n",
> +                                i, map->name, moff, name);

similar as above `is_map_in_map ? "map" : "prog"` will keep it short
and not duplicated


>         }
>
>         return 0;
> @@ -7293,6 +7390,7 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
>         err = err ? : bpf_object__create_maps(obj);
>         err = err ? : bpf_object__relocate(obj, obj->btf_custom_path ? : attr->target_btf_path);
>         err = err ? : bpf_object__load_progs(obj, attr->log_level);
> +       err = err ? : bpf_object_init_prog_array(obj);
>
>         if (obj->gen_loader) {
>                 /* reset FDs */
> --
> 2.30.2



[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