bpf: Support replacing cgroup-bpf program in MULTI mode
Subsystems (6)
v3 cover bpf: Support replacing cgroup-bpf program in MULTI mode
v2->v3:
- rely on DECLARE_LIBBPF_OPTS from libbpf_common.h;
- separate "required" and "optional" arguments in bpf_prog_attach_xattr;
- convert test_cgroup_attach to prog_tests;
- move the new selftest to prog_tests/cgroup_attach_multi.
v1->v2:
- move DECLARE_LIBBPF_OPTS from libbpf.h to bpf.h (patch 4);
- switch new libbpf API to OPTS framework;
- switch selftest to libbpf OPTS framework.
This patch set adds support for replacing cgroup-bpf programs attached with BPF_F_ALLOW_MULTI flag so that any program in a list can be updated to a new version without service interruption and order of programs can be preserved.
Please see patch 3 for details on the use-case and API changes.
Other patches:
Patch 1 is preliminary refactoring of __cgroup_bpf_attach to simplify it. Patch 2 is minor cleanup of hierarchy_allows_attach.
Patch 4 extends libbpf API to support new set of attach attributes. Patch 5 converts test_cgroup_attach to prog_tests.
Patch 6 adds selftest coverage for the new API.
Andrey Ignatov (6):
bpf: Simplify __cgroup_bpf_attach
bpf: Remove unused new_flags in hierarchy_allows_attach()
bpf: Support replacing cgroup-bpf program in MULTI mode
libbpf: Introduce bpf_prog_attach_xattr selftests/bpf: Convert test_cgroup_attach to prog_tests selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
include/linux/bpf-cgroup.h | 4 +- include/uapi/linux/bpf.h | 10 + kernel/bpf/cgroup.c | 97 +-- kernel/bpf/syscall.c | 4 +- kernel/cgroup/cgroup.c | 5 +- tools/include/uapi/linux/bpf.h | 10 + tools/lib/bpf/bpf.c | 16 +- tools/lib/bpf/bpf.h | 11 + tools/lib/bpf/libbpf.map | 1 + tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 285 +++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 15 files changed, 651 insertions(+), 626 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c
Signature · 1 line
2.17.1
PATCH v3 1/6 bpf: Simplify __cgroup_bpf_attach
__cgroup_bpf_attach has a lot of identical code to handle two scenarios:BPF_F_ALLOW_MULTI is set and unset.
Simplify it by splitting the two main steps:
* First, the decision is made whether a new bpf_prog_list entry should be allocated or existing entry should be reused for the new program. This decision is saved in replace_pl pointer;
* Next, replace_pl pointer is used to handle both possible states of BPF_F_ALLOW_MULTI flag (set / unset) instead of doing similar work for them separately.
This splitting, in turn, allows to make further simplifications:
* The check for attaching same program twice in BPF_F_ALLOW_MULTI mode can be done before allocating cgroup storage, so that if user tries to attach same program twice no alloc/free happens as it was before;
* pl_was_allocated becomes redundant so it's removed.
--- kernel/bpf/cgroup.c | 62 +++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 39 deletions(-)
kernel/bpf/cgroup.c +23 -39
@@ -295,9 +295,8 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,struct bpf_prog *old_prog =NULL ;struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE ], *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE ] = {NULL }; +struct bpf_prog_list *pl, *replace_pl =NULL ;enum bpf_cgroup_storage_type stype; -struct bpf_prog_list *pl; -bool pl_was_allocated;int err;if ((flags &BPF_F_ALLOW_OVERRIDE ) && (flags &BPF_F_ALLOW_MULTI ))
@@ -317,6 +316,16 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,if (prog_list_length (progs) >=BPF_CGROUP_MAX_PROGS )return -E2BIG ; +if (flags &BPF_F_ALLOW_MULTI ) { +list_for_each_entry (pl, progs, node) { +if (pl->prog == prog) +/* disallow attaching the same prog twice */ +return -EINVAL ; + } + }else if (!list_empty (progs)) { + replace_pl =list_first_entry (progs,typeof (*pl), node); + } +for_each_cgroup_storage_type (stype) { storage[stype] =bpf_cgroup_storage_alloc (prog, stype);if (IS_ERR (storage[stype])) {
@@ -327,52 +336,27 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, } } -if (flags &BPF_F_ALLOW_MULTI ) { -list_for_each_entry (pl, progs, node) { -if (pl->prog == prog) { -/* disallow attaching the same prog twice */ -for_each_cgroup_storage_type (stype) -bpf_cgroup_storage_free (storage[stype]); -return -EINVAL ; - } +if (replace_pl) { + pl = replace_pl; + old_prog = pl->prog; +for_each_cgroup_storage_type (stype) { + old_storage[stype] = pl->storage[stype]; +bpf_cgroup_storage_unlink (old_storage[stype]); } - + }else { pl =kmalloc (sizeof (*pl),GFP_KERNEL );if (!pl) {for_each_cgroup_storage_type (stype)bpf_cgroup_storage_free (storage[stype]);return -ENOMEM ; } - - pl_was_allocated = true; - pl->prog = prog; -for_each_cgroup_storage_type (stype) - pl->storage[stype] = storage[stype];list_add_tail (&pl->node, progs); - }else { -if (list_empty (progs)) { - pl =kmalloc (sizeof (*pl),GFP_KERNEL ); -if (!pl) { -for_each_cgroup_storage_type (stype) -bpf_cgroup_storage_free (storage[stype]); -return -ENOMEM ; - } - pl_was_allocated = true; -list_add_tail (&pl->node, progs); - }else { - pl =list_first_entry (progs,typeof (*pl), node); - old_prog = pl->prog; -for_each_cgroup_storage_type (stype) { - old_storage[stype] = pl->storage[stype]; -bpf_cgroup_storage_unlink (old_storage[stype]); - } - pl_was_allocated = false; - } - pl->prog = prog; -for_each_cgroup_storage_type (stype) - pl->storage[stype] = storage[stype]; } + pl->prog = prog; +for_each_cgroup_storage_type (stype) + pl->storage[stype] = storage[stype]; + cgrp->bpf.flags[type] = flags; err =update_effective_progs (cgrp, type);
@@ -401,7 +385,7 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, pl->storage[stype] = old_storage[stype];bpf_cgroup_storage_link (old_storage[stype], cgrp, type); } -if (pl_was_allocated) { +if (!replace_pl) {list_del (&pl->node);kfree (pl); }
Signature · 1 line
2.17.1
PATCH v3 2/6 bpf: Remove unused new_flags in hierarchy_allows_attach()
In reply to Andrey Ignatovnew_flags is unused, remove it.
--- kernel/bpf/cgroup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/bpf/cgroup.c +2 -3
@@ -103,8 +103,7 @@ static u32 prog_list_length(struct list_head *head)* if parent has overridable or multi-prog, allow attaching */ static bool hierarchy_allows_attach (struct cgroup *cgrp, -enum bpf_attach_type type, -u32 new_flags) +enum bpf_attach_type type) {struct cgroup *p;
@@ -303,7 +302,7 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,/* invalid combination */ return -EINVAL ; -if (!hierarchy_allows_attach (cgrp, type, flags)) +if (!hierarchy_allows_attach (cgrp, type))return -EPERM ;if (!list_empty (progs) && cgrp->bpf.flags[type] != flags)
Signature · 1 line
2.17.1
PATCH v3 3/6 bpf: Support replacing cgroup-bpf program in MULTI mode
In reply to Andrey IgnatovThe common use-case in production is to have multiple cgroup-bpf programs per attach type that cover multiple use-cases. Such programs are attached with BPF_F_ALLOW_MULTI and can be maintained by different people.
Order of programs usually matters, for example imagine two egress
programs: the first one drops packets and the second one counts packets. If they're swapped the result of counting program will be different.
It brings operational challenges with updating cgroup-bpf program(s) attached with BPF_F_ALLOW_MULTI since there is no way to replace a program:
* One way to update is to detach all programs first and then attach the new version(s) again in the right order. This introduces an interruption in the work a program is doing and may not be acceptable (e.g. if it's egress firewall);
* Another way is attach the new version of a program first and only then detach the old version. This introduces the time interval when two versions of same program are working, what may not be acceptable if a program is not idempotent. It also imposes additional burden on program developers to make sure that two versions of their program can co-exist.
Solve the problem by introducing a "replace" mode in BPF_PROG_ATTACH command for cgroup-bpf programs being attached with BPF_F_ALLOW_MULTI flag. This mode is enabled by newly introduced BPF_F_REPLACE attach flag and bpf_attr.replace_bpf_fd attribute to pass fd of the old program to replace
That way user can replace any program among those attached with BPF_F_ALLOW_MULTI flag without the problems described above.
Details of the new API:
* If BPF_F_REPLACE is set but replace_bpf_fd doesn't have valid descriptor of BPF program, BPF_PROG_ATTACH will return corresponding error (EINVAL or EBADF).
* If replace_bpf_fd has valid descriptor of BPF program but such a program is not attached to specified cgroup, BPF_PROG_ATTACH will return ENOENT.
BPF_F_REPLACE is introduced to make the user intent clear, since replace_bpf_fd alone can't be used for this (its default value, 0, is a valid fd). BPF_F_REPLACE also makes it possible to extend the API in the future (e.g. add BPF_F_BEFORE and BPF_F_AFTER if needed).
--- include/linux/bpf-cgroup.h | 4 +++- include/uapi/linux/bpf.h | 10 ++++++++++ kernel/bpf/cgroup.c | 30 ++++++++++++++++++++++++++---- kernel/bpf/syscall.c | 4 ++-- kernel/cgroup/cgroup.c | 5 +++-- tools/include/uapi/linux/bpf.h | 10 ++++++++++ 6 files changed, 54 insertions(+), 9 deletions(-)
include/linux/bpf-cgroup.h +3 -1
@@ -85,6 +85,7 @@ int cgroup_bpf_inherit(struct cgroup *cgrp);void cgroup_bpf_offline (struct cgroup *cgrp);int __cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *prog, +struct bpf_prog *replace_prog,enum bpf_attach_type type,u32 flags);int __cgroup_bpf_detach (struct cgroup *cgrp,struct bpf_prog *prog,enum bpf_attach_type type);
@@ -93,7 +94,8 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,/* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */ int cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *prog, -enum bpf_attach_type type,u32 flags); +struct bpf_prog *replace_prog,enum bpf_attach_type type, +u32 flags);int cgroup_bpf_detach (struct cgroup *cgrp,struct bpf_prog *prog,enum bpf_attach_type type,u32 flags); int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
include/uapi/linux/bpf.h +10 -0
@@ -231,6 +231,11 @@ enum bpf_attach_type {* When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * +* With BPF_F_ALLOW_MULTI a new program is added to the end of the list of +* programs for a cgroup. Though it's possible to replace an old program at +* any position by also specifying BPF_F_REPLACE flag and position itself in +* replace_bpf_fd attribute. Old program at this position will be released. +* * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1:
@@ -249,6 +254,7 @@ enum bpf_attach_type {*/ #define BPF_F_ALLOW_OVERRIDE (1U << 0)#define BPF_F_ALLOW_MULTI (1U << 1) +#define BPF_F_REPLACE (1U << 2)/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel
@@ -442,6 +448,10 @@ union bpf_attr {__u32 attach_bpf_fd;/* eBPF program to attach */ __u32 attach_type;__u32 attach_flags; +__u32 replace_bpf_fd;/* previously attached eBPF +* program to replace if +* BPF_F_REPLACE is used +*/ };struct {/* anonymous struct used by BPF_PROG_TEST_RUN command */
kernel/bpf/cgroup.c +26 -4
@@ -282,14 +282,17 @@ static int update_effective_progs(struct cgroup *cgrp,* propagate the change to descendants * @cgrp: The cgroup which descendants to traverse * @prog: A program to attach +* @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set * @type: Type of attach operation * @flags: Option flags * * Must be called with cgroup_mutex held. */ int __cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *prog, +struct bpf_prog *replace_prog,enum bpf_attach_type type,u32 flags) { +u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE |BPF_F_ALLOW_MULTI ));struct list_head *progs = &cgrp->bpf.progs[type];struct bpf_prog *old_prog =NULL ; struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE],
@@ -298,14 +301,15 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,enum bpf_cgroup_storage_type stype;int err; -if ((flags &BPF_F_ALLOW_OVERRIDE ) && (flags &BPF_F_ALLOW_MULTI )) +if (((flags &BPF_F_ALLOW_OVERRIDE ) && (flags &BPF_F_ALLOW_MULTI )) || + ((flags &BPF_F_REPLACE ) && !(flags &BPF_F_ALLOW_MULTI )))/* invalid combination */ return -EINVAL ;if (!hierarchy_allows_attach (cgrp, type))return -EPERM ; -if (!list_empty (progs) && cgrp->bpf.flags[type] != flags) +if (!list_empty (progs) && cgrp->bpf.flags[type] != saved_flags)/* Disallow attaching non-overridable on top * of existing overridable in this cgroup. * Disallow attaching multi-prog if overridable or none
@@ -320,7 +324,12 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,if (pl->prog == prog)/* disallow attaching the same prog twice */ return -EINVAL ; +if (pl->prog == replace_prog) + replace_pl = pl; } +if ((flags &BPF_F_REPLACE ) && !replace_pl) +/* prog to replace not found for cgroup */ +return -ENOENT ; }else if (!list_empty (progs)) { replace_pl =list_first_entry (progs,typeof (*pl), node); }
@@ -356,7 +365,7 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,for_each_cgroup_storage_type (stype) pl->storage[stype] = storage[stype]; - cgrp->bpf.flags[type] = flags; + cgrp->bpf.flags[type] = saved_flags; err =update_effective_progs (cgrp, type);if (err)
@@ -522,6 +531,7 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,int cgroup_bpf_prog_attach (const union bpf_attr *attr,enum bpf_prog_type ptype,struct bpf_prog *prog) { +struct bpf_prog *replace_prog =NULL ;struct cgroup *cgrp;int ret;
@@ -529,8 +539,20 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,if (IS_ERR (cgrp))return PTR_ERR (cgrp); - ret =cgroup_bpf_attach (cgrp, prog, attr->attach_type, +if ((attr->attach_flags &BPF_F_ALLOW_MULTI ) && + (attr->attach_flags &BPF_F_REPLACE )) { + replace_prog =bpf_prog_get_type (attr->replace_bpf_fd, ptype); +if (IS_ERR (replace_prog)) { +cgroup_put (cgrp); +return PTR_ERR (replace_prog); + } + } + + ret =cgroup_bpf_attach (cgrp, prog, replace_prog, attr->attach_type, attr->attach_flags); + +if (replace_prog) +bpf_prog_put (replace_prog);cgroup_put (cgrp);return ret; }
kernel/bpf/syscall.c +2 -2
@@ -2073,10 +2073,10 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, } } -#define BPF_PROG_ATTACH_LAST_FIELD attach_flags +#define BPF_PROG_ATTACH_LAST_FIELD replace_bpf_fd#define BPF_F_ATTACH_MASK \ - (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI) + (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI | BPF_F_REPLACE)static int bpf_prog_attach (const union bpf_attr *attr) {
kernel/cgroup/cgroup.c +3 -2
@@ -6288,12 +6288,13 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)#ifdef CONFIG_CGROUP_BPF int cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *prog, -enum bpf_attach_type type,u32 flags) +struct bpf_prog *replace_prog,enum bpf_attach_type type, +u32 flags) {int ret;mutex_lock (&cgroup_mutex); - ret =__cgroup_bpf_attach (cgrp, prog, type, flags); + ret =__cgroup_bpf_attach (cgrp, prog, replace_prog, type, flags);mutex_unlock (&cgroup_mutex);return ret; }
tools/include/uapi/linux/bpf.h +10 -0
@@ -231,6 +231,11 @@ enum bpf_attach_type {* When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * +* With BPF_F_ALLOW_MULTI a new program is added to the end of the list of +* programs for a cgroup. Though it's possible to replace an old program at +* any position by also specifying BPF_F_REPLACE flag and position itself in +* replace_bpf_fd attribute. Old program at this position will be released. +* * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1:
@@ -249,6 +254,7 @@ enum bpf_attach_type {*/ #define BPF_F_ALLOW_OVERRIDE (1U << 0)#define BPF_F_ALLOW_MULTI (1U << 1) +#define BPF_F_REPLACE (1U << 2)/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel
@@ -442,6 +448,10 @@ union bpf_attr {__u32 attach_bpf_fd;/* eBPF program to attach */ __u32 attach_type;__u32 attach_flags; +__u32 replace_bpf_fd;/* previously attached eBPF +* program to replace if +* BPF_F_REPLACE is used +*/ };struct {/* anonymous struct used by BPF_PROG_TEST_RUN command */
Signature · 1 line
2.17.1
PATCH v3 4/6 libbpf: Introduce bpf_prog_attach_xattr
In reply to Andrey IgnatovIntroduce a new bpf_prog_attach_xattr function that, in addition to program fd, target fd and attach type, accepts an extendable struct bpf_prog_attach_opts.
bpf_prog_attach_opts relies on DECLARE_LIBBPF_OPTS macro to maintain backward and forward compatibility and has the following "optional" attach attributes:
* existing attach_flags, since it's not required when attaching in NONE mode. Even though it's quite often used in MULTI and OVERRIDE mode it seems to be a good idea to reduce number of arguments to bpf_prog_attach_xattr;
* newly introduced attribute of BPF_PROG_ATTACH command: replace_prog_fd that is fd of previously attached cgroup-bpf program to replace if BPF_F_REPLACE flag is used.
The new function is named to be consistent with other xattr-functions (bpf_prog_test_run_xattr, bpf_create_map_xattr, bpf_load_program_xattr).
The struct bpf_prog_attach_opts is supposed to be used with DECLARE_LIBBPF_OPTS macro.
--- tools/lib/bpf/bpf.c | 16 +++++++++++++++- tools/lib/bpf/bpf.h | 11 +++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 27 insertions(+), 1 deletion(-)
tools/lib/bpf/bpf.c +15 -1
@@ -466,6 +466,17 @@ int bpf_obj_get(const char *pathname)int bpf_prog_attach (int prog_fd,int target_fd,enum bpf_attach_type type,unsigned int flags) +{ +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, opts, + .flags = flags, + ); + +return bpf_prog_attach_xattr (prog_fd, target_fd, type, &opts); +} + +int bpf_prog_attach_xattr (int prog_fd,int target_fd, +enum bpf_attach_type type, +const struct bpf_prog_attach_opts *opts) {union bpf_attr attr;
@@ -473,7 +484,10 @@ int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, attr.target_fd = target_fd; attr.attach_bpf_fd = prog_fd; attr.attach_type = type; - attr.attach_flags = flags; +if (opts) { + attr.attach_flags = opts->flags; + attr.replace_bpf_fd = opts->replace_prog_fd; + }return sys_bpf (BPF_PROG_ATTACH , &attr,sizeof (attr)); }
tools/lib/bpf/bpf.h +11 -0
@@ -126,8 +126,19 @@ LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key); LIBBPF_API int bpf_map_freeze(int fd); LIBBPF_API int bpf_obj_pin(int fd, const char *pathname); LIBBPF_API int bpf_obj_get(const char *pathname); + +struct bpf_prog_attach_opts { +size_t sz;/* size of this struct for forward/backward compatibility */ +unsigned int flags; +int replace_prog_fd; +}; +#define bpf_prog_attach_opts__last_field replace_prog_fd + LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd,enum bpf_attach_type type,unsigned int flags); +LIBBPF_API int bpf_prog_attach_xattr(int prog_fd, int attachable_fd, +enum bpf_attach_type type, +const struct bpf_prog_attach_opts *opts); LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd,enum bpf_attach_type type);
tools/lib/bpf/libbpf.map +1 -0
@@ -219,6 +219,7 @@ LIBBPF_0.0.7 { bpf_object__detach_skeleton; bpf_object__load_skeleton; bpf_object__open_skeleton; + bpf_prog_attach_xattr; bpf_program__attach; bpf_program__name; btf__align_of;
Signature · 1 line
2.17.1
Re: [PATCH v3 bpf-next 4/6] libbpf: Introduce bpf_prog_attach_xattr
Quoted 43 lines · Andrey Ignatov
Introduce a new
bpf_prog_attach_xattrfunction that, in addition to program fd, target fd and attach type, accepts an extendable structbpf_prog_attach_opts.
bpf_prog_attach_optsrelies onDECLARE_LIBBPF_OPTSmacro to maintain backward and forward compatibility and has the following "optional" attach attributes:* existing
attach_flags, since it's not required when attaching in NONE mode. Even though it's quite often used in MULTI and OVERRIDE mode it seems to be a good idea to reduce number of arguments tobpf_prog_attach_xattr;* newly introduced attribute of
BPF_PROG_ATTACHcommand:replace_prog_fdthat is fd of previously attached cgroup-bpf program to replace ifBPF_F_REPLACEflag is used.The new function is named to be consistent with other xattr-functions (
bpf_prog_test_run_xattr,bpf_create_map_xattr,bpf_load_program_xattr).The struct
bpf_prog_attach_optsis supposed to be used withDECLARE_LIBBPF_OPTSmacro.Signed-off-by Andrey Ignatov <[email protected]>--- tools/lib/bpf/bpf.c | 16 +++++++++++++++- tools/lib/bpf/bpf.h | 11 +++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 27 insertions(+), 1 deletion(-)tools/lib/bpf/bpf.c +4
-0@@ -466,6 +466,17 @@ int bpf_obj_get(const char *pathname)int bpf_prog_attach (int prog_fd,int target_fd,enum bpf_attach_type type,unsigned int flags) +{ +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, opts, + .flags = flags, + );
tools/lib/bpf/bpf.c int bpf_obj_get(const char *pathname) · line 482
Quoted 10 lines
tools/lib/bpf/bpf.c +7
-0+ +return bpf_prog_attach_xattr (prog_fd, target_fd, type, &opts); +} + +int bpf_prog_attach_xattr (int prog_fd,int target_fd, +enum bpf_attach_type type, +const struct bpf_prog_attach_opts *opts) {union bpf_attr attr;
You need to validate opts with OPTS_VALID macro (see btf_dump__emit_type_decl() for simple example).
tools/lib/bpf/bpf.c int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, · line 490
Quoted 9 lines
tools/lib/bpf/bpf.c +4
-1@@ -473,7 +484,10 @@ int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, attr.target_fd = target_fd; attr.attach_bpf_fd = prog_fd; attr.attach_type = type; - attr.attach_flags = flags; +if (opts) { + attr.attach_flags = opts->flags; + attr.replace_bpf_fd = opts->replace_prog_fd; + }
Please use OPTS_GET() macro to fetch values from opts struct and provide default value if they are not specified.
Quoted 3 lines
tools/lib/bpf/bpf.c +0
-0return sys_bpf (BPF_PROG_ATTACH , &attr,sizeof (attr)); }
[...]
Re: [PATCH v3 bpf-next 4/6] libbpf: Introduce bpf_prog_attach_xattr
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:50 -0800]:
Quoted 58 lines · Andrey Ignatov
Quoted 43 lines
Introduce a new
bpf_prog_attach_xattrfunction that, in addition to program fd, target fd and attach type, accepts an extendable structbpf_prog_attach_opts.
bpf_prog_attach_optsrelies onDECLARE_LIBBPF_OPTSmacro to maintain backward and forward compatibility and has the following "optional" attach attributes:* existing
attach_flags, since it's not required when attaching in NONE mode. Even though it's quite often used in MULTI and OVERRIDE mode it seems to be a good idea to reduce number of arguments tobpf_prog_attach_xattr;* newly introduced attribute of
BPF_PROG_ATTACHcommand:replace_prog_fdthat is fd of previously attached cgroup-bpf program to replace ifBPF_F_REPLACEflag is used.The new function is named to be consistent with other xattr-functions (
bpf_prog_test_run_xattr,bpf_create_map_xattr,bpf_load_program_xattr).The struct
bpf_prog_attach_optsis supposed to be used withDECLARE_LIBBPF_OPTSmacro.Signed-off-by Andrey Ignatov <[email protected]>--- tools/lib/bpf/bpf.c | 16 +++++++++++++++- tools/lib/bpf/bpf.h | 11 +++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 27 insertions(+), 1 deletion(-)tools/lib/bpf/bpf.c +4
-0@@ -466,6 +466,17 @@ int bpf_obj_get(const char *pathname)int bpf_prog_attach (int prog_fd,int target_fd,enum bpf_attach_type type,unsigned int flags) +{ +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, opts, + .flags = flags, + );tools/lib/bpf/bpf.c int bpf_obj_get(const char *pathname) · line 482
Quoted 10 lines
tools/lib/bpf/bpf.c +7
-0+ +return bpf_prog_attach_xattr (prog_fd, target_fd, type, &opts); +} + +int bpf_prog_attach_xattr (int prog_fd,int target_fd, +enum bpf_attach_type type, +const struct bpf_prog_attach_opts *opts) {union bpf_attr attr;You need to validate opts with
OPTS_VALIDmacro (seebtf_dump__emit_type_decl()for simple example).
Quoted 12 lines
tools/lib/bpf/bpf.c int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, · line 490
Quoted 9 lines
tools/lib/bpf/bpf.c +4
-1@@ -473,7 +484,10 @@ int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, attr.target_fd = target_fd; attr.attach_bpf_fd = prog_fd; attr.attach_type = type; - attr.attach_flags = flags; +if (opts) { + attr.attach_flags = opts->flags; + attr.replace_bpf_fd = opts->replace_prog_fd; + }Please use
OPTS_GET()macro to fetch values from opts struct and provide default value if they are not specified.
OK, will do both OPTS_VALID and OPTS_GET in v4.
Quoted 7 lines
Quoted 5 lines
tools/lib/bpf/bpf.c +0
-0return sys_bpf (BPF_PROG_ATTACH , &attr,sizeof (attr)); }[...]
Signature · 1 line
Andrey Ignatov
PATCH v3 5/6 selftests/bpf: Convert test_cgroup_attach to prog_tests
In reply to Andrey IgnatovConvert test_cgroup_attach to prog_tests.
This change does a lot of things but in many cases it's pretty expensive to separate them, so they go in one commit. Nevertheless the logic is ketp as is and changes made are just moving things around, simplifying them (w/o changing the meaning of the tests) and making prog_tests compatible:
* split the 3 tests in the file into 3 separate files in prog_tests/;
* rename the test functions to test_<file_base_name>;
* remove unused includes, constants, variables and functions from every test;
* replace if-s with or if (CHECK()) where additional context should be logged and with if (CHECK_FAIL()) where line number is enough;
* switch from log_err() to logging via CHECK();
* replace assert-s with CHECK_FAIL() to avoid crashing the whole test_progs if one assertion fails;
* replace cgroup_helpers with test__join_cgroup() in cgroup_attach_override only, other tests need more fine-grained control for cgroup creation/deletion so cgroup_helpers are still used there;
* simplify cgroup_attach_autodetach by switching to easiest possible program since this test doesn't really need such a complicated program as cgroup_attach_multi does;
* remove test_cgroup_attach.c itself.
--- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 238 ++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 6 files changed, 498 insertions(+), 574 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c
tools/testing/selftests/bpf/.gitignore +0 -1
@@ -21,7 +21,6 @@ test_lirc_mode2_user get_cgroup_id_user test_skb_cgroup_id_user test_socket_cookie -test_cgroup_attach test_cgroup_storage test_select_reuseport test_flow_dissector
tools/testing/selftests/bpf/Makefile +1 -2
@@ -32,7 +32,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \ test_cgroup_storage \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_cgroup_attach test_progs-no_alu32 + test_progs-no_alu32 # Also test bpf-gcc, if present ifneq ($(BPF_GCC),)
@@ -136,7 +136,6 @@ $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c $(OUTPUT)/test_netcnt: cgroup_helpers.c $(OUTPUT)/test_sock_fields: cgroup_helpers.c $(OUTPUT)/test_sysctl: cgroup_helpers.c -$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c .PHONY: force
tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c new file +111 -0
111 lines not on this page · raw message
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +238 -0
238 lines not on this page · raw message
tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c new file +148 -0
148 lines not on this page · raw message
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0 -571
571 lines not on this page · raw message
Signature · 1 line
2.17.1
Re: [PATCH v3 bpf-next 5/6] selftests/bpf: Convert test_cgroup_attach to prog_tests
Quoted 49 lines · Andrey Ignatov
Convert
test_cgroup_attachtoprog_tests.This change does a lot of things but in many cases it's pretty expensive to separate them, so they go in one commit. Nevertheless the logic is ketp as is and changes made are just moving things around, simplifying them (w/o changing the meaning of the tests) and making
prog_testscompatible:* split the 3 tests in the file into 3 separate files in prog_tests/;
* rename the test functions to test_<file_base_name>;
* remove unused includes, constants, variables and functions from every test;
* replace
if-s with orif (CHECK())where additional context should be logged and withif (CHECK_FAIL())where line number is enough;* switch from
log_err()to logging viaCHECK();* replace
assert-s withCHECK_FAIL()to avoid crashing the wholetest_progsif one assertion fails;* replace
cgroup_helperswithtest__join_cgroup()incgroup_attach_overrideonly, other tests need more fine-grained control for cgroup creation/deletion socgroup_helpersare still used there;* simplify
cgroup_attach_autodetachby switching to easiest possible program since this test doesn't really need such a complicated program ascgroup_attach_multidoes;* remove
test_cgroup_attach.citself.Signed-off-by Andrey Ignatov <[email protected]>--- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 238 ++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 6 files changed, 498 insertions(+), 574 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c
[...]
Quoted 46 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +46
-0+ +if (CHECK_FAIL (setup_cgroup_environment ())) + goto err; + + cg1 =create_and_get_cgroup ("/cg1" ); +if (CHECK_FAIL (cg1 <0 )) + goto err; + cg2 =create_and_get_cgroup ("/cg1/cg2" ); +if (CHECK_FAIL (cg2 <0 )) + goto err; + cg3 =create_and_get_cgroup ("/cg1/cg2/cg3" ); +if (CHECK_FAIL (cg3 <0 )) + goto err; + cg4 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4" ); +if (CHECK_FAIL (cg4 <0 )) + goto err; + cg5 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4/cg5" ); +if (CHECK_FAIL (cg5 <0 )) + goto err; + +if (CHECK_FAIL (join_cgroup ("/cg1/cg2/cg3/cg4/cg5" ))) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog0_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (!bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"fail_same_prog_attach_to_cg1" ,"unexpected success\n" )) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[1 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog1_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[2 ], cg2,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_OVERRIDE ), +"prog2_attach_to_cg2_override" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[3 ], cg3,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog3_attach_to_cg3_multi" ,"errno=%d\n" , errno))
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c line 147
Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +10
-0+ goto err; + +if (CHECK (bpf_prog_attach (allow_prog[4 ], cg4,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_OVERRIDE ), +"prog4_attach_to_cg4_override" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[5 ], cg5,BPF_CGROUP_INET_EGRESS ,0 ), +"prog5_attach_to_cg5_none" ,"errno=%d\n" , errno)) + goto err;
nit: this looks like a good candidate for a loop...
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +5
-0+ +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=1 +2 +8 +32 ); +
[...]
tools/testing/selftests/bpf/test_cgroup_attach.c line 563
Quoted 13 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-13-int main (void ) -{ -int (*tests[])(void ) = { - test_foo_bar, - test_multiprog, - test_autodetach, - }; -int errors =0 ; -int i; - -for (i =0 ; i <ARRAY_SIZE (tests); i++) -if (tests[i]()) - errors++;
Depending on what you think is better structure (I couldn't follow through entire test logic...), you could have done this as a single test with 3 subtests. Search for test__start_subtest(). If that saves you some duplication, I think it's worth converting (which will be 1-to-1 with original structure).
But either way thanks a lot for doing the convertion, brings as another step closer to more uniform and consolidated testing infra.
Quoted 11 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-9- -if (errors) -printf ("test_cgroup_attach:FAIL\n" ); -else -printf ("test_cgroup_attach:PASS\n" ); - -return errors ?EXIT_FAILURE :EXIT_SUCCESS ; -} --2.17.1
Re: [PATCH v3 bpf-next 5/6] selftests/bpf: Convert test_cgroup_attach to prog_tests
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 22:11 -0800]:
Quoted 103 lines · Andrey Ignatov
Quoted 49 lines
Convert
test_cgroup_attachtoprog_tests.This change does a lot of things but in many cases it's pretty expensive to separate them, so they go in one commit. Nevertheless the logic is ketp as is and changes made are just moving things around, simplifying them (w/o changing the meaning of the tests) and making
prog_testscompatible:* split the 3 tests in the file into 3 separate files in prog_tests/;
* rename the test functions to test_<file_base_name>;
* remove unused includes, constants, variables and functions from every test;
* replace
if-s with orif (CHECK())where additional context should be logged and withif (CHECK_FAIL())where line number is enough;* switch from
log_err()to logging viaCHECK();* replace
assert-s withCHECK_FAIL()to avoid crashing the wholetest_progsif one assertion fails;* replace
cgroup_helperswithtest__join_cgroup()incgroup_attach_overrideonly, other tests need more fine-grained control for cgroup creation/deletion socgroup_helpersare still used there;* simplify
cgroup_attach_autodetachby switching to easiest possible program since this test doesn't really need such a complicated program ascgroup_attach_multidoes;* remove
test_cgroup_attach.citself.Signed-off-by Andrey Ignatov <[email protected]>--- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 238 ++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 6 files changed, 498 insertions(+), 574 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c[...]
Quoted 50 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +50
-0+ +if (CHECK_FAIL (setup_cgroup_environment ())) + goto err; + + cg1 =create_and_get_cgroup ("/cg1" ); +if (CHECK_FAIL (cg1 <0 )) + goto err; + cg2 =create_and_get_cgroup ("/cg1/cg2" ); +if (CHECK_FAIL (cg2 <0 )) + goto err; + cg3 =create_and_get_cgroup ("/cg1/cg2/cg3" ); +if (CHECK_FAIL (cg3 <0 )) + goto err; + cg4 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4" ); +if (CHECK_FAIL (cg4 <0 )) + goto err; + cg5 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4/cg5" ); +if (CHECK_FAIL (cg5 <0 )) + goto err; + +if (CHECK_FAIL (join_cgroup ("/cg1/cg2/cg3/cg4/cg5" ))) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog0_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (!bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"fail_same_prog_attach_to_cg1" ,"unexpected success\n" )) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[1 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog1_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[2 ], cg2,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_OVERRIDE ), +"prog2_attach_to_cg2_override" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[3 ], cg3,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog3_attach_to_cg3_multi" ,"errno=%d\n" , errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE),
Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c line 147
Quoted 6 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +6
-0+ "prog4_attach_to_cg4_override", "errno=%d\n", errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[5 ], cg5,BPF_CGROUP_INET_EGRESS ,0 ), +"prog5_attach_to_cg5_none" ,"errno=%d\n" , errno)) + goto err;nit: this looks like a good candidate for a loop...
These tests can benefit from a lot of cleanup steps and yeah, this can be one of them. I'd prefer to deal with it separately though since it shouldn't be a blocker for this patch set. That's why I preserved this logic with just adding checks.
Quoted 18 lines
Quoted 4 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +4
-0+CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=1 +2 +8 +32 ); +[...]
Quoted 11 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-11-int main (void ) -{ -int (*tests[])(void ) = { - test_foo_bar, - test_multiprog, - test_autodetach, - }; -int errors =0 ; -int i; - -for (i =0 ; i <ARRAY_SIZE (tests); i++)
Quoted 8 lines
tools/testing/selftests/bpf/test_cgroup_attach.c line 563
Quoted 2 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-2-if (tests[i]()) - errors++;Depending on what you think is better structure (I couldn't follow through entire test logic...), you could have done this as a single test with 3 subtests. Search for
test__start_subtest(). If that saves you some duplication, I think it's worth converting (which will be 1-to-1 with original structure).
Yeah, I saw test__start_subtest() and checked if it would fit here. IMO it wouldn't since these tests don't have much in common and can't be converted to, say, parametrized tests that differ only in input that can be easily described by a struct. They do cover parts of same feature (cgroup attach) but focus on different parts of it. That's why separate files. This actually helped to identify and remove a bunch of junk from each test (like simplifying testing prog in autodetach).
Quoted 14 lines
But either way thanks a lot for doing the convertion, brings as another step closer to more uniform and consolidated testing infra.
Quoted 11 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-9- -if (errors) -printf ("test_cgroup_attach:FAIL\n" ); -else -printf ("test_cgroup_attach:PASS\n" ); - -return errors ?EXIT_FAILURE :EXIT_SUCCESS ; -} --2.17.1
Signature · 1 line
Andrey Ignatov
Andrii Nakryiko
Acked-byRe: [PATCH v3 bpf-next 5/6] selftests/bpf: Convert test_cgroup_attach to prog_tests
Quoted 114 lines · Andrey Ignatov
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 22:11 -0800]:
Quoted 103 lines · Andrey Ignatov
Quoted 49 lines
Convert
test_cgroup_attachtoprog_tests.This change does a lot of things but in many cases it's pretty expensive to separate them, so they go in one commit. Nevertheless the logic is ketp as is and changes made are just moving things around, simplifying them (w/o changing the meaning of the tests) and making
prog_testscompatible:* split the 3 tests in the file into 3 separate files in prog_tests/;
* rename the test functions to test_<file_base_name>;
* remove unused includes, constants, variables and functions from every test;
* replace
if-s with orif (CHECK())where additional context should be logged and withif (CHECK_FAIL())where line number is enough;* switch from
log_err()to logging viaCHECK();* replace
assert-s withCHECK_FAIL()to avoid crashing the wholetest_progsif one assertion fails;* replace
cgroup_helperswithtest__join_cgroup()incgroup_attach_overrideonly, other tests need more fine-grained control for cgroup creation/deletion socgroup_helpersare still used there;* simplify
cgroup_attach_autodetachby switching to easiest possible program since this test doesn't really need such a complicated program ascgroup_attach_multidoes;* remove
test_cgroup_attach.citself.Signed-off-by Andrey Ignatov <[email protected]>--- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 238 ++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 6 files changed, 498 insertions(+), 574 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c[...]
Quoted 50 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +50
-0+ +if (CHECK_FAIL (setup_cgroup_environment ())) + goto err; + + cg1 =create_and_get_cgroup ("/cg1" ); +if (CHECK_FAIL (cg1 <0 )) + goto err; + cg2 =create_and_get_cgroup ("/cg1/cg2" ); +if (CHECK_FAIL (cg2 <0 )) + goto err; + cg3 =create_and_get_cgroup ("/cg1/cg2/cg3" ); +if (CHECK_FAIL (cg3 <0 )) + goto err; + cg4 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4" ); +if (CHECK_FAIL (cg4 <0 )) + goto err; + cg5 =create_and_get_cgroup ("/cg1/cg2/cg3/cg4/cg5" ); +if (CHECK_FAIL (cg5 <0 )) + goto err; + +if (CHECK_FAIL (join_cgroup ("/cg1/cg2/cg3/cg4/cg5" ))) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog0_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (!bpf_prog_attach (allow_prog[0 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"fail_same_prog_attach_to_cg1" ,"unexpected success\n" )) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[1 ], cg1,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog1_attach_to_cg1_multi" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[2 ], cg2,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_OVERRIDE ), +"prog2_attach_to_cg2_override" ,"errno=%d\n" , errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[3 ], cg3,BPF_CGROUP_INET_EGRESS , +BPF_F_ALLOW_MULTI ), +"prog3_attach_to_cg3_multi" ,"errno=%d\n" , errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE),Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c line 147
Quoted 6 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +6
-0+ "prog4_attach_to_cg4_override", "errno=%d\n", errno)) + goto err; + +if (CHECK (bpf_prog_attach (allow_prog[5 ], cg5,BPF_CGROUP_INET_EGRESS ,0 ), +"prog5_attach_to_cg5_none" ,"errno=%d\n" , errno)) + goto err;nit: this looks like a good candidate for a loop...
Quoted 5 lines
These tests can benefit from a lot of cleanup steps and yeah, this can be one of them. I'd prefer to deal with it separately though since it shouldn't be a blocker for this patch set. That's why I preserved this logic with just adding checks.
Sure, no problem.
Quoted 28 lines
Quoted 19 lines
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file +4
-0+CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=1 +2 +8 +32 ); +[...]
Quoted 11 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-11-int main (void ) -{ -int (*tests[])(void ) = { - test_foo_bar, - test_multiprog, - test_autodetach, - }; -int errors =0 ; -int i; - -for (i =0 ; i <ARRAY_SIZE (tests); i++)Quoted 8 lines
tools/testing/selftests/bpf/test_cgroup_attach.c line 563
Quoted 2 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-2-if (tests[i]()) - errors++;Depending on what you think is better structure (I couldn't follow through entire test logic...), you could have done this as a single test with 3 subtests. Search for
test__start_subtest(). If that saves you some duplication, I think it's worth converting (which will be 1-to-1 with original structure).
Quoted 7 lines
Yeah, I saw
test__start_subtest()and checked if it would fit here. IMO it wouldn't since these tests don't have much in common and can't be converted to, say, parametrized tests that differ only in input that can be easily described by a struct. They do cover parts of same feature (cgroup attach) but focus on different parts of it. That's why separate files. This actually helped to identify and remove a bunch of junk from each test (like simplifying testing prog in autodetach).
OK, I don't insist, just wasn't sure if you are aware of subtests facility.
Quoted 19 lines
Quoted 5 lines
But either way thanks a lot for doing the convertion, brings as another step closer to more uniform and consolidated testing infra.
Quoted 11 lines
Quoted 11 lines
tools/testing/selftests/bpf/test_cgroup_attach.c deleted +0
-9- -if (errors) -printf ("test_cgroup_attach:FAIL\n" ); -else -printf ("test_cgroup_attach:PASS\n" ); - -return errors ?EXIT_FAILURE :EXIT_SUCCESS ; -} --2.17.1
--
Andrey Ignatov
PATCH v3 6/6 selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
In reply to Andrey IgnatovTest replacing a cgroup-bpf program attached with BPF_F_ALLOW_MULTI and possible failure modes: invalid combination of flags, invalid replace_bpf_fd, replacing a non-attachd to specified cgroup program.
Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.
Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multi
--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +50 -3
@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;
@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 ); +/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))
@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),
@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 ); prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS,
Signature · 1 line
2.17.1
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Quoted 51 lines · Andrey Ignatov
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );
CHECK macro above can prints both in success and failure scenarios, which means that errno of bpf_prog_attach_xattr can be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.
Quoted 61 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-3+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 ); prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:59 -0800]:
Quoted 53 lines · Andrey Ignatov
Quoted 52 lines
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );
Quoted 14 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );CHECK macro above can prints both in success and failure scenarios, which means that errno of
bpf_prog_attach_xattrcan be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.
If bpf_prog_attach_xattr finishes successfully (what is unexpected here), goto err will be taken and CHECK_FAIL(errno != EINVAL) won't be run at all so "success" case is not a problem.
If bpf_prog_attach_xattr fails (what is expected) it has to set errno and this is the errno that will be checked by CHECK_FAIL, i.e. failure case is not a problem at all.
If you mean printf() that is called from "PASS" branch of CHECK then I don't actually see a way to distinguish errno from failed bpf_prog_attach_xattr (what would mean "PASS" for the CHECK) and printf() from the CHECK() w/o changing CHECK() itself.
I think CHECK() can be improved wrt errno so that it saves errno before calling anything that can affect it and restore it afterwards. But this is not specific to this patch so IMO it should be done separately with, ideally, checking that it doesn't break some other tests.
Quoted 61 lines
Quoted 61 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-3+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 ); prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
Signature · 1 line
Andrey Ignatov
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Quoted 74 lines · Andrey Ignatov
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:59 -0800]:
Quoted 53 lines · Andrey Ignatov
Quoted 52 lines
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );Quoted 14 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );CHECK macro above can prints both in success and failure scenarios, which means that errno of
bpf_prog_attach_xattrcan be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.If
bpf_prog_attach_xattrfinishes successfully (what is unexpected here),goto errwill be taken andCHECK_FAIL(errno != EINVAL)won't be run at all so "success" case is not a problem.
Quoted 8 lines
If
bpf_prog_attach_xattrfails (what is expected) it has to set errno and this is the errno that will be checked byCHECK_FAIL, i.e. failure case is not a problem at all.If you mean
printf()that is called from "PASS" branch of CHECK then I don't actually see a way to distinguish errno from failedbpf_prog_attach_xattr(what would mean "PASS" for the CHECK) andprintf()from theCHECK()w/o changingCHECK()itself.
well, of course you can do that:
err = bpf_xxx(...);saved_errno = errno;
if (CHECK(!err, ...))
goto handle_err;
if (CHECK_FAIL(saved_errno)) { ... }
It's just more cumbersome, but nothing impossible.
Quoted 5 lines
I think
CHECK()can be improved wrt errno so that it saves errno before calling anything that can affect it and restore it afterwards. But this is not specific to this patch so IMO it should be done separately with, ideally, checking that it doesn't break some other tests.
That might be a way to do this, of course. We can add that later. I think that errno for error code is quite inconvenient, I wonder if it might be possible to retrofit kernel-style error code as a result instead. That's what we do for high-level libbpf API, it might be too late for low-level one though.
Quoted 65 lines
Quoted 57 lines
Quoted 57 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-2+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 );Quoted 5 lines
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +0
-1prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
--
Andrey Ignatov
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Andrii Nakryiko <[email protected]> [Thu, 2019-12-19 12:44 -0800]:
Quoted 86 lines · Andrey Ignatov
Quoted 74 lines
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:59 -0800]:
Quoted 53 lines · Andrey Ignatov
Quoted 52 lines
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );Quoted 14 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );CHECK macro above can prints both in success and failure scenarios, which means that errno of
bpf_prog_attach_xattrcan be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.If
bpf_prog_attach_xattrfinishes successfully (what is unexpected here),goto errwill be taken andCHECK_FAIL(errno != EINVAL)won't be run at all so "success" case is not a problem.Quoted 8 lines
If
bpf_prog_attach_xattrfails (what is expected) it has to set errno and this is the errno that will be checked byCHECK_FAIL, i.e. failure case is not a problem at all.If you mean
printf()that is called from "PASS" branch of CHECK then I don't actually see a way to distinguish errno from failedbpf_prog_attach_xattr(what would mean "PASS" for the CHECK) andprintf()from theCHECK()w/o changingCHECK()itself.well, of course you can do that:
Quoted 7 lines
err =
bpf_xxx(...);saved_errno= errno;
if (CHECK(!err, ...))goto handle_err;if (
CHECK_FAIL(saved_errno)) { ... }It's just more cumbersome, but nothing impossible.
Oh, yeah, that would work, but I agree that it doesn't look great :)
Quoted 6 lines
Quoted 4 lines
I think
CHECK()can be improved wrt errno so that it saves errno before calling anything that can affect it and restore it afterwards. But this is not specific to this patch so IMO it should be done separately with, ideally, checking that it doesn't break some other tests.That might be a way to do this, of course. We can add that later.
Sounds good. I can follow-up on the CHECK() change separately so that every test that does similar checks doesn't have to have additional code to deal with errno.
Quoted 5 lines
I
think that errno for error code is quite inconvenient, I wonder if it might be possible to retrofit kernel-style error code as a result instead. That's what we do for high-level libbpf API, it might be too late for low-level one though.
There are a bunch of places that actually do it in bpf.c, e.g. bpf_prog_test_run_xattr() and bpf_load_program_xattr() return -EINVAL for error conditions unrelated to sys_bpf. I also added similar thing in patch 4 (libbpf part) for !OPTS_VALID scenario.
But for vast majority of cases where wrappers simply return whatever sys_bpf returned IMO it makes sense to preserve the semantics similar to that of sys_bpf since it's just thin wrappers. Again, just my opinion.
Quoted 64 lines
Quoted 64 lines
Quoted 56 lines
Quoted 56 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-2+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 );Quoted 5 lines
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +0
-1prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
--
Andrey Ignatov
Signature · 1 line
Andrey Ignatov
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Quoted 103 lines · Andrey Ignatov
Andrii Nakryiko <[email protected]> [Thu, 2019-12-19 12:44 -0800]:
Quoted 86 lines · Andrey Ignatov
Quoted 74 lines
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:59 -0800]:
Quoted 53 lines · Andrey Ignatov
Quoted 52 lines
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );Quoted 14 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );CHECK macro above can prints both in success and failure scenarios, which means that errno of
bpf_prog_attach_xattrcan be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.If
bpf_prog_attach_xattrfinishes successfully (what is unexpected here),goto errwill be taken andCHECK_FAIL(errno != EINVAL)won't be run at all so "success" case is not a problem.Quoted 8 lines
If
bpf_prog_attach_xattrfails (what is expected) it has to set errno and this is the errno that will be checked byCHECK_FAIL, i.e. failure case is not a problem at all.If you mean
printf()that is called from "PASS" branch of CHECK then I don't actually see a way to distinguish errno from failedbpf_prog_attach_xattr(what would mean "PASS" for the CHECK) andprintf()from theCHECK()w/o changingCHECK()itself.well, of course you can do that:
Quoted 7 lines
err =
bpf_xxx(...);saved_errno= errno;
if (CHECK(!err, ...))goto handle_err;if (
CHECK_FAIL(saved_errno)) { ... }It's just more cumbersome, but nothing impossible.
Oh, yeah, that would work, but I agree that it doesn't look great :)
Quoted 5 lines
Quoted 4 lines
I think
CHECK()can be improved wrt errno so that it saves errno before calling anything that can affect it and restore it afterwards. But this is not specific to this patch so IMO it should be done separately with, ideally, checking that it doesn't break some other tests.
Quoted 5 lines
Quoted 1 line
That might be a way to do this, of course. We can add that later.
Sounds good. I can follow-up on the
CHECK()change separately so that every test that does similar checks doesn't have to have additional code to deal with errno.
great
Quoted 7 lines
Quoted 6 lines
I
think that errno for error code is quite inconvenient, I wonder if it might be possible to retrofit kernel-style error code as a result instead. That's what we do for high-level libbpf API, it might be too late for low-level one though.
Quoted 9 lines
There are a bunch of places that actually do it in
bpf.c, e.g.bpf_prog_test_run_xattr()andbpf_load_program_xattr()return-EINVALfor error conditions unrelated tosys_bpf. I also added similar thing in patch 4 (libbpf part) for!OPTS_VALIDscenario.But for vast majority of cases where wrappers simply return whatever
sys_bpfreturned IMO it makes sense to preserve the semantics similar to that ofsys_bpfsince it's just thin wrappers. Again, just my opinion.
can we have both? keep setting errno, but return -errno as a result? If it's already a hybrid approach we are using, I don't think this will break anything.
Quoted 68 lines
Quoted 57 lines
Quoted 57 lines
Quoted 57 lines
Quoted 57 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-2+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 );Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +0
-1prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
--
Andrey Ignatov--
Andrey Ignatov
Re: [PATCH v3 bpf-next 6/6] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi
Andrii Nakryiko <[email protected]> [Thu, 2019-12-19 13:19 -0800]:
Quoted 124 lines · Andrey Ignatov
Quoted 103 lines
Andrii Nakryiko <[email protected]> [Thu, 2019-12-19 12:44 -0800]:
Quoted 86 lines · Andrey Ignatov
Quoted 74 lines
Andrii Nakryiko <[email protected]> [Wed, 2019-12-18 21:59 -0800]:
Quoted 53 lines · Andrey Ignatov
Quoted 52 lines
Test replacing a cgroup-bpf program attached with
BPF_F_ALLOW_MULTIand possible failure modes: invalid combination of flags, invalidreplace_bpf_fd, replacing a non-attachd to specified cgroup program.Example of program replacing:
# gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing.Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multiSigned-off-by Andrey Ignatov <[email protected]>--- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-)tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +2
-1@@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) {__u32 prog_ids[4 ], prog_cnt =0 , attach_flags, saved_prog_id;int cg1 =0 , cg2 =0 , cg3 =0 , cg4 =0 , cg5 =0 , key =0 ; -int allow_prog[6 ] = {-1 }; +DECLARE_LIBBPF_OPTS (bpf_prog_attach_opts, attach_opts); +int allow_prog[7 ] = {-1 };unsigned long long value;__u32 duration =0 ;int i =0 ;@@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value));CHECK_FAIL (value !=1 +2 +8 +16 );Quoted 14 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c void test_cgroup_attach_multi(void) · line 201
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +9
-0+/* test replace */ + + attach_opts.flags =BPF_F_ALLOW_OVERRIDE |BPF_F_REPLACE ; + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_override" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL );CHECK macro above can prints both in success and failure scenarios, which means that errno of
bpf_prog_attach_xattrcan be overriden by a bunch of other functions. So if this check is critical, you'd have to remember errno before calling CHECK. Same for all the check below.If
bpf_prog_attach_xattrfinishes successfully (what is unexpected here),goto errwill be taken andCHECK_FAIL(errno != EINVAL)won't be run at all so "success" case is not a problem.Quoted 8 lines
If
bpf_prog_attach_xattrfails (what is expected) it has to set errno and this is the errno that will be checked byCHECK_FAIL, i.e. failure case is not a problem at all.If you mean
printf()that is called from "PASS" branch of CHECK then I don't actually see a way to distinguish errno from failedbpf_prog_attach_xattr(what would mean "PASS" for the CHECK) andprintf()from theCHECK()w/o changingCHECK()itself.well, of course you can do that:
Quoted 7 lines
err =
bpf_xxx(...);saved_errno= errno;
if (CHECK(!err, ...))goto handle_err;if (
CHECK_FAIL(saved_errno)) { ... }It's just more cumbersome, but nothing impossible.
Oh, yeah, that would work, but I agree that it doesn't look great :)
Quoted 5 lines
Quoted 4 lines
I think
CHECK()can be improved wrt errno so that it saves errno before calling anything that can affect it and restore it afterwards. But this is not specific to this patch so IMO it should be done separately with, ideally, checking that it doesn't break some other tests.Quoted 5 lines
Quoted 1 line
That might be a way to do this, of course. We can add that later.
Sounds good. I can follow-up on the
CHECK()change separately so that every test that does similar checks doesn't have to have additional code to deal with errno.great
Quoted 12 lines
Quoted 6 lines
I
think that errno for error code is quite inconvenient, I wonder if it might be possible to retrofit kernel-style error code as a result instead. That's what we do for high-level libbpf API, it might be too late for low-level one though.There are a bunch of places that actually do it in
bpf.c, e.g.bpf_prog_test_run_xattr()andbpf_load_program_xattr()return-EINVALfor error conditions unrelated tosys_bpf. I also added similar thing in patch 4 (libbpf part) for!OPTS_VALIDscenario.
Quoted 8 lines
Quoted 4 lines
But for vast majority of cases where wrappers simply return whatever
sys_bpfreturned IMO it makes sense to preserve the semantics similar to that ofsys_bpfsince it's just thin wrappers. Again, just my opinion.can we have both? keep setting errno, but return -errno as a result? If it's already a hybrid approach we are using, I don't think this will break anything.
I'd be hesitant to do it for all existing interfaces in bpf.h, since it's quite common patter to have code like:
if (some_func() == -1)
/* handle_error */
, instead of if (some_func() < 0).
Even git grep ' == -1' tools/testing/selftests/bpf shows a lot of such call sites, so it may break a lot of stuff.
Anyway IMO it's out of the scope of my patch set :)
Quoted 67 lines
Quoted 67 lines
Quoted 56 lines
Quoted 56 lines
Quoted 56 lines
Quoted 56 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +39
-2+ + attach_opts.flags =BPF_F_REPLACE ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_multi" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EINVAL ); + + attach_opts.flags =BPF_F_ALLOW_MULTI |BPF_F_REPLACE ; + attach_opts.replace_prog_fd =-1 ; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_bad_fd" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=EBADF ); + +/* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3 ]; +if (CHECK (!bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"fail_prog_replace_no_ent" ,"unexpected success\n" )) + goto err; +CHECK_FAIL (errno !=ENOENT ); + +/* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0 ]; +if (CHECK (bpf_prog_attach_xattr (allow_prog[6 ], cg1, +BPF_CGROUP_INET_EGRESS , &attach_opts), +"prog_replace" ,"errno=%d\n" , errno)) + goto err; + + value =0 ; +CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 )); +CHECK_FAIL (system (PING_CMD )); +CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); +CHECK_FAIL (value !=64 +2 +8 +16 ); +/* detach 3rd from bottom program and ping again */ if (CHECK (!bpf_prog_detach2 (0 , cg3,BPF_CGROUP_INET_EGRESS ),"fail_prog_detach_from_cg3" ,"unexpected success\n" ))@@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +16 ); +CHECK_FAIL (value !=64 +2 +16 );/* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS),@@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void)CHECK_FAIL (bpf_map_update_elem (map_fd, &key, &value,0 ));CHECK_FAIL (system (PING_CMD ));CHECK_FAIL (bpf_map_lookup_elem (map_fd, &key, &value)); -CHECK_FAIL (value !=1 +2 +4 ); +CHECK_FAIL (value !=64 +2 +4 );Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +0
-1prog_cnt =4 ; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, --2.17.1
--
Andrey Ignatov--
Andrey Ignatov
Signature · 1 line
Andrey Ignatov