libbpf: Refactor BTF map definition parsing
authorAndrii Nakryiko <andrii@kernel.org>
Fri, 23 Apr 2021 18:13:36 +0000 (11:13 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 23 Apr 2021 21:05:26 +0000 (14:05 -0700)
Refactor BTF-defined maps parsing logic to allow it to be nicely reused by BPF
static linker. Further, at least for BPF static linker, it's important to know
which attributes of a BPF map were defined explicitly, so provide a bit set
for each known portion of BTF map definition. This allows BPF static linker to
do a simple check when dealing with extern map declarations.

The same capabilities allow to distinguish attributes explicitly set to zero
(e.g., __uint(max_entries, 0)) vs the case of not specifying it at all (no
max_entries attribute at all). Libbpf is currently not utilizing that, but it
could be useful for backwards compatibility reasons later.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20210423181348.1801389-7-andrii@kernel.org
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf_internal.h

index 198d6e1..4f8335f 100644 (file)
@@ -2025,255 +2025,262 @@ static int build_map_pin_path(struct bpf_map *map, const char *path)
        return bpf_map__set_pin_path(map, buf);
 }
 
-
-static int parse_btf_map_def(struct bpf_object *obj,
-                            struct bpf_map *map,
-                            const struct btf_type *def,
-                            bool strict, bool is_inner,
-                            const char *pin_root_path)
+int parse_btf_map_def(const char *map_name, struct btf *btf,
+                     const struct btf_type *def_t, bool strict,
+                     struct btf_map_def *map_def, struct btf_map_def *inner_def)
 {
        const struct btf_type *t;
        const struct btf_member *m;
+       bool is_inner = inner_def == NULL;
        int vlen, i;
 
-       vlen = btf_vlen(def);
-       m = btf_members(def);
+       vlen = btf_vlen(def_t);
+       m = btf_members(def_t);
        for (i = 0; i < vlen; i++, m++) {
-               const char *name = btf__name_by_offset(obj->btf, m->name_off);
+               const char *name = btf__name_by_offset(btf, m->name_off);
 
                if (!name) {
-                       pr_warn("map '%s': invalid field #%d.\n", map->name, i);
+                       pr_warn("map '%s': invalid field #%d.\n", map_name, i);
                        return -EINVAL;
                }
                if (strcmp(name, "type") == 0) {
-                       if (!get_map_field_int(map->name, obj->btf, m,
-                                              &map->def.type))
+                       if (!get_map_field_int(map_name, btf, m, &map_def->map_type))
                                return -EINVAL;
-                       pr_debug("map '%s': found type = %u.\n",
-                                map->name, map->def.type);
+                       map_def->parts |= MAP_DEF_MAP_TYPE;
                } else if (strcmp(name, "max_entries") == 0) {
-                       if (!get_map_field_int(map->name, obj->btf, m,
-                                              &map->def.max_entries))
+                       if (!get_map_field_int(map_name, btf, m, &map_def->max_entries))
                                return -EINVAL;
-                       pr_debug("map '%s': found max_entries = %u.\n",
-                                map->name, map->def.max_entries);
+                       map_def->parts |= MAP_DEF_MAX_ENTRIES;
                } else if (strcmp(name, "map_flags") == 0) {
-                       if (!get_map_field_int(map->name, obj->btf, m,
-                                              &map->def.map_flags))
+                       if (!get_map_field_int(map_name, btf, m, &map_def->map_flags))
                                return -EINVAL;
-                       pr_debug("map '%s': found map_flags = %u.\n",
-                                map->name, map->def.map_flags);
+                       map_def->parts |= MAP_DEF_MAP_FLAGS;
                } else if (strcmp(name, "numa_node") == 0) {
-                       if (!get_map_field_int(map->name, obj->btf, m, &map->numa_node))
+                       if (!get_map_field_int(map_name, btf, m, &map_def->numa_node))
                                return -EINVAL;
-                       pr_debug("map '%s': found numa_node = %u.\n", map->name, map->numa_node);
+                       map_def->parts |= MAP_DEF_NUMA_NODE;
                } else if (strcmp(name, "key_size") == 0) {
                        __u32 sz;
 
-                       if (!get_map_field_int(map->name, obj->btf, m, &sz))
+                       if (!get_map_field_int(map_name, btf, m, &sz))
                                return -EINVAL;
-                       pr_debug("map '%s': found key_size = %u.\n",
-                                map->name, sz);
-                       if (map->def.key_size && map->def.key_size != sz) {
+                       if (map_def->key_size && map_def->key_size != sz) {
                                pr_warn("map '%s': conflicting key size %u != %u.\n",
-                                       map->name, map->def.key_size, sz);
+                                       map_name, map_def->key_size, sz);
                                return -EINVAL;
                        }
-                       map->def.key_size = sz;
+                       map_def->key_size = sz;
+                       map_def->parts |= MAP_DEF_KEY_SIZE;
                } else if (strcmp(name, "key") == 0) {
                        __s64 sz;
 
-                       t = btf__type_by_id(obj->btf, m->type);
+                       t = btf__type_by_id(btf, m->type);
                        if (!t) {
                                pr_warn("map '%s': key type [%d] not found.\n",
-                                       map->name, m->type);
+                                       map_name, m->type);
                                return -EINVAL;
                        }
                        if (!btf_is_ptr(t)) {
                                pr_warn("map '%s': key spec is not PTR: %s.\n",
-                                       map->name, btf_kind_str(t));
+                                       map_name, btf_kind_str(t));
                                return -EINVAL;
                        }
-                       sz = btf__resolve_size(obj->btf, t->type);
+                       sz = btf__resolve_size(btf, t->type);
                        if (sz < 0) {
                                pr_warn("map '%s': can't determine key size for type [%u]: %zd.\n",
-                                       map->name, t->type, (ssize_t)sz);
+                                       map_name, t->type, (ssize_t)sz);
                                return sz;
                        }
