tools: bpftool: allow reuse of maps with bpftool prog load
authorJakub Kicinski <jakub.kicinski@netronome.com>
Tue, 10 Jul 2018 21:43:07 +0000 (14:43 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Wed, 11 Jul 2018 20:13:34 +0000 (22:13 +0200)
Add map parameter to prog load which will allow reuse of existing
maps instead of creating new ones.

We need feature detection and compat code for reallocarray, since
it's not available in many libc versions.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/main.h
tools/bpf/bpftool/map.c
tools/bpf/bpftool/prog.c

index e53e1ad2caf064cbf5b3e001781f013b6a6a7a4a..64156a16d5300b64e0d9c2f5297605b88e6265b2 100644 (file)
@@ -24,9 +24,10 @@ MAP COMMANDS
 |      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
 |      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |      **bpftool** **prog pin** *PROG* *FILE*
-|      **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
+|      **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
 |      **bpftool** **prog help**
 |
+|      *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
 |      *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
 |      *TYPE* := {
 |              **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
@@ -73,10 +74,17 @@ DESCRIPTION
 
                  Note: *FILE* must be located in *bpffs* mount.
 
-       **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
+       **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
                  Load bpf program from binary *OBJ* and pin as *FILE*.
                  **type** is optional, if not specified program type will be
                  inferred from section names.
+                 By default bpftool will create new maps as declared in the ELF
+                 object being loaded.  **map** parameter allows for the reuse
+                 of existing maps.  It can be specified multiple times, each
+                 time for a different map.  *IDX* refers to index of the map
+                 to be replaced in the ELF file counting from 0, while *NAME*
+                 allows to replace a map by name.  *MAP* specifies the map to
+                 use, referring to it by **id** or through a **pinned** file.
                  If **dev** *NAME* is specified program will be loaded onto
                  given networking device (offload).
 
@@ -172,6 +180,14 @@ EXAMPLES
     mov    %rbx,0x0(%rbp)
     48 89 5d 00
 
+|
+| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
+| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
+|   9: xdp  name xdp_prog1  tag 539ec6ce11b52f98  gpl
+|      loaded_at 2018-06-25T16:17:31-0700  uid 0
+|      xlated 488B  jited 336B  memlock 4096B  map_ids 7
+| **# rm /sys/fs/bpf/xdp1**
+|
 
 SEE ALSO
 ========
index caf8711993be4d1bd4cbd20ec77245eab81a05d0..598066c401912a5d69f77ccc68c9fa78da8566e9 100644 (file)
@@ -99,6 +99,29 @@ _bpftool_get_prog_tags()
         command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
 }
 
+_bpftool_get_obj_map_names()
+{
+    local obj
+
+    obj=$1
+
+    maps=$(objdump -j maps -t $obj 2>/dev/null | \
+        command awk '/g     . maps/ {print $NF}')
+
+    COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )
+}
+
+_bpftool_get_obj_map_idxs()
+{
+    local obj
+
+    obj=$1
+
+    nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g     . maps')
+
+    COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )
+}
+
 _sysfs_get_netdevs()
 {
     COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
@@ -220,12 +243,14 @@ _bpftool()
     # Completion depends on object and command in use
     case $object in
         prog)
-            case $prev in
-                id)
-                    _bpftool_get_prog_ids
-                    return 0
-                    ;;
-            esac
+            if [[ $command != "load" ]]; then
+                case $prev in
+                    id)
+                        _bpftool_get_prog_ids
+                        return 0
+                        ;;
+                esac
+            fi
 
             local PROG_TYPE='id pinned tag'
             case $command in
@@ -268,22 +293,52 @@ _bpftool()
                     return 0
                     ;;
                 load)
+                    local obj
+
                     if [[ ${#words[@]} -lt 6 ]]; then
                         _filedir
                         return 0
                     fi
 
+                    obj=${words[3]}
+
+                    if [[ ${words[-4]} == "map" ]]; then
+                        COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
+                        return 0
+                    fi
+                    if [[ ${words[-3]} == "map" ]]; then
+                        if [[ ${words[-2]} == "idx" ]]; then
+                            _bpftool_get_obj_map_idxs $obj
+                        elif [[ ${words[-2]} == "name" ]]; then
+                            _bpftool_get_obj_map_names $obj
+                        fi
+                        return 0
+                    fi
+                    if [[ ${words[-2]} == "map" ]]; then
+                        COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
+                        return 0
+                    fi
+
                     case $prev in
                         type)
                             COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \
                                                    "$cur" ) )
                             return 0
                             ;;
+                        id)
+                            _bpftool_get_map_ids
+                            return 0
+                            ;;
+                        pinned)
+                            _filedir
+                            return 0
+                            ;;
                         dev)
                             _sysfs_get_netdevs
                             return 0
                             ;;
                         *)
+                            COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
                             _bpftool_once_attr 'type'
                             _bpftool_once_attr 'dev'
                             return 0
index 1e02e403169332a818cce3c8d9cfb93bceaa9d9c..41004bb2644a2a47f90f592ba9a0b6f6704b1692 100644 (file)
@@ -75,6 +75,8 @@
        "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
 #define HELP_SPEC_OPTIONS                                              \
        "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
+#define HELP_SPEC_MAP                                                  \
+       "MAP := { id MAP_ID | pinned FILE }"
 
 enum bpf_obj_type {
        BPF_OBJ_UNKNOWN,
@@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);
 
 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);
 
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
index 5989e1575ae43af4e6a635a5a77aa0459253fa4f..e2baec1122fbc3b8e9ee66aed7ed156e90cdcad5 100644 (file)
@@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info)
                return malloc(info->value_size);
 }
 
-static int map_parse_fd(int *argc, char ***argv)
+int map_parse_fd(int *argc, char ***argv)
 {
        int fd;
 
@@ -824,7 +824,7 @@ static int do_help(int argc, char **argv)
                "       %s %s event_pipe MAP [cpu N index M]\n"
                "       %s %s help\n"
                "\n"
-               "       MAP := { id MAP_ID | pinned FILE }\n"
+               "       " HELP_SPEC_MAP "\n"
                "       DATA := { [hex] BYTES }\n"
                "       " HELP_SPEC_PROGRAM "\n"
                "       VALUE := { DATA | MAP | PROG }\n"
index 2bdd5ecd1aad71ac9cd487d381cc2833089f9f14..dce960d22106fb173e2e5e0195f0472209d2bb71 100644 (file)
@@ -31,6 +31,7 @@
  * SOFTWARE.
  */
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv)
        return err;
 }
 
+struct map_replace {
+       int idx;
+       int fd;
+       char *name;
+};
+
+int map_replace_compar(const void *p1, const void *p2)
+{
+       const struct map_replace *a = p1, *b = p2;
+
+       return a->idx - b->idx;
+}
+
 static int do_load(int argc, char **argv)
 {
        enum bpf_attach_type expected_attach_type;
        struct bpf_object_open_attr attr = {
                .prog_type      = BPF_PROG_TYPE_UNSPEC,
        };
+       struct map_replace *map_replace = NULL;
+       unsigned int old_map_fds = 0;
        struct bpf_program *prog;
        struct bpf_object *obj;
        struct bpf_map *map;
        const char *pinfile;
+       unsigned int i, j;
        __u32 ifindex = 0;
-       int err;
+       int idx, err;
 
        if (!REQ_ARGS(2))
                return -1;
@@ -708,16 +725,16 @@ static int do_load(int argc, char **argv)
 
                        if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
                                p_err("program type already specified");
-                               return -1;
+                               goto err_free_reuse_maps;
                        }
                        if (!REQ_ARGS(1))
-                               return -1;
+                               goto err_free_reuse_maps;
 
                        /* Put a '/' at the end of type to appease libbpf */
                        type = malloc(strlen(*argv) + 2);
                        if (!type) {
                                p_err("mem alloc failed");
-                               return -1;
+                               goto err_free_reuse_maps;
                        }
                        *type = 0;
                        strcat(type, *argv);
@@ -728,37 +745,81 @@ static int do_load(int argc, char **argv)
                        free(type);
                        if (err < 0) {
                                p_err("unknown program type '%s'", *argv);
-                               return err;
+                               goto err_free_reuse_maps;
                        }
                        NEXT_ARG();
