Commit 41017e56 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov

libbpf: Refactor BTF-defined map definition parsing logic

Factor out BTF map definition logic into stand-alone routine for easier reuse
for map-in-map case.
Signed-off-by: default avatarAndrii Nakryiko <andriin@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200429002739.48006-2-andriin@fb.com
parent 1f427a80
...@@ -1914,109 +1914,54 @@ static int build_map_pin_path(struct bpf_map *map, const char *path) ...@@ -1914,109 +1914,54 @@ static int build_map_pin_path(struct bpf_map *map, const char *path)
return 0; return 0;
} }
static int bpf_object__init_user_btf_map(struct bpf_object *obj,
const struct btf_type *sec, static int parse_btf_map_def(struct bpf_object *obj,
int var_idx, int sec_idx, struct bpf_map *map,
const Elf_Data *data, bool strict, const struct btf_type *def,
const char *pin_root_path) bool strict,
const char *pin_root_path)
{ {
const struct btf_type *var, *def, *t; const struct btf_type *t;
const struct btf_var_secinfo *vi;
const struct btf_var *var_extra;
const struct btf_member *m; const struct btf_member *m;
const char *map_name;
struct bpf_map *map;
int vlen, i; int vlen, i;
vi = btf_var_secinfos(sec) + var_idx;
var = btf__type_by_id(obj->btf, vi->type);
var_extra = btf_var(var);
map_name = btf__name_by_offset(obj->btf, var->name_off);
vlen = btf_vlen(var);
if (map_name == NULL || map_name[0] == '\0') {
pr_warn("map #%d: empty name.\n", var_idx);
return -EINVAL;
}
if ((__u64)vi->offset + vi->size > data->d_size) {
pr_warn("map '%s' BTF data is corrupted.\n", map_name);
return -EINVAL;
}
if (!btf_is_var(var)) {
pr_warn("map '%s': unexpected var kind %u.\n",
map_name, btf_kind(var));
return -EINVAL;
}
if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
var_extra->linkage != BTF_VAR_STATIC) {
pr_warn("map '%s': unsupported var linkage %u.\n",
map_name, var_extra->linkage);
return -EOPNOTSUPP;
}
def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
if (!btf_is_struct(def)) {
pr_warn("map '%s': unexpected def kind %u.\n",
map_name, btf_kind(var));
return -EINVAL;
}
if (def->size > vi->size) {
pr_warn("map '%s': invalid def size.\n", map_name);
return -EINVAL;
}
map = bpf_object__add_map(obj);
if (IS_ERR(map))
return PTR_ERR(map);
map->name = strdup(map_name);
if (!map->name) {
pr_warn("map '%s': failed to alloc map name.\n", map_name);
return -ENOMEM;
}
map->libbpf_type = LIBBPF_MAP_UNSPEC;
map->def.type = BPF_MAP_TYPE_UNSPEC;
map->sec_idx = sec_idx;
map->sec_offset = vi->offset;
pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
map_name, map->sec_idx, map->sec_offset);
vlen = btf_vlen(def); vlen = btf_vlen(def);
m = btf_members(def); m = btf_members(def);
for (i = 0; i < vlen; i++, m++) { 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(obj->btf, m->name_off);
if (!name) { 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; return -EINVAL;
} }
if (strcmp(name, "type") == 0) { if (strcmp(name, "type") == 0) {
if (!get_map_field_int(map_name, obj->btf, m, if (!get_map_field_int(map->name, obj->btf, m,
&map->def.type)) &map->def.type))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found type = %u.\n", pr_debug("map '%s': found type = %u.\n",
map_name, map->def.type); map->name, map->def.type);
} else if (strcmp(name, "max_entries") == 0) { } else if (strcmp(name, "max_entries") == 0) {
if (!get_map_field_int(map_name, obj->btf, m, if (!get_map_field_int(map->name, obj->btf, m,
&map->def.max_entries)) &map->def.max_entries))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found max_entries = %u.\n", pr_debug("map '%s': found max_entries = %u.\n",
map_name, map->def.max_entries); map->name, map->def.max_entries);
} else if (strcmp(name, "map_flags") == 0) { } else if (strcmp(name, "map_flags") == 0) {
if (!get_map_field_int(map_name, obj->btf, m, if (!get_map_field_int(map->name, obj->btf, m,
&map->def.map_flags)) &map->def.map_flags))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found map_flags = %u.\n", pr_debug("map '%s': found map_flags = %u.\n",
map_name, map->def.map_flags); map->name, map->def.map_flags);
} else if (strcmp(name, "key_size") == 0) { } else if (strcmp(name, "key_size") == 0) {
__u32 sz; __u32 sz;
if (!get_map_field_int(map_name, obj->btf, m, &sz)) if (!get_map_field_int(map->name, obj->btf, m, &sz))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found key_size = %u.\n", pr_debug("map '%s': found key_size = %u.\n",
map_name, sz); 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", 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; return -EINVAL;
} }
map->def.key_size = sz; map->def.key_size = sz;
...@@ -2026,25 +1971,25 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, ...@@ -2026,25 +1971,25 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
t = btf__type_by_id(obj->btf, m->type); t = btf__type_by_id(obj->btf, m->type);
if (!t) { if (!t) {
pr_warn("map '%s': key type [%d] not found.\n", pr_warn("map '%s': key type [%d] not found.\n",
map_name, m->type); map->name, m->type);
return -EINVAL; return -EINVAL;
} }
if (!btf_is_ptr(t)) { if (!btf_is_ptr(t)) {
pr_warn("map '%s': key spec is not PTR: %u.\n", pr_warn("map '%s': key spec is not PTR: %u.\n",
map_name, btf_kind(t)); map->name, btf_kind(t));
return -EINVAL; return -EINVAL;
} }
sz = btf__resolve_size(obj->btf, t->type); sz = btf__resolve_size(obj->btf, t->type);
if (sz < 0) { if (sz < 0) {
pr_warn("map '%s': can't determine key size for type [%u]: %zd.\n", 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; return sz;
} }
pr_debug("map '%s': found key [%u], sz = %zd.\n", pr_debug("map '%s': found key [%u], sz = %zd.\n",
map_name, t->type, (ssize_t)sz); 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", 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; return -EINVAL;
} }
map->def.key_size = sz; map->def.key_size = sz;
...@@ -2052,13 +1997,13 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, ...@@ -2052,13 +1997,13 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
} else if (strcmp(name, "value_size") == 0) { } else if (strcmp(name, "value_size") == 0) {
__u32 sz; __u32 sz;
if (!get_map_field_int(map_name, obj->btf, m, &sz)) if (!get_map_field_int(map->name, obj->btf, m, &sz))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found value_size = %u.\n", pr_debug("map '%s': found value_size = %u.\n",
map_name, sz); 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", 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; return -EINVAL;
} }
map->def.value_size = sz; map->def.value_size = sz;
...@@ -2068,25 +2013,25 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, ...@@ -2068,25 +2013,25 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
t = btf__type_by_id(obj->btf, m->type); t = btf__type_by_id(obj->btf, m->type);
if (!t) { if (!t) {
pr_warn("map '%s': value type [%d] not found.\n", pr_warn("map '%s': value type [%d] not found.\n",
map_name, m->type); map->name, m->type);
return -EINVAL; return -EINVAL;
} }
if (!btf_is_ptr(t)) { if (!btf_is_ptr(t)) {
pr_warn("map '%s': value spec is not PTR: %u.\n", pr_warn("map '%s': value spec is not PTR: %u.\n",
map_name, btf_kind(t)); map->name, btf_kind(t));
return -EINVAL; return -EINVAL;
} }
sz = btf__resolve_size(obj->btf, t->type); sz = btf__resolve_size(obj->btf, t->type);
if (sz < 0) { if (sz < 0) {
pr_warn("map '%s': can't determine value size for type [%u]: %zd.\n", 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; return sz;
} }
pr_debug("map '%s': found value [%u], sz = %zd.\n", pr_debug("map '%s': found value [%u], sz = %zd.\n",
map_name, t->type, (ssize_t)sz); 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", 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; return -EINVAL;
} }
map->def.value_size = sz; map->def.value_size = sz;
...@@ -2095,44 +2040,110 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, ...@@ -2095,44 +2040,110 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
__u32 val; __u32 val;
int err; int err;
if (!get_map_field_int(map_name, obj->btf, m, &val)) if (!get_map_field_int(map->name, obj->btf, m, &val))
return -EINVAL; return -EINVAL;
pr_debug("map '%s': found pinning = %u.\n", pr_debug("map '%s': found pinning = %u.\n",
map_name, val); map->name, val);
if (val != LIBBPF_PIN_NONE && if (val != LIBBPF_PIN_NONE &&
val != LIBBPF_PIN_BY_NAME) { val != LIBBPF_PIN_BY_NAME) {
pr_warn("map '%s': invalid pinning value %u.\n", pr_warn("map '%s': invalid pinning value %u.\n",
map_name, val); map->name, val);
return -EINVAL; return -EINVAL;
} }
if (val == LIBBPF_PIN_BY_NAME) { if (val == LIBBPF_PIN_BY_NAME) {
err = build_map_pin_path(map, pin_root_path); err = build_map_pin_path(map, pin_root_path);
if (err) { if (err) {
pr_warn("map '%s': couldn't build pin path.\n", pr_warn("map '%s': couldn't build pin path.\n",
map_name); map->name);
return err; return err;
} }
} }
} else { } else {
if (strict) { if (strict) {
pr_warn("map '%s': unknown field '%s'.\n", pr_warn("map '%s': unknown field '%s'.\n",
map_name, name); map->name, name);
return -ENOTSUP; return -ENOTSUP;
} }
pr_debug("map '%s': ignoring unknown field '%s'.\n", pr_debug("map '%s': ignoring unknown field '%s'.\n",
map_name, name); map->name, name);
} }
} }
if (map->def.type == BPF_MAP_TYPE_UNSPEC) { if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
pr_warn("map '%s': map type isn't specified.\n", map_name); pr_warn("map '%s': map type isn't specified.\n", map->name);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
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)
{
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;
vi = btf_var_secinfos(sec) + var_idx;
var = btf__type_by_id(obj->btf, vi->type);
var_extra = btf_var(var);
map_name = btf__name_by_offset(obj->btf, var->name_off);
if (map_name == NULL || map_name[0] == '\0') {
pr_warn("map #%d: empty name.\n", var_idx);
return -EINVAL;
}
if ((__u64)vi->offset + vi->size > data->d_size) {
pr_warn("map '%s' BTF data is corrupted.\n", map_name);
return -EINVAL;
}
if (!btf_is_var(var)) {
pr_warn("map '%s': unexpected var kind %u.\n",
map_name, btf_kind(var));
return -EINVAL;
}
if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
var_extra->linkage != BTF_VAR_STATIC) {
pr_warn("map '%s': unsupported var linkage %u.\n",
map_name, var_extra->linkage);
return -EOPNOTSUPP;
}
def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
if (!btf_is_struct(def)) {
pr_warn("map '%s': unexpected def kind %u.\n",
map_name, btf_kind(var));
return -EINVAL;
}
if (def->size > vi->size) {
pr_warn("map '%s': invalid def size.\n", map_name);
return -EINVAL;
}
map = bpf_object__add_map(obj);
if (IS_ERR(map))
return PTR_ERR(map);
map->name = strdup(map_name);
if (!map->name) {
pr_warn("map '%s': failed to alloc map name.\n", map_name);
return -ENOMEM;
}
map->libbpf_type = LIBBPF_MAP_UNSPEC;
map->def.type = BPF_MAP_TYPE_UNSPEC;
map->sec_idx = sec_idx;
map->sec_offset = vi->offset;
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, pin_root_path);
}
static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
const char *pin_root_path) const char *pin_root_path)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment