diff options
Diffstat (limited to 'tools/bpf/bpftool/map.c')
| -rw-r--r-- | tools/bpf/bpftool/map.c | 255 | 
1 files changed, 212 insertions, 43 deletions
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index b455930a3eaf..7bf38f0e152e 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -36,6 +36,7 @@  #include <fcntl.h>  #include <linux/err.h>  #include <linux/kernel.h> +#include <net/if.h>  #include <stdbool.h>  #include <stdio.h>  #include <stdlib.h> @@ -71,13 +72,16 @@ static const char * const map_type_name[] = {  	[BPF_MAP_TYPE_XSKMAP]           = "xskmap",  	[BPF_MAP_TYPE_SOCKHASH]		= "sockhash",  	[BPF_MAP_TYPE_CGROUP_STORAGE]	= "cgroup_storage", +	[BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray", +	[BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE]	= "percpu_cgroup_storage",  };  static bool map_is_per_cpu(__u32 type)  {  	return type == BPF_MAP_TYPE_PERCPU_HASH ||  	       type == BPF_MAP_TYPE_PERCPU_ARRAY || -	       type == BPF_MAP_TYPE_LRU_PERCPU_HASH; +	       type == BPF_MAP_TYPE_LRU_PERCPU_HASH || +	       type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE;  }  static bool map_is_map_of_maps(__u32 type) @@ -91,6 +95,17 @@ static bool map_is_map_of_progs(__u32 type)  	return type == BPF_MAP_TYPE_PROG_ARRAY;  } +static int map_type_from_str(const char *type) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(map_type_name); i++) +		/* Don't allow prefixing in case of possible future shadowing */ +		if (map_type_name[i] && !strcmp(map_type_name[i], type)) +			return i; +	return -1; +} +  static void *alloc_value(struct bpf_map_info *info)  {  	if (map_is_per_cpu(info->type)) @@ -170,9 +185,28 @@ static int do_dump_btf(const struct btf_dumper *d,  	if (ret)  		goto err_end_obj; -	jsonw_name(d->jw, "value"); +	if (!map_is_per_cpu(map_info->type)) { +		jsonw_name(d->jw, "value"); +		ret = btf_dumper_type(d, map_info->btf_value_type_id, value); +	} else { +		unsigned int i, n, step; -	ret = btf_dumper_type(d, map_info->btf_value_type_id, value); +		jsonw_name(d->jw, "values"); +		jsonw_start_array(d->jw); +		n = get_possible_cpus(); +		step = round_up(map_info->value_size, 8); +		for (i = 0; i < n; i++) { +			jsonw_start_object(d->jw); +			jsonw_int_field(d->jw, "cpu", i); +			jsonw_name(d->jw, "value"); +			ret = btf_dumper_type(d, map_info->btf_value_type_id, +					      value + i * step); +			jsonw_end_object(d->jw); +			if (ret) +				break; +		} +		jsonw_end_array(d->jw); +	}  err_end_obj:  	/* end of key-value pair */ @@ -299,11 +333,40 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,  			jsonw_end_object(json_wtr);  		}  		jsonw_end_array(json_wtr); +		if (btf) { +			struct btf_dumper d = { +				.btf = btf, +				.jw = json_wtr, +				.is_plain_text = false, +			}; + +			jsonw_name(json_wtr, "formatted"); +			do_dump_btf(&d, info, key, value); +		}  	}  	jsonw_end_object(json_wtr);  } +static void print_entry_error(struct bpf_map_info *info, unsigned char *key, +			      const char *value) +{ +	int value_size = strlen(value); +	bool single_line, break_names; + +	break_names = info->key_size > 16 || value_size > 16; +	single_line = info->key_size + value_size <= 24 && !break_names; + +	printf("key:%c", break_names ? '\n' : ' '); +	fprint_hex(stdout, key, info->key_size, " "); + +	printf(single_line ? "  " : "\n"); + +	printf("value:%c%s", break_names ? '\n' : ' ', value); + +	printf("\n"); +} +  static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,  			      unsigned char *value)  { @@ -626,6 +689,54 @@ static int do_show(int argc, char **argv)  	return errno == ENOENT ? 0 : -1;  } +static int dump_map_elem(int fd, void *key, void *value, +			 struct bpf_map_info *map_info, struct btf *btf, +			 json_writer_t *btf_wtr) +{ +	int num_elems = 0; +	int lookup_errno; + +	if (!bpf_map_lookup_elem(fd, key, value)) { +		if (json_output) { +			print_entry_json(map_info, key, value, btf); +		} else { +			if (btf) { +				struct btf_dumper d = { +					.btf = btf, +					.jw = btf_wtr, +					.is_plain_text = true, +				}; + +				do_dump_btf(&d, map_info, key, value); +			} else { +				print_entry_plain(map_info, key, value); +			} +			num_elems++; +		} +		return num_elems; +	} + +	/* lookup error handling */ +	lookup_errno = errno; + +	if (map_is_map_of_maps(map_info->type) || +	    map_is_map_of_progs(map_info->type)) +		return 0; + +	if (json_output) { +		jsonw_name(json_wtr, "key"); +		print_hex_data_json(key, map_info->key_size); +		jsonw_name(json_wtr, "value"); +		jsonw_start_object(json_wtr); +		jsonw_string_field(json_wtr, "error", strerror(lookup_errno)); +		jsonw_end_object(json_wtr); +	} else { +		print_entry_error(map_info, key, strerror(lookup_errno)); +	} + +	return 0; +} +  static int do_dump(int argc, char **argv)  {  	struct bpf_map_info info = {}; @@ -644,12 +755,6 @@ static int do_dump(int argc, char **argv)  	if (fd < 0)  		return -1; -	if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { -		p_err("Dumping maps of maps and program maps not supported"); -		close(fd); -		return -1; -	} -  	key = malloc(info.key_size);  	value = alloc_value(&info);  	if (!key || !value) { @@ -687,40 +792,8 @@ static int do_dump(int argc, char **argv)  				err = 0;  			break;  		} - -		if (!bpf_map_lookup_elem(fd, key, value)) { -			if (json_output) -				print_entry_json(&info, key, value, btf); -			else -				if (btf) { -					struct btf_dumper d = { -						.btf = btf, -						.jw = btf_wtr, -						.is_plain_text = true, -					}; - -					do_dump_btf(&d, &info, key, value); -				} else { -					print_entry_plain(&info, key, value); -				} -		} else { -			if (json_output) { -				jsonw_name(json_wtr, "key"); -				print_hex_data_json(key, info.key_size); -				jsonw_name(json_wtr, "value"); -				jsonw_start_object(json_wtr); -				jsonw_string_field(json_wtr, "error", -						   "can't lookup element"); -				jsonw_end_object(json_wtr); -			} else { -				p_info("can't lookup element with key: "); -				fprint_hex(stderr, key, info.key_size, " "); -				fprintf(stderr, "\n"); -			} -		} - +		num_elems += dump_map_elem(fd, key, value, &info, btf, btf_wtr);  		prev_key = key; -		num_elems++;  	}  	if (json_output) @@ -997,6 +1070,92 @@ static int do_pin(int argc, char **argv)  	return err;  } +static int do_create(int argc, char **argv) +{ +	struct bpf_create_map_attr attr = { NULL, }; +	const char *pinfile; +	int err, fd; + +	if (!REQ_ARGS(7)) +		return -1; +	pinfile = GET_ARG(); + +	while (argc) { +		if (!REQ_ARGS(2)) +			return -1; + +		if (is_prefix(*argv, "type")) { +			NEXT_ARG(); + +			if (attr.map_type) { +				p_err("map type already specified"); +				return -1; +			} + +			attr.map_type = map_type_from_str(*argv); +			if ((int)attr.map_type < 0) { +				p_err("unrecognized map type: %s", *argv); +				return -1; +			} +			NEXT_ARG(); +		} else if (is_prefix(*argv, "name")) { +			NEXT_ARG(); +			attr.name = GET_ARG(); +		} else if (is_prefix(*argv, "key")) { +			if (parse_u32_arg(&argc, &argv, &attr.key_size, +					  "key size")) +				return -1; +		} else if (is_prefix(*argv, "value")) { +			if (parse_u32_arg(&argc, &argv, &attr.value_size, +					  "value size")) +				return -1; +		} else if (is_prefix(*argv, "entries")) { +			if (parse_u32_arg(&argc, &argv, &attr.max_entries, +					  "max entries")) +				return -1; +		} else if (is_prefix(*argv, "flags")) { +			if (parse_u32_arg(&argc, &argv, &attr.map_flags, +					  "flags")) +				return -1; +		} else if (is_prefix(*argv, "dev")) { +			NEXT_ARG(); + +			if (attr.map_ifindex) { +				p_err("offload device already specified"); +				return -1; +			} + +			attr.map_ifindex = if_nametoindex(*argv); +			if (!attr.map_ifindex) { +				p_err("unrecognized netdevice '%s': %s", +				      *argv, strerror(errno)); +				return -1; +			} +			NEXT_ARG(); +		} +	} + +	if (!attr.name) { +		p_err("map name not specified"); +		return -1; +	} + +	fd = bpf_create_map_xattr(&attr); +	if (fd < 0) { +		p_err("map create failed: %s", strerror(errno)); +		return -1; +	} + +	err = do_pin_fd(fd, pinfile); +	close(fd); +	if (err) +		return err; + +	if (json_output) +		jsonw_null(json_wtr); +	return 0; +} +  static int do_help(int argc, char **argv)  {  	if (json_output) { @@ -1006,6 +1165,9 @@ static int do_help(int argc, char **argv)  	fprintf(stderr,  		"Usage: %s %s { show | list }   [MAP]\n" +		"       %s %s create     FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n" +		"                              entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" +		"                              [dev NAME]\n"  		"       %s %s dump       MAP\n"  		"       %s %s update     MAP  key DATA value VALUE [UPDATE_FLAGS]\n"  		"       %s %s lookup     MAP  key DATA\n" @@ -1020,11 +1182,17 @@ static int do_help(int argc, char **argv)  		"       " HELP_SPEC_PROGRAM "\n"  		"       VALUE := { DATA | MAP | PROG }\n"  		"       UPDATE_FLAGS := { any | exist | noexist }\n" +		"       TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n" +		"                 percpu_array | stack_trace | cgroup_array | lru_hash |\n" +		"                 lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n" +		"                 devmap | sockmap | cpumap | xskmap | sockhash |\n" +		"                 cgroup_storage | reuseport_sockarray | percpu_cgroup_storage }\n"  		"       " HELP_SPEC_OPTIONS "\n"  		"",  		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],  		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], -		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); +		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], +		bin_name, argv[-2]);  	return 0;  } @@ -1040,6 +1208,7 @@ static const struct cmd cmds[] = {  	{ "delete",	do_delete },  	{ "pin",	do_pin },  	{ "event_pipe",	do_event_pipe }, +	{ "create",	do_create },  	{ 0 }  };  |