Skip to content

Commit 0b592b5

Browse files
Jakub KicinskiAlexei Starovoitov
authored andcommitted
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>
1 parent 2f1d774 commit 0b592b5

File tree

6 files changed

+183
-6
lines changed

6 files changed

+183
-6
lines changed

tools/bpf/bpftool/Documentation/bpftool-map.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ SYNOPSIS
1515
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
1616

1717
*COMMANDS* :=
18-
{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**
19-
| **pin** | **help** }
18+
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext**
19+
| **delete** | **pin** | **help** }
2020
2121
MAP COMMANDS
2222
=============
2323

2424
| **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*]
2527
| **bpftool** **map dump** *MAP*
2628
| **bpftool** **map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*]
2729
| **bpftool** **map lookup** *MAP* **key** *DATA*
@@ -36,6 +38,11 @@ MAP COMMANDS
3638
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
3739
| *VALUE* := { *DATA* | *MAP* | *PROG* }
3840
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
41+
| *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash**
42+
| | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
43+
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
44+
| | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
45+
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** }
3946
4047
DESCRIPTION
4148
===========
@@ -47,6 +54,10 @@ DESCRIPTION
4754
Output will start with map ID followed by map type and
4855
zero or more named attributes (depending on kernel version).
4956

57+
**bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
58+
Create a new map with given parameters and pin it to *bpffs*
59+
as *FILE*.
60+
5061
**bpftool map dump** *MAP*
5162
Dump all entries in a given *MAP*.
5263

tools/bpf/bpftool/Documentation/bpftool.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ SYNOPSIS
2222
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
2323
2424
*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** }
2727
2828
*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin**
2929
| **load** | **attach** | **detach** | **help** }

tools/bpf/bpftool/bash-completion/bpftool

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,42 @@ _bpftool()
387387
;;
388388
esac
389389
;;
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+
;;
390426
lookup|getnext|delete)
391427
case $prev in
392428
$command)
@@ -500,7 +536,7 @@ _bpftool()
500536
*)
501537
[[ $prev == $object ]] && \
502538
COMPREPLY=( $( compgen -W 'delete dump getnext help \
503-
lookup pin event_pipe show list update' -- \
539+
lookup pin event_pipe show list update create' -- \
504540
"$cur" ) )
505541
;;
506542
esac

tools/bpf/bpftool/common.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,24 @@ void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
618618
jsonw_string_field(json_wtr, "ifname", name);
619619
jsonw_end_object(json_wtr);
620620
}
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+
}

tools/bpf/bpftool/main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ int do_cgroup(int argc, char **arg);
139139
int do_perf(int argc, char **arg);
140140
int do_net(int argc, char **arg);
141141

142+
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
142143
int prog_parse_fd(int *argc, char ***argv);
143144
int map_parse_fd(int *argc, char ***argv);
144145
int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);

tools/bpf/bpftool/map.c

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <fcntl.h>
3737
#include <linux/err.h>
3838
#include <linux/kernel.h>
39+
#include <net/if.h>
3940
#include <stdbool.h>
4041
#include <stdio.h>
4142
#include <stdlib.h>
@@ -94,6 +95,17 @@ static bool map_is_map_of_progs(__u32 type)
9495
return type == BPF_MAP_TYPE_PROG_ARRAY;
9596
}
9697

98+
static int map_type_from_str(const char *type)
99+
{
100+
unsigned int i;
101+
102+
for (i = 0; i < ARRAY_SIZE(map_type_name); i++)
103+
/* Don't allow prefixing in case of possible future shadowing */
104+
if (map_type_name[i] && !strcmp(map_type_name[i], type))
105+
return i;
106+
return -1;
107+
}
108+
97109
static void *alloc_value(struct bpf_map_info *info)
98110
{
99111
if (map_is_per_cpu(info->type))
@@ -1058,6 +1070,92 @@ static int do_pin(int argc, char **argv)
10581070
return err;
10591071
}
10601072

