[PATCH bpf-next 1/2] bpftool: Accept symbolic map creation flags
From: Tianyi Chen
Date: Sun Sep 06 2026 - 13:11:02 EST
Accept comma-separated BPF_F_* names for map create flags, so callers
can use the UAPI names without looking up their numeric values. Keep
base-0 numeric input, including bits unknown to this bpftool.
Reject empty names, unknown names, mixed numeric and symbolic lists,
and values outside the unsigned 32-bit range. Limit symbolic names to
map creation flags and let the kernel validate map-specific combinations.
Document the syntax and complete names within comma-separated lists.
Assisted-by: Codex:GPT-6
Signed-off-by: Tianyi Chen <hi@xxxxxxxxx>
---
.../bpf/bpftool/Documentation/bpftool-map.rst | 13 +++-
tools/bpf/bpftool/bash-completion/bpftool | 25 +++++-
tools/bpf/bpftool/map.c | 77 ++++++++++++++++++-
3 files changed, 109 insertions(+), 6 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index 5daf3de5c74..375321d5582 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -76,9 +76,16 @@ bpftool map { show | list } [*MAP*]
bpftool map create *FILE* type *TYPE* key *KEY_SIZE* value *VALUE_SIZE* entries *MAX_ENTRIES* name *NAME* [flags *FLAGS*] [inner_map *MAP*] [offload_dev *NAME*]
Create a new map with given parameters and pin it to *bpffs* as *FILE*.
- *FLAGS* should be an integer which is the combination of desired flags,
- e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h UAPI header for existing
- flags).
+ *FLAGS* accepts an unsigned 32-bit integer combining the desired flags
+ (decimal, hexadecimal with a **0x** prefix, or octal with a **0** prefix),
+ or a comma-separated list of full, case-sensitive map creation flag names
+ from the bpf.h UAPI header. For example, **1024**, **0x400**, and
+ **BPF_F_MMAPABLE** are equivalent. Multiple names are combined with
+ bitwise OR, for example **BPF_F_NO_PREALLOC,BPF_F_RDONLY_PROG**.
+ Repeated names are allowed. Empty list elements, abbreviated names, and
+ lists mixing numbers with names are not accepted. Use **0** for no flags.
+ Numeric values can include bits unknown to bpftool. The kernel checks
+ whether the flags are valid for the requested map type.
To create maps of type array-of-maps or hash-of-maps, the **inner_map**
keyword must be used to pass an inner map. The kernel needs it to collect
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 75cbcb512eb..95f16fff876 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -718,6 +718,10 @@ _bpftool()
esac
;;
create)
+ # Keep a flags list together if readline splits at commas.
+ if [[ $COMP_WORDBREAKS == *,* && ( $cur == , || $prev == , ) ]]; then
+ _get_comp_words_by_ref -n , cur prev
+ fi
case $prev in
$command)
_filedir
@@ -729,7 +733,26 @@ _bpftool()
COMPREPLY=( $( compgen -W "$BPFTOOL_MAP_CREATE_TYPES" -- "$cur" ) )
return 0
;;
- key|value|flags|entries)
+ flags)
+ local flags='BPF_F_NO_PREALLOC BPF_F_NO_COMMON_LRU
+ BPF_F_NUMA_NODE BPF_F_RDONLY BPF_F_WRONLY
+ BPF_F_STACK_BUILD_ID BPF_F_ZERO_SEED
+ BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG BPF_F_CLONE
+ BPF_F_MMAPABLE BPF_F_PRESERVE_ELEMS BPF_F_INNER_MAP
+ BPF_F_LINK BPF_F_VTYPE_BTF_OBJ_FD BPF_F_TOKEN_FD
+ BPF_F_SEGV_ON_FAULT BPF_F_NO_USER_CONV
+ BPF_F_RB_OVERWRITE'
+ local prefix= flag
+ # Readline replaces only the suffix after a word break.
+ if [[ $cur == *,* && $COMP_WORDBREAKS != *,* ]]; then
+ prefix="${cur%,*},"
+ fi
+ for flag in $(compgen -W "$flags" -- "${cur##*,}"); do
+ COMPREPLY+=( "${prefix}${flag}" )
+ done
+ return 0
+ ;;
+ key|value|entries)
return 0
;;
inner_map)
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 684a8fb7241..d703af60d0b 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -1250,6 +1250,73 @@ static int do_pin(int argc, char **argv)
return err;
}
+static const struct {
+ const char *name;
+ __u32 value;
+} map_create_flags[] = {
+#define MAP_CREATE_FLAG(flag) { #flag, flag }
+ MAP_CREATE_FLAG(BPF_F_NO_PREALLOC),
+ MAP_CREATE_FLAG(BPF_F_NO_COMMON_LRU),
+ MAP_CREATE_FLAG(BPF_F_NUMA_NODE),
+ MAP_CREATE_FLAG(BPF_F_RDONLY),
+ MAP_CREATE_FLAG(BPF_F_WRONLY),
+ MAP_CREATE_FLAG(BPF_F_STACK_BUILD_ID),
+ MAP_CREATE_FLAG(BPF_F_ZERO_SEED),
+ MAP_CREATE_FLAG(BPF_F_RDONLY_PROG),
+ MAP_CREATE_FLAG(BPF_F_WRONLY_PROG),
+ MAP_CREATE_FLAG(BPF_F_CLONE),
+ MAP_CREATE_FLAG(BPF_F_MMAPABLE),
+ MAP_CREATE_FLAG(BPF_F_PRESERVE_ELEMS),
+ MAP_CREATE_FLAG(BPF_F_INNER_MAP),
+ MAP_CREATE_FLAG(BPF_F_LINK),
+ MAP_CREATE_FLAG(BPF_F_VTYPE_BTF_OBJ_FD),
+ MAP_CREATE_FLAG(BPF_F_TOKEN_FD),
+ MAP_CREATE_FLAG(BPF_F_SEGV_ON_FAULT),
+ MAP_CREATE_FLAG(BPF_F_NO_USER_CONV),
+ MAP_CREATE_FLAG(BPF_F_RB_OVERWRITE),
+#undef MAP_CREATE_FLAG
+};
+
+static int parse_map_create_flags(const char *arg, __u32 *flags)
+{
+ const char *name = arg, *comma;
+ long long value;
+ __u32 parsed = 0;
+ size_t len, i;
+ char *end;
+
+ /* Keep base-0 numeric input, including bits unknown to this bpftool. */
+ if (strncmp(arg, "BPF_F_", 6)) {
+ errno = 0;
+ value = strtoll(arg, &end, 0);
+ if (errno || end == arg || *end || value < 0 || value > UINT32_MAX)
+ goto invalid;
+ *flags = value;
+ return 0;
+ }
+
+ do {
+ comma = strchr(name, ',');
+ len = comma ? (size_t)(comma - name) : strlen(name);
+ for (i = 0; i < ARRAY_SIZE(map_create_flags); i++) {
+ if (strlen(map_create_flags[i].name) == len &&
+ !strncmp(name, map_create_flags[i].name, len))
+ break;
+ }
+ if (i == ARRAY_SIZE(map_create_flags))
+ goto invalid;
+ parsed |= map_create_flags[i].value;
+ if (comma)
+ name = comma + 1;
+ } while (comma);
+
+ *flags = parsed;
+ return 0;
+invalid:
+ p_err("can't parse %s as map creation flags", arg);
+ return -1;
+}
+
static int do_create(int argc, char **argv)
{
LIBBPF_OPTS(bpf_map_create_opts, attr);
@@ -1301,9 +1368,14 @@ static int do_create(int argc, char **argv)
"max entries"))
goto exit;
} else if (is_prefix(*argv, "flags")) {
- if (parse_u32_arg(&argc, &argv, &attr.map_flags,
- "flags"))
+ NEXT_ARG();
+ if (attr.map_flags) {
+ p_err("flags already specified");
+ goto exit;
+ }
+ if (parse_map_create_flags(*argv, &attr.map_flags))
goto exit;
+ NEXT_ARG();
} else if (is_prefix(*argv, "dev")) {
p_info("Warning: 'bpftool map create [...] dev <ifname>' syntax is deprecated.\n"
"Going further, please use 'offload_dev <ifname>' to request hardware offload for the map.");
@@ -1474,6 +1546,7 @@ static int do_help(int argc, char **argv)
" DATA := { [hex] BYTES }\n"
" " HELP_SPEC_PROGRAM "\n"
" VALUE := { DATA | MAP | PROG }\n"
+ " FLAGS := { integer | BPF_F_NAME[,BPF_F_NAME...] }\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"
--
2.55.0