162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2020 Facebook */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <errno.h>
562306a36Sopenharmony_ci#include <stdio.h>
662306a36Sopenharmony_ci#include <unistd.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <bpf/bpf.h>
1162306a36Sopenharmony_ci#include <bpf/btf.h>
1262306a36Sopenharmony_ci#include <bpf/libbpf.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "json_writer.h"
1562306a36Sopenharmony_ci#include "main.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const struct btf_type *map_info_type;
2062306a36Sopenharmony_cistatic __u32 map_info_alloc_len;
2162306a36Sopenharmony_cistatic struct btf *btf_vmlinux;
2262306a36Sopenharmony_cistatic __s32 map_info_type_id;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct res {
2562306a36Sopenharmony_ci	unsigned int nr_maps;
2662306a36Sopenharmony_ci	unsigned int nr_errs;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const struct btf *get_btf_vmlinux(void)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (btf_vmlinux)
3262306a36Sopenharmony_ci		return btf_vmlinux;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	btf_vmlinux = libbpf_find_kernel_btf();
3562306a36Sopenharmony_ci	if (!btf_vmlinux)
3662306a36Sopenharmony_ci		p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return btf_vmlinux;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	const struct btf *kern_btf;
4462306a36Sopenharmony_ci	const struct btf_type *t;
4562306a36Sopenharmony_ci	const char *st_ops_name;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	kern_btf = get_btf_vmlinux();
4862306a36Sopenharmony_ci	if (!kern_btf)
4962306a36Sopenharmony_ci		return "<btf_vmlinux_not_found>";
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
5262306a36Sopenharmony_ci	st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
5362306a36Sopenharmony_ci	st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return st_ops_name;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic __s32 get_map_info_type_id(void)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	const struct btf *kern_btf;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (map_info_type_id)
6362306a36Sopenharmony_ci		return map_info_type_id;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	kern_btf = get_btf_vmlinux();
6662306a36Sopenharmony_ci	if (!kern_btf)
6762306a36Sopenharmony_ci		return 0;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
7062306a36Sopenharmony_ci						  BTF_KIND_STRUCT);
7162306a36Sopenharmony_ci	if (map_info_type_id < 0) {
7262306a36Sopenharmony_ci		p_err("can't find bpf_map_info from btf_vmlinux");
7362306a36Sopenharmony_ci		return map_info_type_id;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Ensure map_info_alloc() has at least what the bpftool needs */
7862306a36Sopenharmony_ci	map_info_alloc_len = map_info_type->size;
7962306a36Sopenharmony_ci	if (map_info_alloc_len < sizeof(struct bpf_map_info))
8062306a36Sopenharmony_ci		map_info_alloc_len = sizeof(struct bpf_map_info);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return map_info_type_id;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* If the subcmd needs to print out the bpf_map_info,
8662306a36Sopenharmony_ci * it should always call map_info_alloc to allocate
8762306a36Sopenharmony_ci * a bpf_map_info object instead of allocating it
8862306a36Sopenharmony_ci * on the stack.
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * map_info_alloc() will take the running kernel's btf
9162306a36Sopenharmony_ci * into account.  i.e. it will consider the
9262306a36Sopenharmony_ci * sizeof(struct bpf_map_info) of the running kernel.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * It will enable the "struct_ops" cmd to print the latest
9562306a36Sopenharmony_ci * "struct bpf_map_info".
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * [ Recall that "struct_ops" requires the kernel's btf to
9862306a36Sopenharmony_ci *   be available ]
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_cistatic struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct bpf_map_info *info;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (get_map_info_type_id() < 0)
10562306a36Sopenharmony_ci		return NULL;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	info = calloc(1, map_info_alloc_len);
10862306a36Sopenharmony_ci	if (!info)
10962306a36Sopenharmony_ci		p_err("mem alloc failed");
11062306a36Sopenharmony_ci	else
11162306a36Sopenharmony_ci		*alloc_len = map_info_alloc_len;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return info;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* It iterates all struct_ops maps of the system.
11762306a36Sopenharmony_ci * It returns the fd in "*res_fd" and map_info in "*info".
11862306a36Sopenharmony_ci * In the very first iteration, info->id should be 0.
11962306a36Sopenharmony_ci * An optional map "*name" filter can be specified.
12062306a36Sopenharmony_ci * The filter can be made more flexible in the future.
12162306a36Sopenharmony_ci * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * Return value:
12462306a36Sopenharmony_ci *     1: A struct_ops map found.  It is returned in "*res_fd" and "*info".
12562306a36Sopenharmony_ci *	  The caller can continue to call get_next in the future.
12662306a36Sopenharmony_ci *     0: No struct_ops map is returned.
12762306a36Sopenharmony_ci *        All struct_ops map has been found.
12862306a36Sopenharmony_ci *    -1: Error and the caller should abort the iteration.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_cistatic int get_next_struct_ops_map(const char *name, int *res_fd,
13162306a36Sopenharmony_ci				   struct bpf_map_info *info, __u32 info_len)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	__u32 id = info->id;
13462306a36Sopenharmony_ci	int err, fd;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	while (true) {
13762306a36Sopenharmony_ci		err = bpf_map_get_next_id(id, &id);
13862306a36Sopenharmony_ci		if (err) {
13962306a36Sopenharmony_ci			if (errno == ENOENT)
14062306a36Sopenharmony_ci				return 0;
14162306a36Sopenharmony_ci			p_err("can't get next map: %s", strerror(errno));
14262306a36Sopenharmony_ci			return -1;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		fd = bpf_map_get_fd_by_id(id);
14662306a36Sopenharmony_ci		if (fd < 0) {
14762306a36Sopenharmony_ci			if (errno == ENOENT)
14862306a36Sopenharmony_ci				continue;
14962306a36Sopenharmony_ci			p_err("can't get map by id (%u): %s",
15062306a36Sopenharmony_ci			      id, strerror(errno));
15162306a36Sopenharmony_ci			return -1;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		err = bpf_map_get_info_by_fd(fd, info, &info_len);
15562306a36Sopenharmony_ci		if (err) {
15662306a36Sopenharmony_ci			p_err("can't get map info: %s", strerror(errno));
15762306a36Sopenharmony_ci			close(fd);
15862306a36Sopenharmony_ci			return -1;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
16262306a36Sopenharmony_ci		    (!name || !strcmp(name, info->name))) {
16362306a36Sopenharmony_ci			*res_fd = fd;
16462306a36Sopenharmony_ci			return 1;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci		close(fd);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int cmd_retval(const struct res *res, bool must_have_one_map)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	if (res->nr_errs || (!res->nr_maps && must_have_one_map))
17362306a36Sopenharmony_ci		return -1;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* "data" is the work_func private storage */
17962306a36Sopenharmony_citypedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
18062306a36Sopenharmony_ci			 struct json_writer *wtr);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Find all struct_ops map in the system.
18362306a36Sopenharmony_ci * Filter out by "name" (if specified).
18462306a36Sopenharmony_ci * Then call "func(fd, info, data, wtr)" on each struct_ops map found.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_cistatic struct res do_search(const char *name, work_func func, void *data,
18762306a36Sopenharmony_ci			    struct json_writer *wtr)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct bpf_map_info *info;
19062306a36Sopenharmony_ci	struct res res = {};
19162306a36Sopenharmony_ci	__u32 info_len;
19262306a36Sopenharmony_ci	int fd, err;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	info = map_info_alloc(&info_len);
19562306a36Sopenharmony_ci	if (!info) {
19662306a36Sopenharmony_ci		res.nr_errs++;
19762306a36Sopenharmony_ci		return res;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (wtr)
20162306a36Sopenharmony_ci		jsonw_start_array(wtr);
20262306a36Sopenharmony_ci	while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
20362306a36Sopenharmony_ci		res.nr_maps++;
20462306a36Sopenharmony_ci		err = func(fd, info, data, wtr);
20562306a36Sopenharmony_ci		if (err)
20662306a36Sopenharmony_ci			res.nr_errs++;
20762306a36Sopenharmony_ci		close(fd);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	if (wtr)
21062306a36Sopenharmony_ci		jsonw_end_array(wtr);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (err)
21362306a36Sopenharmony_ci		res.nr_errs++;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!wtr && name && !res.nr_errs && !res.nr_maps)
21662306a36Sopenharmony_ci		/* It is not printing empty [].
21762306a36Sopenharmony_ci		 * Thus, needs to specifically say nothing found
21862306a36Sopenharmony_ci		 * for "name" here.
21962306a36Sopenharmony_ci		 */
22062306a36Sopenharmony_ci		p_err("no struct_ops found for %s", name);
22162306a36Sopenharmony_ci	else if (!wtr && json_output && !res.nr_errs)
22262306a36Sopenharmony_ci		/* The "func()" above is not writing any json (i.e. !wtr
22362306a36Sopenharmony_ci		 * test here).
22462306a36Sopenharmony_ci		 *
22562306a36Sopenharmony_ci		 * However, "-j" is enabled and there is no errs here,
22662306a36Sopenharmony_ci		 * so call json_null() as the current convention of
22762306a36Sopenharmony_ci		 * other cmds.
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		jsonw_null(json_wtr);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	free(info);
23262306a36Sopenharmony_ci	return res;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic struct res do_one_id(const char *id_str, work_func func, void *data,
23662306a36Sopenharmony_ci			    struct json_writer *wtr)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct bpf_map_info *info;
23962306a36Sopenharmony_ci	struct res res = {};
24062306a36Sopenharmony_ci	unsigned long id;
24162306a36Sopenharmony_ci	__u32 info_len;
24262306a36Sopenharmony_ci	char *endptr;
24362306a36Sopenharmony_ci	int fd;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	id = strtoul(id_str, &endptr, 0);
24662306a36Sopenharmony_ci	if (*endptr || !id || id > UINT32_MAX) {
24762306a36Sopenharmony_ci		p_err("invalid id %s", id_str);
24862306a36Sopenharmony_ci		res.nr_errs++;
24962306a36Sopenharmony_ci		return res;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	fd = bpf_map_get_fd_by_id(id);
25362306a36Sopenharmony_ci	if (fd < 0) {
25462306a36Sopenharmony_ci		p_err("can't get map by id (%lu): %s", id, strerror(errno));
25562306a36Sopenharmony_ci		res.nr_errs++;
25662306a36Sopenharmony_ci		return res;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	info = map_info_alloc(&info_len);
26062306a36Sopenharmony_ci	if (!info) {
26162306a36Sopenharmony_ci		res.nr_errs++;
26262306a36Sopenharmony_ci		goto done;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (bpf_map_get_info_by_fd(fd, info, &info_len)) {
26662306a36Sopenharmony_ci		p_err("can't get map info: %s", strerror(errno));
26762306a36Sopenharmony_ci		res.nr_errs++;
26862306a36Sopenharmony_ci		goto done;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
27262306a36Sopenharmony_ci		p_err("%s id %u is not a struct_ops map", info->name, info->id);
27362306a36Sopenharmony_ci		res.nr_errs++;
27462306a36Sopenharmony_ci		goto done;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	res.nr_maps++;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (func(fd, info, data, wtr))
28062306a36Sopenharmony_ci		res.nr_errs++;
28162306a36Sopenharmony_ci	else if (!wtr && json_output)
28262306a36Sopenharmony_ci		/* The "func()" above is not writing any json (i.e. !wtr
28362306a36Sopenharmony_ci		 * test here).
28462306a36Sopenharmony_ci		 *
28562306a36Sopenharmony_ci		 * However, "-j" is enabled and there is no errs here,
28662306a36Sopenharmony_ci		 * so call json_null() as the current convention of
28762306a36Sopenharmony_ci		 * other cmds.
28862306a36Sopenharmony_ci		 */
28962306a36Sopenharmony_ci		jsonw_null(json_wtr);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cidone:
29262306a36Sopenharmony_ci	free(info);
29362306a36Sopenharmony_ci	close(fd);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return res;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct res do_work_on_struct_ops(const char *search_type,
29962306a36Sopenharmony_ci					const char *search_term,
30062306a36Sopenharmony_ci					work_func func, void *data,
30162306a36Sopenharmony_ci					struct json_writer *wtr)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	if (search_type) {
30462306a36Sopenharmony_ci		if (is_prefix(search_type, "id"))
30562306a36Sopenharmony_ci			return do_one_id(search_term, func, data, wtr);
30662306a36Sopenharmony_ci		else if (!is_prefix(search_type, "name"))
30762306a36Sopenharmony_ci			usage();
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return do_search(search_term, func, data, wtr);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int __do_show(int fd, const struct bpf_map_info *info, void *data,
31462306a36Sopenharmony_ci		     struct json_writer *wtr)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	if (wtr) {
31762306a36Sopenharmony_ci		jsonw_start_object(wtr);
31862306a36Sopenharmony_ci		jsonw_uint_field(wtr, "id", info->id);
31962306a36Sopenharmony_ci		jsonw_string_field(wtr, "name", info->name);
32062306a36Sopenharmony_ci		jsonw_string_field(wtr, "kernel_struct_ops",
32162306a36Sopenharmony_ci				   get_kern_struct_ops_name(info));
32262306a36Sopenharmony_ci		jsonw_end_object(wtr);
32362306a36Sopenharmony_ci	} else {
32462306a36Sopenharmony_ci		printf("%u: %-15s %-32s\n", info->id, info->name,
32562306a36Sopenharmony_ci		       get_kern_struct_ops_name(info));
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int do_show(int argc, char **argv)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	const char *search_type = NULL, *search_term = NULL;
33462306a36Sopenharmony_ci	struct res res;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (argc && argc != 2)
33762306a36Sopenharmony_ci		usage();
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (argc == 2) {
34062306a36Sopenharmony_ci		search_type = GET_ARG();
34162306a36Sopenharmony_ci		search_term = GET_ARG();
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	res = do_work_on_struct_ops(search_type, search_term, __do_show,
34562306a36Sopenharmony_ci				    NULL, json_wtr);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return cmd_retval(&res, !!search_term);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int __do_dump(int fd, const struct bpf_map_info *info, void *data,
35162306a36Sopenharmony_ci		     struct json_writer *wtr)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct btf_dumper *d = (struct btf_dumper *)data;
35462306a36Sopenharmony_ci	const struct btf_type *struct_ops_type;
35562306a36Sopenharmony_ci	const struct btf *kern_btf = d->btf;
35662306a36Sopenharmony_ci	const char *struct_ops_name;
35762306a36Sopenharmony_ci	int zero = 0;
35862306a36Sopenharmony_ci	void *value;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* note: d->jw == wtr */
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	kern_btf = d->btf;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
36562306a36Sopenharmony_ci	 * btf_vmlinux_value_type_id.
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	struct_ops_type = btf__type_by_id(kern_btf,
36862306a36Sopenharmony_ci					  info->btf_vmlinux_value_type_id);
36962306a36Sopenharmony_ci	struct_ops_name = btf__name_by_offset(kern_btf,
37062306a36Sopenharmony_ci					      struct_ops_type->name_off);
37162306a36Sopenharmony_ci	value = calloc(1, info->value_size);
37262306a36Sopenharmony_ci	if (!value) {
37362306a36Sopenharmony_ci		p_err("mem alloc failed");
37462306a36Sopenharmony_ci		return -1;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (bpf_map_lookup_elem(fd, &zero, value)) {
37862306a36Sopenharmony_ci		p_err("can't lookup struct_ops map %s id %u",
37962306a36Sopenharmony_ci		      info->name, info->id);
38062306a36Sopenharmony_ci		free(value);
38162306a36Sopenharmony_ci		return -1;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	jsonw_start_object(wtr);
38562306a36Sopenharmony_ci	jsonw_name(wtr, "bpf_map_info");
38662306a36Sopenharmony_ci	btf_dumper_type(d, map_info_type_id, (void *)info);
38762306a36Sopenharmony_ci	jsonw_end_object(wtr);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	jsonw_start_object(wtr);
39062306a36Sopenharmony_ci	jsonw_name(wtr, struct_ops_name);
39162306a36Sopenharmony_ci	btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
39262306a36Sopenharmony_ci	jsonw_end_object(wtr);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	free(value);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return 0;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int do_dump(int argc, char **argv)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	const char *search_type = NULL, *search_term = NULL;
40262306a36Sopenharmony_ci	json_writer_t *wtr = json_wtr;
40362306a36Sopenharmony_ci	const struct btf *kern_btf;
40462306a36Sopenharmony_ci	struct btf_dumper d = {};
40562306a36Sopenharmony_ci	struct res res;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (argc && argc != 2)
40862306a36Sopenharmony_ci		usage();
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (argc == 2) {
41162306a36Sopenharmony_ci		search_type = GET_ARG();
41262306a36Sopenharmony_ci		search_term = GET_ARG();
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	kern_btf = get_btf_vmlinux();
41662306a36Sopenharmony_ci	if (!kern_btf)
41762306a36Sopenharmony_ci		return -1;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (!json_output) {
42062306a36Sopenharmony_ci		wtr = jsonw_new(stdout);
42162306a36Sopenharmony_ci		if (!wtr) {
42262306a36Sopenharmony_ci			p_err("can't create json writer");
42362306a36Sopenharmony_ci			return -1;
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci		jsonw_pretty(wtr, true);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	d.btf = kern_btf;
42962306a36Sopenharmony_ci	d.jw = wtr;
43062306a36Sopenharmony_ci	d.is_plain_text = !json_output;
43162306a36Sopenharmony_ci	d.prog_id_as_func_ptr = true;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
43462306a36Sopenharmony_ci				    wtr);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!json_output)
43762306a36Sopenharmony_ci		jsonw_destroy(&wtr);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return cmd_retval(&res, !!search_term);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
44362306a36Sopenharmony_ci			   struct json_writer *wtr)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	int zero = 0;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (bpf_map_delete_elem(fd, &zero)) {
44862306a36Sopenharmony_ci		p_err("can't unload %s %s id %u: %s",
44962306a36Sopenharmony_ci		      get_kern_struct_ops_name(info), info->name,
45062306a36Sopenharmony_ci		      info->id, strerror(errno));
45162306a36Sopenharmony_ci		return -1;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	p_info("Unregistered %s %s id %u",
45562306a36Sopenharmony_ci	       get_kern_struct_ops_name(info), info->name,
45662306a36Sopenharmony_ci	       info->id);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int do_unregister(int argc, char **argv)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	const char *search_type, *search_term;
46462306a36Sopenharmony_ci	struct res res;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (argc != 2)
46762306a36Sopenharmony_ci		usage();
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	search_type = GET_ARG();
47062306a36Sopenharmony_ci	search_term = GET_ARG();
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	res = do_work_on_struct_ops(search_type, search_term,
47362306a36Sopenharmony_ci				    __do_unregister, NULL, NULL);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return cmd_retval(&res, true);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int pin_link(struct bpf_link *link, const char *pindir,
47962306a36Sopenharmony_ci		    const char *name)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	char pinfile[PATH_MAX];
48262306a36Sopenharmony_ci	int err;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	err = pathname_concat(pinfile, sizeof(pinfile), pindir, name);
48562306a36Sopenharmony_ci	if (err)
48662306a36Sopenharmony_ci		return -1;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return bpf_link__pin(link, pinfile);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int do_register(int argc, char **argv)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_object_open_opts, open_opts);
49462306a36Sopenharmony_ci	__u32 link_info_len = sizeof(struct bpf_link_info);
49562306a36Sopenharmony_ci	struct bpf_link_info link_info = {};
49662306a36Sopenharmony_ci	struct bpf_map_info info = {};
49762306a36Sopenharmony_ci	__u32 info_len = sizeof(info);
49862306a36Sopenharmony_ci	int nr_errs = 0, nr_maps = 0;
49962306a36Sopenharmony_ci	const char *linkdir = NULL;
50062306a36Sopenharmony_ci	struct bpf_object *obj;
50162306a36Sopenharmony_ci	struct bpf_link *link;
50262306a36Sopenharmony_ci	struct bpf_map *map;
50362306a36Sopenharmony_ci	const char *file;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (argc != 1 && argc != 2)
50662306a36Sopenharmony_ci		usage();
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	file = GET_ARG();
50962306a36Sopenharmony_ci	if (argc == 1)
51062306a36Sopenharmony_ci		linkdir = GET_ARG();
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (linkdir && mount_bpffs_for_pin(linkdir, true)) {
51362306a36Sopenharmony_ci		p_err("can't mount bpffs for pinning");
51462306a36Sopenharmony_ci		return -1;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (verifier_logs)
51862306a36Sopenharmony_ci		/* log_level1 + log_level2 + stats, but not stable UAPI */
51962306a36Sopenharmony_ci		open_opts.kernel_log_level = 1 + 2 + 4;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	obj = bpf_object__open_file(file, &open_opts);
52262306a36Sopenharmony_ci	if (!obj)
52362306a36Sopenharmony_ci		return -1;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	set_max_rlimit();
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (bpf_object__load(obj)) {
52862306a36Sopenharmony_ci		bpf_object__close(obj);
52962306a36Sopenharmony_ci		return -1;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	bpf_object__for_each_map(map, obj) {
53362306a36Sopenharmony_ci		if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
53462306a36Sopenharmony_ci			continue;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		link = bpf_map__attach_struct_ops(map);
53762306a36Sopenharmony_ci		if (!link) {
53862306a36Sopenharmony_ci			p_err("can't register struct_ops %s: %s",
53962306a36Sopenharmony_ci			      bpf_map__name(map), strerror(errno));
54062306a36Sopenharmony_ci			nr_errs++;
54162306a36Sopenharmony_ci			continue;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		nr_maps++;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info,
54662306a36Sopenharmony_ci					   &info_len)) {
54762306a36Sopenharmony_ci			/* Not p_err.  The struct_ops was attached
54862306a36Sopenharmony_ci			 * successfully.
54962306a36Sopenharmony_ci			 */
55062306a36Sopenharmony_ci			p_info("Registered %s but can't find id: %s",
55162306a36Sopenharmony_ci			       bpf_map__name(map), strerror(errno));
55262306a36Sopenharmony_ci			goto clean_link;
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci		if (!(bpf_map__map_flags(map) & BPF_F_LINK)) {
55562306a36Sopenharmony_ci			p_info("Registered %s %s id %u",
55662306a36Sopenharmony_ci			       get_kern_struct_ops_name(&info),
55762306a36Sopenharmony_ci			       info.name,
55862306a36Sopenharmony_ci			       info.id);
55962306a36Sopenharmony_ci			goto clean_link;
56062306a36Sopenharmony_ci		}
56162306a36Sopenharmony_ci		if (bpf_link_get_info_by_fd(bpf_link__fd(link),
56262306a36Sopenharmony_ci					    &link_info,
56362306a36Sopenharmony_ci					    &link_info_len)) {
56462306a36Sopenharmony_ci			p_err("Registered %s but can't find link id: %s",
56562306a36Sopenharmony_ci			      bpf_map__name(map), strerror(errno));
56662306a36Sopenharmony_ci			nr_errs++;
56762306a36Sopenharmony_ci			goto clean_link;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci		if (linkdir && pin_link(link, linkdir, info.name)) {
57062306a36Sopenharmony_ci			p_err("can't pin link %u for %s: %s",
57162306a36Sopenharmony_ci			      link_info.id, info.name,
57262306a36Sopenharmony_ci			      strerror(errno));
57362306a36Sopenharmony_ci			nr_errs++;
57462306a36Sopenharmony_ci			goto clean_link;
57562306a36Sopenharmony_ci		}
57662306a36Sopenharmony_ci		p_info("Registered %s %s map id %u link id %u",
57762306a36Sopenharmony_ci		       get_kern_struct_ops_name(&info),
57862306a36Sopenharmony_ci		       info.name, info.id, link_info.id);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ciclean_link:
58162306a36Sopenharmony_ci		bpf_link__disconnect(link);
58262306a36Sopenharmony_ci		bpf_link__destroy(link);
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	bpf_object__close(obj);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (nr_errs)
58862306a36Sopenharmony_ci		return -1;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (!nr_maps) {
59162306a36Sopenharmony_ci		p_err("no struct_ops found in %s", file);
59262306a36Sopenharmony_ci		return -1;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (json_output)
59662306a36Sopenharmony_ci		jsonw_null(json_wtr);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int do_help(int argc, char **argv)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	if (json_output) {
60462306a36Sopenharmony_ci		jsonw_null(json_wtr);
60562306a36Sopenharmony_ci		return 0;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	fprintf(stderr,
60962306a36Sopenharmony_ci		"Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
61062306a36Sopenharmony_ci		"       %1$s %2$s dump [STRUCT_OPS_MAP]\n"
61162306a36Sopenharmony_ci		"       %1$s %2$s register OBJ [LINK_DIR]\n"
61262306a36Sopenharmony_ci		"       %1$s %2$s unregister STRUCT_OPS_MAP\n"
61362306a36Sopenharmony_ci		"       %1$s %2$s help\n"
61462306a36Sopenharmony_ci		"\n"
61562306a36Sopenharmony_ci		"       STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
61662306a36Sopenharmony_ci		"       " HELP_SPEC_OPTIONS " }\n"
61762306a36Sopenharmony_ci		"",
61862306a36Sopenharmony_ci		bin_name, argv[-2]);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic const struct cmd cmds[] = {
62462306a36Sopenharmony_ci	{ "show",	do_show },
62562306a36Sopenharmony_ci	{ "list",	do_show },
62662306a36Sopenharmony_ci	{ "register",	do_register },
62762306a36Sopenharmony_ci	{ "unregister",	do_unregister },
62862306a36Sopenharmony_ci	{ "dump",	do_dump },
62962306a36Sopenharmony_ci	{ "help",	do_help },
63062306a36Sopenharmony_ci	{ 0 }
63162306a36Sopenharmony_ci};
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ciint do_struct_ops(int argc, char **argv)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	int err;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	err = cmd_select(cmds, argc, argv, do_help);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	btf__free(btf_vmlinux);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return err;
64262306a36Sopenharmony_ci}
643