-                       pr_debug("map '%s': found key [%u], sz = %zd.\n",
-                                map->name, t->type, (ssize_t)sz);
-                       if (map->def.key_size && map->def.key_size != sz) {
+                       if (map_def->key_size && map_def->key_size != sz) {
                                pr_warn("map '%s': conflicting key size %u != %zd.\n",
-                                       map->name, map->def.key_size, (ssize_t)sz);
+                                       map_name, map_def->key_size, (ssize_t)sz);
                                return -EINVAL;
                        }
-                       map->def.key_size = sz;
-                       map->btf_key_type_id = t->type;
+                       map_def->key_size = sz;
+                       map_def->key_type_id = t->type;
+                       map_def->parts |= MAP_DEF_KEY_SIZE | MAP_DEF_KEY_TYPE;
                } else if (strcmp(name, "value_size") == 0) {
                        __u32 sz;
 
-                       if (!get_map_field_int(map->name, obj->btf, m, &sz))
+                       if (!get_map_field_int(map_name, btf, m, &sz))
                                return -EINVAL;
-                       pr_debug("map '%s': found value_size = %u.\n",
-                                map->name, sz);
-                       if (map->def.value_size && map->def.value_size != sz) {
+                       if (map_def->value_size && map_def->value_size != sz) {
                                pr_warn("map '%s': conflicting value size %u != %u.\n",
-                                       map->name, map->def.value_size, sz);
+                                       map_name, map_def->value_size, sz);
                                return -EINVAL;
                        }
-                       map->def.value_size = sz;
+                       map_def->value_size = sz;
+                       map_def->parts |= MAP_DEF_VALUE_SIZE;
                } else if (strcmp(name, "value") == 0) {
                        __s64 sz;
 
-                       t = btf__type_by_id(obj->btf, m->type);
+                       t = btf__type_by_id(btf, m->type);
                        if (!t) {
                                pr_warn("map '%s': value type [%d] not found.\n",
-                                       map->name, m->type);
+                                       map_name, m->type);
                                return -EINVAL;
                        }
                        if (!btf_is_ptr(t)) {
                                pr_warn("map '%s': value spec is not PTR: %s.\n",
-                                       map->name, btf_kind_str(t));
+                                       map_name, btf_kind_str(t));
                                return -EINVAL;
                        }
