Commit 9327acd0 authored by Andrii Nakryiko's avatar Andrii Nakryiko
Browse files

Merge branch 'bpftool: Switch to libbpf's hashmap for referencing BPF objects'



Quentin Monnet says:

====================

When listing BPF objects, bpftool can print a number of properties about
items holding references to these objects. For example, it can show pinned
paths for BPF programs, maps, and links; or programs and maps using a given
BTF object; or the names and PIDs of processes referencing BPF objects. To
collect this information, bpftool uses hash maps (to be clear: the data
structures, inside bpftool - we are not talking of BPF maps). It uses the
implementation available from the kernel, and picks it up from
tools/include/linux/hashtable.h.

This patchset converts bpftool's hash maps to a distinct implementation
instead, the one coming with libbpf. The main motivation for this change is
that it should ease the path towards a potential out-of-tree mirror for
bpftool, like the one libbpf already has. Although it's not perfect to
depend on libbpf's internal components, bpftool is intimately tied with the
library anyway, and this looks better than depending too much on (non-UAPI)
kernel headers.

The first two patches contain preparatory work on the Makefile and on the
initialisation of the hash maps for collecting pinned paths for objects.
Then the transition is split into several steps, one for each kind of
properties for which the collection is backed by hash maps.

v2:
  - Move hashmap cleanup for pinned paths for links from do_detach() to
    do_show().
  - Handle errors on hashmap__append() (in three of the patches).
  - Rename bpftool_hash_fn() and bpftool_equal_fn() as hash_fn_for_key_id()
    and equal_fn_for_key_id(), respectively.
  - Add curly braces for hashmap__for_each_key_entry() { } in
    show_btf_plain() and show_btf_json(), where the flow was difficult to
    read.
====================

Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 57c8d362 d6699f8e
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -31,22 +31,22 @@ LIBBPF = $(LIBBPF_OUTPUT)libbpf.a
LIBBPF_BOOTSTRAP_OUTPUT = $(BOOTSTRAP_OUTPUT)libbpf/
LIBBPF_BOOTSTRAP = $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a

# We need to copy nlattr.h which is not otherwise exported by libbpf, but still
# required by bpftool.
LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,nlattr.h)
# We need to copy hashmap.h and nlattr.h which is not otherwise exported by
# libbpf, but still required by bpftool.
LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,hashmap.h nlattr.h)

ifeq ($(BPFTOOL_VERSION),)
BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion)
endif

$(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT) $(LIBBPF_BOOTSTRAP_OUTPUT):
$(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT) $(LIBBPF_BOOTSTRAP_OUTPUT) $(LIBBPF_HDRS_DIR):
	$(QUIET_MKDIR)mkdir -p $@

