tools: bpftool: add map create command
authorJakub Kicinski <jakub.kicinski@netronome.com>
Mon, 15 Oct 2018 23:30:36 +0000 (16:30 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 15 Oct 2018 23:39:21 +0000 (16:39 -0700)
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>
tools/bpf/bpftool/Documentation/bpftool-map.rst
tools/bpf/bpftool/Documentation/bpftool.rst
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/common.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/map.c

index a6258bc8ec4f57d0cf1d5b35a95030fd66cbe881..3497f2d803284b1a80c89be3142ee4cdbead8355 100644 (file)
@@ -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*
@@ -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
 ===========
@@ -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*.
 
index 65488317fefa54d5c79d3554efa6aa15f261020c..04cd4f92ab89c9dd8009180c3e4601c7b1956d1e 100644 (file)
@@ -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** }
index ac85207cba8deb36d83d5d274be103e5898a3c28..c56545e87b0dddbd2340a0f4f3db9ecc1e94dd3b 100644 (file)
@@ -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)
@@ -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
index b3a0709ea7ede6f6c5ebe3a49083c561f5d47a1d..3318da8060bd16232a381f789971ea6530070dff 100644 (file)
@@ -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;
+}
index 91fd697303cb31fb9a86f4c729aa6a457dd7191c..28ee769bd11b7fd01e88068d00db43445b676dbf 100644 (file)
@@ -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);
index 9f5de48f8a996ed33cd1261a2b3991fb8e2c6b31..7bf38f0e152e07054abfa761c945a7a85a1b239a 100644 (file)
@@ -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>
@@ -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))
@@ -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) {
@@ -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"
@@ -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;
 }
@@ -1101,6 +1208,7 @@ static const struct cmd cmds[] = {
        { "delete",     do_delete },
        { "pin",        do_pin },
        { "event_pipe", do_event_pipe },
+       { "create",     do_create },
        { 0 }
 };