-                       sz = btf__resolve_size(obj->btf, t->type);
+                       sz = btf__resolve_size(btf, t->type);
                        if (sz < 0) {
                                pr_warn("map '%s': can't determine value size for type [%u]: %zd.\n",
-                                       map->name, t->type, (ssize_t)sz);
+                                       map_name, t->type, (ssize_t)sz);
                                return sz;
                        }
-                       pr_debug("map '%s': found value [%u], sz = %zd.\n",
-                                map->name, t->type, (ssize_t)sz);
-                       if (map->def.value_size && map->def.value_size != sz) {
+                       if (map_def->value_size && map_def->value_size != sz) {
                                pr_warn("map '%s': conflicting value size %u != %zd.\n",
-                                       map->name, map->def.value_size, (ssize_t)sz);
+                                       map_name, map_def->value_size, (ssize_t)sz);
                                return -EINVAL;
                        }
-                       map->def.value_size = sz;
-                       map->btf_value_type_id = t->type;
+                       map_def->value_size = sz;
+                       map_def->value_type_id = t->type;
+                       map_def->parts |= MAP_DEF_VALUE_SIZE | MAP_DEF_VALUE_TYPE;
                }
                else if (strcmp(name, "values") == 0) {
+                       char inner_map_name[128];
                        int err;
 
                        if (is_inner) {
                                pr_warn("map '%s': multi-level inner maps not supported.\n",
-                                       map->name);
+                                       map_name);
                                return -ENOTSUP;
                        }
                        if (i != vlen - 1) {
                                pr_warn("map '%s': '%s' member should be last.\n",
-                                       map->name, name);
+                                       map_name, name);
                                return -EINVAL;
                        }
-                       if (!bpf_map_type__is_map_in_map(map->def.type)) {
+                       if (!bpf_map_type__is_map_in_map(map_def->map_type)) {
                                pr_warn("map '%s': should be map-in-map.\n",
-                                       map->name);
+                                       map_name);
                                return -ENOTSUP;
                        }
-                       if (map->def.value_size && map->def.value_size != 4) {
+                       if (map_def->value_size && map_def->value_size != 4) {
                                pr_warn("map '%s': conflicting value size %u != 4.\n",
-                                       map->name, map->def.value_size);
+                                       map_name, map_def->value_size);
                                return -EINVAL;
                        }
-                       map->def.value_size = 4;
-                       t = btf__type_by_id(obj->btf, m->type);
+                       map_def->value_size = 4;
+                       t = btf__type_by_id(btf, m->type);
                        if (!t) {
                                pr_warn("map '%s': map-in-map inner type [%d] not found.\n",
-                                       map->name, m->type);
+                                       map_name, m->type);
                                return -EINVAL;
                        }
                        if (!btf_is_array(t) || btf_array(t)->nelems) {
                                pr_warn("map '%s': map-in-map inner spec is not a zero-sized array.\n",
-                                       map->name);
+                                       map_name);
                                return -EINVAL;
                        }
-                       t = skip_mods_and_typedefs(obj->btf, btf_array(t)->type,
-                                                  NULL);
+                       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));
+                                       map_name, btf_kind_str(t));
                                return -EINVAL;
                        }
-                       t = skip_mods_and_typedefs(obj->btf, t->type, NULL);
+                       t = skip_mods_and_typedefs(btf, t->type, NULL);
                        if (!btf_is_struct(t)) {
                                pr_warn("map '%s': map-in-map inner def is of unexpected kind %s.\n",
-                                       map->name, btf_kind_str(t));
+                                       map_name, btf_kind_str(t));
                                return -EINVAL;
                        }
 
-                       map->inner_map = calloc(1, sizeof(*map->inner_map));
-                       if (!map->inner_map)
-                               return -ENOMEM;
-                       map->inner_map->fd = -1;
-                       map->inner_map->sec_idx = obj->efile.btf_maps_shndx;
-                       map->inner_map->name = malloc(strlen(map->name) +
-                                                     sizeof(".inner") + 1);
-                       if (!map->inner_map->name)
-                               return -ENOMEM;
-                       sprintf(map->inner_map->name, "%s.inner", map->name);
-
-                       err = parse_btf_map_def(obj, map->inner_map, t, strict,
-                                               true /* is_inner */, NULL);
+                       snprintf(inner_map_name, sizeof(inner_map_name), "%s.inner", map_name);
+                       err = parse_btf_map_def(inner_map_name, btf, t, strict, inner_def, NULL);
                        if (err)
                                return err;
