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]

 



Hi, Andrii

On 11/23/21 11:28 AM, Andrii Nakryiko wrote:
> 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?
> 

Ack.

>> +                                               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:
>
According to the original code ([0]), the "values" field is intended to be

the last field ?

> 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?
> 

Right, will do.

>>                                 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?
> 

Ack.

>> +
>>         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
> 

OK, will remove 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)
> 

Will do.

>> +                       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).
> 
> 

OK, will do, maybe we should also add some tests for these cases ?

>> +                       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
> 
> 

Ack.

>>         }
>>
>>         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

  [0]: https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L2288-L2292

Cheers,
---
Hengqi



[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