1073+
static int do_create(int argc, char **argv)
1074+
{
1075+
struct bpf_create_map_attr attr = { NULL, };
1076+
const char *pinfile;
1077+
int err, fd;
1078+
1079+
if (!REQ_ARGS(7))
1080+
return -1;
1081+
pinfile = GET_ARG();
1082+
1083+
while (argc) {
1084+
if (!REQ_ARGS(2))
1085+
return -1;
1086+
1087+
if (is_prefix(*argv, "type")) {
1088+
NEXT_ARG();
1089+
1090+
if (attr.map_type) {
1091+
p_err("map type already specified");
1092+
return -1;
1093+
}
1094+
1095+
attr.map_type = map_type_from_str(*argv);
1096+
if ((int)attr.map_type < 0) {
1097+
p_err("unrecognized map type: %s", *argv);
1098+
return -1;
1099+
}
1100+
NEXT_ARG();
1101+
} else if (is_prefix(*argv, "name")) {
1102+
NEXT_ARG();
1103+
attr.name = GET_ARG();
1104+
} else if (is_prefix(*argv, "key")) {
1105+
if (parse_u32_arg(&argc, &argv, &attr.key_size,
1106+
"key size"))
1107+
return -1;
1108+
} else if (is_prefix(*argv, "value")) {
1109+
if (parse_u32_arg(&argc, &argv, &attr.value_size,
1110+
"value size"))
1111+
return -1;
1112+
} else if (is_prefix(*argv, "entries")) {
1113+
if (parse_u32_arg(&argc, &argv, &attr.max_entries,
1114+
"max entries"))
1115+
return -1;
1116+
} else if (is_prefix(*argv, "flags")) {
1117+
if (parse_u32_arg(&argc, &argv, &attr.map_flags,
1118+
"flags"))
1119+
return -1;
1120+
} else if (is_prefix(*argv, "dev")) {
1121+
NEXT_ARG();
1122+
1123+
if (attr.map_ifindex) {
1124+
p_err("offload device already specified");
1125+
return -1;
1126+
}
1127+
1128+
attr.map_ifindex = if_nametoindex(*argv);
1129+
if (!attr.map_ifindex) {
1130+
p_err("unrecognized netdevice '%s': %s",
1131+
*argv, strerror(errno));
1132+
return -1;
1133+
}
1134+
NEXT_ARG();
1135+
}
1136+
}
1137+
1138+
if (!attr.name) {
1139+
p_err("map name not specified");
1140+
return -1;
1141+
}
1142+
1143+
fd = bpf_create_map_xattr(&attr);
1144+
if (fd < 0) {
1145+
p_err("map create failed: %s", strerror(errno));
1146+
return -1;
1147+
}
1148+
1149+
err = do_pin_fd(fd, pinfile);
1150+
close(fd);
1151+
if (err)
1152+
return err;
1153+
1154+
if (json_output)
1155+
jsonw_null(json_wtr);
1156+
return 0;
1157+
}
1158+
10611159
static int do_help(int argc, char **argv)
10621160
{
10631161
if (json_output) {
@@ -1067,6 +1165,9 @@ static int do_help(int argc, char **argv)
10671165

10681166
fprintf(stderr,
10691167
"Usage: %s %s { show | list } [MAP]\n"
1168+
" %s %s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n"
1169+
" entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
1170+
" [dev NAME]\n"
10701171
" %s %s dump MAP\n"
10711172
" %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n"
10721173
" %s %s lookup MAP key DATA\n"
@@ -1081,11 +1182,17 @@ static int do_help(int argc, char **argv)
10811182
" " HELP_SPEC_PROGRAM "\n"
10821183
" VALUE := { DATA | MAP | PROG }\n"
10831184
" UPDATE_FLAGS := { any | exist | noexist }\n"
1185+
" TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n"
1186+
" percpu_array | stack_trace | cgroup_array | lru_hash |\n"
1187+
" lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n"
1188+
" devmap | sockmap | cpumap | xskmap | sockhash |\n"
1189+
" cgroup_storage | reuseport_sockarray | percpu_cgroup_storage }\n"
10841190
" " HELP_SPEC_OPTIONS "\n"
10851191
"",
10861192
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
10871193
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1088-
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
1194+
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1195+
bin_name, argv[-2]);
10891196

10901197
return 0;
10911198
}
@@ -1101,6 +1208,7 @@ static const struct cmd cmds[] = {
11011208
{ "delete", do_delete },
11021209
{ "pin", do_pin },
11031210
{ "event_pipe", do_event_pipe },
1211+
{ "create", do_create },
11041212
{ 0 }
11051213
};
11061214

0 commit comments

Comments
 (0)