+
+                       map_def->parts |= MAP_DEF_INNER_MAP;
                } else if (strcmp(name, "pinning") == 0) {
                        __u32 val;
-                       int err;
 
                        if (is_inner) {
-                               pr_debug("map '%s': inner def can't be pinned.\n",
-                                        map->name);
+                               pr_warn("map '%s': inner def can't be pinned.\n", map_name);
                                return -EINVAL;
                        }
-                       if (!get_map_field_int(map->name, obj->btf, m, &val))
+                       if (!get_map_field_int(map_name, btf, m, &val))
                                return -EINVAL;
-                       pr_debug("map '%s': found pinning = %u.\n",
-                                map->name, val);
-
-                       if (val != LIBBPF_PIN_NONE &&
-                           val != LIBBPF_PIN_BY_NAME) {
+                       if (val != LIBBPF_PIN_NONE && val != LIBBPF_PIN_BY_NAME) {
                                pr_warn("map '%s': invalid pinning value %u.\n",
-                                       map->name, val);
+                                       map_name, val);
                                return -EINVAL;
                        }
-                       if (val == LIBBPF_PIN_BY_NAME) {
-                               err = build_map_pin_path(map, pin_root_path);
-                               if (err) {
-                                       pr_warn("map '%s': couldn't build pin path.\n",
-                                               map->name);
-                                       return err;
-                               }
-                       }
+                       map_def->pinning = val;
+                       map_def->parts |= MAP_DEF_PINNING;
                } else {
                        if (strict) {
-                               pr_warn("map '%s': unknown field '%s'.\n",
-                                       map->name, name);
+                               pr_warn("map '%s': unknown field '%s'.\n", map_name, name);
                                return -ENOTSUP;
                        }
-                       pr_debug("map '%s': ignoring unknown field '%s'.\n",
-                                map->name, name);
+                       pr_debug("map '%s': ignoring unknown field '%s'.\n", map_name, name);
                }
        }
 
-       if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
-               pr_warn("map '%s': map type isn't specified.\n", map->name);
+       if (map_def->map_type == BPF_MAP_TYPE_UNSPEC) {
+               pr_warn("map '%s': map type isn't specified.\n", map_name);
                return -EINVAL;
        }
 
        return 0;
 }
 
+static void fill_map_from_def(struct bpf_map *map, const struct btf_map_def *def)
+{
+       map->def.type = def->map_type;
+       map->def.key_size = def->key_size;
+       map->def.value_size = def->value_size;
+       map->def.max_entries = def->max_entries;
+       map->def.map_flags = def->map_flags;
+
+       map->numa_node = def->numa_node;
+       map->btf_key_type_id = def->key_type_id;
+       map->btf_value_type_id = def->value_type_id;
+
+       if (def->parts & MAP_DEF_MAP_TYPE)
+               pr_debug("map '%s': found type = %u.\n", map->name, def->map_type);
+
+       if (def->parts & MAP_DEF_KEY_TYPE)
+               pr_debug("map '%s': found key [%u], sz = %u.\n",
+                        map->name, def->key_type_id, def->key_size);
+       else if (def->parts & MAP_DEF_KEY_SIZE)
+               pr_debug("map '%s': found key_size = %u.\n", map->name, def->key_size);
+
+       if (def->parts & MAP_DEF_VALUE_TYPE)
+               pr_debug("map '%s': found value [%u], sz = %u.\n",
+                        map->name, def->value_type_id, def->value_size);
+       else if (def->parts & MAP_DEF_VALUE_SIZE)
+               pr_debug("map '%s': found value_size = %u.\n", map->name, def->value_size);
+
+       if (def->parts & MAP_DEF_MAX_ENTRIES)
+               pr_debug("map '%s': found max_entries = %u.\n", map->name, def->max_entries);
+       if (def->parts & MAP_DEF_MAP_FLAGS)
+               pr_debug("map '%s': found map_flags = %u.\n", map->name, def->map_flags);
+       if (def->parts & MAP_DEF_PINNING)
+               pr_debug("map '%s': found pinning = %u.\n", map->name, def->pinning);
+       if (def->parts & MAP_DEF_NUMA_NODE)
+               pr_debug("map '%s': found numa_node = %u.\n", map->name, def->numa_node);
+
+       if (def->parts & MAP_DEF_INNER_MAP)
+               pr_debug("map '%s': found inner map definition.\n", map->name);
+}
+
 static int bpf_object__init_user_btf_map(struct bpf_object *obj,
                                         const struct btf_type *sec,
                                         int var_idx, int sec_idx,
                                         const Elf_Data *data, bool strict,
                                         const char *pin_root_path)
 {
+       struct btf_map_def map_def = {}, inner_def = {};
        const struct btf_type *var, *def;
        const struct btf_var_secinfo *vi;
        const struct btf_var *var_extra;
        const char *map_name;
        struct bpf_map *map;
+       int err;
 
        vi = btf_var_secinfos(sec) + var_idx;
        var = btf__type_by_id(obj->btf, vi->type);
@@ -2327,7 +2334,35 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
        pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
                 map_name, map->sec_idx, map->sec_offset);
 
