Commit 3ff5a4dc authored by Jakub Kicinski's avatar Jakub Kicinski Committed by Daniel Borkmann

tools: bpftool: allow reuse of maps with bpftool prog load

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: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarQuentin Monnet <quentin.monnet@netronome.com>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent 26736eb9
...@@ -24,9 +24,10 @@ MAP COMMANDS ...@@ -24,9 +24,10 @@ MAP COMMANDS
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
| **bpftool** **prog pin** *PROG* *FILE* | **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** | **bpftool** **prog help**
| |
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
| *TYPE* := { | *TYPE* := {
| **socket** | **kprobe** | **kretprobe** | **classifier** | **action** | | **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
...@@ -73,10 +74,17 @@ DESCRIPTION ...@@ -73,10 +74,17 @@ DESCRIPTION
Note: *FILE* must be located in *bpffs* mount. 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*. Load bpf program from binary *OBJ* and pin as *FILE*.
**type** is optional, if not specified program type will be **type** is optional, if not specified program type will be
inferred from section names. 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 If **dev** *NAME* is specified program will be loaded onto
given networking device (offload). given networking device (offload).
...@@ -172,6 +180,14 @@ EXAMPLES ...@@ -172,6 +180,14 @@ EXAMPLES
mov %rbx,0x0(%rbp) mov %rbx,0x0(%rbp)
48 89 5d 00 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 SEE ALSO
======== ========
......
...@@ -99,6 +99,29 @@ _bpftool_get_prog_tags() ...@@ -99,6 +99,29 @@ _bpftool_get_prog_tags()
command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) 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() _sysfs_get_netdevs()
{ {
COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \ COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
...@@ -220,12 +243,14 @@ _bpftool() ...@@ -220,12 +243,14 @@ _bpftool()
# Completion depends on object and command in use # Completion depends on object and command in use
case $object in case $object in
prog) prog)
case $prev in if [[ $command != "load" ]]; then
id) case $prev in
_bpftool_get_prog_ids id)
return 0 _bpftool_get_prog_ids
;; return 0
esac ;;
esac
fi
local PROG_TYPE='id pinned tag' local PROG_TYPE='id pinned tag'
case $command in case $command in
...@@ -268,22 +293,52 @@ _bpftool() ...@@ -268,22 +293,52 @@ _bpftool()
return 0 return 0
;; ;;
load) load)
local obj
if [[ ${#words[@]} -lt 6 ]]; then if [[ ${#words[@]} -lt 6 ]]; then
_filedir _filedir
return 0 return 0
fi 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 case $prev in
type) 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" -- \ 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" ) ) "$cur" ) )
return 0 return 0
;; ;;
id)
_bpftool_get_map_ids
return 0
;;
pinned)
_filedir
return 0
;;
dev) dev)
_sysfs_get_netdevs _sysfs_get_netdevs
return 0 return 0
;; ;;
*) *)
COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
_bpftool_once_attr 'type' _bpftool_once_attr 'type'
_bpftool_once_attr 'dev' _bpftool_once_attr 'dev'
return 0 return 0
......
...@@ -75,6 +75,8 @@ ...@@ -75,6 +75,8 @@
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
#define HELP_SPEC_OPTIONS \ #define HELP_SPEC_OPTIONS \
"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }" "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
#define HELP_SPEC_MAP \
"MAP := { id MAP_ID | pinned FILE }"
enum bpf_obj_type { enum bpf_obj_type {
BPF_OBJ_UNKNOWN, BPF_OBJ_UNKNOWN,
...@@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg); ...@@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg);
int do_perf(int argc, char **arg); int do_perf(int argc, char **arg);
int prog_parse_fd(int *argc, char ***argv); 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); 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, void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
......
...@@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info) ...@@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info)
return malloc(info->value_size); return malloc(info->value_size);
} }
static int map_parse_fd(int *argc, char ***argv) int map_parse_fd(int *argc, char ***argv)
{ {
int fd; int fd;
...@@ -824,7 +824,7 @@ static int do_help(int argc, char **argv) ...@@ -824,7 +824,7 @@ static int do_help(int argc, char **argv)
" %s %s event_pipe MAP [cpu N index M]\n" " %s %s event_pipe MAP [cpu N index M]\n"
" %s %s help\n" " %s %s help\n"
"\n" "\n"
" MAP := { id MAP_ID | pinned FILE }\n" " " HELP_SPEC_MAP "\n"
" DATA := { [hex] BYTES }\n" " DATA := { [hex] BYTES }\n"
" " HELP_SPEC_PROGRAM "\n" " " HELP_SPEC_PROGRAM "\n"
" VALUE := { DATA | MAP | PROG }\n" " VALUE := { DATA | MAP | PROG }\n"
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#define _GNU_SOURCE
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
...@@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv) ...@@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv)
return err; 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) static int do_load(int argc, char **argv)
{ {
enum bpf_attach_type expected_attach_type; enum bpf_attach_type expected_attach_type;
struct bpf_object_open_attr attr = { struct bpf_object_open_attr attr = {
.prog_type = BPF_PROG_TYPE_UNSPEC, .prog_type = BPF_PROG_TYPE_UNSPEC,
}; };
struct map_replace *map_replace = NULL;
unsigned int old_map_fds = 0;
struct bpf_program *prog; struct bpf_program *prog;
struct bpf_object *obj; struct bpf_object *obj;
struct bpf_map *map; struct bpf_map *map;
const char *pinfile; const char *pinfile;
unsigned int i, j;
__u32 ifindex = 0; __u32 ifindex = 0;
int err; int idx, err;
if (!REQ_ARGS(2)) if (!REQ_ARGS(2))
return -1; return -1;
...@@ -708,16 +725,16 @@ static int do_load(int argc, char **argv) ...@@ -708,16 +725,16 @@ static int do_load(int argc, char **argv)
if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
p_err("program type already specified"); p_err("program type already specified");
return -1; goto err_free_reuse_maps;
} }
if (!REQ_ARGS(1)) if (!REQ_ARGS(1))
return -1; goto err_free_reuse_maps;
/* Put a '/' at the end of type to appease libbpf */ /* Put a '/' at the end of type to appease libbpf */
type = malloc(strlen(*argv) + 2); type = malloc(strlen(*argv) + 2);
if (!type) { if (!type) {
p_err("mem alloc failed"); p_err("mem alloc failed");
return -1; goto err_free_reuse_maps;
} }
*type = 0; *type = 0;
strcat(type, *argv); strcat(type, *argv);
...@@ -728,37 +745,81 @@ static int do_load(int argc, char **argv) ...@@ -728,37 +745,81 @@ static int do_load(int argc, char **argv)
free(type); free(type);
if (err < 0) { if (err < 0) {
p_err("unknown program type '%s'", *argv); p_err("unknown program type '%s'", *argv);
return err; goto err_free_reuse_maps;
} }
NEXT_ARG(); 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")) { } else if (is_prefix(*argv, "dev")) {
NEXT_ARG(); NEXT_ARG();
if (ifindex) { if (ifindex) {
p_err("offload device already specified"); p_err("offload device already specified");
return -1; goto err_free_reuse_maps;
} }
if (!REQ_ARGS(1)) if (!REQ_ARGS(1))
return -1; goto err_free_reuse_maps;
ifindex = if_nametoindex(*argv); ifindex = if_nametoindex(*argv);
if (!ifindex) { if (!ifindex) {
p_err("unrecognized netdevice '%s': %s", p_err("unrecognized netdevice '%s': %s",
*argv, strerror(errno)); *argv, strerror(errno));
return -1; goto err_free_reuse_maps;
} }
NEXT_ARG(); NEXT_ARG();
} else { } 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); *argv);
return -1; goto err_free_reuse_maps;
} }
} }
obj = bpf_object__open_xattr(&attr); obj = bpf_object__open_xattr(&attr);
if (IS_ERR_OR_NULL(obj)) { if (IS_ERR_OR_NULL(obj)) {
p_err("failed to open object file"); p_err("failed to open object file");
return -1; goto err_free_reuse_maps;
} }
prog = bpf_program__next(NULL, obj); prog = bpf_program__next(NULL, obj);
...@@ -782,10 +843,62 @@ static int do_load(int argc, char **argv) ...@@ -782,10 +843,62 @@ static int do_load(int argc, char **argv)
bpf_program__set_type(prog, attr.prog_type); bpf_program__set_type(prog, attr.prog_type);
bpf_program__set_expected_attach_type(prog, expected_attach_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)) if (!bpf_map__is_offload_neutral(map))
bpf_map__set_ifindex(map, ifindex); 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); err = bpf_object__load(obj);
if (err) { if (err) {
p_err("failed to load object file"); p_err("failed to load object file");
...@@ -799,11 +912,18 @@ static int do_load(int argc, char **argv) ...@@ -799,11 +912,18 @@ static int do_load(int argc, char **argv)
jsonw_null(json_wtr); jsonw_null(json_wtr);
bpf_object__close(obj); bpf_object__close(obj);
for (i = 0; i < old_map_fds; i++)
close(map_replace[i].fd);
free(map_replace);
return 0; return 0;
err_close_obj: err_close_obj:
bpf_object__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; return -1;
} }
...@@ -819,9 +939,11 @@ static int do_help(int argc, char **argv) ...@@ -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 xlated PROG [{ file FILE | opcodes | visual }]\n"
" %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s dump jited PROG [{ file FILE | opcodes }]\n"
" %s %s pin PROG FILE\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" " %s %s help\n"
"\n" "\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n" " " HELP_SPEC_PROGRAM "\n"
" TYPE := { socket | kprobe | kretprobe | classifier | action |\n" " TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
" tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment