This patch adds support for the new BPF_CPU flag in bpftool, to enable single-cpu updates of per-cpu maps. It can be combined with existing flags; for example, to update the value for key 0 on CPU 9 only if it doesn't already exist: bpftool map update key 0 0 0 0 value 1 0 0 0 noexist cpu 9 Signed-off-by: Paul Chaignon <paul.chaignon@xxxxxxxxxx> --- .../bpf/bpftool/Documentation/bpftool-map.rst | 13 ++-- tools/bpf/bpftool/bash-completion/bpftool | 2 +- tools/bpf/bpftool/map.c | 70 ++++++++++++++----- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index cdeae8ae90ba..72aa9b72f08f 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -25,7 +25,7 @@ MAP COMMANDS | **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 update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]... | **bpftool** **map lookup** *MAP* [**key** *DATA*] | **bpftool** **map getnext** *MAP* [**key** *DATA*] | **bpftool** **map delete** *MAP* **key** *DATA* @@ -43,7 +43,7 @@ MAP COMMANDS | *DATA* := { [**hex**] *BYTES* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* } | *VALUE* := { *DATA* | *MAP* | *PROG* } -| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** } +| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** | **cpu** *CPU_ID* } | *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** @@ -73,9 +73,12 @@ DESCRIPTION **bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] Update map entry for a given *KEY*. - *UPDATE_FLAGS* can be one of: **any** update existing entry - or add if doesn't exit; **exist** update only if entry already - exists; **noexist** update only if entry doesn't exist. + *UPDATE_FLAGS* can be: **any** update existing entry or add + if doesn't exit; **exist** update only if entry already + exists; **noexist** update only if entry doesn't exist; + **cpu** update only value for given *CPU_ID* in per-CPU map. + Only one of **any**, **exist**, and **noexist** can be + specified. If the **hex** keyword is provided in front of the bytes sequence, the bytes are parsed as hexadeximal values, even if diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 754d8395e451..116bf3dffb47 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -689,7 +689,7 @@ _bpftool() esac _bpftool_once_attr 'key' - local UPDATE_FLAGS='any exist noexist' + local UPDATE_FLAGS='any exist noexist cpu' for (( idx=3; idx < ${#words[@]}-1; idx++ )); do if [[ ${words[idx]} == 'value' ]]; then # 'value' is present, but is not the last diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index c01f76fa6876..da1455f460b1 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -470,9 +470,10 @@ static void fill_per_cpu_value(struct bpf_map_info *info, void *value) memcpy(value + i * step, value, info->value_size); } -static int parse_elem(char **argv, struct bpf_map_info *info, - void *key, void *value, __u32 key_size, __u32 value_size, - __u32 *flags, __u32 **value_fd) +static int +__parse_elem(char **argv, struct bpf_map_info *info, void *key, void *value, + __u32 key_size, __u32 value_size, __u64 *flags, __u32 **value_fd, + bool any_flag) { if (!*argv) { if (!key && !value) @@ -494,8 +495,8 @@ static int parse_elem(char **argv, struct bpf_map_info *info, if (!argv) return -1; - return parse_elem(argv, info, NULL, value, key_size, value_size, - flags, value_fd); + return __parse_elem(argv, info, NULL, value, key_size, + value_size, flags, value_fd, any_flag); } else if (is_prefix(*argv, "value")) { int fd; @@ -556,30 +557,63 @@ static int parse_elem(char **argv, struct bpf_map_info *info, fill_per_cpu_value(info, value); } - return parse_elem(argv, info, key, NULL, key_size, value_size, - flags, NULL); + return __parse_elem(argv, info, key, NULL, key_size, + value_size, flags, NULL, any_flag); } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || is_prefix(*argv, "exist")) { - if (!flags) { + if (any_flag || *flags & (BPF_NOEXIST | BPF_EXIST)) { p_err("flags specified multiple times: %s", *argv); return -1; } - if (is_prefix(*argv, "any")) - *flags = BPF_ANY; - else if (is_prefix(*argv, "noexist")) - *flags = BPF_NOEXIST; - else if (is_prefix(*argv, "exist")) - *flags = BPF_EXIST; + if (is_prefix(*argv, "any")) { + *flags |= BPF_ANY; + any_flag = true; + } else if (is_prefix(*argv, "noexist")) { + *flags |= BPF_NOEXIST; + } else if (is_prefix(*argv, "exist")) { + *flags |= BPF_EXIST; + } + + return __parse_elem(argv + 1, info, key, value, key_size, + value_size, flags, value_fd, any_flag); + } else if (is_prefix(*argv, "cpu")) { + unsigned long long cpuid; + char *endptr; + + if (*flags & BPF_CPU) { + p_err("flags specified multiple times: %s", *argv); + return -1; + } - return parse_elem(argv + 1, info, key, value, key_size, - value_size, NULL, value_fd); + cpuid = strtoull(*(argv + 1), &endptr, 0); + if (*endptr) { + p_err("can't parse CPU id %s", *(argv + 1)); + return -1; + } + if (cpuid >= get_possible_cpus()) { + p_err("incorrect value for CPU id"); + return -1; + } + + *flags |= (cpuid << 32) | BPF_CPU; + + return __parse_elem(argv + 2, info, key, value, key_size, + value_size, flags, value_fd, any_flag); } p_err("expected key or value, got: %s", *argv); return -1; } +static int +parse_elem(char **argv, struct bpf_map_info *info, void *key, void *value, + __u32 key_size, __u32 value_size, __u64 *flags, __u32 **value_fd) +{ + return __parse_elem(argv, info, key, value, key_size, value_size, + flags, value_fd, false); +} + static void show_map_header_json(struct bpf_map_info *info, json_writer_t *wtr) { jsonw_uint_field(wtr, "id", info->id); @@ -1114,7 +1148,7 @@ static int do_update(int argc, char **argv) struct bpf_map_info info = {}; __u32 len = sizeof(info); __u32 *value_fd = NULL; - __u32 flags = BPF_ANY; + __u64 flags = BPF_ANY; void *key, *value; int fd, err; @@ -1558,7 +1592,7 @@ static int do_help(int argc, char **argv) " DATA := { [hex] BYTES }\n" " " HELP_SPEC_PROGRAM "\n" " VALUE := { DATA | MAP | PROG }\n" - " UPDATE_FLAGS := { any | exist | noexist }\n" + " UPDATE_FLAGS := { any | exist | noexist | cpu CPU_ID }\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" -- 2.24.0