Commit 4edc01b8 authored by Daniel Borkmann's avatar Daniel Borkmann

Merge branch 'bpf-bpftool-queue-stack'

Stanislav Fomichev says:

====================
This patch series add support for queue/stack manipulations.

It goes like this:

   commands by permitting empty keys.

v2:
* removed unneeded jsonw_null from patch #6
* improved bash completions (and moved them into separate patch #7)
====================
Reviewed-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents e13279e2 55c70bff
...@@ -25,12 +25,17 @@ MAP COMMANDS ...@@ -25,12 +25,17 @@ MAP COMMANDS
| **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \ | **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
| **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*] | **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
| **bpftool** **map dump** *MAP* | **bpftool** **map dump** *MAP*
| **bpftool** **map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*] | **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
| **bpftool** **map lookup** *MAP* **key** *DATA* | **bpftool** **map lookup** *MAP* [**key** *DATA*]
| **bpftool** **map getnext** *MAP* [**key** *DATA*] | **bpftool** **map getnext** *MAP* [**key** *DATA*]
| **bpftool** **map delete** *MAP* **key** *DATA* | **bpftool** **map delete** *MAP* **key** *DATA*
| **bpftool** **map pin** *MAP* *FILE* | **bpftool** **map pin** *MAP* *FILE*
| **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*] | **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
| **bpftool** **map peek** *MAP*
| **bpftool** **map push** *MAP* **value** *VALUE*
| **bpftool** **map pop** *MAP*
| **bpftool** **map enqueue** *MAP* **value** *VALUE*
| **bpftool** **map dequeue** *MAP*
| **bpftool** **map help** | **bpftool** **map help**
| |
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
...@@ -62,7 +67,7 @@ DESCRIPTION ...@@ -62,7 +67,7 @@ DESCRIPTION
**bpftool map dump** *MAP* **bpftool map dump** *MAP*
Dump all entries in a given *MAP*. Dump all entries in a given *MAP*.
**bpftool map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*] **bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
Update map entry for a given *KEY*. Update map entry for a given *KEY*.
*UPDATE_FLAGS* can be one of: **any** update existing entry *UPDATE_FLAGS* can be one of: **any** update existing entry
...@@ -75,7 +80,7 @@ DESCRIPTION ...@@ -75,7 +80,7 @@ DESCRIPTION
the bytes are parsed as decimal values, unless a "0x" prefix the bytes are parsed as decimal values, unless a "0x" prefix
(for hexadecimal) or a "0" prefix (for octal) is provided. (for hexadecimal) or a "0" prefix (for octal) is provided.
**bpftool map lookup** *MAP* **key** *DATA* **bpftool map lookup** *MAP* [**key** *DATA*]
Lookup **key** in the map. Lookup **key** in the map.
**bpftool map getnext** *MAP* [**key** *DATA*] **bpftool map getnext** *MAP* [**key** *DATA*]
...@@ -107,6 +112,21 @@ DESCRIPTION ...@@ -107,6 +112,21 @@ DESCRIPTION
replace any existing ring. Any other application will stop replace any existing ring. Any other application will stop
receiving events if it installed its rings earlier. receiving events if it installed its rings earlier.
**bpftool map peek** *MAP*
Peek next **value** in the queue or stack.
**bpftool map push** *MAP* **value** *VALUE*
Push **value** onto the stack.
**bpftool map pop** *MAP*
Pop and print **value** from the stack.
**bpftool map enqueue** *MAP* **value** *VALUE*
Enqueue **value** into the queue.
**bpftool map dequeue** *MAP*
Dequeue and print **value** from the queue.
**bpftool map help** **bpftool map help**
Print short help message. Print short help message.
......
...@@ -50,14 +50,15 @@ _bpftool_get_map_ids() ...@@ -50,14 +50,15 @@ _bpftool_get_map_ids()
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
} }
_bpftool_get_perf_map_ids() # Takes map type and adds matching map ids to the list of suggestions.
_bpftool_get_map_ids_for_type()
{ {
local type="$1"
COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \
command grep -C2 perf_event_array | \ command grep -C2 "$type" | \
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
} }
_bpftool_get_prog_ids() _bpftool_get_prog_ids()
{ {
COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
...@@ -99,15 +100,25 @@ _sysfs_get_netdevs() ...@@ -99,15 +100,25 @@ _sysfs_get_netdevs()
"$cur" ) ) "$cur" ) )
} }
# For bpftool map update: retrieve type of the map to update. # Retrieve type of the map that we are operating on.
_bpftool_map_update_map_type() _bpftool_map_guess_map_type()
{ {
local keyword ref local keyword ref
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
if [[ ${words[$((idx-2))]} == "update" ]]; then case "${words[$((idx-2))]}" in
lookup|update)
keyword=${words[$((idx-1))]} keyword=${words[$((idx-1))]}
ref=${words[$((idx))]} ref=${words[$((idx))]}
fi ;;
push)
printf "stack"
return 0
;;
enqueue)
printf "queue"
return 0
;;
esac
done done
[[ -z $ref ]] && return 0 [[ -z $ref ]] && return 0
...@@ -119,6 +130,8 @@ _bpftool_map_update_map_type() ...@@ -119,6 +130,8 @@ _bpftool_map_update_map_type()
_bpftool_map_update_get_id() _bpftool_map_update_get_id()
{ {
local command="$1"
# Is it the map to update, or a map to insert into the map to update? # Is it the map to update, or a map to insert into the map to update?
# Search for "value" keyword. # Search for "value" keyword.
local idx value local idx value
...@@ -128,11 +141,24 @@ _bpftool_map_update_get_id() ...@@ -128,11 +141,24 @@ _bpftool_map_update_get_id()
break break
fi fi
done done
[[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0 if [[ $value -eq 0 ]]; then
case "$command" in
push)
_bpftool_get_map_ids_for_type stack
;;
enqueue)
_bpftool_get_map_ids_for_type queue
;;
*)
_bpftool_get_map_ids
;;
esac
return 0
fi
# Id to complete is for a value. It can be either prog id or map id. This # Id to complete is for a value. It can be either prog id or map id. This
# depends on the type of the map to update. # depends on the type of the map to update.
local type=$(_bpftool_map_update_map_type) local type=$(_bpftool_map_guess_map_type)
case $type in case $type in
array_of_maps|hash_of_maps) array_of_maps|hash_of_maps)
_bpftool_get_map_ids _bpftool_get_map_ids
...@@ -382,14 +408,28 @@ _bpftool() ...@@ -382,14 +408,28 @@ _bpftool()
map) map)
local MAP_TYPE='id pinned' local MAP_TYPE='id pinned'
case $command in case $command in
show|list|dump) show|list|dump|peek|pop|dequeue)
case $prev in case $prev in
$command) $command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0 return 0
;; ;;
id) id)
case "$command" in
peek)
_bpftool_get_map_ids_for_type stack
_bpftool_get_map_ids_for_type queue
;;
pop)
_bpftool_get_map_ids_for_type stack
;;
dequeue)
_bpftool_get_map_ids_for_type queue
;;
*)
_bpftool_get_map_ids _bpftool_get_map_ids
;;
esac
return 0 return 0
;; ;;
*) *)
...@@ -447,19 +487,25 @@ _bpftool() ...@@ -447,19 +487,25 @@ _bpftool()
COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
;; ;;
*) *)
case $(_bpftool_map_guess_map_type) in
queue|stack)
return 0
;;
esac
_bpftool_once_attr 'key' _bpftool_once_attr 'key'
return 0 return 0
;; ;;
esac esac
;; ;;
update) update|push|enqueue)
case $prev in case $prev in
$command) $command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0 return 0
;; ;;
id) id)
_bpftool_map_update_get_id _bpftool_map_update_get_id $command
return 0 return 0
;; ;;
key) key)
...@@ -468,7 +514,7 @@ _bpftool() ...@@ -468,7 +514,7 @@ _bpftool()
value) value)
# We can have bytes, or references to a prog or a # We can have bytes, or references to a prog or a
# map, depending on the type of the map to update. # map, depending on the type of the map to update.
case $(_bpftool_map_update_map_type) in case "$(_bpftool_map_guess_map_type)" in
array_of_maps|hash_of_maps) array_of_maps|hash_of_maps)
local MAP_TYPE='id pinned' local MAP_TYPE='id pinned'
COMPREPLY+=( $( compgen -W "$MAP_TYPE" \ COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
...@@ -490,6 +536,13 @@ _bpftool() ...@@ -490,6 +536,13 @@ _bpftool()
return 0 return 0
;; ;;
*) *)
case $(_bpftool_map_guess_map_type) in
queue|stack)
_bpftool_once_attr 'value'
return 0;
;;
esac
_bpftool_once_attr 'key' _bpftool_once_attr 'key'
local UPDATE_FLAGS='any exist noexist' local UPDATE_FLAGS='any exist noexist'
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
...@@ -508,6 +561,7 @@ _bpftool() ...@@ -508,6 +561,7 @@ _bpftool()
return 0 return 0
fi fi
done done
return 0 return 0
;; ;;
esac esac
...@@ -527,7 +581,7 @@ _bpftool() ...@@ -527,7 +581,7 @@ _bpftool()
return 0 return 0
;; ;;
id) id)
_bpftool_get_perf_map_ids _bpftool_get_map_ids_for_type perf_event_array
return 0 return 0
;; ;;
cpu) cpu)
...@@ -546,7 +600,8 @@ _bpftool() ...@@ -546,7 +600,8 @@ _bpftool()
*) *)
[[ $prev == $object ]] && \ [[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'delete dump getnext help \ COMPREPLY=( $( compgen -W 'delete dump getnext help \
lookup pin event_pipe show list update create' -- \ lookup pin event_pipe show list update create \
peek push enqueue pop dequeue' -- \
"$cur" ) ) "$cur" ) )
;; ;;
esac esac
......
...@@ -285,16 +285,21 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, ...@@ -285,16 +285,21 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
single_line = info->key_size + info->value_size <= 24 && single_line = info->key_size + info->value_size <= 24 &&
!break_names; !break_names;
if (info->key_size) {
printf("key:%c", break_names ? '\n' : ' '); printf("key:%c", break_names ? '\n' : ' ');
fprint_hex(stdout, key, info->key_size, " "); fprint_hex(stdout, key, info->key_size, " ");
printf(single_line ? " " : "\n"); printf(single_line ? " " : "\n");
}
if (info->value_size) {
printf("value:%c", break_names ? '\n' : ' '); printf("value:%c", break_names ? '\n' : ' ');
if (value) if (value)
fprint_hex(stdout, value, info->value_size, " "); fprint_hex(stdout, value, info->value_size,
" ");
else else
printf("<no entry>"); printf("<no entry>");
}
printf("\n"); printf("\n");
} else { } else {
...@@ -303,9 +308,12 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, ...@@ -303,9 +308,12 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
n = get_possible_cpus(); n = get_possible_cpus();
step = round_up(info->value_size, 8); step = round_up(info->value_size, 8);
if (info->key_size) {
printf("key:\n"); printf("key:\n");
fprint_hex(stdout, key, info->key_size, " "); fprint_hex(stdout, key, info->key_size, " ");
printf("\n"); printf("\n");
}
if (info->value_size) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
printf("value (CPU %02d):%c", printf("value (CPU %02d):%c",
i, info->value_size > 16 ? '\n' : ' '); i, info->value_size > 16 ? '\n' : ' ');
...@@ -317,6 +325,7 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, ...@@ -317,6 +325,7 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
printf("\n"); printf("\n");
} }
} }
}
} }
static char **parse_bytes(char **argv, const char *name, unsigned char *val, static char **parse_bytes(char **argv, const char *name, unsigned char *val,
...@@ -779,6 +788,32 @@ static int do_dump(int argc, char **argv) ...@@ -779,6 +788,32 @@ static int do_dump(int argc, char **argv)
return err; return err;
} }
static int alloc_key_value(struct bpf_map_info *info, void **key, void **value)
{
*key = NULL;
*value = NULL;
if (info->key_size) {
*key = malloc(info->key_size);
if (!*key) {
p_err("key mem alloc failed");
return -1;
}
}
if (info->value_size) {
*value = alloc_value(info);
if (!*value) {
p_err("value mem alloc failed");
free(*key);
*key = NULL;
return -1;
}
}
return 0;
}
static int do_update(int argc, char **argv) static int do_update(int argc, char **argv)
{ {
struct bpf_map_info info = {}; struct bpf_map_info info = {};
...@@ -795,13 +830,9 @@ static int do_update(int argc, char **argv) ...@@ -795,13 +830,9 @@ static int do_update(int argc, char **argv)
if (fd < 0) if (fd < 0)
return -1; return -1;
key = malloc(info.key_size); err = alloc_key_value(&info, &key, &value);
value = alloc_value(&info); if (err)
if (!key || !value) {
p_err("mem alloc failed");
err = -1;
goto exit_free; goto exit_free;
}
err = parse_elem(argv, &info, key, value, info.key_size, err = parse_elem(argv, &info, key, value, info.key_size,
info.value_size, &flags, &value_fd); info.value_size, &flags, &value_fd);
...@@ -826,12 +857,51 @@ static int do_update(int argc, char **argv) ...@@ -826,12 +857,51 @@ static int do_update(int argc, char **argv)
return err; return err;
} }
static void print_key_value(struct bpf_map_info *info, void *key,
void *value)
{
json_writer_t *btf_wtr;
struct btf *btf = NULL;
int err;
err = btf__get_from_id(info->btf_id, &btf);
if (err) {
p_err("failed to get btf");
return;
}
if (json_output) {
print_entry_json(info, key, value, btf);
} else if (btf) {
/* if here json_wtr wouldn't have been initialised,
* so let's create separate writer for btf
*/
btf_wtr = get_btf_writer();
if (!btf_wtr) {
p_info("failed to create json writer for btf. falling back to plain output");
btf__free(btf);
btf = NULL;
print_entry_plain(info, key, value);
} else {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};
do_dump_btf(&d, info, key, value);
jsonw_destroy(&btf_wtr);
}
} else {
print_entry_plain(info, key, value);
}
btf__free(btf);
}
static int do_lookup(int argc, char **argv) static int do_lookup(int argc, char **argv)
{ {
struct bpf_map_info info = {}; struct bpf_map_info info = {};
__u32 len = sizeof(info); __u32 len = sizeof(info);
json_writer_t *btf_wtr;
struct btf *btf = NULL;
void *key, *value; void *key, *value;
int err; int err;
int fd; int fd;
...@@ -843,13 +913,9 @@ static int do_lookup(int argc, char **argv) ...@@ -843,13 +913,9 @@ static int do_lookup(int argc, char **argv)
if (fd < 0) if (fd < 0)
return -1; return -1;
key = malloc(info.key_size); err = alloc_key_value(&info, &key, &value);
value = alloc_value(&info); if (err)
if (!key || !value) {
p_err("mem alloc failed");
err = -1;
goto exit_free; goto exit_free;
}
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
if (err) if (err)
...@@ -873,43 +939,12 @@ static int do_lookup(int argc, char **argv) ...@@ -873,43 +939,12 @@ static int do_lookup(int argc, char **argv)
} }
/* here means bpf_map_lookup_elem() succeeded */ /* here means bpf_map_lookup_elem() succeeded */
err = btf__get_from_id(info.btf_id, &btf); print_key_value(&info, key, value);
if (err) {
p_err("failed to get btf");
goto exit_free;
}
if (json_output) {
print_entry_json(&info, key, value, btf);
} else if (btf) {
/* if here json_wtr wouldn't have been initialised,
* so let's create separate writer for btf
*/
btf_wtr = get_btf_writer();
if (!btf_wtr) {
p_info("failed to create json writer for btf. falling back to plain output");
btf__free(btf);
btf = NULL;
print_entry_plain(&info, key, value);
} else {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};
do_dump_btf(&d, &info, key, value);
jsonw_destroy(&btf_wtr);
}
} else {
print_entry_plain(&info, key, value);
}
exit_free: exit_free:
free(key); free(key);
free(value); free(value);
close(fd); close(fd);
btf__free(btf);
return err; return err;
} }
...@@ -1122,6 +1157,49 @@ static int do_create(int argc, char **argv) ...@@ -1122,6 +1157,49 @@ static int do_create(int argc, char **argv)
return 0; return 0;
} }
static int do_pop_dequeue(int argc, char **argv)
{
struct bpf_map_info info = {};
__u32 len = sizeof(info);
void *key, *value;
int err;
int fd;
if (argc < 2)
usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
if (fd < 0)
return -1;
err = alloc_key_value(&info, &key, &value);
if (err)
goto exit_free;
err = bpf_map_lookup_and_delete_elem(fd, key, value);
if (err) {
if (errno == ENOENT) {
if (json_output)
jsonw_null(json_wtr);
else
printf("Error: empty map\n");
} else {
p_err("pop failed: %s", strerror(errno));
}
goto exit_free;
}
print_key_value(&info, key, value);
exit_free:
free(key);
free(value);
close(fd);
return err;
}
static int do_help(int argc, char **argv) static int do_help(int argc, char **argv)
{ {
if (json_output) { if (json_output) {
...@@ -1135,12 +1213,17 @@ static int do_help(int argc, char **argv) ...@@ -1135,12 +1213,17 @@ static int do_help(int argc, char **argv)
" entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
" [dev NAME]\n" " [dev NAME]\n"
" %s %s dump MAP\n" " %s %s dump MAP\n"
" %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n" " %s %s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n"
" %s %s lookup MAP key DATA\n" " %s %s lookup MAP [key DATA]\n"
" %s %s getnext MAP [key DATA]\n" " %s %s getnext MAP [key DATA]\n"
" %s %s delete MAP key DATA\n" " %s %s delete MAP key DATA\n"
" %s %s pin MAP FILE\n" " %s %s pin MAP FILE\n"
" %s %s event_pipe MAP [cpu N index M]\n" " %s %s event_pipe MAP [cpu N index M]\n"
" %s %s peek MAP\n"
" %s %s push MAP value VALUE\n"
" %s %s pop MAP\n"
" %s %s enqueue MAP value VALUE\n"
" %s %s dequeue MAP\n"
" %s %s help\n" " %s %s help\n"
"\n" "\n"
" " HELP_SPEC_MAP "\n" " " HELP_SPEC_MAP "\n"
...@@ -1158,7 +1241,8 @@ static int do_help(int argc, char **argv) ...@@ -1158,7 +1241,8 @@ static int do_help(int argc, char **argv)
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], 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; return 0;
} }
...@@ -1175,6 +1259,11 @@ static const struct cmd cmds[] = { ...@@ -1175,6 +1259,11 @@ static const struct cmd cmds[] = {
{ "pin", do_pin }, { "pin", do_pin },
{ "event_pipe", do_event_pipe }, { "event_pipe", do_event_pipe },
{ "create", do_create }, { "create", do_create },
{ "peek", do_lookup },
{ "push", do_update },
{ "enqueue", do_update },
{ "pop", do_pop_dequeue },
{ "dequeue", do_pop_dequeue },
{ 0 } { 0 }
}; };
......
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