Merge branch 'btf_get_next_id'
Quentin Monnet says: ==================== This set adds a new command BPF_BTF_GET_NEXT_ID to the bpf() system call, adds the relevant API function in libbpf, and uses it in bpftool to list all BTF objects loaded on the system (and to dump the ids of maps and programs associated with them, if any). The main motivation of listing BTF objects is introspection and debugging purposes. By getting BPF program and map information, it should already be possible to list all BTF objects associated to at least one map or one program. But there may be unattached BTF objects, held by a file descriptor from a user space process only, and we may want to list them too. As a side note, it also turned useful for examining the BTF objects attached to offloaded programs, which would not show in program information because the BTF id is not copied when retrieving such info. A fix is in progress on that side. v2: - Rebase patch with new libbpf function on top of Andrii's changes regarding libbpf versioning. ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Коммит
51746f9486
|
@ -24,6 +24,9 @@ struct seq_file;
|
|||
struct btf;
|
||||
struct btf_type;
|
||||
|
||||
extern struct idr btf_idr;
|
||||
extern spinlock_t btf_idr_lock;
|
||||
|
||||
/* map is generic key/value storage optionally accesible by eBPF programs */
|
||||
struct bpf_map_ops {
|
||||
/* funcs callable from userspace (via syscall) */
|
||||
|
|
|
@ -106,6 +106,7 @@ enum bpf_cmd {
|
|||
BPF_TASK_FD_QUERY,
|
||||
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
|
||||
BPF_MAP_FREEZE,
|
||||
BPF_BTF_GET_NEXT_ID,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
|
|
|
@ -195,8 +195,8 @@
|
|||
i < btf_type_vlen(struct_type); \
|
||||
i++, member++)
|
||||
|
||||
static DEFINE_IDR(btf_idr);
|
||||
static DEFINE_SPINLOCK(btf_idr_lock);
|
||||
DEFINE_IDR(btf_idr);
|
||||
DEFINE_SPINLOCK(btf_idr_lock);
|
||||
|
||||
struct btf {
|
||||
void *data;
|
||||
|
|
|
@ -2884,6 +2884,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
|||
err = bpf_obj_get_next_id(&attr, uattr,
|
||||
&map_idr, &map_idr_lock);
|
||||
break;
|
||||
case BPF_BTF_GET_NEXT_ID:
|
||||
err = bpf_obj_get_next_id(&attr, uattr,
|
||||
&btf_idr, &btf_idr_lock);
|
||||
break;
|
||||
case BPF_PROG_GET_FD_BY_ID:
|
||||
err = bpf_prog_get_fd_by_id(&attr);
|
||||
break;
|
||||
|
|
|
@ -19,6 +19,7 @@ SYNOPSIS
|
|||
BTF COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
|
||||
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
|
||||
| **bpftool** **btf help**
|
||||
|
|
||||
|
@ -29,6 +30,12 @@ BTF COMMANDS
|
|||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool btf { show | list }** [**id** *BTF_ID*]
|
||||
Show information about loaded BTF objects. If a BTF ID is
|
||||
specified, show information only about given BTF object,
|
||||
otherwise list all BTF objects currently loaded on the
|
||||
system.
|
||||
|
||||
**bpftool btf dump** *BTF_SRC*
|
||||
Dump BTF entries from a given *BTF_SRC*.
|
||||
|
||||
|
|
|
@ -73,8 +73,8 @@ _bpftool_get_prog_tags()
|
|||
|
||||
_bpftool_get_btf_ids()
|
||||
{
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
|
||||
command sed -n 's/.*"btf_id": \(.*\),\?$/\1/p' )" -- "$cur" ) )
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \
|
||||
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_obj_map_names()
|
||||
|
@ -670,7 +670,7 @@ _bpftool()
|
|||
map)
|
||||
_bpftool_get_map_ids
|
||||
;;
|
||||
dump)
|
||||
$command)
|
||||
_bpftool_get_btf_ids
|
||||
;;
|
||||
esac
|
||||
|
@ -698,9 +698,21 @@ _bpftool()
|
|||
;;
|
||||
esac
|
||||
;;
|
||||
show|list)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY+=( $( compgen -W "id" -- "$cur" ) )
|
||||
;;
|
||||
id)
|
||||
_bpftool_get_btf_ids
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'dump help' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W 'dump help show list' \
|
||||
-- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <bpf.h>
|
||||
#include <libbpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "btf.h"
|
||||
#include "json_writer.h"
|
||||
|
@ -35,6 +36,16 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
|||
[BTF_KIND_DATASEC] = "DATASEC",
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
switch (encoding) {
|
||||
|
@ -522,6 +533,330 @@ done:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int btf_parse_fd(int *argc, char ***argv)
|
||||
{
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
int fd;
|
||||
|
||||
if (!is_prefix(*argv[0], "id")) {
|
||||
p_err("expected 'id', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
fd = bpf_btf_get_fd_by_id(id);
|
||||
if (fd < 0)
|
||||
p_err("can't get BTF object by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
|
||||
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,
|
||||
void *info, __u32 *len)
|
||||
{
|
||||
static const char * const names[] = {
|
||||
[BPF_OBJ_UNKNOWN] = "unknown",
|
||||
[BPF_OBJ_PROG] = "prog",
|
||||
[BPF_OBJ_MAP] = "map",
|
||||
};
|
||||
struct btf_attach_point *obj_node;
|
||||
__u32 btf_id, id = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
while (true) {
|
||||
switch (type) {
|
||||
case BPF_OBJ_PROG:
|
||||
err = bpf_prog_get_next_id(id, &id);
|
||||
break;
|
||||
case BPF_OBJ_MAP:
|
||||
err = bpf_map_get_next_id(id, &id);
|
||||
break;
|
||||
default:
|
||||
err = -1;
|
||||
p_err("unexpected object type: %d", type);
|
||||
goto err_free;
|
||||
}
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
p_err("can't get next %s: %s%s", names[type],
|
||||
strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case BPF_OBJ_PROG:
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
break;
|
||||
case BPF_OBJ_MAP:
|
||||
fd = bpf_map_get_fd_by_id(id);
|
||||
break;
|
||||
default:
|
||||
err = -1;
|
||||
p_err("unexpected object type: %d", type);
|
||||
goto err_free;
|
||||
}
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
p_err("can't get %s by id (%u): %s", names[type], id,
|
||||
strerror(errno));
|
||||
err = -1;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
memset(info, 0, *len);
|
||||
err = bpf_obj_get_info_by_fd(fd, info, len);
|
||||
close(fd);
|
||||
if (err) {
|
||||
p_err("can't get %s info: %s", names[type],
|
||||
strerror(errno));
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case BPF_OBJ_PROG:
|
||||
btf_id = ((struct bpf_prog_info *)info)->btf_id;
|
||||
break;
|
||||
case BPF_OBJ_MAP:
|
||||
btf_id = ((struct bpf_map_info *)info)->btf_id;
|
||||
break;
|
||||
default:
|
||||
err = -1;
|
||||
p_err("unexpected object type: %d", type);
|
||||
goto err_free;
|
||||
}
|
||||
if (!btf_id)
|
||||
continue;
|
||||
|
||||
obj_node = calloc(1, sizeof(*obj_node));
|
||||
if (!obj_node) {
|
||||
p_err("failed to allocate memory: %s", 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);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
build_btf_tables(struct btf_attach_table *btf_prog_table,
|
||||
struct btf_attach_table *btf_map_table)
|
||||
{
|
||||
struct bpf_prog_info prog_info;
|
||||
__u32 prog_len = sizeof(prog_info);
|
||||
struct bpf_map_info map_info;
|
||||
__u32 map_len = sizeof(map_info);
|
||||
int err = 0;
|
||||
|
||||
err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
|
||||
&prog_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
|
||||
&map_len);
|
||||
if (err) {
|
||||
delete_btf_table(btf_prog_table);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 btf_attach_point *obj;
|
||||
int n;
|
||||
|
||||
printf("%u: ", info->id);
|
||||
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)
|
||||
printf("%s%u", n++ == 0 ? " prog_ids " : ",",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
n = 0;
|
||||
hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
|
||||
if (obj->btf_id == info->id)
|
||||
printf("%s%u", n++ == 0 ? " map_ids " : ",",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
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 btf_attach_point *obj;
|
||||
|
||||
jsonw_start_object(json_wtr); /* btf object */
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
jsonw_uint_field(json_wtr, "size", info->btf_size);
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
jsonw_end_array(json_wtr); /* map_ids */
|
||||
jsonw_end_object(json_wtr); /* btf object */
|
||||
}
|
||||
|
||||
static int
|
||||
show_btf(int fd, struct btf_attach_table *btf_prog_table,
|
||||
struct btf_attach_table *btf_map_table)
|
||||
{
|
||||
struct bpf_btf_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get BTF object info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
show_btf_json(&info, fd, btf_prog_table, btf_map_table);
|
||||
else
|
||||
show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
struct btf_attach_table btf_prog_table;
|
||||
struct btf_attach_table btf_map_table;
|
||||
int err, fd = -1;
|
||||
__u32 id = 0;
|
||||
|
||||
if (argc == 2) {
|
||||
fd = btf_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
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);
|
||||
if (err) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
err = show_btf(fd, &btf_prog_table, &btf_map_table);
|
||||
close(fd);
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr); /* root array */
|
||||
|
||||
while (true) {
|
||||
err = bpf_btf_get_next_id(id, &id);
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
p_err("can't get next BTF object: %s%s",
|
||||
strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = bpf_btf_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
p_err("can't get BTF object by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
err = show_btf(fd, &btf_prog_table, &btf_map_table);
|
||||
close(fd);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr); /* root array */
|
||||
|
||||
exit_free:
|
||||
delete_btf_table(&btf_prog_table);
|
||||
delete_btf_table(&btf_map_table);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
|
@ -530,7 +865,8 @@ static int do_help(int argc, char **argv)
|
|||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s btf dump BTF_SRC [format FORMAT]\n"
|
||||
"Usage: %s btf { show | list } [id BTF_ID]\n"
|
||||
" %s btf dump BTF_SRC [format FORMAT]\n"
|
||||
" %s btf help\n"
|
||||
"\n"
|
||||
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
|
||||
|
@ -539,12 +875,14 @@ static int do_help(int argc, char **argv)
|
|||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, bin_name);
|
||||
bin_name, bin_name, bin_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "list", do_show },
|
||||
{ "help", do_help },
|
||||
{ "dump", do_dump },
|
||||
{ 0 }
|
||||
|
|
|
@ -106,6 +106,7 @@ enum bpf_cmd {
|
|||
BPF_TASK_FD_QUERY,
|
||||
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
|
||||
BPF_MAP_FREEZE,
|
||||
BPF_BTF_GET_NEXT_ID,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
|
|
|
@ -568,7 +568,7 @@ int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
|
@ -576,26 +576,26 @@ int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
|||
memset(&attr, 0, sizeof(attr));
|
||||
attr.start_id = start_id;
|
||||
|
||||
err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
|
||||
err = sys_bpf(cmd, &attr, sizeof(attr));
|
||||
if (!err)
|
||||
*next_id = attr.next_id;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
return bpf_obj_get_next_id(start_id, next_id, BPF_PROG_GET_NEXT_ID);
|
||||
}
|
||||
|
||||
int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
return bpf_obj_get_next_id(start_id, next_id, BPF_MAP_GET_NEXT_ID);
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.start_id = start_id;
|
||||
|
||||
err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
|
||||
if (!err)
|
||||
*next_id = attr.next_id;
|
||||
|
||||
return err;
|
||||
int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
|
||||
}
|
||||
|
||||
int bpf_prog_get_fd_by_id(__u32 id)
|
||||
|
|
|
@ -156,6 +156,7 @@ LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
|
|||
__u32 *retval, __u32 *duration);
|
||||
LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
|
||||
|
|
|
@ -186,4 +186,6 @@ LIBBPF_0.0.4 {
|
|||
} LIBBPF_0.0.3;
|
||||
|
||||
LIBBPF_0.0.5 {
|
||||
global:
|
||||
bpf_btf_get_next_id;
|
||||
} LIBBPF_0.0.4;
|
||||
|
|
Загрузка…
Ссылка в новой задаче