bpftool: Add support for BPF_F_PREORDER cgroup attach flag

Commit 4b82b181a2 ("bpf: Allow pre-ordering for bpf cgroup progs")
introduced BPF_F_PREORDER to request pre-order execution across the
cgroup hierarchy. Furthermore, attachments legitimately use combinations
such as BPF_F_ALLOW_MULTI | BPF_F_PREORDER or
BPF_F_ALLOW_OVERRIDE | BPF_F_PREORDER.

With BPF_PROG_QUERY reporting the per-program BPF_F_PREORDER attribute,
bpftool's exact-match formatter falls back to "unknown(40)" when
BPF_F_PREORDER is present alone, or "unknown(41)" / "unknown(42)" when
combined with BPF_F_ALLOW_OVERRIDE or BPF_F_ALLOW_MULTI. Additionally,
do_attach() only accepts "multi" and "override", rejecting "preorder"
with "unknown option".

Before:
  $ bpftool cgroup show <cg>
  1234  cgroup_inet_ingress  unknown(42)  test_prog
  $ bpftool cgroup attach <cg> cgroup_inet_ingress id 5678 multi preorder
  Error: unknown option: preorder

After:
  $ bpftool cgroup show <cg>
  1234  cgroup_inet_ingress  multi,preorder  test_prog
  $ bpftool cgroup attach <cg> cgroup_inet_ingress id 5678 multi preorder
  (attaches successfully)

Refactor the attach flags formatter into a bitmask formatter that outputs
comma-separated flag names for plain text while preserving unrecognized
bits as "unknown(...)". Render attach flags as an array of flag names in
JSON output. Accept "preorder" in do_attach(), update the cgroup
documentation and synopsis to express valid flag combinations, and teach
bash completion about them ("multi" or "override" optionally combined
with "preorder").

Signed-off-by: Hui Su <sh_def@163.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Quentin Monnet <qmo@kernel.org>
Link: https://patch.msgid.link/20260922025442.3176057-4-sh_def@163.com
This commit is contained in:
Hui Su 2026-09-22 11:54:42 +09:00 committed by Alexei Starovoitov
parent 47211d1200
commit c39bf6b670
No known key found for this signature in database
3 changed files with 89 additions and 39 deletions

View File

@ -45,7 +45,7 @@ CGROUP COMMANDS
| **cgroup_unix_recvmsg** | **cgroup_sysctl** |
| **cgroup_getsockopt** | **cgroup_setsockopt** |
| **cgroup_inet_sock_release** }
| *ATTACH_FLAGS* := { **multi** | **override** }
| *ATTACH_FLAGS* := { [ **multi** | **override** ] [ **preorder** ] }
DESCRIPTION
===========
@ -75,20 +75,27 @@ bpftool cgroup attach *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
Attach program *PROG* to the cgroup *CGROUP* with attach type *ATTACH_TYPE*
and optional *ATTACH_FLAGS*.
*ATTACH_FLAGS* can be one of: **override** if a sub-cgroup installs some
*ATTACH_FLAGS* can include: **override** if a sub-cgroup installs some
bpf program, the program in this cgroup yields to sub-cgroup program;
**multi** if a sub-cgroup installs some bpf program, that cgroup program
gets run in addition to the program in this cgroup.
gets run in addition to the program in this cgroup;
**preorder** requests ancestor-to-descendant execution for this program,
before non-preorder programs, which execute descendants-to-ancestors
across the cgroup hierarchy. Note that **preorder** alone does not enable
multi-program attachment; specify **multi** together with **preorder** to
attach multiple programs.
Only one program is allowed to be attached to a cgroup with no attach flags
or the **override** flag. Attaching another program will release old
program and attach the new one.
Only one program is allowed to be attached to a cgroup unless the
**multi** flag is specified. Without **multi**, attaching another program
replaces the existing program, provided the **override** setting matches.
Multiple programs are allowed to be attached to a cgroup with **multi**.
They are executed in FIFO order (those that were attached first, run
first).
Programs marked with **preorder** are placed before non-preorder programs
in the effective program array. Within each ordering class at the same
cgroup level, attachment order is preserved.
Non-default *ATTACH_FLAGS* are supported by kernel version 4.14 and later.
**multi** and **override** are supported by kernel version 4.14 and later.
**preorder** was introduced upstream in Linux 6.15.
*ATTACH_TYPE* can be one of:

View File

@ -1064,7 +1064,6 @@ _bpftool()
attach|detach)
local BPFTOOL_CGROUP_ATTACH_TYPES="$(bpftool feature list_builtins attach_types 2>/dev/null | \
grep '^cgroup_')"
local ATTACH_FLAGS='multi override'
# Check for $prev = $command first
if [ $prev = $command ]; then
_filedir
@ -1094,7 +1093,8 @@ _bpftool()
# "id|pinned|tag|name" (we already checked for
# that). This should only leave the case when
# we need attach flags for "attach" commamnd.
_bpftool_one_of_list "$ATTACH_FLAGS"
_bpftool_one_of_list 'multi override'
_bpftool_once_attr 'preorder'
fi
return 0
;;

View File

