Commit af6eea57 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov

bpf: Implement bpf_link-based cgroup BPF program attachment

Implement new sub-command to attach cgroup BPF programs and return FD-based
bpf_link back on success. bpf_link, once attached to cgroup, cannot be
replaced, except by owner having its FD. Cgroup bpf_link supports only
BPF_F_ALLOW_MULTI semantics. Both link-based and prog-based BPF_F_ALLOW_MULTI
attachments can be freely intermixed.

To prevent bpf_cgroup_link from keeping cgroup alive past the point when no
BPF program can be executed, implement auto-detachment of link. When
cgroup_bpf_release() is called, all attached bpf_links are forced to release
cgroup refcounts, but they leave bpf_link otherwise active and allocated, as
well as still owning underlying bpf_prog. This is because user-space might
still have FDs open and active, so bpf_link as a user-referenced object can't
be freed yet. Once last active FD is closed, bpf_link will be freed and
underlying bpf_prog refcount will be dropped. But cgroup refcount won't be
touched, because cgroup is released already.

The inherent race between bpf_cgroup_link release (from closing last FD) and
cgroup_bpf_release() is resolved by both operations taking cgroup_mutex. So
the only additional check required is when bpf_cgroup_link attempts to detach
itself from cgroup. At that time we need to check whether there is still
cgroup associated with that link. And if not, exit with success, because
bpf_cgroup_link was already successfully detached.
Signed-off-by: default avatarAndrii Nakryiko <andriin@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarRoman Gushchin <guro@fb.com>
Link: https://lore.kernel.org/bpf/20200330030001.2312810-2-andriin@fb.com
parent e5ffcc91
...@@ -51,9 +51,18 @@ struct bpf_cgroup_storage { ...@@ -51,9 +51,18 @@ struct bpf_cgroup_storage {
struct rcu_head rcu; struct rcu_head rcu;
}; };
struct bpf_cgroup_link {
struct bpf_link link;
struct cgroup *cgroup;
enum bpf_attach_type type;
};
extern const struct bpf_link_ops bpf_cgroup_link_lops;
struct bpf_prog_list { struct bpf_prog_list {
struct list_head node; struct list_head node;
struct bpf_prog *prog; struct bpf_prog *prog;
struct bpf_cgroup_link *link;
struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE]; struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE];
}; };
...@@ -84,20 +93,23 @@ struct cgroup_bpf { ...@@ -84,20 +93,23 @@ struct cgroup_bpf {
int cgroup_bpf_inherit(struct cgroup *cgrp); int cgroup_bpf_inherit(struct cgroup *cgrp);
void cgroup_bpf_offline(struct cgroup *cgrp); void cgroup_bpf_offline(struct cgroup *cgrp);
int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, int __cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *replace_prog, struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link,
enum bpf_attach_type type, u32 flags); enum bpf_attach_type type, u32 flags);
int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
struct bpf_cgroup_link *link,
enum bpf_attach_type type); enum bpf_attach_type type);
int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
union bpf_attr __user *uattr); union bpf_attr __user *uattr);
/* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */ /* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */
int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, int cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *replace_prog, enum bpf_attach_type type, struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link, enum bpf_attach_type type,
u32 flags); u32 flags);
int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
enum bpf_attach_type type, u32 flags); enum bpf_attach_type type);
int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
union bpf_attr __user *uattr); union bpf_attr __user *uattr);
...@@ -332,6 +344,7 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr, ...@@ -332,6 +344,7 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,
enum bpf_prog_type ptype, struct bpf_prog *prog); enum bpf_prog_type ptype, struct bpf_prog *prog);
int cgroup_bpf_prog_detach(const union bpf_attr *attr, int cgroup_bpf_prog_detach(const union bpf_attr *attr,
enum bpf_prog_type ptype); enum bpf_prog_type ptype);
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
int cgroup_bpf_prog_query(const union bpf_attr *attr, int cgroup_bpf_prog_query(const union bpf_attr *attr,
union bpf_attr __user *uattr); union bpf_attr __user *uattr);
#else #else
...@@ -354,6 +367,12 @@ static inline int cgroup_bpf_prog_detach(const union bpf_attr *attr, ...@@ -354,6 +367,12 @@ static inline int cgroup_bpf_prog_detach(const union bpf_attr *attr,
return -EINVAL; return -EINVAL;
} }
static inline int cgroup_bpf_link_attach(const union bpf_attr *attr,
struct bpf_prog *prog)
{
return -EINVAL;
}
static inline int cgroup_bpf_prog_query(const union bpf_attr *attr, static inline int cgroup_bpf_prog_query(const union bpf_attr *attr,
union bpf_attr __user *uattr) union bpf_attr __user *uattr)
{ {
......
...@@ -1082,15 +1082,23 @@ extern int sysctl_unprivileged_bpf_disabled; ...@@ -1082,15 +1082,23 @@ extern int sysctl_unprivileged_bpf_disabled;
int bpf_map_new_fd(struct bpf_map *map, int flags); int bpf_map_new_fd(struct bpf_map *map, int flags);
int bpf_prog_new_fd(struct bpf_prog *prog); int bpf_prog_new_fd(struct bpf_prog *prog);
struct bpf_link; struct bpf_link {
atomic64_t refcnt;
const struct bpf_link_ops *ops;
struct bpf_prog *prog;
struct work_struct work;
};
struct bpf_link_ops { struct bpf_link_ops {
void (*release)(struct bpf_link *link); void (*release)(struct bpf_link *link);
void (*dealloc)(struct bpf_link *link); void (*dealloc)(struct bpf_link *link);
}; };
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
struct bpf_prog *prog); struct bpf_prog *prog);
void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
int link_fd);
void bpf_link_inc(struct bpf_link *link); void bpf_link_inc(struct bpf_link *link);
void bpf_link_put(struct bpf_link *link); void bpf_link_put(struct bpf_link *link);
int bpf_link_new_fd(struct bpf_link *link); int bpf_link_new_fd(struct bpf_link *link);
......
...@@ -111,6 +111,7 @@ enum bpf_cmd { ...@@ -111,6 +111,7 @@ enum bpf_cmd {
BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH,
BPF_MAP_UPDATE_BATCH, BPF_MAP_UPDATE_BATCH,
BPF_MAP_DELETE_BATCH, BPF_MAP_DELETE_BATCH,
BPF_LINK_CREATE,
}; };
enum bpf_map_type { enum bpf_map_type {
...@@ -541,7 +542,7 @@ union bpf_attr { ...@@ -541,7 +542,7 @@ union bpf_attr {
__u32 prog_cnt; __u32 prog_cnt;
} query; } query;
struct { struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */
__u64 name; __u64 name;
__u32 prog_fd; __u32 prog_fd;
} raw_tracepoint; } raw_tracepoint;
...@@ -569,6 +570,13 @@ union bpf_attr { ...@@ -569,6 +570,13 @@ union bpf_attr {
__u64 probe_offset; /* output: probe_offset */ __u64 probe_offset; /* output: probe_offset */
__u64 probe_addr; /* output: probe_addr */ __u64 probe_addr; /* output: probe_addr */
} task_fd_query; } task_fd_query;
struct { /* struct used by BPF_LINK_CREATE command */
__u32 prog_fd; /* eBPF program to attach */
__u32 target_fd; /* object to attach to */
__u32 attach_type; /* attach type */
__u32 flags; /* extra flags */
} link_create;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
/* The description below is an attempt at providing documentation to eBPF /* The description below is an attempt at providing documentation to eBPF
......
This diff is collapsed.
...@@ -2175,13 +2175,6 @@ static int bpf_obj_get(const union bpf_attr *attr) ...@@ -2175,13 +2175,6 @@ static int bpf_obj_get(const union bpf_attr *attr)
attr->file_flags); attr->file_flags);
} }
struct bpf_link {
atomic64_t refcnt;
const struct bpf_link_ops *ops;
struct bpf_prog *prog;
struct work_struct work;
};
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
struct bpf_prog *prog) struct bpf_prog *prog)
{ {
...@@ -2195,8 +2188,8 @@ void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, ...@@ -2195,8 +2188,8 @@ void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
* anon_inode's release() call. This helper manages marking bpf_link as * anon_inode's release() call. This helper manages marking bpf_link as
* defunct, releases anon_inode file and puts reserved FD. * defunct, releases anon_inode file and puts reserved FD.
*/ */
static void bpf_link_cleanup(struct bpf_link *link, struct file *link_file, void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
int link_fd) int link_fd)
{ {
link->prog = NULL; link->prog = NULL;
fput(link_file); fput(link_file);
...@@ -2266,6 +2259,10 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp) ...@@ -2266,6 +2259,10 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
link_type = "raw_tracepoint"; link_type = "raw_tracepoint";
else if (link->ops == &bpf_tracing_link_lops) else if (link->ops == &bpf_tracing_link_lops)
link_type = "tracing"; link_type = "tracing";
#ifdef CONFIG_CGROUP_BPF
else if (link->ops == &bpf_cgroup_link_lops)
link_type = "cgroup";
#endif
else else
link_type = "unknown"; link_type = "unknown";
...@@ -3553,6 +3550,52 @@ static int bpf_map_do_batch(const union bpf_attr *attr, ...@@ -3553,6 +3550,52 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
return err; return err;
} }
#define BPF_LINK_CREATE_LAST_FIELD link_create.flags
static int link_create(union bpf_attr *attr)
{
enum bpf_prog_type ptype;
struct bpf_prog *prog;
int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (CHECK_ATTR(BPF_LINK_CREATE))
return -EINVAL;
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
if (ptype == BPF_PROG_TYPE_UNSPEC)
return -EINVAL;
prog = bpf_prog_get_type(attr->link_create.prog_fd, ptype);
if (IS_ERR(prog))
return PTR_ERR(prog);
ret = bpf_prog_attach_check_attach_type(prog,
attr->link_create.attach_type);
if (ret)
goto err_out;
switch (ptype) {
case BPF_PROG_TYPE_CGROUP_SKB:
case BPF_PROG_TYPE_CGROUP_SOCK:
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
case BPF_PROG_TYPE_SOCK_OPS:
case BPF_PROG_TYPE_CGROUP_DEVICE:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
ret = cgroup_bpf_link_attach(attr, prog);
break;
default:
ret = -EINVAL;
}
err_out:
if (ret < 0)
bpf_prog_put(prog);
return ret;
}
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
{ {
union bpf_attr attr = {}; union bpf_attr attr = {};
...@@ -3663,6 +3706,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -3663,6 +3706,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
case BPF_MAP_DELETE_BATCH: case BPF_MAP_DELETE_BATCH:
err = bpf_map_do_batch(&attr, uattr, BPF_MAP_DELETE_BATCH); err = bpf_map_do_batch(&attr, uattr, BPF_MAP_DELETE_BATCH);
break; break;
case BPF_LINK_CREATE:
err = link_create(&attr);
break;
default: default:
err = -EINVAL; err = -EINVAL;
break; break;
......
...@@ -6303,27 +6303,31 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd) ...@@ -6303,27 +6303,31 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)
#endif /* CONFIG_SOCK_CGROUP_DATA */ #endif /* CONFIG_SOCK_CGROUP_DATA */
#ifdef CONFIG_CGROUP_BPF #ifdef CONFIG_CGROUP_BPF
int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, int cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *replace_prog, enum bpf_attach_type type, struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link,
enum bpf_attach_type type,
u32 flags) u32 flags)
{ {
int ret; int ret;
mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_mutex);
ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, type, flags); ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, link, type, flags);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
return ret; return ret;
} }
int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
enum bpf_attach_type type, u32 flags) enum bpf_attach_type type)
{ {
int ret; int ret;
mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_mutex);
ret = __cgroup_bpf_detach(cgrp, prog, type); ret = __cgroup_bpf_detach(cgrp, prog, NULL, type);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
return ret; return ret;
} }
int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
union bpf_attr __user *uattr) union bpf_attr __user *uattr)
{ {
......
...@@ -111,6 +111,7 @@ enum bpf_cmd { ...@@ -111,6 +111,7 @@ enum bpf_cmd {
BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH,
BPF_MAP_UPDATE_BATCH, BPF_MAP_UPDATE_BATCH,
BPF_MAP_DELETE_BATCH, BPF_MAP_DELETE_BATCH,
BPF_LINK_CREATE,
}; };
enum bpf_map_type { enum bpf_map_type {
...@@ -541,7 +542,7 @@ union bpf_attr { ...@@ -541,7 +542,7 @@ union bpf_attr {
__u32 prog_cnt; __u32 prog_cnt;
} query; } query;
struct { struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */
__u64 name; __u64 name;
__u32 prog_fd; __u32 prog_fd;
} raw_tracepoint; } raw_tracepoint;
...@@ -569,6 +570,13 @@ union bpf_attr { ...@@ -569,6 +570,13 @@ union bpf_attr {
__u64 probe_offset; /* output: probe_offset */ __u64 probe_offset; /* output: probe_offset */
__u64 probe_addr; /* output: probe_addr */ __u64 probe_addr; /* output: probe_addr */
} task_fd_query; } task_fd_query;
struct { /* struct used by BPF_LINK_CREATE command */
__u32 prog_fd; /* eBPF program to attach */
__u32 target_fd; /* object to attach to */
__u32 attach_type; /* attach type */
__u32 flags; /* extra flags */
} link_create;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
/* The description below is an attempt at providing documentation to eBPF /* The description below is an attempt at providing documentation to eBPF
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment