Skip to content

Commit

Permalink
tools: bpftool: add map create command
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Kicinski authored and Alexei Starovoitov committed Oct 15, 2018
1 parent 2f1d774 commit 0b592b5
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 6 deletions.
15 changes: 13 additions & 2 deletions tools/bpf/bpftool/Documentation/bpftool-map.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ SYNOPSIS
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }

*COMMANDS* :=
{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**
| **pin** | **help** }
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext**
| **delete** | **pin** | **help** }
MAP COMMANDS
=============

| **bpftool** **map { show | list }** [*MAP*]
| **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
| **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
| **bpftool** **map dump** *MAP*
| **bpftool** **map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*]
| **bpftool** **map lookup** *MAP* **key** *DATA*
Expand All @@ -36,6 +38,11 @@ MAP COMMANDS
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
| *VALUE* := { *DATA* | *MAP* | *PROG* }
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
| *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash**
| | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
| | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** }
DESCRIPTION
===========
Expand All @@ -47,6 +54,10 @@ DESCRIPTION
Output will start with map ID followed by map type and
zero or more named attributes (depending on kernel version).

**bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
Create a new map with given parameters and pin it to *bpffs*
as *FILE*.

**bpftool map dump** *MAP*
Dump all entries in a given *MAP*.

Expand Down
4 changes: 2 additions & 2 deletions tools/bpf/bpftool/Documentation/bpftool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ SYNOPSIS
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
*MAP-COMMANDS* :=
{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**
| **pin** | **event_pipe** | **help** }
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext**
| **delete** | **pin** | **event_pipe** | **help** }
*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin**
| **load** | **attach** | **detach** | **help** }
Expand Down
38 changes: 37 additions & 1 deletion tools/bpf/bpftool/bash-completion/bpftool
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,42 @@ _bpftool()
;;
esac
;;
create)
case $prev in
$command)
_filedir
return 0
;;
type)
COMPREPLY=( $( compgen -W 'hash array prog_array \
perf_event_array percpu_hash percpu_array \
stack_trace cgroup_array lru_hash \
lru_percpu_hash lpm_trie array_of_maps \
hash_of_maps devmap sockmap cpumap xskmap \
sockhash cgroup_storage reuseport_sockarray \
percpu_cgroup_storage' -- \
"$cur" ) )
return 0
;;
key|value|flags|name|entries)
return 0
;;
dev)
_sysfs_get_netdevs
return 0
;;
*)
_bpftool_once_attr 'type'
_bpftool_once_attr 'key'
_bpftool_once_attr 'value'
_bpftool_once_attr 'entries'
_bpftool_once_attr 'name'
_bpftool_once_attr 'flags'
_bpftool_once_attr 'dev'
return 0
;;
esac
;;
lookup|getnext|delete)
case $prev in
$command)
Expand Down Expand Up @@ -500,7 +536,7 @@ _bpftool()
*)
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'delete dump getnext help \
lookup pin event_pipe show list update' -- \
lookup pin event_pipe show list update create' -- \
"$cur" ) )
;;
esac
Expand Down
21 changes: 21 additions & 0 deletions tools/bpf/bpftool/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,24 @@ void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
jsonw_string_field(json_wtr, "ifname", name);
jsonw_end_object(json_wtr);
}

int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what)
{
char *endptr;

NEXT_ARGP();

if (*val) {
p_err("%s already specified", what);
return -1;
}

*val = strtoul(**argv, &endptr, 0);
if (*endptr) {
p_err("can't parse %s as %s", **argv, what);
return -1;
}
NEXT_ARGP();

return 0;
}
1 change: 1 addition & 0 deletions tools/bpf/bpftool/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ int do_cgroup(int argc, char **arg);
int do_perf(int argc, char **arg);
int do_net(int argc, char **arg);

int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
int prog_parse_fd(int *argc, char ***argv);
int map_parse_fd(int *argc, char ***argv);
int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
Expand Down
110 changes: 109 additions & 1 deletion tools/bpf/bpftool/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -94,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))
Expand Down Expand Up @@ -1058,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) {
Expand All @@ -1067,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"
Expand All @@ -1081,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;
}
Expand All @@ -1101,6 +1208,7 @@ static const struct cmd cmds[] = {
{ "delete", do_delete },
{ "pin", do_pin },
{ "event_pipe", do_event_pipe },
{ "create", do_create },
{ 0 }
};

Expand Down

0 comments on commit 0b592b5

Please sign in to comment.