$(LIBBPF): $(wildcard $(BPF_DIR)/*.[ch] $(BPF_DIR)/Makefile) | $(LIBBPF_OUTPUT)
	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) \
		DESTDIR=$(LIBBPF_DESTDIR) prefix= $(LIBBPF) install_headers

$(LIBBPF_INTERNAL_HDRS): $(LIBBPF_HDRS_DIR)/%.h: $(BPF_DIR)/%.h $(LIBBPF)
$(LIBBPF_INTERNAL_HDRS): $(LIBBPF_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_HDRS_DIR)
	$(call QUIET_INSTALL, $@)
	$(Q)install -m 644 -t $(LIBBPF_HDRS_DIR) $<

@@ -209,7 +209,7 @@ $(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF_BOOTSTRAP)
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
	$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

$(BOOTSTRAP_OUTPUT)%.o: %.c | $(BOOTSTRAP_OUTPUT)
$(BOOTSTRAP_OUTPUT)%.o: %.c $(LIBBPF_INTERNAL_HDRS) | $(BOOTSTRAP_OUTPUT)
	$(QUIET_CC)$(HOSTCC) $(CFLAGS) -c -MMD -o $@ $<

$(OUTPUT)%.o: %.c
+60 −72
Original line number Diff line number Diff line
@@ -8,14 +8,15 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <linux/btf.h>
#include <linux/hashtable.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h>

#include "json_writer.h"
#include "main.h"

@@ -40,14 +41,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
};

struct btf_attach_table {
	DECLARE_HASHTABLE(table, 16);
};

struct btf_attach_point {
	__u32 obj_id;
	__u32 btf_id;
	struct hlist_node hash;
};

static const char *btf_int_enc_str(__u8 encoding)
@@ -645,21 +641,8 @@ static int btf_parse_fd(int *argc, char ***argv)
	return fd;
}

static void delete_btf_table(struct btf_attach_table *tab)
{
	struct btf_attach_point *obj;
	struct hlist_node *tmp;

	unsigned int bkt;

	hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
		hash_del(&obj->hash);
		free(obj);
	}
}

static int
build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
		     void *info, __u32 *len)
{
	static const char * const names[] = {
@@ -667,7 +650,6 @@ build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
		[BPF_OBJ_PROG]		= "prog",
		[BPF_OBJ_MAP]		= "map",
	};
	struct btf_attach_point *obj_node;
	__u32 btf_id, id = 0;
	int err;
	int fd;
@@ -741,28 +723,25 @@ build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
		if (!btf_id)
			continue;

		obj_node = calloc(1, sizeof(*obj_node));
		if (!obj_node) {
			p_err("failed to allocate memory: %s", strerror(errno));
			err = -ENOMEM;
		err = hashmap__append(tab, u32_as_hash_field(btf_id),
				      u32_as_hash_field(id));
		if (err) {
			p_err("failed to append entry to hashmap for BTF ID %u, object ID %u: %s",
			      btf_id, id, strerror(errno));
			goto err_free;
		}

		obj_node->obj_id = id;
		obj_node->btf_id = btf_id;
		hash_add(tab->table, &obj_node->hash, obj_node->btf_id);
	}

	return 0;

err_free:
	delete_btf_table(tab);
	hashmap__free(tab);
	return err;
}

static int
build_btf_tables(struct btf_attach_table *btf_prog_table,
		 struct btf_attach_table *btf_map_table)
build_btf_tables(struct hashmap *btf_prog_table,
		 struct hashmap *btf_map_table)
{
	struct bpf_prog_info prog_info;
	__u32 prog_len = sizeof(prog_info);
@@ -778,7 +757,7 @@ build_btf_tables(struct btf_attach_table *btf_prog_table,
	err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
				   &map_len);
	if (err) {
		delete_btf_table(btf_prog_table);
		hashmap__free(btf_prog_table);
		return err;
	}

@@ -787,10 +766,10 @@ build_btf_tables(struct btf_attach_table *btf_prog_table,

static void
show_btf_plain(struct bpf_btf_info *info, int fd,
	       struct btf_attach_table *btf_prog_table,
	       struct btf_attach_table *btf_map_table)
	       struct hashmap *btf_prog_table,
	       struct hashmap *btf_map_table)
{
	struct btf_attach_point *obj;
	struct hashmap_entry *entry;
	const char *name = u64_to_ptr(info->name);
	int n;

@@ -804,29 +783,30 @@ show_btf_plain(struct bpf_btf_info *info, int fd,
	printf("size %uB", info->btf_size);

	n = 0;
	hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) {
		if (obj->btf_id == info->id)
	hashmap__for_each_key_entry(btf_prog_table, entry,
				    u32_as_hash_field(info->id)) {
		printf("%s%u", n++ == 0 ? "  prog_ids " : ",",
			       obj->obj_id);
		       hash_field_as_u32(entry->value));
	}

	n = 0;
	hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
		if (obj->btf_id == info->id)
	hashmap__for_each_key_entry(btf_map_table, entry,
				    u32_as_hash_field(info->id)) {
		printf("%s%u", n++ == 0 ? "  map_ids " : ",",
			       obj->obj_id);
		       hash_field_as_u32(entry->value));
	}
	emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");

	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");

	printf("\n");
}

static void
show_btf_json(struct bpf_btf_info *info, int fd,
	      struct btf_attach_table *btf_prog_table,
	      struct btf_attach_table *btf_map_table)
	      struct hashmap *btf_prog_table,
	      struct hashmap *btf_map_table)
{
	struct btf_attach_point *obj;
	struct hashmap_entry *entry;
	const char *name = u64_to_ptr(info->name);

	jsonw_start_object(json_wtr);	/* btf object */
@@ -835,23 +815,21 @@ show_btf_json(struct bpf_btf_info *info, int fd,

	jsonw_name(json_wtr, "prog_ids");
	jsonw_start_array(json_wtr);	/* prog_ids */
	hash_for_each_possible(btf_prog_table->table, obj, hash,
			       info->id) {
		if (obj->btf_id == info->id)
			jsonw_uint(json_wtr, obj->obj_id);
	hashmap__for_each_key_entry(btf_prog_table, entry,
				    u32_as_hash_field(info->id)) {
		jsonw_uint(json_wtr, hash_field_as_u32(entry->value));
	}
	jsonw_end_array(json_wtr);	/* prog_ids */

	jsonw_name(json_wtr, "map_ids");
	jsonw_start_array(json_wtr);	/* map_ids */
	hash_for_each_possible(btf_map_table->table, obj, hash,
			       info->id) {
		if (obj->btf_id == info->id)
			jsonw_uint(json_wtr, obj->obj_id);
	hashmap__for_each_key_entry(btf_map_table, entry,
				    u32_as_hash_field(info->id)) {
		jsonw_uint(json_wtr, hash_field_as_u32(entry->value));
	}
	jsonw_end_array(json_wtr);	/* map_ids */

	emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
	emit_obj_refs_json(refs_table, info->id, json_wtr); /* pids */

	jsonw_bool_field(json_wtr, "kernel", info->kernel_btf);

@@ -862,8 +840,8 @@ show_btf_json(struct bpf_btf_info *info, int fd,
}

static int
show_btf(int fd, struct btf_attach_table *btf_prog_table,
	 struct btf_attach_table *btf_map_table)
show_btf(int fd, struct hashmap *btf_prog_table,
	 struct hashmap *btf_map_table)
{
	struct bpf_btf_info info;
	__u32 len = sizeof(info);
@@ -900,8 +878,8 @@ show_btf(int fd, struct btf_attach_table *btf_prog_table,

static int do_show(int argc, char **argv)
{
	struct btf_attach_table btf_prog_table;
	struct btf_attach_table btf_map_table;
	struct hashmap *btf_prog_table;
	struct hashmap *btf_map_table;
	int err, fd = -1;
	__u32 id = 0;

@@ -917,9 +895,19 @@ static int do_show(int argc, char **argv)
		return BAD_ARG();
	}

	hash_init(btf_prog_table.table);
	hash_init(btf_map_table.table);
	err = build_btf_tables(&btf_prog_table, &btf_map_table);
	btf_prog_table = hashmap__new(hash_fn_for_key_as_id,
				      equal_fn_for_key_as_id, NULL);
	btf_map_table = hashmap__new(hash_fn_for_key_as_id,
				     equal_fn_for_key_as_id, NULL);
	if (!btf_prog_table || !btf_map_table) {
		hashmap__free(btf_prog_table);
		hashmap__free(btf_map_table);
		if (fd >= 0)
			close(fd);
		p_err("failed to create hashmap for object references");
		return -1;
	}
	err = build_btf_tables(btf_prog_table, btf_map_table);
	if (err) {
		if (fd >= 0)
			close(fd);
@@ -928,7 +916,7 @@ static int do_show(int argc, char **argv)
	build_obj_refs_table(&refs_table, BPF_OBJ_BTF);

	if (fd >= 0) {
		err = show_btf(fd, &btf_prog_table, &btf_map_table);
		err = show_btf(fd, btf_prog_table, btf_map_table);
		close(fd);
		goto exit_free;
	}
@@ -960,7 +948,7 @@ static int do_show(int argc, char **argv)
			break;
		}

		err = show_btf(fd, &btf_prog_table, &btf_map_table);
		err = show_btf(fd, btf_prog_table, btf_map_table);
		close(fd);
		if (err)
			break;
@@ -970,9 +958,9 @@ static int do_show(int argc, char **argv)
		jsonw_end_array(json_wtr);	/* root array */

exit_free:
	delete_btf_table(&btf_prog_table);
	delete_btf_table(&btf_map_table);
	delete_obj_refs_table(&refs_table);
	hashmap__free(btf_prog_table);
	hashmap__free(btf_map_table);
	delete_obj_refs_table(refs_table);

	return err;
}
+30 −20
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <sys/vfs.h>

#include <bpf/bpf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h> /* libbpf_num_possible_cpus */

#include "main.h"
@@ -393,7 +394,7 @@ void print_hex_data_json(uint8_t *data, size_t len)
}

/* extra params for nftw cb */
static struct pinned_obj_table *build_fn_table;
static struct hashmap *build_fn_table;
static enum bpf_obj_type build_fn_type;

static int do_build_table_cb(const char *fpath, const struct stat *sb,
@@ -401,9 +402,9 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb,
{
	struct bpf_prog_info pinned_info;
	__u32 len = sizeof(pinned_info);
	struct pinned_obj *obj_node;
	enum bpf_obj_type objtype;
	int fd, err = 0;
	char *path;

	if (typeflag != FTW_F)
		goto out_ret;
@@ -420,28 +421,26 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb,
	if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len))
		goto out_close;

	obj_node = calloc(1, sizeof(*obj_node));
	if (!obj_node) {
	path = strdup(fpath);
	if (!path) {
		err = -1;
		goto out_close;
	}

	obj_node->id = pinned_info.id;
	obj_node->path = strdup(fpath);
	if (!obj_node->path) {
		err = -1;
		free(obj_node);
	err = hashmap__append(build_fn_table, u32_as_hash_field(pinned_info.id), path);
	if (err) {
		p_err("failed to append entry to hashmap for ID %u, path '%s': %s",
		      pinned_info.id, path, strerror(errno));
		goto out_close;
	}

	hash_add(build_fn_table->table, &obj_node->hash, obj_node->id);
out_close:
	close(fd);
out_ret:
	return err;
}

int build_pinned_obj_table(struct pinned_obj_table *tab,
int build_pinned_obj_table(struct hashmap *tab,
			   enum bpf_obj_type type)
{
	struct mntent *mntent = NULL;
@@ -470,17 +469,18 @@ int build_pinned_obj_table(struct pinned_obj_table *tab,
	return err;
}

void delete_pinned_obj_table(struct pinned_obj_table *tab)
void delete_pinned_obj_table(struct hashmap *map)
{
	struct pinned_obj *obj;
	struct hlist_node *tmp;
	unsigned int bkt;
	struct hashmap_entry *entry;
	size_t bkt;

	hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
		hash_del(&obj->hash);
		free(obj->path);
		free(obj);
	}
	if (!map)
		return;

	hashmap__for_each_entry(map, entry, bkt)
		free(entry->value);

	hashmap__free(map);
}

unsigned int get_page_size(void)
@@ -962,3 +962,13 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)

	return fd;
}

size_t hash_fn_for_key_as_id(const void *key, void *ctx)
{
	return (size_t)key;
}

bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx)
{
	return k1 == k2;
}
+28 −17
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <unistd.h>

#include <bpf/bpf.h>
#include <bpf/hashmap.h>

#include "json_writer.h"
#include "main.h"
@@ -20,6 +21,8 @@ static const char * const link_type_name[] = {
	[BPF_LINK_TYPE_NETNS]			= "netns",
};

static struct hashmap *link_table;

static int link_parse_fd(int *argc, char ***argv)
{
	int fd;
@@ -156,19 +159,18 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
		break;
	}

	if (!hash_empty(link_table.table)) {
		struct pinned_obj *obj;
	if (!hashmap__empty(link_table)) {
		struct hashmap_entry *entry;

		jsonw_name(json_wtr, "pinned");
		jsonw_start_array(json_wtr);
		hash_for_each_possible(link_table.table, obj, hash, info->id) {
			if (obj->id == info->id)
				jsonw_string(json_wtr, obj->path);
		}
		hashmap__for_each_key_entry(link_table, entry,
					    u32_as_hash_field(info->id))
			jsonw_string(json_wtr, entry->value);
		jsonw_end_array(json_wtr);
	}

	emit_obj_refs_json(&refs_table, info->id, json_wtr);
	emit_obj_refs_json(refs_table, info->id, json_wtr);

	jsonw_end_object(json_wtr);

@@ -244,15 +246,14 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
		break;
	}

	if (!hash_empty(link_table.table)) {
		struct pinned_obj *obj;
	if (!hashmap__empty(link_table)) {
		struct hashmap_entry *entry;

		hash_for_each_possible(link_table.table, obj, hash, info->id) {
			if (obj->id == info->id)
				printf("\n\tpinned %s", obj->path);
		}
		hashmap__for_each_key_entry(link_table, entry,
					    u32_as_hash_field(info->id))
			printf("\n\tpinned %s", (char *)entry->value);
	}
	emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");

	printf("\n");

@@ -302,8 +303,15 @@ static int do_show(int argc, char **argv)
	__u32 id = 0;
	int err, fd;

	if (show_pinned)
		build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
	if (show_pinned) {
		link_table = hashmap__new(hash_fn_for_key_as_id,
					  equal_fn_for_key_as_id, NULL);
		if (!link_table) {
			p_err("failed to create hashmap for pinned paths");
			return -1;
		}
		build_pinned_obj_table(link_table, BPF_OBJ_LINK);
	}
	build_obj_refs_table(&refs_table, BPF_OBJ_LINK);

	if (argc == 2) {
@@ -344,7 +352,10 @@ static int do_show(int argc, char **argv)
	if (json_output)
		jsonw_end_array(json_wtr);

	delete_obj_refs_table(&refs_table);
	delete_obj_refs_table(refs_table);

	if (show_pinned)
		delete_pinned_obj_table(link_table);

	return errno == ENOENT ? 0 : -1;
}
+3 −14
Original line number Diff line number Diff line
@@ -10,8 +10,9 @@
#include <string.h>

#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/btf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h>

#include "main.h"

@@ -31,10 +32,7 @@ bool verifier_logs;
bool relaxed_maps;
bool use_loader;
struct btf *base_btf;
struct pinned_obj_table prog_table;
struct pinned_obj_table map_table;
struct pinned_obj_table link_table;
struct obj_refs_table refs_table;
struct hashmap *refs_table;

static void __noreturn clean_and_exit(int i)
{
@@ -409,10 +407,6 @@ int main(int argc, char **argv)
	block_mount = false;
	bin_name = argv[0];

	hash_init(prog_table.table);
	hash_init(map_table.table);
	hash_init(link_table.table);

	opterr = 0;
	while ((opt = getopt_long(argc, argv, "VhpjfLmndB:",
				  options, NULL)) >= 0) {
@@ -479,11 +473,6 @@ int main(int argc, char **argv)
	if (json_output)
		jsonw_destroy(&json_wtr);

	if (show_pinned) {
		delete_pinned_obj_table(&prog_table);
		delete_pinned_obj_table(&map_table);
		delete_pinned_obj_table(&link_table);
	}
	btf__free(base_btf);

	return ret;
Loading