Commit c7ef5ec9 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov
Browse files

libbpf: Refactor BTF map definition parsing



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: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarYonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20210423181348.1801389-7-andrii@kernel.org
parent 6245947c
Loading
Loading
Loading
Loading
+146 −111
Original line number Diff line number Diff line
@@ -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,
+32 −0
Original line number Diff line number Diff line
@@ -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);