Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools: bpftool: add map create command

Add a way of creating maps from user space. The command takes
as parameters most of the attributes of the map creation system
call command. After map is created its pinned to bpffs. This makes
it possible to easily and dynamically (without rebuilding programs)
test various corner cases related to map creation.

Map type names are taken from bpftool's array used for printing.
In general these days we try to make use of libbpf type names, but
there are no map type names in libbpf as of today.

As with most features I add the motivation is testing (offloads) :)

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Jakub Kicinski and committed by
Alexei Starovoitov
0b592b5a 2f1d774f

+183 -6
+13 -2
tools/bpf/bpftool/Documentation/bpftool-map.rst
··· 15 15 *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } 16 16 17 17 *COMMANDS* := 18 - { **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete** 19 - | **pin** | **help** } 18 + { **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** 19 + | **delete** | **pin** | **help** } 20 20 21 21 MAP COMMANDS 22 22 ============= 23 23 24 24 | **bpftool** **map { show | list }** [*MAP*] 25 + | **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \ 26 + | **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*] 25 27 | **bpftool** **map dump** *MAP* 26 28 | **bpftool** **map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*] 27 29 | **bpftool** **map lookup** *MAP* **key** *DATA* ··· 38 36 | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } 39 37 | *VALUE* := { *DATA* | *MAP* | *PROG* } 40 38 | *UPDATE_FLAGS* := { **any** | **exist** | **noexist** } 39 + | *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash** 40 + | | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash** 41 + | | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps** 42 + | | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash** 43 + | | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** } 41 44 42 45 DESCRIPTION 43 46 =========== ··· 53 46 54 47 Output will start with map ID followed by map type and 55 48 zero or more named attributes (depending on kernel version). 49 + 50 + **bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*] 51 + Create a new map with given parameters and pin it to *bpffs* 52 + as *FILE*. 56 53 57 54 **bpftool map dump** *MAP* 58 55 Dump all entries in a given *MAP*.
+2 -2
tools/bpf/bpftool/Documentation/bpftool.rst
··· 22 22 | { **-j** | **--json** } [{ **-p** | **--pretty** }] } 23 23 24 24 *MAP-COMMANDS* := 25 - { **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete** 26 - | **pin** | **event_pipe** | **help** } 25 + { **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** 26 + | **delete** | **pin** | **event_pipe** | **help** } 27 27 28 28 *PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin** 29 29 | **load** | **attach** | **detach** | **help** }
+37 -1
tools/bpf/bpftool/bash-completion/bpftool
··· 387 387 ;; 388 388 esac 389 389 ;; 390 + create) 391 + case $prev in 392 + $command) 393 + _filedir 394 + return 0 395 + ;; 396 + type) 397 + COMPREPLY=( $( compgen -W 'hash array prog_array \ 398 + perf_event_array percpu_hash percpu_array \ 399 + stack_trace cgroup_array lru_hash \ 400 + lru_percpu_hash lpm_trie array_of_maps \ 401 + hash_of_maps devmap sockmap cpumap xskmap \ 402 + sockhash cgroup_storage reuseport_sockarray \ 403 + percpu_cgroup_storage' -- \ 404 + "$cur" ) ) 405 + return 0 406 + ;; 407 + key|value|flags|name|entries) 408 + return 0 409 + ;; 410 + dev) 411 + _sysfs_get_netdevs 412 + return 0 413 + ;; 414 + *) 415 + _bpftool_once_attr 'type' 416 + _bpftool_once_attr 'key' 417 + _bpftool_once_attr 'value' 418 + _bpftool_once_attr 'entries' 419 + _bpftool_once_attr 'name' 420 + _bpftool_once_attr 'flags' 421 + _bpftool_once_attr 'dev' 422 + return 0 423 + ;; 424 + esac 425 + ;; 390 426 lookup|getnext|delete) 391 427 case $prev in 392 428 $command) ··· 536 500 *) 537 501 [[ $prev == $object ]] && \ 538 502 COMPREPLY=( $( compgen -W 'delete dump getnext help \ 539 - lookup pin event_pipe show list update' -- \ 503 + lookup pin event_pipe show list update create' -- \ 540 504 "$cur" ) ) 541 505 ;; 542 506 esac
+21
tools/bpf/bpftool/common.c
··· 618 618 jsonw_string_field(json_wtr, "ifname", name); 619 619 jsonw_end_object(json_wtr); 620 620 } 621 + 622 + int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what) 623 + { 624 + char *endptr; 625 + 626 + NEXT_ARGP(); 627 + 628 + if (*val) { 629 + p_err("%s already specified", what); 630 + return -1; 631 + } 632 + 633 + *val = strtoul(**argv, &endptr, 0); 634 + if (*endptr) { 635 + p_err("can't parse %s as %s", **argv, what); 636 + return -1; 637 + } 638 + NEXT_ARGP(); 639 + 640 + return 0; 641 + }
+1
tools/bpf/bpftool/main.h
··· 139 139 int do_perf(int argc, char **arg); 140 140 int do_net(int argc, char **arg); 141 141 142 + int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what); 142 143 int prog_parse_fd(int *argc, char ***argv); 143 144 int map_parse_fd(int *argc, char ***argv); 144 145 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
+109 -1
tools/bpf/bpftool/map.c
··· 36 36 #include <fcntl.h> 37 37 #include <linux/err.h> 38 38 #include <linux/kernel.h> 39 + #include <net/if.h> 39 40 #include <stdbool.h> 40 41 #include <stdio.h> 41 42 #include <stdlib.h> ··· 93 92 static bool map_is_map_of_progs(__u32 type) 94 93 { 95 94 return type == BPF_MAP_TYPE_PROG_ARRAY; 95 + } 96 + 97 + static int map_type_from_str(const char *type) 98 + { 99 + unsigned int i; 100 + 101 + for (i = 0; i < ARRAY_SIZE(map_type_name); i++) 102 + /* Don't allow prefixing in case of possible future shadowing */ 103 + if (map_type_name[i] && !strcmp(map_type_name[i], type)) 104 + return i; 105 + return -1; 96 106 } 97 107 98 108 static void *alloc_value(struct bpf_map_info *info) ··· 1070 1058 return err; 1071 1059 } 1072 1060 1061 + static int do_create(int argc, char **argv) 1062 + { 1063 + struct bpf_create_map_attr attr = { NULL, }; 1064 + const char *pinfile; 1065 + int err, fd; 1066 + 1067 + if (!REQ_ARGS(7)) 1068 + return -1; 1069 + pinfile = GET_ARG(); 1070 + 1071 + while (argc) { 1072 + if (!REQ_ARGS(2)) 1073 + return -1; 1074 + 1075 + if (is_prefix(*argv, "type")) { 1076 + NEXT_ARG(); 1077 + 1078 + if (attr.map_type) { 1079 + p_err("map type already specified"); 1080 + return -1; 1081 + } 1082 + 1083 + attr.map_type = map_type_from_str(*argv); 1084 + if ((int)attr.map_type < 0) { 1085 + p_err("unrecognized map type: %s", *argv); 1086 + return -1; 1087 + } 1088 + NEXT_ARG(); 1089 + } else if (is_prefix(*argv, "name")) { 1090 + NEXT_ARG(); 1091 + attr.name = GET_ARG(); 1092 + } else if (is_prefix(*argv, "key")) { 1093 + if (parse_u32_arg(&argc, &argv, &attr.key_size, 1094 + "key size")) 1095 + return -1; 1096 + } else if (is_prefix(*argv, "value")) { 1097 + if (parse_u32_arg(&argc, &argv, &attr.value_size, 1098 + "value size")) 1099 + return -1; 1100 + } else if (is_prefix(*argv, "entries")) { 1101 + if (parse_u32_arg(&argc, &argv, &attr.max_entries, 1102 + "max entries")) 1103 + return -1; 1104 + } else if (is_prefix(*argv, "flags")) { 1105 + if (parse_u32_arg(&argc, &argv, &attr.map_flags, 1106 + "flags")) 1107 + return -1; 1108 + } else if (is_prefix(*argv, "dev")) { 1109 + NEXT_ARG(); 1110 + 1111 + if (attr.map_ifindex) { 1112 + p_err("offload device already specified"); 1113 + return -1; 1114 + } 1115 + 1116 + attr.map_ifindex = if_nametoindex(*argv); 1117 + if (!attr.map_ifindex) { 1118 + p_err("unrecognized netdevice '%s': %s", 1119 + *argv, strerror(errno)); 1120 + return -1; 1121 + } 1122 + NEXT_ARG(); 1123 + } 1124 + } 1125 + 1126 + if (!attr.name) { 1127 + p_err("map name not specified"); 1128 + return -1; 1129 + } 1130 + 1131 + fd = bpf_create_map_xattr(&attr); 1132 + if (fd < 0) { 1133 + p_err("map create failed: %s", strerror(errno)); 1134 + return -1; 1135 + } 1136 + 1137 + err = do_pin_fd(fd, pinfile); 1138 + close(fd); 1139 + if (err) 1140 + return err; 1141 + 1142 + if (json_output) 1143 + jsonw_null(json_wtr); 1144 + return 0; 1145 + } 1146 + 1073 1147 static int do_help(int argc, char **argv) 1074 1148 { 1075 1149 if (json_output) { ··· 1165 1067 1166 1068 fprintf(stderr, 1167 1069 "Usage: %s %s { show | list } [MAP]\n" 1070 + " %s %s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n" 1071 + " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" 1072 + " [dev NAME]\n" 1168 1073 " %s %s dump MAP\n" 1169 1074 " %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n" 1170 1075 " %s %s lookup MAP key DATA\n" ··· 1182 1081 " " HELP_SPEC_PROGRAM "\n" 1183 1082 " VALUE := { DATA | MAP | PROG }\n" 1184 1083 " UPDATE_FLAGS := { any | exist | noexist }\n" 1084 + " TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n" 1085 + " percpu_array | stack_trace | cgroup_array | lru_hash |\n" 1086 + " lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n" 1087 + " devmap | sockmap | cpumap | xskmap | sockhash |\n" 1088 + " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage }\n" 1185 1089 " " HELP_SPEC_OPTIONS "\n" 1186 1090 "", 1187 1091 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1188 1092 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1189 - bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 1093 + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1094 + bin_name, argv[-2]); 1190 1095 1191 1096 return 0; 1192 1097 } ··· 1208 1101 { "delete", do_delete }, 1209 1102 { "pin", do_pin }, 1210 1103 { "event_pipe", do_event_pipe }, 1104 + { "create", do_create }, 1211 1105 { 0 } 1212 1106 }; 1213 1107