Commit 8f184732 authored by Quentin Monnet's avatar Quentin Monnet Committed by Andrii Nakryiko

bpftool: Switch to libbpf's hashmap for pinned paths of BPF objects

In order to show pinned paths for BPF programs, maps, or links when
listing them with the "-f" option, bpftool creates hash maps to store
all relevant paths under the bpffs. So far, it would rely on the
kernel implementation (from tools/include/linux/hashtable.h).

We can make bpftool rely on libbpf's implementation instead. The
motivation is to make bpftool less dependent of kernel headers, to ease
the path to a potential out-of-tree mirror, like libbpf has.

This commit is the first step of the conversion: the hash maps for
pinned paths for programs, maps, and links are converted to libbpf's
hashmap.{c,h}. Other hash maps used for the PIDs of process holding
references to BPF objects are left unchanged for now. On the build side,
this requires adding a dependency to a second header internal to libbpf,
and making it a dependency for the bootstrap bpftool version as well.
The rest of the changes are a rather straightforward conversion.
Signed-off-by: default avatarQuentin Monnet <quentin@isovalent.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20211023205154.6710-4-quentin@isovalent.com
parent 46241271
...@@ -31,9 +31,9 @@ LIBBPF = $(LIBBPF_OUTPUT)libbpf.a ...@@ -31,9 +31,9 @@ LIBBPF = $(LIBBPF_OUTPUT)libbpf.a
LIBBPF_BOOTSTRAP_OUTPUT = $(BOOTSTRAP_OUTPUT)libbpf/ LIBBPF_BOOTSTRAP_OUTPUT = $(BOOTSTRAP_OUTPUT)libbpf/
LIBBPF_BOOTSTRAP = $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a LIBBPF_BOOTSTRAP = $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a
# We need to copy nlattr.h which is not otherwise exported by libbpf, but still # We need to copy hashmap.h and nlattr.h which is not otherwise exported by
# required by bpftool. # libbpf, but still required by bpftool.
LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,nlattr.h) LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,hashmap.h nlattr.h)
ifeq ($(BPFTOOL_VERSION),) ifeq ($(BPFTOOL_VERSION),)
BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion)
...@@ -209,7 +209,7 @@ $(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF_BOOTSTRAP) ...@@ -209,7 +209,7 @@ $(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF_BOOTSTRAP)
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF) $(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(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 $@ $< $(QUIET_CC)$(HOSTCC) $(CFLAGS) -c -MMD -o $@ $<
$(OUTPUT)%.o: %.c $(OUTPUT)%.o: %.c
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <sys/vfs.h> #include <sys/vfs.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h> /* libbpf_num_possible_cpus */ #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
#include "main.h" #include "main.h"
...@@ -393,7 +394,7 @@ void print_hex_data_json(uint8_t *data, size_t len) ...@@ -393,7 +394,7 @@ void print_hex_data_json(uint8_t *data, size_t len)
} }
/* extra params for nftw cb */ /* 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 enum bpf_obj_type build_fn_type;
static int do_build_table_cb(const char *fpath, const struct stat *sb, 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, ...@@ -401,9 +402,9 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb,
{ {
struct bpf_prog_info pinned_info; struct bpf_prog_info pinned_info;
__u32 len = sizeof(pinned_info); __u32 len = sizeof(pinned_info);
struct pinned_obj *obj_node;
enum bpf_obj_type objtype; enum bpf_obj_type objtype;
int fd, err = 0; int fd, err = 0;
char *path;
if (typeflag != FTW_F) if (typeflag != FTW_F)
goto out_ret; goto out_ret;
...@@ -420,28 +421,26 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb, ...@@ -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)) if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len))
goto out_close; goto out_close;
obj_node = calloc(1, sizeof(*obj_node)); path = strdup(fpath);
if (!obj_node) { if (!path) {
err = -1; err = -1;
goto out_close; goto out_close;
} }
obj_node->id = pinned_info.id; err = hashmap__append(build_fn_table, u32_as_hash_field(pinned_info.id), path);
obj_node->path = strdup(fpath); if (err) {
if (!obj_node->path) { p_err("failed to append entry to hashmap for ID %u, path '%s': %s",
err = -1; pinned_info.id, path, strerror(errno));
free(obj_node);
goto out_close; goto out_close;
} }
hash_add(build_fn_table->table, &obj_node->hash, obj_node->id);
out_close: out_close:
close(fd); close(fd);
out_ret: out_ret:
return err; 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) enum bpf_obj_type type)
{ {
struct mntent *mntent = NULL; struct mntent *mntent = NULL;
...@@ -470,17 +469,18 @@ int build_pinned_obj_table(struct pinned_obj_table *tab, ...@@ -470,17 +469,18 @@ int build_pinned_obj_table(struct pinned_obj_table *tab,
return err; 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 hashmap_entry *entry;
struct hlist_node *tmp; size_t bkt;
unsigned int bkt;
hash_for_each_safe(tab->table, bkt, tmp, obj, hash) { if (!map)
hash_del(&obj->hash); return;
free(obj->path);
free(obj); hashmap__for_each_entry(map, entry, bkt)
} free(entry->value);
hashmap__free(map);
} }
unsigned int get_page_size(void) 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) ...@@ -962,3 +962,13 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
return fd; 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;
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <unistd.h> #include <unistd.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/hashmap.h>
#include "json_writer.h" #include "json_writer.h"
#include "main.h" #include "main.h"
...@@ -20,7 +21,7 @@ static const char * const link_type_name[] = { ...@@ -20,7 +21,7 @@ static const char * const link_type_name[] = {
[BPF_LINK_TYPE_NETNS] = "netns", [BPF_LINK_TYPE_NETNS] = "netns",
}; };
static struct pinned_obj_table link_table; static struct hashmap *link_table;
static int link_parse_fd(int *argc, char ***argv) static int link_parse_fd(int *argc, char ***argv)
{ {
...@@ -158,15 +159,14 @@ static int show_link_close_json(int fd, struct bpf_link_info *info) ...@@ -158,15 +159,14 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
break; break;
} }
if (!hash_empty(link_table.table)) { if (!hashmap__empty(link_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
jsonw_name(json_wtr, "pinned"); jsonw_name(json_wtr, "pinned");
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
hash_for_each_possible(link_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(link_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
jsonw_string(json_wtr, obj->path); jsonw_string(json_wtr, entry->value);
}
jsonw_end_array(json_wtr); jsonw_end_array(json_wtr);
} }
...@@ -246,13 +246,12 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info) ...@@ -246,13 +246,12 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
break; break;
} }
if (!hash_empty(link_table.table)) { if (!hashmap__empty(link_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
hash_for_each_possible(link_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(link_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
printf("\n\tpinned %s", obj->path); 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 ");
...@@ -305,8 +304,13 @@ static int do_show(int argc, char **argv) ...@@ -305,8 +304,13 @@ static int do_show(int argc, char **argv)
int err, fd; int err, fd;
if (show_pinned) { if (show_pinned) {
hash_init(link_table.table); link_table = hashmap__new(hash_fn_for_key_as_id,
build_pinned_obj_table(&link_table, BPF_OBJ_LINK); 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); build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
...@@ -351,7 +355,7 @@ static int do_show(int argc, char **argv) ...@@ -351,7 +355,7 @@ static int do_show(int argc, char **argv)
delete_obj_refs_table(&refs_table); delete_obj_refs_table(&refs_table);
if (show_pinned) if (show_pinned)
delete_pinned_obj_table(&link_table); delete_pinned_obj_table(link_table);
return errno == ENOENT ? 0 : -1; return errno == ENOENT ? 0 : -1;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <tools/libc_compat.h> #include <tools/libc_compat.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include "json_writer.h" #include "json_writer.h"
...@@ -105,16 +106,6 @@ void set_max_rlimit(void); ...@@ -105,16 +106,6 @@ void set_max_rlimit(void);
int mount_tracefs(const char *target); int mount_tracefs(const char *target);
struct pinned_obj_table {
DECLARE_HASHTABLE(table, 16);
};
struct pinned_obj {
__u32 id;
char *path;
struct hlist_node hash;
};
struct obj_refs_table { struct obj_refs_table {
DECLARE_HASHTABLE(table, 16); DECLARE_HASHTABLE(table, 16);
}; };
...@@ -134,9 +125,9 @@ struct obj_refs { ...@@ -134,9 +125,9 @@ struct obj_refs {
struct btf; struct btf;
struct bpf_line_info; struct bpf_line_info;
int build_pinned_obj_table(struct pinned_obj_table *table, int build_pinned_obj_table(struct hashmap *table,
enum bpf_obj_type type); enum bpf_obj_type type);
void delete_pinned_obj_table(struct pinned_obj_table *tab); void delete_pinned_obj_table(struct hashmap *table);
__weak int build_obj_refs_table(struct obj_refs_table *table, __weak int build_obj_refs_table(struct obj_refs_table *table,
enum bpf_obj_type type); enum bpf_obj_type type);
__weak void delete_obj_refs_table(struct obj_refs_table *table); __weak void delete_obj_refs_table(struct obj_refs_table *table);
...@@ -256,4 +247,18 @@ int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, ...@@ -256,4 +247,18 @@ int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind,
int print_all_levels(__maybe_unused enum libbpf_print_level level, int print_all_levels(__maybe_unused enum libbpf_print_level level,
const char *format, va_list args); const char *format, va_list args);
size_t hash_fn_for_key_as_id(const void *key, void *ctx);
bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx);
static inline void *u32_as_hash_field(__u32 x)
{
return (void *)(uintptr_t)x;
}
static inline bool hashmap__empty(struct hashmap *map)
{
return map ? hashmap__size(map) == 0 : true;
}
#endif #endif
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#include <bpf/hashmap.h>
#include "json_writer.h" #include "json_writer.h"
#include "main.h" #include "main.h"
...@@ -56,7 +57,7 @@ const char * const map_type_name[] = { ...@@ -56,7 +57,7 @@ const char * const map_type_name[] = {
const size_t map_type_name_size = ARRAY_SIZE(map_type_name); const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
static struct pinned_obj_table map_table; static struct hashmap *map_table;
static bool map_is_per_cpu(__u32 type) static bool map_is_per_cpu(__u32 type)
{ {
...@@ -537,15 +538,14 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) ...@@ -537,15 +538,14 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
if (info->btf_id) if (info->btf_id)
jsonw_int_field(json_wtr, "btf_id", info->btf_id); jsonw_int_field(json_wtr, "btf_id", info->btf_id);
if (!hash_empty(map_table.table)) { if (!hashmap__empty(map_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
jsonw_name(json_wtr, "pinned"); jsonw_name(json_wtr, "pinned");
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
hash_for_each_possible(map_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(map_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
jsonw_string(json_wtr, obj->path); jsonw_string(json_wtr, entry->value);
}
jsonw_end_array(json_wtr); jsonw_end_array(json_wtr);
} }
...@@ -612,13 +612,12 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) ...@@ -612,13 +612,12 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
} }
close(fd); close(fd);
if (!hash_empty(map_table.table)) { if (!hashmap__empty(map_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
hash_for_each_possible(map_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(map_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
printf("\n\tpinned %s", obj->path); printf("\n\tpinned %s", (char *)entry->value);
}
} }
printf("\n"); printf("\n");
...@@ -697,8 +696,13 @@ static int do_show(int argc, char **argv) ...@@ -697,8 +696,13 @@ static int do_show(int argc, char **argv)
int fd; int fd;
if (show_pinned) { if (show_pinned) {
hash_init(map_table.table); map_table = hashmap__new(hash_fn_for_key_as_id,
build_pinned_obj_table(&map_table, BPF_OBJ_MAP); equal_fn_for_key_as_id, NULL);
if (!map_table) {
p_err("failed to create hashmap for pinned paths");
return -1;
}
build_pinned_obj_table(map_table, BPF_OBJ_MAP);
} }
build_obj_refs_table(&refs_table, BPF_OBJ_MAP); build_obj_refs_table(&refs_table, BPF_OBJ_MAP);
...@@ -747,7 +751,7 @@ static int do_show(int argc, char **argv) ...@@ -747,7 +751,7 @@ static int do_show(int argc, char **argv)
delete_obj_refs_table(&refs_table); delete_obj_refs_table(&refs_table);
if (show_pinned) if (show_pinned)
delete_pinned_obj_table(&map_table); delete_pinned_obj_table(map_table);
return errno == ENOENT ? 0 : -1; return errno == ENOENT ? 0 : -1;
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include <bpf/skel_internal.h> #include <bpf/skel_internal.h>
...@@ -84,7 +85,7 @@ static const char * const attach_type_strings[] = { ...@@ -84,7 +85,7 @@ static const char * const attach_type_strings[] = {
[__MAX_BPF_ATTACH_TYPE] = NULL, [__MAX_BPF_ATTACH_TYPE] = NULL,
}; };
static struct pinned_obj_table prog_table; static struct hashmap *prog_table;
static enum bpf_attach_type parse_attach_type(const char *str) static enum bpf_attach_type parse_attach_type(const char *str)
{ {
...@@ -418,15 +419,14 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) ...@@ -418,15 +419,14 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)
if (info->btf_id) if (info->btf_id)
jsonw_int_field(json_wtr, "btf_id", info->btf_id); jsonw_int_field(json_wtr, "btf_id", info->btf_id);
if (!hash_empty(prog_table.table)) { if (!hashmap__empty(prog_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
jsonw_name(json_wtr, "pinned"); jsonw_name(json_wtr, "pinned");
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
hash_for_each_possible(prog_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(prog_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
jsonw_string(json_wtr, obj->path); jsonw_string(json_wtr, entry->value);
}
jsonw_end_array(json_wtr); jsonw_end_array(json_wtr);
} }
...@@ -490,13 +490,12 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) ...@@ -490,13 +490,12 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
if (info->nr_map_ids) if (info->nr_map_ids)
show_prog_maps(fd, info->nr_map_ids); show_prog_maps(fd, info->nr_map_ids);
if (!hash_empty(prog_table.table)) { if (!hashmap__empty(prog_table)) {
struct pinned_obj *obj; struct hashmap_entry *entry;
hash_for_each_possible(prog_table.table, obj, hash, info->id) { hashmap__for_each_key_entry(prog_table, entry,
if (obj->id == info->id) u32_as_hash_field(info->id))
printf("\n\tpinned %s", obj->path); printf("\n\tpinned %s", (char *)entry->value);
}
} }
if (info->btf_id) if (info->btf_id)
...@@ -570,8 +569,13 @@ static int do_show(int argc, char **argv) ...@@ -570,8 +569,13 @@ static int do_show(int argc, char **argv)
int fd; int fd;
if (show_pinned) { if (show_pinned) {
hash_init(prog_table.table); prog_table = hashmap__new(hash_fn_for_key_as_id,
build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); equal_fn_for_key_as_id, NULL);
if (!prog_table) {
p_err("failed to create hashmap for pinned paths");
return -1;
}
build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
} }
build_obj_refs_table(&refs_table, BPF_OBJ_PROG); build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
...@@ -618,7 +622,7 @@ static int do_show(int argc, char **argv) ...@@ -618,7 +622,7 @@ static int do_show(int argc, char **argv)
delete_obj_refs_table(&refs_table); delete_obj_refs_table(&refs_table);
if (show_pinned) if (show_pinned)
delete_pinned_obj_table(&prog_table); delete_pinned_obj_table(prog_table);
return err; return err;
} }
......
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