165c93628SMartin KaFai Lau // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
265c93628SMartin KaFai Lau /* Copyright (C) 2020 Facebook */
365c93628SMartin KaFai Lau
465c93628SMartin KaFai Lau #include <errno.h>
565c93628SMartin KaFai Lau #include <stdio.h>
665c93628SMartin KaFai Lau #include <unistd.h>
765c93628SMartin KaFai Lau
865c93628SMartin KaFai Lau #include <linux/err.h>
965c93628SMartin KaFai Lau
1065c93628SMartin KaFai Lau #include <bpf/bpf.h>
1165c93628SMartin KaFai Lau #include <bpf/btf.h>
1265c93628SMartin KaFai Lau #include <bpf/libbpf.h>
1365c93628SMartin KaFai Lau
1465c93628SMartin KaFai Lau #include "json_writer.h"
1565c93628SMartin KaFai Lau #include "main.h"
1665c93628SMartin KaFai Lau
1765c93628SMartin KaFai Lau #define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
1865c93628SMartin KaFai Lau
1965c93628SMartin KaFai Lau static const struct btf_type *map_info_type;
2065c93628SMartin KaFai Lau static __u32 map_info_alloc_len;
2165c93628SMartin KaFai Lau static struct btf *btf_vmlinux;
2265c93628SMartin KaFai Lau static __s32 map_info_type_id;
2365c93628SMartin KaFai Lau
2465c93628SMartin KaFai Lau struct res {
2565c93628SMartin KaFai Lau unsigned int nr_maps;
2665c93628SMartin KaFai Lau unsigned int nr_errs;
2765c93628SMartin KaFai Lau };
2865c93628SMartin KaFai Lau
get_btf_vmlinux(void)2965c93628SMartin KaFai Lau static const struct btf *get_btf_vmlinux(void)
3065c93628SMartin KaFai Lau {
3165c93628SMartin KaFai Lau if (btf_vmlinux)
3265c93628SMartin KaFai Lau return btf_vmlinux;
3365c93628SMartin KaFai Lau
3465c93628SMartin KaFai Lau btf_vmlinux = libbpf_find_kernel_btf();
35d1313e01SSahid Orentino Ferdjaoui if (!btf_vmlinux)
3665c93628SMartin KaFai Lau p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
3765c93628SMartin KaFai Lau
3865c93628SMartin KaFai Lau return btf_vmlinux;
3965c93628SMartin KaFai Lau }
4065c93628SMartin KaFai Lau
get_kern_struct_ops_name(const struct bpf_map_info * info)4165c93628SMartin KaFai Lau static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
4265c93628SMartin KaFai Lau {
4365c93628SMartin KaFai Lau const struct btf *kern_btf;
4465c93628SMartin KaFai Lau const struct btf_type *t;
4565c93628SMartin KaFai Lau const char *st_ops_name;
4665c93628SMartin KaFai Lau
4765c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux();
48d1313e01SSahid Orentino Ferdjaoui if (!kern_btf)
4965c93628SMartin KaFai Lau return "<btf_vmlinux_not_found>";
5065c93628SMartin KaFai Lau
5165c93628SMartin KaFai Lau t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
5265c93628SMartin KaFai Lau st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
5365c93628SMartin KaFai Lau st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
5465c93628SMartin KaFai Lau
5565c93628SMartin KaFai Lau return st_ops_name;
5665c93628SMartin KaFai Lau }
5765c93628SMartin KaFai Lau
get_map_info_type_id(void)5865c93628SMartin KaFai Lau static __s32 get_map_info_type_id(void)
5965c93628SMartin KaFai Lau {
6065c93628SMartin KaFai Lau const struct btf *kern_btf;
6165c93628SMartin KaFai Lau
6265c93628SMartin KaFai Lau if (map_info_type_id)
6365c93628SMartin KaFai Lau return map_info_type_id;
6465c93628SMartin KaFai Lau
6565c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux();
66989f2851SSahid Orentino Ferdjaoui if (!kern_btf)
67989f2851SSahid Orentino Ferdjaoui return 0;
6865c93628SMartin KaFai Lau
6965c93628SMartin KaFai Lau map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
7065c93628SMartin KaFai Lau BTF_KIND_STRUCT);
7165c93628SMartin KaFai Lau if (map_info_type_id < 0) {
7265c93628SMartin KaFai Lau p_err("can't find bpf_map_info from btf_vmlinux");
7365c93628SMartin KaFai Lau return map_info_type_id;
7465c93628SMartin KaFai Lau }
7565c93628SMartin KaFai Lau map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
7665c93628SMartin KaFai Lau
7765c93628SMartin KaFai Lau /* Ensure map_info_alloc() has at least what the bpftool needs */
7865c93628SMartin KaFai Lau map_info_alloc_len = map_info_type->size;
7965c93628SMartin KaFai Lau if (map_info_alloc_len < sizeof(struct bpf_map_info))
8065c93628SMartin KaFai Lau map_info_alloc_len = sizeof(struct bpf_map_info);
8165c93628SMartin KaFai Lau
8265c93628SMartin KaFai Lau return map_info_type_id;
8365c93628SMartin KaFai Lau }
8465c93628SMartin KaFai Lau
8565c93628SMartin KaFai Lau /* If the subcmd needs to print out the bpf_map_info,
8665c93628SMartin KaFai Lau * it should always call map_info_alloc to allocate
8765c93628SMartin KaFai Lau * a bpf_map_info object instead of allocating it
8865c93628SMartin KaFai Lau * on the stack.
8965c93628SMartin KaFai Lau *
9065c93628SMartin KaFai Lau * map_info_alloc() will take the running kernel's btf
9165c93628SMartin KaFai Lau * into account. i.e. it will consider the
9265c93628SMartin KaFai Lau * sizeof(struct bpf_map_info) of the running kernel.
9365c93628SMartin KaFai Lau *
9465c93628SMartin KaFai Lau * It will enable the "struct_ops" cmd to print the latest
9565c93628SMartin KaFai Lau * "struct bpf_map_info".
9665c93628SMartin KaFai Lau *
9765c93628SMartin KaFai Lau * [ Recall that "struct_ops" requires the kernel's btf to
9865c93628SMartin KaFai Lau * be available ]
9965c93628SMartin KaFai Lau */
map_info_alloc(__u32 * alloc_len)10065c93628SMartin KaFai Lau static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
10165c93628SMartin KaFai Lau {
10265c93628SMartin KaFai Lau struct bpf_map_info *info;
10365c93628SMartin KaFai Lau
10465c93628SMartin KaFai Lau if (get_map_info_type_id() < 0)
10565c93628SMartin KaFai Lau return NULL;
10665c93628SMartin KaFai Lau
10765c93628SMartin KaFai Lau info = calloc(1, map_info_alloc_len);
10865c93628SMartin KaFai Lau if (!info)
10965c93628SMartin KaFai Lau p_err("mem alloc failed");
11065c93628SMartin KaFai Lau else
11165c93628SMartin KaFai Lau *alloc_len = map_info_alloc_len;
11265c93628SMartin KaFai Lau
11365c93628SMartin KaFai Lau return info;
11465c93628SMartin KaFai Lau }
11565c93628SMartin KaFai Lau
11665c93628SMartin KaFai Lau /* It iterates all struct_ops maps of the system.
11765c93628SMartin KaFai Lau * It returns the fd in "*res_fd" and map_info in "*info".
11865c93628SMartin KaFai Lau * In the very first iteration, info->id should be 0.
11965c93628SMartin KaFai Lau * An optional map "*name" filter can be specified.
12065c93628SMartin KaFai Lau * The filter can be made more flexible in the future.
12165c93628SMartin KaFai Lau * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
12265c93628SMartin KaFai Lau *
12365c93628SMartin KaFai Lau * Return value:
12465c93628SMartin KaFai Lau * 1: A struct_ops map found. It is returned in "*res_fd" and "*info".
12565c93628SMartin KaFai Lau * The caller can continue to call get_next in the future.
12665c93628SMartin KaFai Lau * 0: No struct_ops map is returned.
12765c93628SMartin KaFai Lau * All struct_ops map has been found.
12865c93628SMartin KaFai Lau * -1: Error and the caller should abort the iteration.
12965c93628SMartin KaFai Lau */
get_next_struct_ops_map(const char * name,int * res_fd,struct bpf_map_info * info,__u32 info_len)13065c93628SMartin KaFai Lau static int get_next_struct_ops_map(const char *name, int *res_fd,
13165c93628SMartin KaFai Lau struct bpf_map_info *info, __u32 info_len)
13265c93628SMartin KaFai Lau {
13365c93628SMartin KaFai Lau __u32 id = info->id;
13465c93628SMartin KaFai Lau int err, fd;
13565c93628SMartin KaFai Lau
13665c93628SMartin KaFai Lau while (true) {
13765c93628SMartin KaFai Lau err = bpf_map_get_next_id(id, &id);
13865c93628SMartin KaFai Lau if (err) {
13965c93628SMartin KaFai Lau if (errno == ENOENT)
14065c93628SMartin KaFai Lau return 0;
14165c93628SMartin KaFai Lau p_err("can't get next map: %s", strerror(errno));
14265c93628SMartin KaFai Lau return -1;
14365c93628SMartin KaFai Lau }
14465c93628SMartin KaFai Lau
14565c93628SMartin KaFai Lau fd = bpf_map_get_fd_by_id(id);
14665c93628SMartin KaFai Lau if (fd < 0) {
14765c93628SMartin KaFai Lau if (errno == ENOENT)
14865c93628SMartin KaFai Lau continue;
14965c93628SMartin KaFai Lau p_err("can't get map by id (%u): %s",
15065c93628SMartin KaFai Lau id, strerror(errno));
15165c93628SMartin KaFai Lau return -1;
15265c93628SMartin KaFai Lau }
15365c93628SMartin KaFai Lau
15438f0408eSIlya Leoshkevich err = bpf_map_get_info_by_fd(fd, info, &info_len);
15565c93628SMartin KaFai Lau if (err) {
15665c93628SMartin KaFai Lau p_err("can't get map info: %s", strerror(errno));
15765c93628SMartin KaFai Lau close(fd);
15865c93628SMartin KaFai Lau return -1;
15965c93628SMartin KaFai Lau }
16065c93628SMartin KaFai Lau
16165c93628SMartin KaFai Lau if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
16265c93628SMartin KaFai Lau (!name || !strcmp(name, info->name))) {
16365c93628SMartin KaFai Lau *res_fd = fd;
16465c93628SMartin KaFai Lau return 1;
16565c93628SMartin KaFai Lau }
16665c93628SMartin KaFai Lau close(fd);
16765c93628SMartin KaFai Lau }
16865c93628SMartin KaFai Lau }
16965c93628SMartin KaFai Lau
cmd_retval(const struct res * res,bool must_have_one_map)17065c93628SMartin KaFai Lau static int cmd_retval(const struct res *res, bool must_have_one_map)
17165c93628SMartin KaFai Lau {
17265c93628SMartin KaFai Lau if (res->nr_errs || (!res->nr_maps && must_have_one_map))
17365c93628SMartin KaFai Lau return -1;
17465c93628SMartin KaFai Lau
17565c93628SMartin KaFai Lau return 0;
17665c93628SMartin KaFai Lau }
17765c93628SMartin KaFai Lau
17865c93628SMartin KaFai Lau /* "data" is the work_func private storage */
17965c93628SMartin KaFai Lau typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
18065c93628SMartin KaFai Lau struct json_writer *wtr);
18165c93628SMartin KaFai Lau
18265c93628SMartin KaFai Lau /* Find all struct_ops map in the system.
18365c93628SMartin KaFai Lau * Filter out by "name" (if specified).
18465c93628SMartin KaFai Lau * Then call "func(fd, info, data, wtr)" on each struct_ops map found.
18565c93628SMartin KaFai Lau */
do_search(const char * name,work_func func,void * data,struct json_writer * wtr)18665c93628SMartin KaFai Lau static struct res do_search(const char *name, work_func func, void *data,
18765c93628SMartin KaFai Lau struct json_writer *wtr)
18865c93628SMartin KaFai Lau {
18965c93628SMartin KaFai Lau struct bpf_map_info *info;
19065c93628SMartin KaFai Lau struct res res = {};
19165c93628SMartin KaFai Lau __u32 info_len;
19265c93628SMartin KaFai Lau int fd, err;
19365c93628SMartin KaFai Lau
19465c93628SMartin KaFai Lau info = map_info_alloc(&info_len);
19565c93628SMartin KaFai Lau if (!info) {
19665c93628SMartin KaFai Lau res.nr_errs++;
19765c93628SMartin KaFai Lau return res;
19865c93628SMartin KaFai Lau }
19965c93628SMartin KaFai Lau
20065c93628SMartin KaFai Lau if (wtr)
20165c93628SMartin KaFai Lau jsonw_start_array(wtr);
20265c93628SMartin KaFai Lau while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
20365c93628SMartin KaFai Lau res.nr_maps++;
20465c93628SMartin KaFai Lau err = func(fd, info, data, wtr);
20565c93628SMartin KaFai Lau if (err)
20665c93628SMartin KaFai Lau res.nr_errs++;
20765c93628SMartin KaFai Lau close(fd);
20865c93628SMartin KaFai Lau }
20965c93628SMartin KaFai Lau if (wtr)
21065c93628SMartin KaFai Lau jsonw_end_array(wtr);
21165c93628SMartin KaFai Lau
21265c93628SMartin KaFai Lau if (err)
21365c93628SMartin KaFai Lau res.nr_errs++;
21465c93628SMartin KaFai Lau
21565c93628SMartin KaFai Lau if (!wtr && name && !res.nr_errs && !res.nr_maps)
21665c93628SMartin KaFai Lau /* It is not printing empty [].
21765c93628SMartin KaFai Lau * Thus, needs to specifically say nothing found
21865c93628SMartin KaFai Lau * for "name" here.
21965c93628SMartin KaFai Lau */
22065c93628SMartin KaFai Lau p_err("no struct_ops found for %s", name);
22165c93628SMartin KaFai Lau else if (!wtr && json_output && !res.nr_errs)
22265c93628SMartin KaFai Lau /* The "func()" above is not writing any json (i.e. !wtr
22365c93628SMartin KaFai Lau * test here).
22465c93628SMartin KaFai Lau *
22565c93628SMartin KaFai Lau * However, "-j" is enabled and there is no errs here,
22665c93628SMartin KaFai Lau * so call json_null() as the current convention of
22765c93628SMartin KaFai Lau * other cmds.
22865c93628SMartin KaFai Lau */
22965c93628SMartin KaFai Lau jsonw_null(json_wtr);
23065c93628SMartin KaFai Lau
23165c93628SMartin KaFai Lau free(info);
23265c93628SMartin KaFai Lau return res;
23365c93628SMartin KaFai Lau }
23465c93628SMartin KaFai Lau
do_one_id(const char * id_str,work_func func,void * data,struct json_writer * wtr)23565c93628SMartin KaFai Lau static struct res do_one_id(const char *id_str, work_func func, void *data,
23665c93628SMartin KaFai Lau struct json_writer *wtr)
23765c93628SMartin KaFai Lau {
23865c93628SMartin KaFai Lau struct bpf_map_info *info;
23965c93628SMartin KaFai Lau struct res res = {};
24065c93628SMartin KaFai Lau unsigned long id;
24165c93628SMartin KaFai Lau __u32 info_len;
24265c93628SMartin KaFai Lau char *endptr;
24365c93628SMartin KaFai Lau int fd;
24465c93628SMartin KaFai Lau
24565c93628SMartin KaFai Lau id = strtoul(id_str, &endptr, 0);
24665c93628SMartin KaFai Lau if (*endptr || !id || id > UINT32_MAX) {
24765c93628SMartin KaFai Lau p_err("invalid id %s", id_str);
24865c93628SMartin KaFai Lau res.nr_errs++;
24965c93628SMartin KaFai Lau return res;
25065c93628SMartin KaFai Lau }
25165c93628SMartin KaFai Lau
25265c93628SMartin KaFai Lau fd = bpf_map_get_fd_by_id(id);
25360f27075SDave Marchevsky if (fd < 0) {
25465c93628SMartin KaFai Lau p_err("can't get map by id (%lu): %s", id, strerror(errno));
25565c93628SMartin KaFai Lau res.nr_errs++;
25665c93628SMartin KaFai Lau return res;
25765c93628SMartin KaFai Lau }
25865c93628SMartin KaFai Lau
25965c93628SMartin KaFai Lau info = map_info_alloc(&info_len);
26065c93628SMartin KaFai Lau if (!info) {
26165c93628SMartin KaFai Lau res.nr_errs++;
26265c93628SMartin KaFai Lau goto done;
26365c93628SMartin KaFai Lau }
26465c93628SMartin KaFai Lau
26538f0408eSIlya Leoshkevich if (bpf_map_get_info_by_fd(fd, info, &info_len)) {
26665c93628SMartin KaFai Lau p_err("can't get map info: %s", strerror(errno));
26765c93628SMartin KaFai Lau res.nr_errs++;
26865c93628SMartin KaFai Lau goto done;
26965c93628SMartin KaFai Lau }
27065c93628SMartin KaFai Lau
27165c93628SMartin KaFai Lau if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
27265c93628SMartin KaFai Lau p_err("%s id %u is not a struct_ops map", info->name, info->id);
27365c93628SMartin KaFai Lau res.nr_errs++;
27465c93628SMartin KaFai Lau goto done;
27565c93628SMartin KaFai Lau }
27665c93628SMartin KaFai Lau
27765c93628SMartin KaFai Lau res.nr_maps++;
27865c93628SMartin KaFai Lau
27965c93628SMartin KaFai Lau if (func(fd, info, data, wtr))
28065c93628SMartin KaFai Lau res.nr_errs++;
28165c93628SMartin KaFai Lau else if (!wtr && json_output)
28265c93628SMartin KaFai Lau /* The "func()" above is not writing any json (i.e. !wtr
28365c93628SMartin KaFai Lau * test here).
28465c93628SMartin KaFai Lau *
28565c93628SMartin KaFai Lau * However, "-j" is enabled and there is no errs here,
28665c93628SMartin KaFai Lau * so call json_null() as the current convention of
28765c93628SMartin KaFai Lau * other cmds.
28865c93628SMartin KaFai Lau */
28965c93628SMartin KaFai Lau jsonw_null(json_wtr);
29065c93628SMartin KaFai Lau
29165c93628SMartin KaFai Lau done:
29265c93628SMartin KaFai Lau free(info);
29365c93628SMartin KaFai Lau close(fd);
29465c93628SMartin KaFai Lau
29565c93628SMartin KaFai Lau return res;
29665c93628SMartin KaFai Lau }
29765c93628SMartin KaFai Lau
do_work_on_struct_ops(const char * search_type,const char * search_term,work_func func,void * data,struct json_writer * wtr)29865c93628SMartin KaFai Lau static struct res do_work_on_struct_ops(const char *search_type,
29965c93628SMartin KaFai Lau const char *search_term,
30065c93628SMartin KaFai Lau work_func func, void *data,
30165c93628SMartin KaFai Lau struct json_writer *wtr)
30265c93628SMartin KaFai Lau {
30365c93628SMartin KaFai Lau if (search_type) {
30465c93628SMartin KaFai Lau if (is_prefix(search_type, "id"))
30565c93628SMartin KaFai Lau return do_one_id(search_term, func, data, wtr);
30665c93628SMartin KaFai Lau else if (!is_prefix(search_type, "name"))
30765c93628SMartin KaFai Lau usage();
30865c93628SMartin KaFai Lau }
30965c93628SMartin KaFai Lau
31065c93628SMartin KaFai Lau return do_search(search_term, func, data, wtr);
31165c93628SMartin KaFai Lau }
31265c93628SMartin KaFai Lau
__do_show(int fd,const struct bpf_map_info * info,void * data,struct json_writer * wtr)31365c93628SMartin KaFai Lau static int __do_show(int fd, const struct bpf_map_info *info, void *data,
31465c93628SMartin KaFai Lau struct json_writer *wtr)
31565c93628SMartin KaFai Lau {
31665c93628SMartin KaFai Lau if (wtr) {
31765c93628SMartin KaFai Lau jsonw_start_object(wtr);
31865c93628SMartin KaFai Lau jsonw_uint_field(wtr, "id", info->id);
31965c93628SMartin KaFai Lau jsonw_string_field(wtr, "name", info->name);
32065c93628SMartin KaFai Lau jsonw_string_field(wtr, "kernel_struct_ops",
32165c93628SMartin KaFai Lau get_kern_struct_ops_name(info));
32265c93628SMartin KaFai Lau jsonw_end_object(wtr);
32365c93628SMartin KaFai Lau } else {
32465c93628SMartin KaFai Lau printf("%u: %-15s %-32s\n", info->id, info->name,
32565c93628SMartin KaFai Lau get_kern_struct_ops_name(info));
32665c93628SMartin KaFai Lau }
32765c93628SMartin KaFai Lau
32865c93628SMartin KaFai Lau return 0;
32965c93628SMartin KaFai Lau }
33065c93628SMartin KaFai Lau
do_show(int argc,char ** argv)33165c93628SMartin KaFai Lau static int do_show(int argc, char **argv)
33265c93628SMartin KaFai Lau {
33365c93628SMartin KaFai Lau const char *search_type = NULL, *search_term = NULL;
33465c93628SMartin KaFai Lau struct res res;
33565c93628SMartin KaFai Lau
33665c93628SMartin KaFai Lau if (argc && argc != 2)
33765c93628SMartin KaFai Lau usage();
33865c93628SMartin KaFai Lau
33965c93628SMartin KaFai Lau if (argc == 2) {
34065c93628SMartin KaFai Lau search_type = GET_ARG();
34165c93628SMartin KaFai Lau search_term = GET_ARG();
34265c93628SMartin KaFai Lau }
34365c93628SMartin KaFai Lau
34465c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term, __do_show,
34565c93628SMartin KaFai Lau NULL, json_wtr);
34665c93628SMartin KaFai Lau
34765c93628SMartin KaFai Lau return cmd_retval(&res, !!search_term);
34865c93628SMartin KaFai Lau }
34965c93628SMartin KaFai Lau
__do_dump(int fd,const struct bpf_map_info * info,void * data,struct json_writer * wtr)35065c93628SMartin KaFai Lau static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
35165c93628SMartin KaFai Lau struct json_writer *wtr)
35265c93628SMartin KaFai Lau {
35365c93628SMartin KaFai Lau struct btf_dumper *d = (struct btf_dumper *)data;
35465c93628SMartin KaFai Lau const struct btf_type *struct_ops_type;
35565c93628SMartin KaFai Lau const struct btf *kern_btf = d->btf;
35665c93628SMartin KaFai Lau const char *struct_ops_name;
35765c93628SMartin KaFai Lau int zero = 0;
35865c93628SMartin KaFai Lau void *value;
35965c93628SMartin KaFai Lau
36065c93628SMartin KaFai Lau /* note: d->jw == wtr */
36165c93628SMartin KaFai Lau
36265c93628SMartin KaFai Lau kern_btf = d->btf;
36365c93628SMartin KaFai Lau
36465c93628SMartin KaFai Lau /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
36565c93628SMartin KaFai Lau * btf_vmlinux_value_type_id.
36665c93628SMartin KaFai Lau */
36765c93628SMartin KaFai Lau struct_ops_type = btf__type_by_id(kern_btf,
36865c93628SMartin KaFai Lau info->btf_vmlinux_value_type_id);
36965c93628SMartin KaFai Lau struct_ops_name = btf__name_by_offset(kern_btf,
37065c93628SMartin KaFai Lau struct_ops_type->name_off);
37165c93628SMartin KaFai Lau value = calloc(1, info->value_size);
37265c93628SMartin KaFai Lau if (!value) {
37365c93628SMartin KaFai Lau p_err("mem alloc failed");
37465c93628SMartin KaFai Lau return -1;
37565c93628SMartin KaFai Lau }
37665c93628SMartin KaFai Lau
37765c93628SMartin KaFai Lau if (bpf_map_lookup_elem(fd, &zero, value)) {
37865c93628SMartin KaFai Lau p_err("can't lookup struct_ops map %s id %u",
37965c93628SMartin KaFai Lau info->name, info->id);
38065c93628SMartin KaFai Lau free(value);
38165c93628SMartin KaFai Lau return -1;
38265c93628SMartin KaFai Lau }
38365c93628SMartin KaFai Lau
38465c93628SMartin KaFai Lau jsonw_start_object(wtr);
38565c93628SMartin KaFai Lau jsonw_name(wtr, "bpf_map_info");
38665c93628SMartin KaFai Lau btf_dumper_type(d, map_info_type_id, (void *)info);
38765c93628SMartin KaFai Lau jsonw_end_object(wtr);
38865c93628SMartin KaFai Lau
38965c93628SMartin KaFai Lau jsonw_start_object(wtr);
39065c93628SMartin KaFai Lau jsonw_name(wtr, struct_ops_name);
39165c93628SMartin KaFai Lau btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
39265c93628SMartin KaFai Lau jsonw_end_object(wtr);
39365c93628SMartin KaFai Lau
39465c93628SMartin KaFai Lau free(value);
39565c93628SMartin KaFai Lau
39665c93628SMartin KaFai Lau return 0;
39765c93628SMartin KaFai Lau }
39865c93628SMartin KaFai Lau
do_dump(int argc,char ** argv)39965c93628SMartin KaFai Lau static int do_dump(int argc, char **argv)
40065c93628SMartin KaFai Lau {
40165c93628SMartin KaFai Lau const char *search_type = NULL, *search_term = NULL;
40265c93628SMartin KaFai Lau json_writer_t *wtr = json_wtr;
40365c93628SMartin KaFai Lau const struct btf *kern_btf;
40465c93628SMartin KaFai Lau struct btf_dumper d = {};
40565c93628SMartin KaFai Lau struct res res;
40665c93628SMartin KaFai Lau
40765c93628SMartin KaFai Lau if (argc && argc != 2)
40865c93628SMartin KaFai Lau usage();
40965c93628SMartin KaFai Lau
41065c93628SMartin KaFai Lau if (argc == 2) {
41165c93628SMartin KaFai Lau search_type = GET_ARG();
41265c93628SMartin KaFai Lau search_term = GET_ARG();
41365c93628SMartin KaFai Lau }
41465c93628SMartin KaFai Lau
41565c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux();
416d1313e01SSahid Orentino Ferdjaoui if (!kern_btf)
41765c93628SMartin KaFai Lau return -1;
41865c93628SMartin KaFai Lau
41965c93628SMartin KaFai Lau if (!json_output) {
42065c93628SMartin KaFai Lau wtr = jsonw_new(stdout);
42165c93628SMartin KaFai Lau if (!wtr) {
42265c93628SMartin KaFai Lau p_err("can't create json writer");
42365c93628SMartin KaFai Lau return -1;
42465c93628SMartin KaFai Lau }
42565c93628SMartin KaFai Lau jsonw_pretty(wtr, true);
42665c93628SMartin KaFai Lau }
42765c93628SMartin KaFai Lau
42865c93628SMartin KaFai Lau d.btf = kern_btf;
42965c93628SMartin KaFai Lau d.jw = wtr;
43065c93628SMartin KaFai Lau d.is_plain_text = !json_output;
43165c93628SMartin KaFai Lau d.prog_id_as_func_ptr = true;
43265c93628SMartin KaFai Lau
43365c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
43465c93628SMartin KaFai Lau wtr);
43565c93628SMartin KaFai Lau
43665c93628SMartin KaFai Lau if (!json_output)
43765c93628SMartin KaFai Lau jsonw_destroy(&wtr);
43865c93628SMartin KaFai Lau
43965c93628SMartin KaFai Lau return cmd_retval(&res, !!search_term);
44065c93628SMartin KaFai Lau }
44165c93628SMartin KaFai Lau
__do_unregister(int fd,const struct bpf_map_info * info,void * data,struct json_writer * wtr)44265c93628SMartin KaFai Lau static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
44365c93628SMartin KaFai Lau struct json_writer *wtr)
44465c93628SMartin KaFai Lau {
44565c93628SMartin KaFai Lau int zero = 0;
44665c93628SMartin KaFai Lau
44765c93628SMartin KaFai Lau if (bpf_map_delete_elem(fd, &zero)) {
44865c93628SMartin KaFai Lau p_err("can't unload %s %s id %u: %s",
44965c93628SMartin KaFai Lau get_kern_struct_ops_name(info), info->name,
45065c93628SMartin KaFai Lau info->id, strerror(errno));
45165c93628SMartin KaFai Lau return -1;
45265c93628SMartin KaFai Lau }
45365c93628SMartin KaFai Lau
45465c93628SMartin KaFai Lau p_info("Unregistered %s %s id %u",
45565c93628SMartin KaFai Lau get_kern_struct_ops_name(info), info->name,
45665c93628SMartin KaFai Lau info->id);
45765c93628SMartin KaFai Lau
45865c93628SMartin KaFai Lau return 0;
45965c93628SMartin KaFai Lau }
46065c93628SMartin KaFai Lau
do_unregister(int argc,char ** argv)46165c93628SMartin KaFai Lau static int do_unregister(int argc, char **argv)
46265c93628SMartin KaFai Lau {
46365c93628SMartin KaFai Lau const char *search_type, *search_term;
46465c93628SMartin KaFai Lau struct res res;
46565c93628SMartin KaFai Lau
46665c93628SMartin KaFai Lau if (argc != 2)
46765c93628SMartin KaFai Lau usage();
46865c93628SMartin KaFai Lau
46965c93628SMartin KaFai Lau search_type = GET_ARG();
47065c93628SMartin KaFai Lau search_term = GET_ARG();
47165c93628SMartin KaFai Lau
47265c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term,
47365c93628SMartin KaFai Lau __do_unregister, NULL, NULL);
47465c93628SMartin KaFai Lau
47565c93628SMartin KaFai Lau return cmd_retval(&res, true);
47665c93628SMartin KaFai Lau }
47765c93628SMartin KaFai Lau
pin_link(struct bpf_link * link,const char * pindir,const char * name)4780232b788SKui-Feng Lee static int pin_link(struct bpf_link *link, const char *pindir,
4790232b788SKui-Feng Lee const char *name)
4800232b788SKui-Feng Lee {
4810232b788SKui-Feng Lee char pinfile[PATH_MAX];
4820232b788SKui-Feng Lee int err;
4830232b788SKui-Feng Lee
4840232b788SKui-Feng Lee err = pathname_concat(pinfile, sizeof(pinfile), pindir, name);
4850232b788SKui-Feng Lee if (err)
4860232b788SKui-Feng Lee return -1;
4870232b788SKui-Feng Lee
4880232b788SKui-Feng Lee return bpf_link__pin(link, pinfile);
4890232b788SKui-Feng Lee }
4900232b788SKui-Feng Lee
do_register(int argc,char ** argv)49165c93628SMartin KaFai Lau static int do_register(int argc, char **argv)
49265c93628SMartin KaFai Lau {
493b59e4ce8SAndrii Nakryiko LIBBPF_OPTS(bpf_object_open_opts, open_opts);
4940232b788SKui-Feng Lee __u32 link_info_len = sizeof(struct bpf_link_info);
4950232b788SKui-Feng Lee struct bpf_link_info link_info = {};
49665c93628SMartin KaFai Lau struct bpf_map_info info = {};
49765c93628SMartin KaFai Lau __u32 info_len = sizeof(info);
49865c93628SMartin KaFai Lau int nr_errs = 0, nr_maps = 0;
4990232b788SKui-Feng Lee const char *linkdir = NULL;
50065c93628SMartin KaFai Lau struct bpf_object *obj;
50165c93628SMartin KaFai Lau struct bpf_link *link;
50265c93628SMartin KaFai Lau struct bpf_map *map;
50365c93628SMartin KaFai Lau const char *file;
50465c93628SMartin KaFai Lau
5050232b788SKui-Feng Lee if (argc != 1 && argc != 2)
50665c93628SMartin KaFai Lau usage();
50765c93628SMartin KaFai Lau
50865c93628SMartin KaFai Lau file = GET_ARG();
5090232b788SKui-Feng Lee if (argc == 1)
5100232b788SKui-Feng Lee linkdir = GET_ARG();
5110232b788SKui-Feng Lee
512*f92aebf1SSahil Siddiq if (linkdir && create_and_mount_bpffs_dir(linkdir)) {
5130232b788SKui-Feng Lee p_err("can't mount bpffs for pinning");
5140232b788SKui-Feng Lee return -1;
5150232b788SKui-Feng Lee }
51665c93628SMartin KaFai Lau
517b59e4ce8SAndrii Nakryiko if (verifier_logs)
518b59e4ce8SAndrii Nakryiko /* log_level1 + log_level2 + stats, but not stable UAPI */
519b59e4ce8SAndrii Nakryiko open_opts.kernel_log_level = 1 + 2 + 4;
520b59e4ce8SAndrii Nakryiko
521b59e4ce8SAndrii Nakryiko obj = bpf_object__open_file(file, &open_opts);
522d1313e01SSahid Orentino Ferdjaoui if (!obj)
52365c93628SMartin KaFai Lau return -1;
52465c93628SMartin KaFai Lau
5256b4384ffSQuentin Monnet set_max_rlimit();
5266b4384ffSQuentin Monnet
527b59e4ce8SAndrii Nakryiko if (bpf_object__load(obj)) {
52865c93628SMartin KaFai Lau bpf_object__close(obj);
52965c93628SMartin KaFai Lau return -1;
53065c93628SMartin KaFai Lau }
53165c93628SMartin KaFai Lau
53265c93628SMartin KaFai Lau bpf_object__for_each_map(map, obj) {
5333c28919fSChristy Lee if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
53465c93628SMartin KaFai Lau continue;
53565c93628SMartin KaFai Lau
53665c93628SMartin KaFai Lau link = bpf_map__attach_struct_ops(map);
537d2973ffdSSahid Orentino Ferdjaoui if (!link) {
53865c93628SMartin KaFai Lau p_err("can't register struct_ops %s: %s",
539d2973ffdSSahid Orentino Ferdjaoui bpf_map__name(map), strerror(errno));
54065c93628SMartin KaFai Lau nr_errs++;
54165c93628SMartin KaFai Lau continue;
54265c93628SMartin KaFai Lau }
54365c93628SMartin KaFai Lau nr_maps++;
54465c93628SMartin KaFai Lau
5450232b788SKui-Feng Lee if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info,
5460232b788SKui-Feng Lee &info_len)) {
54765c93628SMartin KaFai Lau /* Not p_err. The struct_ops was attached
54865c93628SMartin KaFai Lau * successfully.
54965c93628SMartin KaFai Lau */
55065c93628SMartin KaFai Lau p_info("Registered %s but can't find id: %s",
55165c93628SMartin KaFai Lau bpf_map__name(map), strerror(errno));
5520232b788SKui-Feng Lee goto clean_link;
5530232b788SKui-Feng Lee }
5540232b788SKui-Feng Lee if (!(bpf_map__map_flags(map) & BPF_F_LINK)) {
5550232b788SKui-Feng Lee p_info("Registered %s %s id %u",
5560232b788SKui-Feng Lee get_kern_struct_ops_name(&info),
5570232b788SKui-Feng Lee info.name,
5580232b788SKui-Feng Lee info.id);
5590232b788SKui-Feng Lee goto clean_link;
5600232b788SKui-Feng Lee }
5610232b788SKui-Feng Lee if (bpf_link_get_info_by_fd(bpf_link__fd(link),
5620232b788SKui-Feng Lee &link_info,
5630232b788SKui-Feng Lee &link_info_len)) {
5640232b788SKui-Feng Lee p_err("Registered %s but can't find link id: %s",
5650232b788SKui-Feng Lee bpf_map__name(map), strerror(errno));
5660232b788SKui-Feng Lee nr_errs++;
5670232b788SKui-Feng Lee goto clean_link;
5680232b788SKui-Feng Lee }
5690232b788SKui-Feng Lee if (linkdir && pin_link(link, linkdir, info.name)) {
5700232b788SKui-Feng Lee p_err("can't pin link %u for %s: %s",
5710232b788SKui-Feng Lee link_info.id, info.name,
5720232b788SKui-Feng Lee strerror(errno));
5730232b788SKui-Feng Lee nr_errs++;
5740232b788SKui-Feng Lee goto clean_link;
5750232b788SKui-Feng Lee }
5760232b788SKui-Feng Lee p_info("Registered %s %s map id %u link id %u",
5770232b788SKui-Feng Lee get_kern_struct_ops_name(&info),
5780232b788SKui-Feng Lee info.name, info.id, link_info.id);
5790232b788SKui-Feng Lee
5800232b788SKui-Feng Lee clean_link:
5810232b788SKui-Feng Lee bpf_link__disconnect(link);
5820232b788SKui-Feng Lee bpf_link__destroy(link);
58365c93628SMartin KaFai Lau }
58465c93628SMartin KaFai Lau
58565c93628SMartin KaFai Lau bpf_object__close(obj);
58665c93628SMartin KaFai Lau
58765c93628SMartin KaFai Lau if (nr_errs)
58865c93628SMartin KaFai Lau return -1;
58965c93628SMartin KaFai Lau
59065c93628SMartin KaFai Lau if (!nr_maps) {
59165c93628SMartin KaFai Lau p_err("no struct_ops found in %s", file);
59265c93628SMartin KaFai Lau return -1;
59365c93628SMartin KaFai Lau }
59465c93628SMartin KaFai Lau
59565c93628SMartin KaFai Lau if (json_output)
59665c93628SMartin KaFai Lau jsonw_null(json_wtr);
59765c93628SMartin KaFai Lau
59865c93628SMartin KaFai Lau return 0;
59965c93628SMartin KaFai Lau }
60065c93628SMartin KaFai Lau
do_help(int argc,char ** argv)60165c93628SMartin KaFai Lau static int do_help(int argc, char **argv)
60265c93628SMartin KaFai Lau {
60365c93628SMartin KaFai Lau if (json_output) {
60465c93628SMartin KaFai Lau jsonw_null(json_wtr);
60565c93628SMartin KaFai Lau return 0;
60665c93628SMartin KaFai Lau }
60765c93628SMartin KaFai Lau
60865c93628SMartin KaFai Lau fprintf(stderr,
60990040351SQuentin Monnet "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
61090040351SQuentin Monnet " %1$s %2$s dump [STRUCT_OPS_MAP]\n"
6110232b788SKui-Feng Lee " %1$s %2$s register OBJ [LINK_DIR]\n"
61290040351SQuentin Monnet " %1$s %2$s unregister STRUCT_OPS_MAP\n"
61390040351SQuentin Monnet " %1$s %2$s help\n"
61465c93628SMartin KaFai Lau "\n"
61590040351SQuentin Monnet " STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
616c07ba629SQuentin Monnet " " HELP_SPEC_OPTIONS " }\n"
61790040351SQuentin Monnet "",
61865c93628SMartin KaFai Lau bin_name, argv[-2]);
61965c93628SMartin KaFai Lau
62065c93628SMartin KaFai Lau return 0;
62165c93628SMartin KaFai Lau }
62265c93628SMartin KaFai Lau
62365c93628SMartin KaFai Lau static const struct cmd cmds[] = {
62465c93628SMartin KaFai Lau { "show", do_show },
62565c93628SMartin KaFai Lau { "list", do_show },
62665c93628SMartin KaFai Lau { "register", do_register },
62765c93628SMartin KaFai Lau { "unregister", do_unregister },
62865c93628SMartin KaFai Lau { "dump", do_dump },
62965c93628SMartin KaFai Lau { "help", do_help },
63065c93628SMartin KaFai Lau { 0 }
63165c93628SMartin KaFai Lau };
63265c93628SMartin KaFai Lau
do_struct_ops(int argc,char ** argv)63365c93628SMartin KaFai Lau int do_struct_ops(int argc, char **argv)
63465c93628SMartin KaFai Lau {
63565c93628SMartin KaFai Lau int err;
63665c93628SMartin KaFai Lau
63765c93628SMartin KaFai Lau err = cmd_select(cmds, argc, argv, do_help);
63865c93628SMartin KaFai Lau
63965c93628SMartin KaFai Lau btf__free(btf_vmlinux);
64096b2eb6eSDaniel T. Lee
64165c93628SMartin KaFai Lau return err;
64265c93628SMartin KaFai Lau }
643