+               } else if (is_prefix(*argv, "map")) {
+                       char *endptr, *name;
+                       int fd;
+
+                       NEXT_ARG();
+
+                       if (!REQ_ARGS(4))
+                               goto err_free_reuse_maps;
+
+                       if (is_prefix(*argv, "idx")) {
+                               NEXT_ARG();
+
+                               idx = strtoul(*argv, &endptr, 0);
+                               if (*endptr) {
+                                       p_err("can't parse %s as IDX", *argv);
+                                       goto err_free_reuse_maps;
+                               }
+                               name = NULL;
+                       } else if (is_prefix(*argv, "name")) {
+                               NEXT_ARG();
+
+                               name = *argv;
+                               idx = -1;
+                       } else {
+                               p_err("expected 'idx' or 'name', got: '%s'?",
+                                     *argv);
+                               goto err_free_reuse_maps;
+                       }
+                       NEXT_ARG();
+
+                       fd = map_parse_fd(&argc, &argv);
+                       if (fd < 0)
+                               goto err_free_reuse_maps;
+
+                       map_replace = reallocarray(map_replace, old_map_fds + 1,
+                                                  sizeof(*map_replace));
+                       if (!map_replace) {
+                               p_err("mem alloc failed");
+                               goto err_free_reuse_maps;
+                       }
+                       map_replace[old_map_fds].idx = idx;
+                       map_replace[old_map_fds].name = name;
+                       map_replace[old_map_fds].fd = fd;
+                       old_map_fds++;
                } else if (is_prefix(*argv, "dev")) {
                        NEXT_ARG();
 
                        if (ifindex) {
                                p_err("offload device already specified");
-                               return -1;
+                               goto err_free_reuse_maps;
                        }
                        if (!REQ_ARGS(1))
-                               return -1;
+                               goto err_free_reuse_maps;
 
                        ifindex = if_nametoindex(*argv);
                        if (!ifindex) {
                                p_err("unrecognized netdevice '%s': %s",
                                      *argv, strerror(errno));
-                               return -1;
+                               goto err_free_reuse_maps;
                        }
                        NEXT_ARG();
                } else {
-                       p_err("expected no more arguments, 'type' or 'dev', got: '%s'?",
+                       p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
                              *argv);
-                       return -1;
+                       goto err_free_reuse_maps;
                }
        }
 
        obj = bpf_object__open_xattr(&attr);
        if (IS_ERR_OR_NULL(obj)) {
                p_err("failed to open object file");
-               return -1;
+               goto err_free_reuse_maps;
        }
 
        prog = bpf_program__next(NULL, obj);
@@ -782,10 +843,62 @@ static int do_load(int argc, char **argv)
        bpf_program__set_type(prog, attr.prog_type);
        bpf_program__set_expected_attach_type(prog, expected_attach_type);
 
-       bpf_map__for_each(map, obj)
+       qsort(map_replace, old_map_fds, sizeof(*map_replace),
+             map_replace_compar);
+
+       /* After the sort maps by name will be first on the list, because they
+        * have idx == -1.  Resolve them.
+        */
+       j = 0;
+       while (j < old_map_fds && map_replace[j].name) {
+               i = 0;
+               bpf_map__for_each(map, obj) {
+                       if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
+                               map_replace[j].idx = i;
+                               break;
+                       }
+                       i++;
+               }
+               if (map_replace[j].idx == -1) {
+                       p_err("unable to find map '%s'", map_replace[j].name);
+                       goto err_close_obj;
+               }
+               j++;
+       }
+       /* Resort if any names were resolved */
+       if (j)
+               qsort(map_replace, old_map_fds, sizeof(*map_replace),
+                     map_replace_compar);
+
+       /* Set ifindex and name reuse */
+       j = 0;
+       idx = 0;
+       bpf_map__for_each(map, obj) {
                if (!bpf_map__is_offload_neutral(map))
                        bpf_map__set_ifindex(map, ifindex);
 
+               if (j < old_map_fds && idx == map_replace[j].idx) {
+                       err = bpf_map__reuse_fd(map, map_replace[j++].fd);
+                       if (err) {
+                               p_err("unable to set up map reuse: %d", err);
+                               goto err_close_obj;
+                       }
+
+                       /* Next reuse wants to apply to the same map */
+                       if (j < old_map_fds && map_replace[j].idx == idx) {
+                               p_err("replacement for map idx %d specified more than once",
+                                     idx);
+                               goto err_close_obj;
+                       }
+               }
+
+               idx++;
+       }
+       if (j < old_map_fds) {
+               p_err("map idx '%d' not used", map_replace[j].idx);
+               goto err_close_obj;
+       }
+
        err = bpf_object__load(obj);
        if (err) {
                p_err("failed to load object file");
@@ -799,11 +912,18 @@ static int do_load(int argc, char **argv)
                jsonw_null(json_wtr);
 
        bpf_object__close(obj);
+       for (i = 0; i < old_map_fds; i++)
+               close(map_replace[i].fd);
+       free(map_replace);
 
        return 0;
 
 err_close_obj:
        bpf_object__close(obj);
+err_free_reuse_maps:
+       for (i = 0; i < old_map_fds; i++)
+               close(map_replace[i].fd);
+       free(map_replace);
        return -1;
 }
 
@@ -819,9 +939,11 @@ static int do_help(int argc, char **argv)
                "       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
                "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
                "       %s %s pin   PROG FILE\n"
-               "       %s %s load  OBJ  FILE [type TYPE] [dev NAME]\n"
+               "       %s %s load  OBJ  FILE [type TYPE] [dev NAME] \\\n"
+               "                         [map { idx IDX | name NAME } MAP]\n"
                "       %s %s help\n"
                "\n"
+               "       " HELP_SPEC_MAP "\n"
                "       " HELP_SPEC_PROGRAM "\n"
                "       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
                "                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"