@ -56,7 +56,7 @@ static const int cgroup_attach_types[] = {
};
#define HELP_SPEC_ATTACH_FLAGS \
"ATTACH_FLAGS := { multi | override }"
"ATTACH_FLAGS := { [ multi | override ] [ preorder ] }"
#define HELP_SPEC_ATTACH_TYPES \
" ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
@ -138,11 +138,69 @@ static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
close(fd);
}
static const struct {
__u32 flag;
const char *name;
} attach_flag_names[] = {
{ BPF_F_ALLOW_MULTI, "multi" },
{ BPF_F_ALLOW_OVERRIDE, "override" },
{ BPF_F_PREORDER, "preorder" },
};
static const char *format_attach_flags(__u32 flags, char *buf, size_t sz)
{
size_t len = 0;
size_t i;
int n;
buf[0] = '\0';
for (i = 0; i < ARRAY_SIZE(attach_flag_names); i++) {
if (flags & attach_flag_names[i].flag) {
n = snprintf(buf + len, sz - len, "%s%s",
len ? "," : "", attach_flag_names[i].name);
if (n < 0 || (size_t)n >= sz - len)
return buf;
len += n;
flags &= ~attach_flag_names[i].flag;
}
}
if (flags)
snprintf(buf + len, sz - len, "%sunknown(%x)",
len ? "," : "", flags);
return buf;
}
static void show_attach_flags_json(__u32 flags)
{
char buf[32];
size_t i;
jsonw_name(json_wtr, "attach_flags");
jsonw_start_array(json_wtr);
for (i = 0; i < ARRAY_SIZE(attach_flag_names); i++) {
if (!(flags & attach_flag_names[i].flag))
continue;
jsonw_string(json_wtr, attach_flag_names[i].name);
flags &= ~attach_flag_names[i].flag;
}
if (flags) {
snprintf(buf, sizeof(buf), "unknown(%x)", flags);
jsonw_string(json_wtr, buf);
}
jsonw_end_array(json_wtr);
}
static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
const char *attach_flags_str,
int level)
__u32 attach_flags, int level)
{
char prog_name[MAX_PROG_FULL_NAME];
char attach_flags_str[64];
const char *attach_btf_name = NULL;
struct bpf_prog_info info = {};
const char *attach_type_str;
@ -182,7 +240,7 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
else
jsonw_uint_field(json_wtr, "attach_type", attach_type);
if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
show_attach_flags_json(attach_flags);
jsonw_string_field(json_wtr, "name", prog_name);
if (attach_btf_name)
jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
@ -198,7 +256,9 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
if (query_flags & BPF_F_QUERY_EFFECTIVE)
printf(" %-15s", prog_name);
else
printf(" %-15s %-15s", attach_flags_str, prog_name);
printf(" %-17s %-15s",
format_attach_flags(attach_flags, attach_flags_str,
sizeof(attach_flags_str)), prog_name);
if (attach_btf_name)
printf(" %-15s", attach_btf_name);
else if (info.attach_btf_id)
@ -264,7 +324,7 @@ static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
return 0;
for (iter = 0; iter < p.prog_cnt; iter++)
show_bpf_prog(prog_ids[iter], type, NULL, level);
show_bpf_prog(prog_ids[iter], type, 0, level);
return 0;
}
@ -274,9 +334,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
{
LIBBPF_OPTS(bpf_prog_query_opts, p);
__u32 prog_attach_flags[1024] = {0};
const char *attach_flags_str;
__u32 prog_ids[1024] = {0};
char buf[32];
__u32 iter;
int ret;
@ -296,24 +354,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
__u32 attach_flags;
attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
switch (attach_flags) {
case BPF_F_ALLOW_MULTI:
attach_flags_str = "multi";
break;
case BPF_F_ALLOW_OVERRIDE:
attach_flags_str = "override";
break;
case 0:
attach_flags_str = "";
break;
default:
snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
attach_flags_str = buf;
}
show_bpf_prog(prog_ids[iter], type,
attach_flags_str, level);
show_bpf_prog(prog_ids[iter], type, attach_flags, level);
}
return 0;
@ -377,7 +418,7 @@ static int do_show(int argc, char **argv)
else if (query_flags & BPF_F_QUERY_EFFECTIVE)
printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
else
printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
printf("%-8s %-15s %-17s %-15s\n", "ID", "AttachType",
"AttachFlags", "Name");
btf_vmlinux = libbpf_find_kernel_btf();
@ -531,7 +572,7 @@ static int do_show_tree(int argc, char **argv)
"ID", "AttachType", "Name");
else
printf("%s\n"
"%-8s %-15s %-15s %-15s\n",
"%-8s %-15s %-17s %-15s\n",
"CgroupPath",
"ID", "AttachType", "AttachFlags", "Name");
@ -593,6 +634,8 @@ static int do_attach(int argc, char **argv)
attach_flags |= BPF_F_ALLOW_MULTI;
} else if (is_prefix(argv[i], "override")) {
attach_flags |= BPF_F_ALLOW_OVERRIDE;
} else if (is_prefix(argv[i], "preorder")) {
attach_flags |= BPF_F_PREORDER;
} else {
p_err("unknown option: %s", argv[i]);
goto exit_cgroup;