-       return parse_btf_map_def(obj, map, def, strict, false, pin_root_path);
+       err = parse_btf_map_def(map->name, obj->btf, def, strict, &map_def, &inner_def);
+       if (err)
+               return err;
+
+       fill_map_from_def(map, &map_def);
+
+       if (map_def.pinning == LIBBPF_PIN_BY_NAME) {
+               err = build_map_pin_path(map, pin_root_path);
+               if (err) {
+                       pr_warn("map '%s': couldn't build pin path.\n", map->name);
+                       return err;
+               }
+       }
+
+       if (map_def.parts & MAP_DEF_INNER_MAP) {
+               map->inner_map = calloc(1, sizeof(*map->inner_map));
+               if (!map->inner_map)
+                       return -ENOMEM;
+               map->inner_map->fd = -1;
+               map->inner_map->sec_idx = sec_idx;
+               map->inner_map->name = malloc(strlen(map_name) + sizeof(".inner") + 1);
+               if (!map->inner_map->name)
+                       return -ENOMEM;
+               sprintf(map->inner_map->name, "%s.inner", map_name);
+
+               fill_map_from_def(map->inner_map, &inner_def);
+       }
+
+       return 0;
 }
 
 static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
index 92b7eae..1788307 100644 (file)
@@ -138,6 +138,38 @@ static inline __u32 btf_type_info(int kind, int vlen, int kflag)
        return (kflag << 31) | (kind << 24) | vlen;
 }
 
+enum map_def_parts {
+       MAP_DEF_MAP_TYPE        = 0x001,
+       MAP_DEF_KEY_TYPE        = 0x002,
+       MAP_DEF_KEY_SIZE        = 0x004,
+       MAP_DEF_VALUE_TYPE      = 0x008,
+       MAP_DEF_VALUE_SIZE      = 0x010,
+       MAP_DEF_MAX_ENTRIES     = 0x020,
+       MAP_DEF_MAP_FLAGS       = 0x040,
+       MAP_DEF_NUMA_NODE       = 0x080,
+       MAP_DEF_PINNING         = 0x100,
+       MAP_DEF_INNER_MAP       = 0x200,
+
+       MAP_DEF_ALL             = 0x3ff, /* combination of all above */
+};
+
+struct btf_map_def {
+       enum map_def_parts parts;
+       __u32 map_type;
+       __u32 key_type_id;
+       __u32 key_size;
+       __u32 value_type_id;
+       __u32 value_size;
+       __u32 max_entries;
+       __u32 map_flags;
+       __u32 numa_node;
+       __u32 pinning;
+};
+
+int parse_btf_map_def(const char *map_name, struct btf *btf,
+                     const struct btf_type *def_t, bool strict,
+                     struct btf_map_def *map_def, struct btf_map_def *inner_def);
+
 void *libbpf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz,
                     size_t cur_cnt, size_t max_cnt, size_t add_cnt);
 int libbpf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt);