bpf: muli prog support for cgroup-bpf
Subsystems (8)
v2 cover bpf: muli prog support for cgroup-bpf
v1->v2:
- fixed accidentally swapped two lines which caused static_key not going to zero
- addressed Martin's feedback and changed prog_query to be consistent with verifier output: return -enospc and fill supplied buffer instead of just returning -enospc when buffer is too small to fit all prog_ids
v1:
cgroup-bpf use cases are getting more advanced and running only one program per cgroup is no longer enough. Therefore introduce support for attaching multiple programs per cgroup and running a set of effective programs.
These patches introduces BPF_F_ALLOW_MULTI flag for BPF_PROG_ATTACH cmd. The default is still NONE and behavior of BPF_F_ALLOW_OVERRIDE flag is unchanged.
The difference between three possible flags for BPF_PROG_ATTACH command:
- NONE(default): No further bpf programs allowed in the subtree.
- BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, the program in this cgroup yields to sub-cgroup program.
- BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, that cgroup program gets run in addition to the program in this cgroup.
Most of the logic is in patch 1. Even when cgroup doesn't have any programs attached its set of effective program can be non-empty. To quickly execute them and avoid penalizing cgroups without any effective programs introduce 'struct bpf_prog_array' which has an optimization for cgroups with zero effective programs.
Patch 2 introduces BPF_PROG_QUERY command for introspection Patch 3 makes verifier more strict for cgroup-bpf program types. Patch 4+ are tests.
More details in individual patches
Alexei Starovoitov (8):
bpf: multi program support for cgroup+bpf
bpf: introduce BPF_PROG_QUERY command
bpf: enforce return code for cgroup-bpf programs
libbpf: introduce bpf_prog_detach2() samples/bpf: add multi-prog cgroup test case
libbpf: sync bpf.h
libbpf: add support for BPF_PROG_QUERY samples/bpf: use bpf_prog_query() interface
include/linux/bpf-cgroup.h | 54 ++- include/linux/bpf.h | 35 ++ include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 55 ++- kernel/bpf/cgroup.c | 513 +++++++++++++++++++++------- kernel/bpf/core.c | 69 ++++ kernel/bpf/syscall.c | 71 +++- kernel/bpf/verifier.c | 40 +++ kernel/cgroup/cgroup.c | 38 ++- samples/bpf/cgroup_helpers.c | 4 +- samples/bpf/test_cgrp2_attach2.c | 224 +++++++++++- tools/include/uapi/linux/bpf.h | 55 ++- tools/lib/bpf/bpf.c | 32 ++ tools/lib/bpf/bpf.h | 4 +- tools/testing/selftests/bpf/test_verifier.c | 72 ++++ 15 files changed, 1086 insertions(+), 182 deletions(-)
Signature · 1 line
2.9.5
PATCH v2 1/8 bpf: multi program support for cgroup+bpf
introduce BPF_F_ALLOW_MULTI flag that can be used to attach multiple bpf programs to a cgroup.
The difference between three possible flags for BPF_PROG_ATTACH command:
- NONE(default): No further bpf programs allowed in the subtree.
- BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, the program in this cgroup yields to sub-cgroup program.
- BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, that cgroup program gets run in addition to the program in this cgroup.
NONE and BPF_F_ALLOW_OVERRIDE existed before. This patch doesn't change their behavior. It only clarifies the semantics in relation to new flag.
Only one program is allowed to be attached to a cgroup with NONE or BPF_F_ALLOW_OVERRIDE flag.
Multiple programs are allowed to be attached to a cgroup with BPF_F_ALLOW_MULTI flag. They are executed in FIFO order (those that were attached first, run first)
The programs of sub-cgroup are executed first, then programs of this cgroup and then programs of parent cgroup.
All eligible programs are executed regardless of return code from earlier programs.
To allow efficient execution of multiple programs attached to a cgroup and to avoid penalizing cgroups without any programs attached introduce 'struct bpf_prog_array' which is RCU protected array of pointers to bpf programs.
for cgroup bits
--- include/linux/bpf-cgroup.h | 46 +++-- include/linux/bpf.h | 32 ++++ include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 42 +++- kernel/bpf/cgroup.c | 467 ++++++++++++++++++++++++++++++++------------- kernel/bpf/core.c | 31 +++ kernel/bpf/syscall.c | 37 ++-- kernel/cgroup/cgroup.c | 28 ++- 8 files changed, 516 insertions(+), 169 deletions(-)
include/linux/bpf-cgroup.h +30 -16
@@ -14,27 +14,42 @@ struct bpf_sock_ops_kern;extern struct static_key_false cgroup_bpf_enabled_key;#define cgroup_bpf_enabled static_branch_unlikely(&cgroup_bpf_enabled_key) +struct bpf_prog_list { +struct list_head node; +struct bpf_prog *prog; +}; + +struct bpf_prog_array ; +struct cgroup_bpf { -/* -* Store two sets of bpf_prog pointers, one for programs that are -* pinned directly to this cgroup, and one for those that are effective -* when this cgroup is accessed. +/* array of effective progs in this cgroup */ +struct bpf_prog_array __rcu *effective[MAX_BPF_ATTACH_TYPE ]; + +/* attached progs to this cgroup and attach flags +* when flags == 0 or BPF_F_ALLOW_OVERRIDE the progs list will +* have either zero or one element +* when BPF_F_ALLOW_MULTI the list can have up to BPF_CGROUP_MAX_PROGS */ -struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE ]; -struct bpf_prog __rcu *effective[MAX_BPF_ATTACH_TYPE ]; -bool disallow_override[MAX_BPF_ATTACH_TYPE ]; +struct list_head progs[MAX_BPF_ATTACH_TYPE ]; +u32 flags[MAX_BPF_ATTACH_TYPE ]; + +/* temp storage for effective prog array used by prog_attach/detach */ +struct bpf_prog_array __rcu *inactive; };void cgroup_bpf_put (struct cgroup *cgrp); -void cgroup_bpf_inherit (struct cgroup *cgrp,struct cgroup *parent); +int cgroup_bpf_inherit (struct cgroup *cgrp); -int __cgroup_bpf_update (struct cgroup *cgrp,struct cgroup *parent, -struct bpf_prog *prog,enum bpf_attach_type type, -bool overridable); +int __cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *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); -/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */ -int cgroup_bpf_update (struct cgroup *cgrp,struct bpf_prog *prog, -enum bpf_attach_type type,bool overridable); +/* 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); +int cgroup_bpf_detach (struct cgroup *cgrp,struct bpf_prog *prog, +enum bpf_attach_type type,u32 flags); int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb,
@@ -96,8 +111,7 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,struct cgroup_bpf {};static inline void cgroup_bpf_put (struct cgroup *cgrp) {} -static inline void cgroup_bpf_inherit (struct cgroup *cgrp, -struct cgroup *parent) {} +static inline int cgroup_bpf_inherit (struct cgroup *cgrp) {return 0 ; }#define BPF_CGROUP_RUN_PROG_INET_INGRESS (sk,skb) ({ 0; })#define BPF_CGROUP_RUN_PROG_INET_EGRESS (sk,skb) ({ 0; })
include/linux/bpf.h +32 -0
@@ -241,6 +241,38 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,int bpf_prog_test_run_skb (struct bpf_prog *prog,const union bpf_attr *kattr,union bpf_attr __user *uattr); +/* an array of programs to be executed under rcu_lock. +* +* Typical usage: +* ret = BPF_PROG_RUN_ARRAY(&bpf_prog_array, ctx, BPF_PROG_RUN); +* +* the structure returned by bpf_prog_array_alloc() should be populated +* with program pointers and the last pointer must be NULL. +* The user has to keep refcnt on the program and make sure the program +* is removed from the array before bpf_prog_put(). +* The 'struct bpf_prog_array *' should only be replaced with xchg() +* since other cpus are walking the array of pointers in parallel. +*/ +struct bpf_prog_array { +struct rcu_head rcu; +struct bpf_prog *progs[0 ]; +}; + +struct bpf_prog_array __rcu *bpf_prog_array_alloc (u32 prog_cnt,gfp_t flags); +void bpf_prog_array_free (struct bpf_prog_array __rcu *progs); + +#define BPF_PROG_RUN_ARRAY (array, ctx, func) \ + ({ \ + struct bpf_prog **_prog; \ + u32 _ret = 1; \ + rcu_read_lock(); \ + _prog = rcu_dereference(array)->progs; \ + for (; *_prog; _prog++) \ + _ret &= func(*_prog, ctx); \ + rcu_read_unlock(); \ + _ret; \ + }) +#ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU (int, bpf_prog_active);
include/linux/filter.h +1 -1
@@ -481,7 +481,7 @@ struct sk_filter {struct bpf_prog *prog; }; -#define BPF_PROG_RUN (filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) +#define BPF_PROG_RUN (filter, ctx) (*(filter)->bpf_func)(ctx, (filter)->insnsi)#define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
include/uapi/linux/bpf.h +39 -3
50 lines not on this page · raw message
kernel/bpf/cgroup.c +336 -131
538 lines not on this page · raw message
kernel/bpf/core.c +31 -0
@@ -1381,6 +1381,37 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) }EXPORT_SYMBOL_GPL (bpf_prog_select_runtime); +/* to avoid allocating empty bpf_prog_array for cgroups that +* don't have bpf program attached use one global 'empty_prog_array' +* It will not be modified the caller of bpf_prog_array_alloc() +* (since caller requested prog_cnt == 0) +* that pointer should be 'freed' by bpf_prog_array_free() +*/ +static struct { +struct bpf_prog_array hdr; +struct bpf_prog *null_prog; +} empty_prog_array = { + .null_prog =NULL , +}; + +struct bpf_prog_array __rcu *bpf_prog_array_alloc (u32 prog_cnt,gfp_t flags) +{ +if (prog_cnt) +return kzalloc (sizeof (struct bpf_prog_array ) + +sizeof (struct bpf_prog *) * (prog_cnt +1 ), + flags); + +return &empty_prog_array.hdr; +} + +void bpf_prog_array_free (struct bpf_prog_array __rcu *progs) +{ +if (!progs || + progs == (struct bpf_prog_array __rcu *)&empty_prog_array.hdr) +return ; +kfree_rcu (progs, rcu); +} +static void bpf_prog_free_deferred (struct work_struct *work) {struct bpf_prog_aux *aux;
kernel/bpf/syscall.c +26 -11
@@ -1168,6 +1168,9 @@ static int sockmap_get_from_fd(const union bpf_attr *attr, bool attach)return 0 ; } +#define BPF_F_ATTACH_MASK \ + (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI) +static int bpf_prog_attach (const union bpf_attr *attr) {enum bpf_prog_type ptype;
@@ -1181,7 +1184,7 @@ static int bpf_prog_attach(const union bpf_attr *attr)if (CHECK_ATTR (BPF_PROG_ATTACH ))return -EINVAL ; -if (attr->attach_flags & ~BPF_F_ALLOW_OVERRIDE ) +if (attr->attach_flags & ~BPF_F_ATTACH_MASK )return -EINVAL ;switch (attr->attach_type) {
@@ -1212,8 +1215,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)return PTR_ERR (cgrp); } - ret =cgroup_bpf_update (cgrp, prog, attr->attach_type, - attr->attach_flags &BPF_F_ALLOW_OVERRIDE ); + ret =cgroup_bpf_attach (cgrp, prog, attr->attach_type, + attr->attach_flags);if (ret)bpf_prog_put (prog);cgroup_put (cgrp);
@@ -1225,6 +1228,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)static int bpf_prog_detach (const union bpf_attr *attr) { +enum bpf_prog_type ptype; +struct bpf_prog *prog;struct cgroup *cgrp;int ret;
@@ -1237,23 +1242,33 @@ static int bpf_prog_detach(const union bpf_attr *attr)switch (attr->attach_type) {case BPF_CGROUP_INET_INGRESS :case BPF_CGROUP_INET_EGRESS : + ptype =BPF_PROG_TYPE_CGROUP_SKB ; +break ;case BPF_CGROUP_INET_SOCK_CREATE : + ptype =BPF_PROG_TYPE_CGROUP_SOCK ; +break ;case BPF_CGROUP_SOCK_OPS : - cgrp =cgroup_get_from_fd (attr->target_fd); -if (IS_ERR (cgrp)) -return PTR_ERR (cgrp); - - ret =cgroup_bpf_update (cgrp,NULL , attr->attach_type, false); -cgroup_put (cgrp); + ptype =BPF_PROG_TYPE_SOCK_OPS ;break ;case BPF_SK_SKB_STREAM_PARSER :case BPF_SK_SKB_STREAM_VERDICT : - ret =sockmap_get_from_fd (attr, false); -break ; +return sockmap_get_from_fd (attr, false);default :return -EINVAL ; } + cgrp =cgroup_get_from_fd (attr->target_fd); +if (IS_ERR (cgrp)) +return PTR_ERR (cgrp); + + prog =bpf_prog_get_type (attr->attach_bpf_fd, ptype); +if (IS_ERR (prog)) + prog =NULL ; + + ret =cgroup_bpf_detach (cgrp, prog, attr->attach_type,0 ); +if (prog) +bpf_prog_put (prog); +cgroup_put (cgrp);return ret; }
kernel/cgroup/cgroup.c +21 -7
@@ -1896,6 +1896,9 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask, int ref_flags)if (ret) goto destroy_root; + ret =cgroup_bpf_inherit (root_cgrp); +WARN_ON_ONCE (ret); +trace_cgroup_setup_root (root);/*
@@ -4713,6 +4716,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent) cgrp->self.parent = &parent->self; cgrp->root = root; cgrp->level = level; + ret =cgroup_bpf_inherit (cgrp); +if (ret) + goto out_idr_free;for (tcgrp = cgrp; tcgrp; tcgrp =cgroup_parent (tcgrp)) { cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;
@@ -4747,13 +4753,12 @@ static struct cgroup *cgroup_create(struct cgroup *parent)if (!cgroup_on_dfl (cgrp)) cgrp->subtree_control =cgroup_control (cgrp); -if (parent) -cgroup_bpf_inherit (cgrp, parent); -cgroup_propagate_control (cgrp);return cgrp; +out_idr_free: +cgroup_idr_remove (&root->cgroup_idr, cgrp->id); out_cancel_ref:percpu_ref_exit (&cgrp->self.refcnt); out_free_cgrp:
@@ -5736,14 +5741,23 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)#endif /* CONFIG_SOCK_CGROUP_DATA */ #ifdef CONFIG_CGROUP_BPF -int cgroup_bpf_update (struct cgroup *cgrp,struct bpf_prog *prog, -enum bpf_attach_type type,bool overridable) +int cgroup_bpf_attach (struct cgroup *cgrp,struct bpf_prog *prog, +enum bpf_attach_type type,u32 flags) +{ +int ret; + +mutex_lock (&cgroup_mutex); + ret =__cgroup_bpf_attach (cgrp, prog, type, flags); +mutex_unlock (&cgroup_mutex); +return ret; +} +int cgroup_bpf_detach (struct cgroup *cgrp,struct bpf_prog *prog, +enum bpf_attach_type type,u32 flags) { -struct cgroup *parent =cgroup_parent (cgrp);int ret;mutex_lock (&cgroup_mutex); - ret =__cgroup_bpf_update (cgrp, parent, prog, type, overridable); + ret =__cgroup_bpf_detach (cgrp, prog, type, flags);mutex_unlock (&cgroup_mutex);return ret; }
Signature · 1 line
2.9.5
PATCH v2 2/8 bpf: introduce BPF_PROG_QUERY command
In reply to Alexei Starovoitovintroduce BPF_PROG_QUERY command to retrieve a set of either attached programs to given cgroup or a set of effective programs that will execute for events within a cgroup
for cgroup bits
--- include/linux/bpf-cgroup.h | 4 ++++ include/linux/bpf.h | 3 +++ include/uapi/linux/bpf.h | 13 +++++++++++++ kernel/bpf/cgroup.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/core.c | 38 ++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 34 ++++++++++++++++++++++++++++++++++ kernel/cgroup/cgroup.c | 10 ++++++++++ 7 files changed, 148 insertions(+)
include/linux/bpf-cgroup.h +4 -0
@@ -44,12 +44,16 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *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, +union bpf_attr __user *uattr);/* 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);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, +union bpf_attr __user *uattr); int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb,
include/linux/bpf.h +3 -0
@@ -260,6 +260,9 @@ struct bpf_prog_array {struct bpf_prog_array __rcu *bpf_prog_array_alloc (u32 prog_cnt,gfp_t flags);void bpf_prog_array_free (struct bpf_prog_array __rcu *progs); +int bpf_prog_array_length (struct bpf_prog_array __rcu *progs); +int bpf_prog_array_copy_to_user (struct bpf_prog_array __rcu *progs, +__u32 __user *prog_ids,u32 cnt);#define BPF_PROG_RUN_ARRAY (array, ctx, func) \ ({ \
include/uapi/linux/bpf.h +13 -0
@@ -92,6 +92,7 @@ enum bpf_cmd {BPF_PROG_GET_FD_BY_ID ,BPF_MAP_GET_FD_BY_ID ,BPF_OBJ_GET_INFO_BY_FD , + BPF_PROG_QUERY, }; enum bpf_map_type {
@@ -211,6 +212,9 @@ enum bpf_attach_type {/* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) +/* flags for BPF_PROG_QUERY */ +#define BPF_F_QUERY_EFFECTIVE (1U << 0) +#define BPF_OBJ_NAME_LEN 16Uunion bpf_attr {
@@ -289,6 +293,15 @@ union bpf_attr {__u32 info_len;__aligned_u64 info; } info; + +struct {/* anonymous struct used by BPF_PROG_QUERY command */ +__u32 target_fd;/* container object to query */ +__u32 attach_type; +__u32 query_flags; +__u32 attach_flags; +__aligned_u64 prog_ids; +__u32 prog_cnt; + } query; } __attribute__((aligned(8)));/* BPF helper function descriptions:
kernel/bpf/cgroup.c +46 -0
52 lines not on this page · raw message
kernel/bpf/core.c +38 -0
@@ -1412,6 +1412,44 @@ void bpf_prog_array_free(struct bpf_prog_array __rcu *progs)kfree_rcu (progs, rcu); } +int bpf_prog_array_length (struct bpf_prog_array __rcu *progs) +{ +struct bpf_prog **prog; +u32 cnt =0 ; + +rcu_read_lock (); + prog =rcu_dereference (progs)->progs; +for (; *prog; prog++) + cnt++; +rcu_read_unlock (); +return cnt; +} + +int bpf_prog_array_copy_to_user (struct bpf_prog_array __rcu *progs, +__u32 __user *prog_ids,u32 cnt) +{ +struct bpf_prog **prog; +u32 i =0 , id; + +rcu_read_lock (); + prog =rcu_dereference (progs)->progs; +for (; *prog; prog++) { + id = (*prog)->aux->id; +if (copy_to_user (prog_ids + i, &id,sizeof (id))) { +rcu_read_unlock (); +return -EFAULT ; + } +if (++i == cnt) { + prog++; +break ; + } + } +rcu_read_unlock (); +if (*prog) +return -ENOSPC ; +return 0 ; +} +static void bpf_prog_free_deferred (struct work_struct *work) {struct bpf_prog_aux *aux;
kernel/bpf/syscall.c +34 -0
@@ -1272,6 +1272,37 @@ static int bpf_prog_detach(const union bpf_attr *attr)return ret; } +#define BPF_PROG_QUERY_LAST_FIELD query.prog_cnt + +static int bpf_prog_query (const union bpf_attr *attr, +union bpf_attr __user *uattr) +{ +struct cgroup *cgrp; +int ret; + +if (!capable (CAP_NET_ADMIN )) +return -EPERM ; +if (CHECK_ATTR (BPF_PROG_QUERY )) +return -EINVAL ; +if (attr->query.query_flags & ~BPF_F_QUERY_EFFECTIVE ) +return -EINVAL ; + +switch (attr->query.attach_type) { +case BPF_CGROUP_INET_INGRESS : +case BPF_CGROUP_INET_EGRESS : +case BPF_CGROUP_INET_SOCK_CREATE : +case BPF_CGROUP_SOCK_OPS : +break ; +default : +return -EINVAL ; + } + cgrp =cgroup_get_from_fd (attr->query.target_fd); +if (IS_ERR (cgrp)) +return PTR_ERR (cgrp); + ret =cgroup_bpf_query (cgrp, attr, uattr); +cgroup_put (cgrp); +return ret; +}#endif /* CONFIG_CGROUP_BPF */ #define BPF_PROG_TEST_RUN_LAST_FIELD test.duration
@@ -1568,6 +1599,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizcase BPF_PROG_DETACH : err =bpf_prog_detach (&attr);break ; +case BPF_PROG_QUERY : + err =bpf_prog_query (&attr, uattr); +break ;#endif case BPF_PROG_TEST_RUN : err =bpf_prog_test_run (&attr, uattr);
kernel/cgroup/cgroup.c +10 -0
@@ -5761,4 +5761,14 @@ int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,mutex_unlock (&cgroup_mutex); return ret; } +int cgroup_bpf_query (struct cgroup *cgrp,const union bpf_attr *attr, +union bpf_attr __user *uattr) +{ +int ret; + +mutex_lock (&cgroup_mutex); + ret =__cgroup_bpf_query (cgrp, attr, uattr); +mutex_unlock (&cgroup_mutex); +return ret; +}#endif /* CONFIG_CGROUP_BPF */
Signature · 1 line
2.9.5
PATCH v2 3/8 bpf: enforce return code for cgroup-bpf programs
In reply to Alexei Starovoitovwith addition of tnum logic the verifier got smart enough and we can enforce return codes at program load time. For now do so for cgroup-bpf program types.
--- kernel/bpf/verifier.c | 40 ++++++++++++++++ tools/testing/selftests/bpf/test_verifier.c | 72 +++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+)
kernel/bpf/verifier.c +40 -0
@@ -3073,6 +3073,43 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)return 0 ; } +static int check_return_code (struct bpf_verifier_env *env) +{ +struct bpf_reg_state *reg; +struct tnum range =tnum_range (0 ,1 ); + +switch (env->prog->type) { +case BPF_PROG_TYPE_CGROUP_SKB : +case BPF_PROG_TYPE_CGROUP_SOCK : +case BPF_PROG_TYPE_SOCK_OPS : +break ; +default : +return 0 ; + } + + reg = &env->cur_state.regs[BPF_REG_0 ]; +if (reg->type !=SCALAR_VALUE ) { +verbose ("At program exit the register R0 is not a known value (%s)\n" , + reg_type_str[reg->type]); +return -EINVAL ; + } + +if (!tnum_in (range, reg->var_off)) { +verbose ("At program exit the register R0 " ); +if (!tnum_is_unknown (reg->var_off)) { +char tn_buf[48 ]; + +tnum_strn (tn_buf,sizeof (tn_buf), reg->var_off); +verbose ("has value %s" , tn_buf); + }else { +verbose ("has unknown scalar value" ); + } +verbose (" should have been 0 or 1\n" ); +return -EINVAL ; + } +return 0 ; +} +/* non-recursive DFS pseudo code * 1 procedure DFS-iterative(G,v): * 2 label v as discovered
@@ -3863,6 +3900,9 @@ static int do_check(struct bpf_verifier_env *env)return -EACCES ; } + err =check_return_code (env); +if (err) +return err; process_bpf_exit: insn_idx =pop_stack (env, &prev_insn_idx);if (insn_idx <0 ) {
tools/testing/selftests/bpf/test_verifier.c +72 -0
78 lines not on this page · raw message
Signature · 1 line
2.9.5
PATCH v2 4/8 libbpf: introduce bpf_prog_detach2()
In reply to Alexei Starovoitovintroduce bpf_prog_detach2() that takes one more argument prog_fd vs bpf_prog_detach() that takes only attach_fd and type.
--- tools/lib/bpf/bpf.c | 12 ++++++++++++ tools/lib/bpf/bpf.h | 1 + 2 files changed, 13 insertions(+)
tools/lib/bpf/bpf.c +12 -0
@@ -291,6 +291,18 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type)return sys_bpf (BPF_PROG_DETACH , &attr,sizeof (attr)); } +int bpf_prog_detach2 (int prog_fd,int target_fd,enum bpf_attach_type type) +{ +union bpf_attr attr; + +bzero (&attr,sizeof (attr)); + attr.target_fd = target_fd; + attr.attach_bpf_fd = prog_fd; + attr.attach_type = type; + +return sys_bpf (BPF_PROG_DETACH , &attr,sizeof (attr)); +} +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size,void *data_out,__u32 *size_out,__u32 *retval,__u32 *duration)
tools/lib/bpf/bpf.h +1 -0
@@ -66,6 +66,7 @@ int bpf_obj_get(const char *pathname);int bpf_prog_attach (int prog_fd,int attachable_fd,enum bpf_attach_type type,unsigned int flags);int bpf_prog_detach (int attachable_fd,enum bpf_attach_type type); +int bpf_prog_detach2 (int prog_fd,int attachable_fd,enum bpf_attach_type type);int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size,void *data_out,__u32 *size_out,__u32 *retval,__u32 *duration);
Signature · 1 line
2.9.5
PATCH v2 5/8 samples/bpf: add multi-prog cgroup test case
In reply to Alexei Starovoitovcreate 5 cgroups, attach 6 progs and check that progs are executed as:
cgrp1 (MULTI progs A, B) ->
cgrp2 (OVERRIDE prog C) ->
cgrp3 (MULTI prog D) ->
cgrp4 (OVERRIDE prog E) ->
cgrp5 (NONE prog F)the event in cgrp5 triggers execution of F,D,A,B in that order. if prog F is detached, the execution is E,D,A,B
if prog F and D are detached, the execution is E,A,B if prog F, E and D are detached, the execution is C,A,B
--- samples/bpf/cgroup_helpers.c | 4 +- samples/bpf/test_cgrp2_attach2.c | 188 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 185 insertions(+), 7 deletions(-)
samples/bpf/cgroup_helpers.c +2 -2
@@ -56,7 +56,7 @@ int setup_cgroup_environment(void)return 1 ; } -if (mount ("none" ,CGROUP_MOUNT_PATH ,"cgroup2" ,0 ,NULL )) { +if (mount ("none" ,CGROUP_MOUNT_PATH ,"cgroup2" ,0 ,NULL ) && errno !=EBUSY ) {log_err ("mount cgroup2" );return 1 ; }
@@ -163,7 +163,7 @@ int create_and_get_cgroup(char *path)format_cgroup_path (cgroup_path, path);if (mkdir (cgroup_path,0777 ) && errno !=EEXIST ) { -log_err ("mkdiring cgroup" ); +log_err ("mkdiring cgroup %s .. %s" , path, cgroup_path);return 0 ; }
samples/bpf/test_cgrp2_attach2.c +183 -5
206 lines not on this page · raw message
Signature · 1 line
2.9.5
PATCH v2 6/8 libbpf: sync bpf.h
In reply to Alexei Starovoitovtools/include/uapi/linux/bpf.h got out of sync with actual kernel header. Update it.
--- tools/include/uapi/linux/bpf.h | 55 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-)
tools/include/uapi/linux/bpf.h +52 -3
@@ -92,6 +92,7 @@ enum bpf_cmd {BPF_PROG_GET_FD_BY_ID ,BPF_MAP_GET_FD_BY_ID ,BPF_OBJ_GET_INFO_BY_FD , + BPF_PROG_QUERY, }; enum bpf_map_type {
@@ -143,11 +144,47 @@ enum bpf_attach_type {#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE -/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command -* to the given target_fd cgroup the descendent cgroup will be able to -* override effective bpf program that was inherited from this cgroup +/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command +* +* NONE(default): No further bpf programs allowed in the subtree. +* +* BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, +* the program in this cgroup yields to sub-cgroup program. +* +* BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, +* that cgroup program gets run in addition to the program in this cgroup. +* +* Only one program is allowed to be attached to a cgroup with +* NONE or BPF_F_ALLOW_OVERRIDE flag. +* Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will +* release old program and attach the new one. Attach flags has to match. +* +* Multiple programs are allowed to be attached to a cgroup with +* BPF_F_ALLOW_MULTI flag. They are executed in FIFO order +* (those that were attached first, run first) +* The programs of sub-cgroup are executed first, then programs of +* this cgroup and then programs of parent cgroup. +* When children program makes decision (like picking TCP CA or sock bind) +* parent program has a chance to override it. +* +* 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: +* cgrp1 (MULTI progs A, B) -> +* cgrp2 (OVERRIDE prog C) -> +* cgrp3 (MULTI prog D) -> +* cgrp4 (OVERRIDE prog E) -> +* cgrp5 (NONE prog F) +* the event in cgrp5 triggers execution of F,D,A,B in that order. +* if prog F is detached, the execution is E,D,A,B +* if prog F and D are detached, the execution is E,A,B +* if prog F, E and D are detached, the execution is C,A,B +* +* All eligible programs are executed regardless of return code from +* earlier programs. */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) +#define BPF_F_ALLOW_MULTI (1U << 1)/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel
@@ -175,6 +212,9 @@ enum bpf_attach_type {/* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) +/* flags for BPF_PROG_QUERY */ +#define BPF_F_QUERY_EFFECTIVE (1U << 0) +#define BPF_OBJ_NAME_LEN 16Uunion bpf_attr {
@@ -253,6 +293,15 @@ union bpf_attr {__u32 info_len;__aligned_u64 info; } info; + +struct {/* anonymous struct used by BPF_PROG_QUERY command */ +__u32 target_fd;/* container object to query */ +__u32 attach_type; +__u32 query_flags; +__u32 attach_flags; +__aligned_u64 prog_ids; +__u32 prog_cnt; + } query; } __attribute__((aligned(8)));/* BPF helper function descriptions:
Signature · 1 line
2.9.5
PATCH v2 7/8 libbpf: add support for BPF_PROG_QUERY
In reply to Alexei Starovoitovadd support for BPF_PROG_QUERY command to libbpf
--- tools/lib/bpf/bpf.c | 20 ++++++++++++++++++++ tools/lib/bpf/bpf.h | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-)
tools/lib/bpf/bpf.c +20 -0
@@ -303,6 +303,26 @@ int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)return sys_bpf (BPF_PROG_DETACH , &attr,sizeof (attr)); } +int bpf_prog_query (int target_fd,enum bpf_attach_type type,__u32 query_flags, +__u32 *attach_flags,__u32 *prog_ids,__u32 *prog_cnt) +{ +union bpf_attr attr; +int ret; + +bzero (&attr,sizeof (attr)); + attr.query.target_fd = target_fd; + attr.query.attach_type = type; + attr.query.query_flags = query_flags; + attr.query.prog_cnt = *prog_cnt; + attr.query.prog_ids =ptr_to_u64 (prog_ids); + + ret =sys_bpf (BPF_PROG_QUERY , &attr,sizeof (attr)); +if (attach_flags) + *attach_flags = attr.query.attach_flags; + *prog_cnt = attr.query.prog_cnt; +return ret; +} +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size,void *data_out,__u32 *size_out,__u32 *retval,__u32 *duration)
tools/lib/bpf/bpf.h +2 -1
@@ -75,5 +75,6 @@ int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);int bpf_prog_get_fd_by_id (__u32 id);int bpf_map_get_fd_by_id (__u32 id);int bpf_obj_get_info_by_fd (int prog_fd,void *info,__u32 *info_len); - +int bpf_prog_query (int target_fd,enum bpf_attach_type type,__u32 query_flags, +__u32 *attach_flags,__u32 *prog_ids,__u32 *prog_cnt);#endif
Signature · 1 line
2.9.5
PATCH v2 8/8 samples/bpf: use bpf_prog_query() interface
In reply to Alexei Starovoitovuse BPF_PROG_QUERY command to strengthen test coverage
--- samples/bpf/test_cgrp2_attach2.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
samples/bpf/test_cgrp2_attach2.c +36 -0
@@ -236,6 +236,7 @@ static int prog_load_cnt(int verdict, int val)static int test_multiprog (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 drop_prog, allow_prog[6 ] = {}, rc =0 ;unsigned long long value;
@@ -304,6 +305,32 @@ static int test_multiprog(void)assert (bpf_map_lookup_elem (map_fd, &key, &value) ==0 );assert (value ==1 +2 +8 +32 ); +/* query the number of effective progs in cg5 */ +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,BPF_F_QUERY_EFFECTIVE , +NULL ,NULL , &prog_cnt) ==0 ); +assert (prog_cnt ==4 ); +/* retrieve prog_ids of effective progs in cg5 */ +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,BPF_F_QUERY_EFFECTIVE , + &attach_flags, prog_ids, &prog_cnt) ==0 ); +assert (prog_cnt ==4 ); +assert (attach_flags ==0 ); + saved_prog_id = prog_ids[0 ]; +/* check enospc handling */ + prog_ids[0 ] =0 ; + prog_cnt =2 ; +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,BPF_F_QUERY_EFFECTIVE , + &attach_flags, prog_ids, &prog_cnt) ==-1 && + errno ==ENOSPC ); +assert (prog_cnt ==4 ); +/* check that prog_ids are returned even when buffer is too small */ +assert (prog_ids[0 ] == saved_prog_id); +/* retrieve prog_id of single attached prog in cg5 */ + prog_ids[0 ] =0 ; +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,0 , +NULL , prog_ids, &prog_cnt) ==0 ); +assert (prog_cnt ==1 ); +assert (prog_ids[0 ] == saved_prog_id); +/* detach bottom program and ping again */ if (bpf_prog_detach2 (-1 , cg5,BPF_CGROUP_INET_EGRESS )) {log_err ("Detaching prog from cg5" );
@@ -341,6 +368,15 @@ static int test_multiprog(void)assert (system (PING_CMD ) ==0 );assert (bpf_map_lookup_elem (map_fd, &key, &value) ==0 );assert (value ==1 +2 +4 ); + + prog_cnt =4 ; +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,BPF_F_QUERY_EFFECTIVE , + &attach_flags, prog_ids, &prog_cnt) ==0 ); +assert (prog_cnt ==3 ); +assert (attach_flags ==0 ); +assert (bpf_prog_query (cg5,BPF_CGROUP_INET_EGRESS ,0 , +NULL , prog_ids, &prog_cnt) ==0 ); +assert (prog_cnt ==0 ); goto out; err: rc =1 ;
Signature · 1 line
2.9.5
David Miller
maintainerFrom: Alexei Starovoitov <[email protected]>
Date: Mon, 2 Oct 2017 22:50:20 -0700
Quoted 28 lines
v1->v2:
- fixed accidentally swapped two lines which causedstatic_keynot going to zero
- addressed Martin's feedback and changedprog_queryto be consistent with verifier output: return -enospc and fill supplied buffer instead of just returning -enospc when buffer is too small to fit allprog_idsv1:
cgroup-bpf use cases are getting more advanced and running only one program per cgroup is no longer enough. Therefore introduce support for attaching multiple programs per cgroup and running a set of effective programs.These patches introduces
BPF_F_ALLOW_MULTIflag forBPF_PROG_ATTACHcmd. The default is still NONE and behavior ofBPF_F_ALLOW_OVERRIDEflag is unchanged.
The difference between three possible flags forBPF_PROG_ATTACHcommand:
- NONE(default): No further bpf programs allowed in the subtree.
-BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, the program in this cgroup yields to sub-cgroup program.
-BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, that cgroup program gets run in addition to the program in this cgroup.Most of the logic is in patch 1. Even when cgroup doesn't have any programs attached its set of effective program can be non-empty. To quickly execute them and avoid penalizing cgroups without any effective programs introduce 'struct
bpf_prog_array' which has an optimization for cgroups with zero effective programs.
Quoted 5 lines
Patch 2 introduces
BPF_PROG_QUERYcommand for introspection Patch 3 makes verifier more strict for cgroup-bpf program types. Patch 4+ are tests.More details in individual patches
Looks good, series applied, thanks!