xref: /openbmc/linux/tools/bpf/bpftool/struct_ops.c (revision c07ba629df97b796ca7bbdfbf4748266ead27745)
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 
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();
3565c93628SMartin KaFai Lau 	if (IS_ERR(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 
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();
4865c93628SMartin KaFai Lau 	if (IS_ERR(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 
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();
6665c93628SMartin KaFai Lau 	if (IS_ERR(kern_btf)) {
6765c93628SMartin KaFai Lau 		map_info_type_id = PTR_ERR(kern_btf);
6865c93628SMartin KaFai Lau 		return map_info_type_id;
6965c93628SMartin KaFai Lau 	}
7065c93628SMartin KaFai Lau 
7165c93628SMartin KaFai Lau 	map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
7265c93628SMartin KaFai Lau 						  BTF_KIND_STRUCT);
7365c93628SMartin KaFai Lau 	if (map_info_type_id < 0) {
7465c93628SMartin KaFai Lau 		p_err("can't find bpf_map_info from btf_vmlinux");
7565c93628SMartin KaFai Lau 		return map_info_type_id;
7665c93628SMartin KaFai Lau 	}
7765c93628SMartin KaFai Lau 	map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
7865c93628SMartin KaFai Lau 
7965c93628SMartin KaFai Lau 	/* Ensure map_info_alloc() has at least what the bpftool needs */
8065c93628SMartin KaFai Lau 	map_info_alloc_len = map_info_type->size;
8165c93628SMartin KaFai Lau 	if (map_info_alloc_len < sizeof(struct bpf_map_info))
8265c93628SMartin KaFai Lau 		map_info_alloc_len = sizeof(struct bpf_map_info);
8365c93628SMartin KaFai Lau 
8465c93628SMartin KaFai Lau 	return map_info_type_id;
8565c93628SMartin KaFai Lau }
8665c93628SMartin KaFai Lau 
8765c93628SMartin KaFai Lau /* If the subcmd needs to print out the bpf_map_info,
8865c93628SMartin KaFai Lau  * it should always call map_info_alloc to allocate
8965c93628SMartin KaFai Lau  * a bpf_map_info object instead of allocating it
9065c93628SMartin KaFai Lau  * on the stack.
9165c93628SMartin KaFai Lau  *
9265c93628SMartin KaFai Lau  * map_info_alloc() will take the running kernel's btf
9365c93628SMartin KaFai Lau  * into account.  i.e. it will consider the
9465c93628SMartin KaFai Lau  * sizeof(struct bpf_map_info) of the running kernel.
9565c93628SMartin KaFai Lau  *
9665c93628SMartin KaFai Lau  * It will enable the "struct_ops" cmd to print the latest
9765c93628SMartin KaFai Lau  * "struct bpf_map_info".
9865c93628SMartin KaFai Lau  *
9965c93628SMartin KaFai Lau  * [ Recall that "struct_ops" requires the kernel's btf to
10065c93628SMartin KaFai Lau  *   be available ]
10165c93628SMartin KaFai Lau  */
10265c93628SMartin KaFai Lau static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
10365c93628SMartin KaFai Lau {
10465c93628SMartin KaFai Lau 	struct bpf_map_info *info;
10565c93628SMartin KaFai Lau 
10665c93628SMartin KaFai Lau 	if (get_map_info_type_id() < 0)
10765c93628SMartin KaFai Lau 		return NULL;
10865c93628SMartin KaFai Lau 
10965c93628SMartin KaFai Lau 	info = calloc(1, map_info_alloc_len);
11065c93628SMartin KaFai Lau 	if (!info)
11165c93628SMartin KaFai Lau 		p_err("mem alloc failed");
11265c93628SMartin KaFai Lau 	else
11365c93628SMartin KaFai Lau 		*alloc_len = map_info_alloc_len;
11465c93628SMartin KaFai Lau 
11565c93628SMartin KaFai Lau 	return info;
11665c93628SMartin KaFai Lau }
11765c93628SMartin KaFai Lau 
11865c93628SMartin KaFai Lau /* It iterates all struct_ops maps of the system.
11965c93628SMartin KaFai Lau  * It returns the fd in "*res_fd" and map_info in "*info".
12065c93628SMartin KaFai Lau  * In the very first iteration, info->id should be 0.
12165c93628SMartin KaFai Lau  * An optional map "*name" filter can be specified.
12265c93628SMartin KaFai Lau  * The filter can be made more flexible in the future.
12365c93628SMartin KaFai Lau  * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
12465c93628SMartin KaFai Lau  *
12565c93628SMartin KaFai Lau  * Return value:
12665c93628SMartin KaFai Lau  *     1: A struct_ops map found.  It is returned in "*res_fd" and "*info".
12765c93628SMartin KaFai Lau  *	  The caller can continue to call get_next in the future.
12865c93628SMartin KaFai Lau  *     0: No struct_ops map is returned.
12965c93628SMartin KaFai Lau  *        All struct_ops map has been found.
13065c93628SMartin KaFai Lau  *    -1: Error and the caller should abort the iteration.
13165c93628SMartin KaFai Lau  */
13265c93628SMartin KaFai Lau static int get_next_struct_ops_map(const char *name, int *res_fd,
13365c93628SMartin KaFai Lau 				   struct bpf_map_info *info, __u32 info_len)
13465c93628SMartin KaFai Lau {
13565c93628SMartin KaFai Lau 	__u32 id = info->id;
13665c93628SMartin KaFai Lau 	int err, fd;
13765c93628SMartin KaFai Lau 
13865c93628SMartin KaFai Lau 	while (true) {
13965c93628SMartin KaFai Lau 		err = bpf_map_get_next_id(id, &id);
14065c93628SMartin KaFai Lau 		if (err) {
14165c93628SMartin KaFai Lau 			if (errno == ENOENT)
14265c93628SMartin KaFai Lau 				return 0;
14365c93628SMartin KaFai Lau 			p_err("can't get next map: %s", strerror(errno));
14465c93628SMartin KaFai Lau 			return -1;
14565c93628SMartin KaFai Lau 		}
14665c93628SMartin KaFai Lau 
14765c93628SMartin KaFai Lau 		fd = bpf_map_get_fd_by_id(id);
14865c93628SMartin KaFai Lau 		if (fd < 0) {
14965c93628SMartin KaFai Lau 			if (errno == ENOENT)
15065c93628SMartin KaFai Lau 				continue;
15165c93628SMartin KaFai Lau 			p_err("can't get map by id (%u): %s",
15265c93628SMartin KaFai Lau 			      id, strerror(errno));
15365c93628SMartin KaFai Lau 			return -1;
15465c93628SMartin KaFai Lau 		}
15565c93628SMartin KaFai Lau 
15665c93628SMartin KaFai Lau 		err = bpf_obj_get_info_by_fd(fd, info, &info_len);
15765c93628SMartin KaFai Lau 		if (err) {
15865c93628SMartin KaFai Lau 			p_err("can't get map info: %s", strerror(errno));
15965c93628SMartin KaFai Lau 			close(fd);
16065c93628SMartin KaFai Lau 			return -1;
16165c93628SMartin KaFai Lau 		}
16265c93628SMartin KaFai Lau 
16365c93628SMartin KaFai Lau 		if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
16465c93628SMartin KaFai Lau 		    (!name || !strcmp(name, info->name))) {
16565c93628SMartin KaFai Lau 			*res_fd = fd;
16665c93628SMartin KaFai Lau 			return 1;
16765c93628SMartin KaFai Lau 		}
16865c93628SMartin KaFai Lau 		close(fd);
16965c93628SMartin KaFai Lau 	}
17065c93628SMartin KaFai Lau }
17165c93628SMartin KaFai Lau 
17265c93628SMartin KaFai Lau static int cmd_retval(const struct res *res, bool must_have_one_map)
17365c93628SMartin KaFai Lau {
17465c93628SMartin KaFai Lau 	if (res->nr_errs || (!res->nr_maps && must_have_one_map))
17565c93628SMartin KaFai Lau 		return -1;
17665c93628SMartin KaFai Lau 
17765c93628SMartin KaFai Lau 	return 0;
17865c93628SMartin KaFai Lau }
17965c93628SMartin KaFai Lau 
18065c93628SMartin KaFai Lau /* "data" is the work_func private storage */
18165c93628SMartin KaFai Lau typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
18265c93628SMartin KaFai Lau 			 struct json_writer *wtr);
18365c93628SMartin KaFai Lau 
18465c93628SMartin KaFai Lau /* Find all struct_ops map in the system.
18565c93628SMartin KaFai Lau  * Filter out by "name" (if specified).
18665c93628SMartin KaFai Lau  * Then call "func(fd, info, data, wtr)" on each struct_ops map found.
18765c93628SMartin KaFai Lau  */
18865c93628SMartin KaFai Lau static struct res do_search(const char *name, work_func func, void *data,
18965c93628SMartin KaFai Lau 			    struct json_writer *wtr)
19065c93628SMartin KaFai Lau {
19165c93628SMartin KaFai Lau 	struct bpf_map_info *info;
19265c93628SMartin KaFai Lau 	struct res res = {};
19365c93628SMartin KaFai Lau 	__u32 info_len;
19465c93628SMartin KaFai Lau 	int fd, err;
19565c93628SMartin KaFai Lau 
19665c93628SMartin KaFai Lau 	info = map_info_alloc(&info_len);
19765c93628SMartin KaFai Lau 	if (!info) {
19865c93628SMartin KaFai Lau 		res.nr_errs++;
19965c93628SMartin KaFai Lau 		return res;
20065c93628SMartin KaFai Lau 	}
20165c93628SMartin KaFai Lau 
20265c93628SMartin KaFai Lau 	if (wtr)
20365c93628SMartin KaFai Lau 		jsonw_start_array(wtr);
20465c93628SMartin KaFai Lau 	while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
20565c93628SMartin KaFai Lau 		res.nr_maps++;
20665c93628SMartin KaFai Lau 		err = func(fd, info, data, wtr);
20765c93628SMartin KaFai Lau 		if (err)
20865c93628SMartin KaFai Lau 			res.nr_errs++;
20965c93628SMartin KaFai Lau 		close(fd);
21065c93628SMartin KaFai Lau 	}
21165c93628SMartin KaFai Lau 	if (wtr)
21265c93628SMartin KaFai Lau 		jsonw_end_array(wtr);
21365c93628SMartin KaFai Lau 
21465c93628SMartin KaFai Lau 	if (err)
21565c93628SMartin KaFai Lau 		res.nr_errs++;
21665c93628SMartin KaFai Lau 
21765c93628SMartin KaFai Lau 	if (!wtr && name && !res.nr_errs && !res.nr_maps)
21865c93628SMartin KaFai Lau 		/* It is not printing empty [].
21965c93628SMartin KaFai Lau 		 * Thus, needs to specifically say nothing found
22065c93628SMartin KaFai Lau 		 * for "name" here.
22165c93628SMartin KaFai Lau 		 */
22265c93628SMartin KaFai Lau 		p_err("no struct_ops found for %s", name);
22365c93628SMartin KaFai Lau 	else if (!wtr && json_output && !res.nr_errs)
22465c93628SMartin KaFai Lau 		/* The "func()" above is not writing any json (i.e. !wtr
22565c93628SMartin KaFai Lau 		 * test here).
22665c93628SMartin KaFai Lau 		 *
22765c93628SMartin KaFai Lau 		 * However, "-j" is enabled and there is no errs here,
22865c93628SMartin KaFai Lau 		 * so call json_null() as the current convention of
22965c93628SMartin KaFai Lau 		 * other cmds.
23065c93628SMartin KaFai Lau 		 */
23165c93628SMartin KaFai Lau 		jsonw_null(json_wtr);
23265c93628SMartin KaFai Lau 
23365c93628SMartin KaFai Lau 	free(info);
23465c93628SMartin KaFai Lau 	return res;
23565c93628SMartin KaFai Lau }
23665c93628SMartin KaFai Lau 
23765c93628SMartin KaFai Lau static struct res do_one_id(const char *id_str, work_func func, void *data,
23865c93628SMartin KaFai Lau 			    struct json_writer *wtr)
23965c93628SMartin KaFai Lau {
24065c93628SMartin KaFai Lau 	struct bpf_map_info *info;
24165c93628SMartin KaFai Lau 	struct res res = {};
24265c93628SMartin KaFai Lau 	unsigned long id;
24365c93628SMartin KaFai Lau 	__u32 info_len;
24465c93628SMartin KaFai Lau 	char *endptr;
24565c93628SMartin KaFai Lau 	int fd;
24665c93628SMartin KaFai Lau 
24765c93628SMartin KaFai Lau 	id = strtoul(id_str, &endptr, 0);
24865c93628SMartin KaFai Lau 	if (*endptr || !id || id > UINT32_MAX) {
24965c93628SMartin KaFai Lau 		p_err("invalid id %s", id_str);
25065c93628SMartin KaFai Lau 		res.nr_errs++;
25165c93628SMartin KaFai Lau 		return res;
25265c93628SMartin KaFai Lau 	}
25365c93628SMartin KaFai Lau 
25465c93628SMartin KaFai Lau 	fd = bpf_map_get_fd_by_id(id);
25565c93628SMartin KaFai Lau 	if (fd == -1) {
25665c93628SMartin KaFai Lau 		p_err("can't get map by id (%lu): %s", id, strerror(errno));
25765c93628SMartin KaFai Lau 		res.nr_errs++;
25865c93628SMartin KaFai Lau 		return res;
25965c93628SMartin KaFai Lau 	}
26065c93628SMartin KaFai Lau 
26165c93628SMartin KaFai Lau 	info = map_info_alloc(&info_len);
26265c93628SMartin KaFai Lau 	if (!info) {
26365c93628SMartin KaFai Lau 		res.nr_errs++;
26465c93628SMartin KaFai Lau 		goto done;
26565c93628SMartin KaFai Lau 	}
26665c93628SMartin KaFai Lau 
26765c93628SMartin KaFai Lau 	if (bpf_obj_get_info_by_fd(fd, info, &info_len)) {
26865c93628SMartin KaFai Lau 		p_err("can't get map info: %s", strerror(errno));
26965c93628SMartin KaFai Lau 		res.nr_errs++;
27065c93628SMartin KaFai Lau 		goto done;
27165c93628SMartin KaFai Lau 	}
27265c93628SMartin KaFai Lau 
27365c93628SMartin KaFai Lau 	if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
27465c93628SMartin KaFai Lau 		p_err("%s id %u is not a struct_ops map", info->name, info->id);
27565c93628SMartin KaFai Lau 		res.nr_errs++;
27665c93628SMartin KaFai Lau 		goto done;
27765c93628SMartin KaFai Lau 	}
27865c93628SMartin KaFai Lau 
27965c93628SMartin KaFai Lau 	res.nr_maps++;
28065c93628SMartin KaFai Lau 
28165c93628SMartin KaFai Lau 	if (func(fd, info, data, wtr))
28265c93628SMartin KaFai Lau 		res.nr_errs++;
28365c93628SMartin KaFai Lau 	else if (!wtr && json_output)
28465c93628SMartin KaFai Lau 		/* The "func()" above is not writing any json (i.e. !wtr
28565c93628SMartin KaFai Lau 		 * test here).
28665c93628SMartin KaFai Lau 		 *
28765c93628SMartin KaFai Lau 		 * However, "-j" is enabled and there is no errs here,
28865c93628SMartin KaFai Lau 		 * so call json_null() as the current convention of
28965c93628SMartin KaFai Lau 		 * other cmds.
29065c93628SMartin KaFai Lau 		 */
29165c93628SMartin KaFai Lau 		jsonw_null(json_wtr);
29265c93628SMartin KaFai Lau 
29365c93628SMartin KaFai Lau done:
29465c93628SMartin KaFai Lau 	free(info);
29565c93628SMartin KaFai Lau 	close(fd);
29665c93628SMartin KaFai Lau 
29765c93628SMartin KaFai Lau 	return res;
29865c93628SMartin KaFai Lau }
29965c93628SMartin KaFai Lau 
30065c93628SMartin KaFai Lau static struct res do_work_on_struct_ops(const char *search_type,
30165c93628SMartin KaFai Lau 					const char *search_term,
30265c93628SMartin KaFai Lau 					work_func func, void *data,
30365c93628SMartin KaFai Lau 					struct json_writer *wtr)
30465c93628SMartin KaFai Lau {
30565c93628SMartin KaFai Lau 	if (search_type) {
30665c93628SMartin KaFai Lau 		if (is_prefix(search_type, "id"))
30765c93628SMartin KaFai Lau 			return do_one_id(search_term, func, data, wtr);
30865c93628SMartin KaFai Lau 		else if (!is_prefix(search_type, "name"))
30965c93628SMartin KaFai Lau 			usage();
31065c93628SMartin KaFai Lau 	}
31165c93628SMartin KaFai Lau 
31265c93628SMartin KaFai Lau 	return do_search(search_term, func, data, wtr);
31365c93628SMartin KaFai Lau }
31465c93628SMartin KaFai Lau 
31565c93628SMartin KaFai Lau static int __do_show(int fd, const struct bpf_map_info *info, void *data,
31665c93628SMartin KaFai Lau 		     struct json_writer *wtr)
31765c93628SMartin KaFai Lau {
31865c93628SMartin KaFai Lau 	if (wtr) {
31965c93628SMartin KaFai Lau 		jsonw_start_object(wtr);
32065c93628SMartin KaFai Lau 		jsonw_uint_field(wtr, "id", info->id);
32165c93628SMartin KaFai Lau 		jsonw_string_field(wtr, "name", info->name);
32265c93628SMartin KaFai Lau 		jsonw_string_field(wtr, "kernel_struct_ops",
32365c93628SMartin KaFai Lau 				   get_kern_struct_ops_name(info));
32465c93628SMartin KaFai Lau 		jsonw_end_object(wtr);
32565c93628SMartin KaFai Lau 	} else {
32665c93628SMartin KaFai Lau 		printf("%u: %-15s %-32s\n", info->id, info->name,
32765c93628SMartin KaFai Lau 		       get_kern_struct_ops_name(info));
32865c93628SMartin KaFai Lau 	}
32965c93628SMartin KaFai Lau 
33065c93628SMartin KaFai Lau 	return 0;
33165c93628SMartin KaFai Lau }
33265c93628SMartin KaFai Lau 
33365c93628SMartin KaFai Lau static int do_show(int argc, char **argv)
33465c93628SMartin KaFai Lau {
33565c93628SMartin KaFai Lau 	const char *search_type = NULL, *search_term = NULL;
33665c93628SMartin KaFai Lau 	struct res res;
33765c93628SMartin KaFai Lau 
33865c93628SMartin KaFai Lau 	if (argc && argc != 2)
33965c93628SMartin KaFai Lau 		usage();
34065c93628SMartin KaFai Lau 
34165c93628SMartin KaFai Lau 	if (argc == 2) {
34265c93628SMartin KaFai Lau 		search_type = GET_ARG();
34365c93628SMartin KaFai Lau 		search_term = GET_ARG();
34465c93628SMartin KaFai Lau 	}
34565c93628SMartin KaFai Lau 
34665c93628SMartin KaFai Lau 	res = do_work_on_struct_ops(search_type, search_term, __do_show,
34765c93628SMartin KaFai Lau 				    NULL, json_wtr);
34865c93628SMartin KaFai Lau 
34965c93628SMartin KaFai Lau 	return cmd_retval(&res, !!search_term);
35065c93628SMartin KaFai Lau }
35165c93628SMartin KaFai Lau 
35265c93628SMartin KaFai Lau static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
35365c93628SMartin KaFai Lau 		     struct json_writer *wtr)
35465c93628SMartin KaFai Lau {
35565c93628SMartin KaFai Lau 	struct btf_dumper *d = (struct btf_dumper *)data;
35665c93628SMartin KaFai Lau 	const struct btf_type *struct_ops_type;
35765c93628SMartin KaFai Lau 	const struct btf *kern_btf = d->btf;
35865c93628SMartin KaFai Lau 	const char *struct_ops_name;
35965c93628SMartin KaFai Lau 	int zero = 0;
36065c93628SMartin KaFai Lau 	void *value;
36165c93628SMartin KaFai Lau 
36265c93628SMartin KaFai Lau 	/* note: d->jw == wtr */
36365c93628SMartin KaFai Lau 
36465c93628SMartin KaFai Lau 	kern_btf = d->btf;
36565c93628SMartin KaFai Lau 
36665c93628SMartin KaFai Lau 	/* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
36765c93628SMartin KaFai Lau 	 * btf_vmlinux_value_type_id.
36865c93628SMartin KaFai Lau 	 */
36965c93628SMartin KaFai Lau 	struct_ops_type = btf__type_by_id(kern_btf,
37065c93628SMartin KaFai Lau 					  info->btf_vmlinux_value_type_id);
37165c93628SMartin KaFai Lau 	struct_ops_name = btf__name_by_offset(kern_btf,
37265c93628SMartin KaFai Lau 					      struct_ops_type->name_off);
37365c93628SMartin KaFai Lau 	value = calloc(1, info->value_size);
37465c93628SMartin KaFai Lau 	if (!value) {
37565c93628SMartin KaFai Lau 		p_err("mem alloc failed");
37665c93628SMartin KaFai Lau 		return -1;
37765c93628SMartin KaFai Lau 	}
37865c93628SMartin KaFai Lau 
37965c93628SMartin KaFai Lau 	if (bpf_map_lookup_elem(fd, &zero, value)) {
38065c93628SMartin KaFai Lau 		p_err("can't lookup struct_ops map %s id %u",
38165c93628SMartin KaFai Lau 		      info->name, info->id);
38265c93628SMartin KaFai Lau 		free(value);
38365c93628SMartin KaFai Lau 		return -1;
38465c93628SMartin KaFai Lau 	}
38565c93628SMartin KaFai Lau 
38665c93628SMartin KaFai Lau 	jsonw_start_object(wtr);
38765c93628SMartin KaFai Lau 	jsonw_name(wtr, "bpf_map_info");
38865c93628SMartin KaFai Lau 	btf_dumper_type(d, map_info_type_id, (void *)info);
38965c93628SMartin KaFai Lau 	jsonw_end_object(wtr);
39065c93628SMartin KaFai Lau 
39165c93628SMartin KaFai Lau 	jsonw_start_object(wtr);
39265c93628SMartin KaFai Lau 	jsonw_name(wtr, struct_ops_name);
39365c93628SMartin KaFai Lau 	btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
39465c93628SMartin KaFai Lau 	jsonw_end_object(wtr);
39565c93628SMartin KaFai Lau 
39665c93628SMartin KaFai Lau 	free(value);
39765c93628SMartin KaFai Lau 
39865c93628SMartin KaFai Lau 	return 0;
39965c93628SMartin KaFai Lau }
40065c93628SMartin KaFai Lau 
40165c93628SMartin KaFai Lau static int do_dump(int argc, char **argv)
40265c93628SMartin KaFai Lau {
40365c93628SMartin KaFai Lau 	const char *search_type = NULL, *search_term = NULL;
40465c93628SMartin KaFai Lau 	json_writer_t *wtr = json_wtr;
40565c93628SMartin KaFai Lau 	const struct btf *kern_btf;
40665c93628SMartin KaFai Lau 	struct btf_dumper d = {};
40765c93628SMartin KaFai Lau 	struct res res;
40865c93628SMartin KaFai Lau 
40965c93628SMartin KaFai Lau 	if (argc && argc != 2)
41065c93628SMartin KaFai Lau 		usage();
41165c93628SMartin KaFai Lau 
41265c93628SMartin KaFai Lau 	if (argc == 2) {
41365c93628SMartin KaFai Lau 		search_type = GET_ARG();
41465c93628SMartin KaFai Lau 		search_term = GET_ARG();
41565c93628SMartin KaFai Lau 	}
41665c93628SMartin KaFai Lau 
41765c93628SMartin KaFai Lau 	kern_btf = get_btf_vmlinux();
41865c93628SMartin KaFai Lau 	if (IS_ERR(kern_btf))
41965c93628SMartin KaFai Lau 		return -1;
42065c93628SMartin KaFai Lau 
42165c93628SMartin KaFai Lau 	if (!json_output) {
42265c93628SMartin KaFai Lau 		wtr = jsonw_new(stdout);
42365c93628SMartin KaFai Lau 		if (!wtr) {
42465c93628SMartin KaFai Lau 			p_err("can't create json writer");
42565c93628SMartin KaFai Lau 			return -1;
42665c93628SMartin KaFai Lau 		}
42765c93628SMartin KaFai Lau 		jsonw_pretty(wtr, true);
42865c93628SMartin KaFai Lau 	}
42965c93628SMartin KaFai Lau 
43065c93628SMartin KaFai Lau 	d.btf = kern_btf;
43165c93628SMartin KaFai Lau 	d.jw = wtr;
43265c93628SMartin KaFai Lau 	d.is_plain_text = !json_output;
43365c93628SMartin KaFai Lau 	d.prog_id_as_func_ptr = true;
43465c93628SMartin KaFai Lau 
43565c93628SMartin KaFai Lau 	res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
43665c93628SMartin KaFai Lau 				    wtr);
43765c93628SMartin KaFai Lau 
43865c93628SMartin KaFai Lau 	if (!json_output)
43965c93628SMartin KaFai Lau 		jsonw_destroy(&wtr);
44065c93628SMartin KaFai Lau 
44165c93628SMartin KaFai Lau 	return cmd_retval(&res, !!search_term);
44265c93628SMartin KaFai Lau }
44365c93628SMartin KaFai Lau 
44465c93628SMartin KaFai Lau static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
44565c93628SMartin KaFai Lau 			   struct json_writer *wtr)
44665c93628SMartin KaFai Lau {
44765c93628SMartin KaFai Lau 	int zero = 0;
44865c93628SMartin KaFai Lau 
44965c93628SMartin KaFai Lau 	if (bpf_map_delete_elem(fd, &zero)) {
45065c93628SMartin KaFai Lau 		p_err("can't unload %s %s id %u: %s",
45165c93628SMartin KaFai Lau 		      get_kern_struct_ops_name(info), info->name,
45265c93628SMartin KaFai Lau 		      info->id, strerror(errno));
45365c93628SMartin KaFai Lau 		return -1;
45465c93628SMartin KaFai Lau 	}
45565c93628SMartin KaFai Lau 
45665c93628SMartin KaFai Lau 	p_info("Unregistered %s %s id %u",
45765c93628SMartin KaFai Lau 	       get_kern_struct_ops_name(info), info->name,
45865c93628SMartin KaFai Lau 	       info->id);
45965c93628SMartin KaFai Lau 
46065c93628SMartin KaFai Lau 	return 0;
46165c93628SMartin KaFai Lau }
46265c93628SMartin KaFai Lau 
46365c93628SMartin KaFai Lau static int do_unregister(int argc, char **argv)
46465c93628SMartin KaFai Lau {
46565c93628SMartin KaFai Lau 	const char *search_type, *search_term;
46665c93628SMartin KaFai Lau 	struct res res;
46765c93628SMartin KaFai Lau 
46865c93628SMartin KaFai Lau 	if (argc != 2)
46965c93628SMartin KaFai Lau 		usage();
47065c93628SMartin KaFai Lau 
47165c93628SMartin KaFai Lau 	search_type = GET_ARG();
47265c93628SMartin KaFai Lau 	search_term = GET_ARG();
47365c93628SMartin KaFai Lau 
47465c93628SMartin KaFai Lau 	res = do_work_on_struct_ops(search_type, search_term,
47565c93628SMartin KaFai Lau 				    __do_unregister, NULL, NULL);
47665c93628SMartin KaFai Lau 
47765c93628SMartin KaFai Lau 	return cmd_retval(&res, true);
47865c93628SMartin KaFai Lau }
47965c93628SMartin KaFai Lau 
48065c93628SMartin KaFai Lau static int do_register(int argc, char **argv)
48165c93628SMartin KaFai Lau {
48232e4c6f4SMartin KaFai Lau 	struct bpf_object_load_attr load_attr = {};
48365c93628SMartin KaFai Lau 	const struct bpf_map_def *def;
48465c93628SMartin KaFai Lau 	struct bpf_map_info info = {};
48565c93628SMartin KaFai Lau 	__u32 info_len = sizeof(info);
48665c93628SMartin KaFai Lau 	int nr_errs = 0, nr_maps = 0;
48765c93628SMartin KaFai Lau 	struct bpf_object *obj;
48865c93628SMartin KaFai Lau 	struct bpf_link *link;
48965c93628SMartin KaFai Lau 	struct bpf_map *map;
49065c93628SMartin KaFai Lau 	const char *file;
49165c93628SMartin KaFai Lau 
49265c93628SMartin KaFai Lau 	if (argc != 1)
49365c93628SMartin KaFai Lau 		usage();
49465c93628SMartin KaFai Lau 
49565c93628SMartin KaFai Lau 	file = GET_ARG();
49665c93628SMartin KaFai Lau 
49765c93628SMartin KaFai Lau 	obj = bpf_object__open(file);
49865c93628SMartin KaFai Lau 	if (IS_ERR_OR_NULL(obj))
49965c93628SMartin KaFai Lau 		return -1;
50065c93628SMartin KaFai Lau 
50165c93628SMartin KaFai Lau 	set_max_rlimit();
50265c93628SMartin KaFai Lau 
50332e4c6f4SMartin KaFai Lau 	load_attr.obj = obj;
50432e4c6f4SMartin KaFai Lau 	if (verifier_logs)
50532e4c6f4SMartin KaFai Lau 		/* log_level1 + log_level2 + stats, but not stable UAPI */
50632e4c6f4SMartin KaFai Lau 		load_attr.log_level = 1 + 2 + 4;
50732e4c6f4SMartin KaFai Lau 
50832e4c6f4SMartin KaFai Lau 	if (bpf_object__load_xattr(&load_attr)) {
50965c93628SMartin KaFai Lau 		bpf_object__close(obj);
51065c93628SMartin KaFai Lau 		return -1;
51165c93628SMartin KaFai Lau 	}
51265c93628SMartin KaFai Lau 
51365c93628SMartin KaFai Lau 	bpf_object__for_each_map(map, obj) {
51465c93628SMartin KaFai Lau 		def = bpf_map__def(map);
51565c93628SMartin KaFai Lau 		if (def->type != BPF_MAP_TYPE_STRUCT_OPS)
51665c93628SMartin KaFai Lau 			continue;
51765c93628SMartin KaFai Lau 
51865c93628SMartin KaFai Lau 		link = bpf_map__attach_struct_ops(map);
51965c93628SMartin KaFai Lau 		if (IS_ERR(link)) {
52065c93628SMartin KaFai Lau 			p_err("can't register struct_ops %s: %s",
52165c93628SMartin KaFai Lau 			      bpf_map__name(map),
52265c93628SMartin KaFai Lau 			      strerror(-PTR_ERR(link)));
52365c93628SMartin KaFai Lau 			nr_errs++;
52465c93628SMartin KaFai Lau 			continue;
52565c93628SMartin KaFai Lau 		}
52665c93628SMartin KaFai Lau 		nr_maps++;
52765c93628SMartin KaFai Lau 
52865c93628SMartin KaFai Lau 		bpf_link__disconnect(link);
52965c93628SMartin KaFai Lau 		bpf_link__destroy(link);
53065c93628SMartin KaFai Lau 
53165c93628SMartin KaFai Lau 		if (!bpf_obj_get_info_by_fd(bpf_map__fd(map), &info,
53265c93628SMartin KaFai Lau 					    &info_len))
53365c93628SMartin KaFai Lau 			p_info("Registered %s %s id %u",
53465c93628SMartin KaFai Lau 			       get_kern_struct_ops_name(&info),
53565c93628SMartin KaFai Lau 			       bpf_map__name(map),
53665c93628SMartin KaFai Lau 			       info.id);
53765c93628SMartin KaFai Lau 		else
53865c93628SMartin KaFai Lau 			/* Not p_err.  The struct_ops was attached
53965c93628SMartin KaFai Lau 			 * successfully.
54065c93628SMartin KaFai Lau 			 */
54165c93628SMartin KaFai Lau 			p_info("Registered %s but can't find id: %s",
54265c93628SMartin KaFai Lau 			       bpf_map__name(map), strerror(errno));
54365c93628SMartin KaFai Lau 	}
54465c93628SMartin KaFai Lau 
54565c93628SMartin KaFai Lau 	bpf_object__close(obj);
54665c93628SMartin KaFai Lau 
54765c93628SMartin KaFai Lau 	if (nr_errs)
54865c93628SMartin KaFai Lau 		return -1;
54965c93628SMartin KaFai Lau 
55065c93628SMartin KaFai Lau 	if (!nr_maps) {
55165c93628SMartin KaFai Lau 		p_err("no struct_ops found in %s", file);
55265c93628SMartin KaFai Lau 		return -1;
55365c93628SMartin KaFai Lau 	}
55465c93628SMartin KaFai Lau 
55565c93628SMartin KaFai Lau 	if (json_output)
55665c93628SMartin KaFai Lau 		jsonw_null(json_wtr);
55765c93628SMartin KaFai Lau 
55865c93628SMartin KaFai Lau 	return 0;
55965c93628SMartin KaFai Lau }
56065c93628SMartin KaFai Lau 
56165c93628SMartin KaFai Lau static int do_help(int argc, char **argv)
56265c93628SMartin KaFai Lau {
56365c93628SMartin KaFai Lau 	if (json_output) {
56465c93628SMartin KaFai Lau 		jsonw_null(json_wtr);
56565c93628SMartin KaFai Lau 		return 0;
56665c93628SMartin KaFai Lau 	}
56765c93628SMartin KaFai Lau 
56865c93628SMartin KaFai Lau 	fprintf(stderr,
56990040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
57090040351SQuentin Monnet 		"       %1$s %2$s dump [STRUCT_OPS_MAP]\n"
57190040351SQuentin Monnet 		"       %1$s %2$s register OBJ\n"
57290040351SQuentin Monnet 		"       %1$s %2$s unregister STRUCT_OPS_MAP\n"
57390040351SQuentin Monnet 		"       %1$s %2$s help\n"
57465c93628SMartin KaFai Lau 		"\n"
57590040351SQuentin Monnet 		"       STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
576*c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " }\n"
57790040351SQuentin Monnet 		"",
57865c93628SMartin KaFai Lau 		bin_name, argv[-2]);
57965c93628SMartin KaFai Lau 
58065c93628SMartin KaFai Lau 	return 0;
58165c93628SMartin KaFai Lau }
58265c93628SMartin KaFai Lau 
58365c93628SMartin KaFai Lau static const struct cmd cmds[] = {
58465c93628SMartin KaFai Lau 	{ "show",	do_show },
58565c93628SMartin KaFai Lau 	{ "list",	do_show },
58665c93628SMartin KaFai Lau 	{ "register",	do_register },
58765c93628SMartin KaFai Lau 	{ "unregister",	do_unregister },
58865c93628SMartin KaFai Lau 	{ "dump",	do_dump },
58965c93628SMartin KaFai Lau 	{ "help",	do_help },
59065c93628SMartin KaFai Lau 	{ 0 }
59165c93628SMartin KaFai Lau };
59265c93628SMartin KaFai Lau 
59365c93628SMartin KaFai Lau int do_struct_ops(int argc, char **argv)
59465c93628SMartin KaFai Lau {
59565c93628SMartin KaFai Lau 	int err;
59665c93628SMartin KaFai Lau 
59765c93628SMartin KaFai Lau 	err = cmd_select(cmds, argc, argv, do_help);
59865c93628SMartin KaFai Lau 
59996b2eb6eSDaniel T. Lee 	if (!IS_ERR(btf_vmlinux))
60065c93628SMartin KaFai Lau 		btf__free(btf_vmlinux);
60196b2eb6eSDaniel T. Lee 
60265c93628SMartin KaFai Lau 	return err;
60365c93628SMartin KaFai Lau }
604