• Andrii Nakryiko's avatar
    libbpf: Add BTF-defined map-in-map support · 646f02ff
    Andrii Nakryiko authored
    As discussed at LPC 2019 ([0]), this patch brings (a quite belated) support
    for declarative BTF-defined map-in-map support in libbpf. It allows to define
    ARRAY_OF_MAPS and HASH_OF_MAPS BPF maps without any user-space initialization
    code involved.
    
    Additionally, it allows to initialize outer map's slots with references to
    respective inner maps at load time, also completely declaratively.
    
    Despite a weak type system of C, the way BTF-defined map-in-map definition
    works, it's actually quite hard to accidentally initialize outer map with
    incompatible inner maps. This being C, of course, it's still possible, but
    even that would be caught at load time and error returned with helpful debug
    log pointing exactly to the slot that failed to be initialized.
    
    As an example, here's a rather advanced HASH_OF_MAPS declaration and
    initialization example, filling slots #0 and #4 with two inner maps:
    
      #include <bpf/bpf_helpers.h>
    
      struct inner_map {
              __uint(type, BPF_MAP_TYPE_ARRAY);
              __uint(max_entries, 1);
              __type(key, int);
              __type(value, int);
      } inner_map1 SEC(".maps"),
        inner_map2 SEC(".maps");
    
      struct outer_hash {
              __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
              __uint(max_entries, 5);
              __uint(key_size, sizeof(int));
              __array(values, struct inner_map);
      } outer_hash SEC(".maps") = {
              .values = {
                      [0] = &inner_map2,
                      [4] = &inner_map1,
              },
      };
    
    Here's the relevant part of libbpf debug log showing pretty clearly of what's
    going on with map-in-map initialization:
    
      libbpf: .maps relo #0: for 6 value 0 rel.r_offset 96 name 260 ('inner_map1')
      libbpf: .maps relo #0: map 'outer_arr' slot [0] points to map 'inner_map1'
      libbpf: .maps relo #1: for 7 value 32 rel.r_offset 112 name 249 ('inner_map2')
      libbpf: .maps relo #1: map 'outer_arr' slot [2] points to map 'inner_map2'
      libbpf: .maps relo #2: for 7 value 32 rel.r_offset 144 name 249 ('inner_map2')
      libbpf: .maps relo #2: map 'outer_hash' slot [0] points to map 'inner_map2'
      libbpf: .maps relo #3: for 6 value 0 rel.r_offset 176 name 260 ('inner_map1')
      libbpf: .maps relo #3: map 'outer_hash' slot [4] points to map 'inner_map1'
      libbpf: map 'inner_map1': created successfully, fd=4
      libbpf: map 'inner_map2': created successfully, fd=5
      libbpf: map 'outer_hash': created successfully, fd=7
      libbpf: map 'outer_hash': slot [0] set to map 'inner_map2' fd=5
      libbpf: map 'outer_hash': slot [4] set to map 'inner_map1' fd=4
    
    Notice from the log above that fd=6 (not logged explicitly) is used for inner
    "prototype" map, necessary for creation of outer map. It is destroyed
    immediately after outer map is created.
    
    See also included selftest with some extra comments explaining extra details
    of usage. Additionally, similar initialization syntax and libbpf functionality
    can be used to do initialization of BPF_PROG_ARRAY with references to BPF
    sub-programs. This can be done in follow up patches, if there will be a demand
    for this.
    
      [0] https://linuxplumbersconf.org/event/4/contributions/448/Signed-off-by: default avatarAndrii Nakryiko <andriin@fb.com>
    Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    Acked-by: default avatarToke Høiland-Jørgensen <toke@redhat.com>
    Link: https://lore.kernel.org/bpf/20200429002739.48006-4-andriin@fb.com
    646f02ff
btf_map_in_map.c 1.63 KB