brintos

brintos / linux-shallow public Read only

0
0
Text · 14.3 KiB · aa43dea Raw
649 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/* Copyright (C) 2020 Facebook */3 4#include <errno.h>5#include <stdio.h>6#include <unistd.h>7 8#include <linux/err.h>9 10#include <bpf/bpf.h>11#include <bpf/btf.h>12#include <bpf/libbpf.h>13 14#include "json_writer.h"15#include "main.h"16 17#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"18 19static const struct btf_type *map_info_type;20static __u32 map_info_alloc_len;21static struct btf *btf_vmlinux;22static __s32 map_info_type_id;23 24struct res {25	unsigned int nr_maps;26	unsigned int nr_errs;27};28 29static const struct btf *get_btf_vmlinux(void)30{31	if (btf_vmlinux)32		return btf_vmlinux;33 34	btf_vmlinux = libbpf_find_kernel_btf();35	if (!btf_vmlinux)36		p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");37 38	return btf_vmlinux;39}40 41static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)42{43	const struct btf *kern_btf;44	const struct btf_type *t;45	const char *st_ops_name;46 47	kern_btf = get_btf_vmlinux();48	if (!kern_btf)49		return "<btf_vmlinux_not_found>";50 51	t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);52	st_ops_name = btf__name_by_offset(kern_btf, t->name_off);53	st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);54 55	return st_ops_name;56}57 58static __s32 get_map_info_type_id(void)59{60	const struct btf *kern_btf;61 62	if (map_info_type_id)63		return map_info_type_id;64 65	kern_btf = get_btf_vmlinux();66	if (!kern_btf)67		return 0;68 69	map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",70						  BTF_KIND_STRUCT);71	if (map_info_type_id < 0) {72		p_err("can't find bpf_map_info from btf_vmlinux");73		return map_info_type_id;74	}75	map_info_type = btf__type_by_id(kern_btf, map_info_type_id);76 77	/* Ensure map_info_alloc() has at least what the bpftool needs */78	map_info_alloc_len = map_info_type->size;79	if (map_info_alloc_len < sizeof(struct bpf_map_info))80		map_info_alloc_len = sizeof(struct bpf_map_info);81 82	return map_info_type_id;83}84 85/* If the subcmd needs to print out the bpf_map_info,86 * it should always call map_info_alloc to allocate87 * a bpf_map_info object instead of allocating it88 * on the stack.89 *90 * map_info_alloc() will take the running kernel's btf91 * into account.  i.e. it will consider the92 * sizeof(struct bpf_map_info) of the running kernel.93 *94 * It will enable the "struct_ops" cmd to print the latest95 * "struct bpf_map_info".96 *97 * [ Recall that "struct_ops" requires the kernel's btf to98 *   be available ]99 */100static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)101{102	struct bpf_map_info *info;103 104	if (get_map_info_type_id() < 0)105		return NULL;106 107	info = calloc(1, map_info_alloc_len);108	if (!info)109		p_err("mem alloc failed");110	else111		*alloc_len = map_info_alloc_len;112 113	return info;114}115 116/* It iterates all struct_ops maps of the system.117 * It returns the fd in "*res_fd" and map_info in "*info".118 * In the very first iteration, info->id should be 0.119 * An optional map "*name" filter can be specified.120 * The filter can be made more flexible in the future.121 * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.122 *123 * Return value:124 *     1: A struct_ops map found.  It is returned in "*res_fd" and "*info".125 *	  The caller can continue to call get_next in the future.126 *     0: No struct_ops map is returned.127 *        All struct_ops map has been found.128 *    -1: Error and the caller should abort the iteration.129 */130static int get_next_struct_ops_map(const char *name, int *res_fd,131				   struct bpf_map_info *info, __u32 info_len)132{133	__u32 id = info->id;134	int err, fd;135 136	while (true) {137		err = bpf_map_get_next_id(id, &id);138		if (err) {139			if (errno == ENOENT)140				return 0;141			p_err("can't get next map: %s", strerror(errno));142			return -1;143		}144 145		fd = bpf_map_get_fd_by_id(id);146		if (fd < 0) {147			if (errno == ENOENT)148				continue;149			p_err("can't get map by id (%u): %s",150			      id, strerror(errno));151			return -1;152		}153 154		err = bpf_map_get_info_by_fd(fd, info, &info_len);155		if (err) {156			p_err("can't get map info: %s", strerror(errno));157			close(fd);158			return -1;159		}160 161		if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&162		    (!name || !strcmp(name, info->name))) {163			*res_fd = fd;164			return 1;165		}166		close(fd);167	}168}169 170static int cmd_retval(const struct res *res, bool must_have_one_map)171{172	if (res->nr_errs || (!res->nr_maps && must_have_one_map))173		return -1;174 175	return 0;176}177 178/* "data" is the work_func private storage */179typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,180			 struct json_writer *wtr);181 182/* Find all struct_ops map in the system.183 * Filter out by "name" (if specified).184 * Then call "func(fd, info, data, wtr)" on each struct_ops map found.185 */186static struct res do_search(const char *name, work_func func, void *data,187			    struct json_writer *wtr)188{189	struct bpf_map_info *info;190	struct res res = {};191	__u32 info_len;192	int fd, err;193 194	info = map_info_alloc(&info_len);195	if (!info) {196		res.nr_errs++;197		return res;198	}199 200	if (wtr)201		jsonw_start_array(wtr);202	while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {203		res.nr_maps++;204		err = func(fd, info, data, wtr);205		if (err)206			res.nr_errs++;207		close(fd);208	}209	if (wtr)210		jsonw_end_array(wtr);211 212	if (err)213		res.nr_errs++;214 215	if (!wtr && name && !res.nr_errs && !res.nr_maps)216		/* It is not printing empty [].217		 * Thus, needs to specifically say nothing found218		 * for "name" here.219		 */220		p_err("no struct_ops found for %s", name);221	else if (!wtr && json_output && !res.nr_errs)222		/* The "func()" above is not writing any json (i.e. !wtr223		 * test here).224		 *225		 * However, "-j" is enabled and there is no errs here,226		 * so call json_null() as the current convention of227		 * other cmds.228		 */229		jsonw_null(json_wtr);230 231	free(info);232	return res;233}234 235static struct res do_one_id(const char *id_str, work_func func, void *data,236			    struct json_writer *wtr)237{238	struct bpf_map_info *info;239	struct res res = {};240	unsigned long id;241	__u32 info_len;242	char *endptr;243	int fd;244 245	id = strtoul(id_str, &endptr, 0);246	if (*endptr || !id || id > UINT32_MAX) {247		p_err("invalid id %s", id_str);248		res.nr_errs++;249		return res;250	}251 252	fd = bpf_map_get_fd_by_id(id);253	if (fd < 0) {254		p_err("can't get map by id (%lu): %s", id, strerror(errno));255		res.nr_errs++;256		return res;257	}258 259	info = map_info_alloc(&info_len);260	if (!info) {261		res.nr_errs++;262		goto done;263	}264 265	if (bpf_map_get_info_by_fd(fd, info, &info_len)) {266		p_err("can't get map info: %s", strerror(errno));267		res.nr_errs++;268		goto done;269	}270 271	if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {272		p_err("%s id %u is not a struct_ops map", info->name, info->id);273		res.nr_errs++;274		goto done;275	}276 277	res.nr_maps++;278 279	if (wtr)280		jsonw_start_array(wtr);281 282	if (func(fd, info, data, wtr))283		res.nr_errs++;284	else if (!wtr && json_output)285		/* The "func()" above is not writing any json (i.e. !wtr286		 * test here).287		 *288		 * However, "-j" is enabled and there is no errs here,289		 * so call json_null() as the current convention of290		 * other cmds.291		 */292		jsonw_null(json_wtr);293 294	if (wtr)295		jsonw_end_array(wtr);296 297done:298	free(info);299	close(fd);300 301	return res;302}303 304static struct res do_work_on_struct_ops(const char *search_type,305					const char *search_term,306					work_func func, void *data,307					struct json_writer *wtr)308{309	if (search_type) {310		if (is_prefix(search_type, "id"))311			return do_one_id(search_term, func, data, wtr);312		else if (!is_prefix(search_type, "name"))313			usage();314	}315 316	return do_search(search_term, func, data, wtr);317}318 319static int __do_show(int fd, const struct bpf_map_info *info, void *data,320		     struct json_writer *wtr)321{322	if (wtr) {323		jsonw_start_object(wtr);324		jsonw_uint_field(wtr, "id", info->id);325		jsonw_string_field(wtr, "name", info->name);326		jsonw_string_field(wtr, "kernel_struct_ops",327				   get_kern_struct_ops_name(info));328		jsonw_end_object(wtr);329	} else {330		printf("%u: %-15s %-32s\n", info->id, info->name,331		       get_kern_struct_ops_name(info));332	}333 334	return 0;335}336 337static int do_show(int argc, char **argv)338{339	const char *search_type = NULL, *search_term = NULL;340	struct res res;341 342	if (argc && argc != 2)343		usage();344 345	if (argc == 2) {346		search_type = GET_ARG();347		search_term = GET_ARG();348	}349 350	res = do_work_on_struct_ops(search_type, search_term, __do_show,351				    NULL, json_wtr);352 353	return cmd_retval(&res, !!search_term);354}355 356static int __do_dump(int fd, const struct bpf_map_info *info, void *data,357		     struct json_writer *wtr)358{359	struct btf_dumper *d = (struct btf_dumper *)data;360	const struct btf_type *struct_ops_type;361	const struct btf *kern_btf = d->btf;362	const char *struct_ops_name;363	int zero = 0;364	void *value;365 366	/* note: d->jw == wtr */367 368	kern_btf = d->btf;369 370	/* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have371	 * btf_vmlinux_value_type_id.372	 */373	struct_ops_type = btf__type_by_id(kern_btf,374					  info->btf_vmlinux_value_type_id);375	struct_ops_name = btf__name_by_offset(kern_btf,376					      struct_ops_type->name_off);377	value = calloc(1, info->value_size);378	if (!value) {379		p_err("mem alloc failed");380		return -1;381	}382 383	if (bpf_map_lookup_elem(fd, &zero, value)) {384		p_err("can't lookup struct_ops map %s id %u",385		      info->name, info->id);386		free(value);387		return -1;388	}389 390	jsonw_start_object(wtr);391	jsonw_name(wtr, "bpf_map_info");392	btf_dumper_type(d, map_info_type_id, (void *)info);393	jsonw_end_object(wtr);394 395	jsonw_start_object(wtr);396	jsonw_name(wtr, struct_ops_name);397	btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);398	jsonw_end_object(wtr);399 400	free(value);401 402	return 0;403}404 405static int do_dump(int argc, char **argv)406{407	const char *search_type = NULL, *search_term = NULL;408	json_writer_t *wtr = json_wtr;409	const struct btf *kern_btf;410	struct btf_dumper d = {};411	struct res res;412 413	if (argc && argc != 2)414		usage();415 416	if (argc == 2) {417		search_type = GET_ARG();418		search_term = GET_ARG();419	}420 421	kern_btf = get_btf_vmlinux();422	if (!kern_btf)423		return -1;424 425	if (!json_output) {426		wtr = jsonw_new(stdout);427		if (!wtr) {428			p_err("can't create json writer");429			return -1;430		}431		jsonw_pretty(wtr, true);432	}433 434	d.btf = kern_btf;435	d.jw = wtr;436	d.is_plain_text = !json_output;437	d.prog_id_as_func_ptr = true;438 439	res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,440				    wtr);441 442	if (!json_output)443		jsonw_destroy(&wtr);444 445	return cmd_retval(&res, !!search_term);446}447 448static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,449			   struct json_writer *wtr)450{451	int zero = 0;452 453	if (bpf_map_delete_elem(fd, &zero)) {454		p_err("can't unload %s %s id %u: %s",455		      get_kern_struct_ops_name(info), info->name,456		      info->id, strerror(errno));457		return -1;458	}459 460	p_info("Unregistered %s %s id %u",461	       get_kern_struct_ops_name(info), info->name,462	       info->id);463 464	return 0;465}466 467static int do_unregister(int argc, char **argv)468{469	const char *search_type, *search_term;470	struct res res;471 472	if (argc != 2)473		usage();474 475	search_type = GET_ARG();476	search_term = GET_ARG();477 478	res = do_work_on_struct_ops(search_type, search_term,479				    __do_unregister, NULL, NULL);480 481	return cmd_retval(&res, true);482}483 484static int pin_link(struct bpf_link *link, const char *pindir,485		    const char *name)486{487	char pinfile[PATH_MAX];488	int err;489 490	err = pathname_concat(pinfile, sizeof(pinfile), pindir, name);491	if (err)492		return -1;493 494	return bpf_link__pin(link, pinfile);495}496 497static int do_register(int argc, char **argv)498{499	LIBBPF_OPTS(bpf_object_open_opts, open_opts);500	__u32 link_info_len = sizeof(struct bpf_link_info);501	struct bpf_link_info link_info = {};502	struct bpf_map_info info = {};503	__u32 info_len = sizeof(info);504	int nr_errs = 0, nr_maps = 0;505	const char *linkdir = NULL;506	struct bpf_object *obj;507	struct bpf_link *link;508	struct bpf_map *map;509	const char *file;510 511	if (argc != 1 && argc != 2)512		usage();513 514	file = GET_ARG();515	if (argc == 1)516		linkdir = GET_ARG();517 518	if (linkdir && create_and_mount_bpffs_dir(linkdir)) {519		p_err("can't mount bpffs for pinning");520		return -1;521	}522 523	if (verifier_logs)524		/* log_level1 + log_level2 + stats, but not stable UAPI */525		open_opts.kernel_log_level = 1 + 2 + 4;526 527	obj = bpf_object__open_file(file, &open_opts);528	if (!obj)529		return -1;530 531	set_max_rlimit();532 533	if (bpf_object__load(obj)) {534		bpf_object__close(obj);535		return -1;536	}537 538	bpf_object__for_each_map(map, obj) {539		if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)540			continue;541 542		link = bpf_map__attach_struct_ops(map);543		if (!link) {544			p_err("can't register struct_ops %s: %s",545			      bpf_map__name(map), strerror(errno));546			nr_errs++;547			continue;548		}549		nr_maps++;550 551		if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info,552					   &info_len)) {553			/* Not p_err.  The struct_ops was attached554			 * successfully.555			 */556			p_info("Registered %s but can't find id: %s",557			       bpf_map__name(map), strerror(errno));558			goto clean_link;559		}560		if (!(bpf_map__map_flags(map) & BPF_F_LINK)) {561			p_info("Registered %s %s id %u",562			       get_kern_struct_ops_name(&info),563			       info.name,564			       info.id);565			goto clean_link;566		}567		if (bpf_link_get_info_by_fd(bpf_link__fd(link),568					    &link_info,569					    &link_info_len)) {570			p_err("Registered %s but can't find link id: %s",571			      bpf_map__name(map), strerror(errno));572			nr_errs++;573			goto clean_link;574		}575		if (linkdir && pin_link(link, linkdir, info.name)) {576			p_err("can't pin link %u for %s: %s",577			      link_info.id, info.name,578			      strerror(errno));579			nr_errs++;580			goto clean_link;581		}582		p_info("Registered %s %s map id %u link id %u",583		       get_kern_struct_ops_name(&info),584		       info.name, info.id, link_info.id);585 586clean_link:587		bpf_link__disconnect(link);588		bpf_link__destroy(link);589	}590 591	bpf_object__close(obj);592 593	if (nr_errs)594		return -1;595 596	if (!nr_maps) {597		p_err("no struct_ops found in %s", file);598		return -1;599	}600 601	if (json_output)602		jsonw_null(json_wtr);603 604	return 0;605}606 607static int do_help(int argc, char **argv)608{609	if (json_output) {610		jsonw_null(json_wtr);611		return 0;612	}613 614	fprintf(stderr,615		"Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"616		"       %1$s %2$s dump [STRUCT_OPS_MAP]\n"617		"       %1$s %2$s register OBJ [LINK_DIR]\n"618		"       %1$s %2$s unregister STRUCT_OPS_MAP\n"619		"       %1$s %2$s help\n"620		"\n"621		"       STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"622		"       " HELP_SPEC_OPTIONS " }\n"623		"",624		bin_name, argv[-2]);625 626	return 0;627}628 629static const struct cmd cmds[] = {630	{ "show",	do_show },631	{ "list",	do_show },632	{ "register",	do_register },633	{ "unregister",	do_unregister },634	{ "dump",	do_dump },635	{ "help",	do_help },636	{ 0 }637};638 639int do_struct_ops(int argc, char **argv)640{641	int err;642 643	err = cmd_select(cmds, argc, argv, do_help);644 645	btf__free(btf_vmlinux);646 647	return err;648}649