Commit abbc61a5 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'bpf_link'

Andrii Nakryiko says:

====================
This patch series adds bpf_link abstraction, analogous to libbpf's already
existing bpf_link abstraction. This formalizes and makes more uniform existing
bpf_link-like BPF program link (attachment) types (raw tracepoint and tracing
links), which are FD-based objects that are automatically detached when last
file reference is closed. These types of BPF program links are switched to
using bpf_link framework.

FD-based bpf_link approach provides great safety guarantees, by ensuring there
is not going to be an abandoned BPF program attached, if user process suddenly
exits or forgets to clean up after itself. This is especially important in
production environment and is what all the recent new BPF link types followed.

One of the previously existing  inconveniences of FD-based approach, though,
was the scenario in which user process wants to install BPF link and exit, but
let attached BPF program run. Now, with bpf_link abstraction in place, it's
easy to support pinning links in BPF FS, which is done as part of the same
patch #1. This allows FD-based BPF program links to survive exit of a user
process and original file descriptor being closed, by creating an file entry
in BPF FS. This provides great safety by default, with simple way to opt out
for cases where it's needed.

Corresponding libbpf APIs are added in the same patch set, as well as
selftests for this functionality.

Other types of BPF program attachments (XDP, cgroup, perf_event, etc) are
going to be converted in subsequent patches to follow similar approach.

v1->v2:
- use bpf_link_new_fd() uniformly (Alexei).
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 775a2be5 6489b8e1
...@@ -1056,6 +1056,19 @@ extern int sysctl_unprivileged_bpf_disabled; ...@@ -1056,6 +1056,19 @@ 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_ops {
void (*release)(struct bpf_link *link);
};
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
struct bpf_prog *prog);
void bpf_link_inc(struct bpf_link *link);
void bpf_link_put(struct bpf_link *link);
int bpf_link_new_fd(struct bpf_link *link);
struct bpf_link *bpf_link_get_from_fd(u32 ufd);
int bpf_obj_pin_user(u32 ufd, const char __user *pathname); int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
int bpf_obj_get_user(const char __user *pathname, int flags); int bpf_obj_get_user(const char __user *pathname, int flags);
......
...@@ -25,6 +25,7 @@ enum bpf_type { ...@@ -25,6 +25,7 @@ enum bpf_type {
BPF_TYPE_UNSPEC = 0, BPF_TYPE_UNSPEC = 0,
BPF_TYPE_PROG, BPF_TYPE_PROG,
BPF_TYPE_MAP, BPF_TYPE_MAP,
BPF_TYPE_LINK,
}; };
static void *bpf_any_get(void *raw, enum bpf_type type) static void *bpf_any_get(void *raw, enum bpf_type type)
...@@ -36,6 +37,9 @@ static void *bpf_any_get(void *raw, enum bpf_type type) ...@@ -36,6 +37,9 @@ static void *bpf_any_get(void *raw, enum bpf_type type)
case BPF_TYPE_MAP: case BPF_TYPE_MAP:
bpf_map_inc_with_uref(raw); bpf_map_inc_with_uref(raw);
break; break;
case BPF_TYPE_LINK:
bpf_link_inc(raw);
break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
break; break;
...@@ -53,6 +57,9 @@ static void bpf_any_put(void *raw, enum bpf_type type) ...@@ -53,6 +57,9 @@ static void bpf_any_put(void *raw, enum bpf_type type)
case BPF_TYPE_MAP: case BPF_TYPE_MAP:
bpf_map_put_with_uref(raw); bpf_map_put_with_uref(raw);
break; break;
case BPF_TYPE_LINK:
bpf_link_put(raw);
break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
break; break;
...@@ -63,20 +70,32 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type) ...@@ -63,20 +70,32 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type)
{ {
void *raw; void *raw;
*type = BPF_TYPE_MAP;
raw = bpf_map_get_with_uref(ufd); raw = bpf_map_get_with_uref(ufd);
if (IS_ERR(raw)) { if (!IS_ERR(raw)) {
*type = BPF_TYPE_PROG; *type = BPF_TYPE_MAP;
return raw;
}
raw = bpf_prog_get(ufd); raw = bpf_prog_get(ufd);
if (!IS_ERR(raw)) {
*type = BPF_TYPE_PROG;
return raw;
} }
raw = bpf_link_get_from_fd(ufd);
if (!IS_ERR(raw)) {
*type = BPF_TYPE_LINK;
return raw; return raw;
}
return ERR_PTR(-EINVAL);
} }
static const struct inode_operations bpf_dir_iops; static const struct inode_operations bpf_dir_iops;
static const struct inode_operations bpf_prog_iops = { }; static const struct inode_operations bpf_prog_iops = { };
static const struct inode_operations bpf_map_iops = { }; static const struct inode_operations bpf_map_iops = { };
static const struct inode_operations bpf_link_iops = { };
static struct inode *bpf_get_inode(struct super_block *sb, static struct inode *bpf_get_inode(struct super_block *sb,
const struct inode *dir, const struct inode *dir,
...@@ -114,6 +133,8 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type) ...@@ -114,6 +133,8 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)
*type = BPF_TYPE_PROG; *type = BPF_TYPE_PROG;
else if (inode->i_op == &bpf_map_iops) else if (inode->i_op == &bpf_map_iops)
*type = BPF_TYPE_MAP; *type = BPF_TYPE_MAP;
else if (inode->i_op == &bpf_link_iops)
*type = BPF_TYPE_LINK;
else else
return -EACCES; return -EACCES;
...@@ -335,6 +356,12 @@ static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg) ...@@ -335,6 +356,12 @@ static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)
&bpffs_map_fops : &bpffs_obj_fops); &bpffs_map_fops : &bpffs_obj_fops);
} }
static int bpf_mklink(struct dentry *dentry, umode_t mode, void *arg)
{
return bpf_mkobj_ops(dentry, mode, arg, &bpf_link_iops,
&bpffs_obj_fops);
}
static struct dentry * static struct dentry *
bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags) bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)
{ {
...@@ -411,6 +438,9 @@ static int bpf_obj_do_pin(const char __user *pathname, void *raw, ...@@ -411,6 +438,9 @@ static int bpf_obj_do_pin(const char __user *pathname, void *raw,
case BPF_TYPE_MAP: case BPF_TYPE_MAP:
ret = vfs_mkobj(dentry, mode, bpf_mkmap, raw); ret = vfs_mkobj(dentry, mode, bpf_mkmap, raw);
break; break;
case BPF_TYPE_LINK:
ret = vfs_mkobj(dentry, mode, bpf_mklink, raw);
break;
default: default:
ret = -EPERM; ret = -EPERM;
} }
...@@ -487,6 +517,8 @@ int bpf_obj_get_user(const char __user *pathname, int flags) ...@@ -487,6 +517,8 @@ int bpf_obj_get_user(const char __user *pathname, int flags)
ret = bpf_prog_new_fd(raw); ret = bpf_prog_new_fd(raw);
else if (type == BPF_TYPE_MAP) else if (type == BPF_TYPE_MAP)
ret = bpf_map_new_fd(raw, f_flags); ret = bpf_map_new_fd(raw, f_flags);
else if (type == BPF_TYPE_LINK)
ret = bpf_link_new_fd(raw);
else else
return -ENOENT; return -ENOENT;
...@@ -504,6 +536,8 @@ static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type ...@@ -504,6 +536,8 @@ static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type
if (inode->i_op == &bpf_map_iops) if (inode->i_op == &bpf_map_iops)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (inode->i_op == &bpf_link_iops)
return ERR_PTR(-EINVAL);
if (inode->i_op != &bpf_prog_iops) if (inode->i_op != &bpf_prog_iops)
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
......
...@@ -2173,24 +2173,154 @@ static int bpf_obj_get(const union bpf_attr *attr) ...@@ -2173,24 +2173,154 @@ static int bpf_obj_get(const union bpf_attr *attr)
attr->file_flags); attr->file_flags);
} }
static int bpf_tracing_prog_release(struct inode *inode, struct file *filp) 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,
struct bpf_prog *prog)
{ {
struct bpf_prog *prog = filp->private_data; atomic64_set(&link->refcnt, 1);
link->ops = ops;
link->prog = prog;
}
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog)); void bpf_link_inc(struct bpf_link *link)
{
atomic64_inc(&link->refcnt);
}
/* bpf_link_free is guaranteed to be called from process context */
static void bpf_link_free(struct bpf_link *link)
{
struct bpf_prog *prog;
/* remember prog locally, because release below will free link memory */
prog = link->prog;
/* extra clean up and kfree of container link struct */
link->ops->release(link);
/* no more accesing of link members after this point */
bpf_prog_put(prog); bpf_prog_put(prog);
}
static void bpf_link_put_deferred(struct work_struct *work)
{
struct bpf_link *link = container_of(work, struct bpf_link, work);
bpf_link_free(link);
}
/* bpf_link_put can be called from atomic context, but ensures that resources
* are freed from process context
*/
void bpf_link_put(struct bpf_link *link)
{
if (!atomic64_dec_and_test(&link->refcnt))
return;
if (in_atomic()) {
INIT_WORK(&link->work, bpf_link_put_deferred);
schedule_work(&link->work);
} else {
bpf_link_free(link);
}
}
static int bpf_link_release(struct inode *inode, struct file *filp)
{
struct bpf_link *link = filp->private_data;
bpf_link_put(link);
return 0; return 0;
} }
static const struct file_operations bpf_tracing_prog_fops = { #ifdef CONFIG_PROC_FS
.release = bpf_tracing_prog_release, static const struct bpf_link_ops bpf_raw_tp_lops;
static const struct bpf_link_ops bpf_tracing_link_lops;
static const struct bpf_link_ops bpf_xdp_link_lops;
static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
{
const struct bpf_link *link = filp->private_data;
const struct bpf_prog *prog = link->prog;
char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
const char *link_type;
if (link->ops == &bpf_raw_tp_lops)
link_type = "raw_tracepoint";
else if (link->ops == &bpf_tracing_link_lops)
link_type = "tracing";
else
link_type = "unknown";
bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
seq_printf(m,
"link_type:\t%s\n"
"prog_tag:\t%s\n"
"prog_id:\t%u\n",
link_type,
prog_tag,
prog->aux->id);
}
#endif
const struct file_operations bpf_link_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = bpf_link_show_fdinfo,
#endif
.release = bpf_link_release,
.read = bpf_dummy_read, .read = bpf_dummy_read,
.write = bpf_dummy_write, .write = bpf_dummy_write,
}; };
int bpf_link_new_fd(struct bpf_link *link)
{
return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
}
struct bpf_link *bpf_link_get_from_fd(u32 ufd)
{
struct fd f = fdget(ufd);
struct bpf_link *link;
if (!f.file)
return ERR_PTR(-EBADF);
if (f.file->f_op != &bpf_link_fops) {
fdput(f);
return ERR_PTR(-EINVAL);
}
link = f.file->private_data;
bpf_link_inc(link);
fdput(f);
return link;
}
struct bpf_tracing_link {
struct bpf_link link;
};
static void bpf_tracing_link_release(struct bpf_link *link)
{
struct bpf_tracing_link *tr_link =
container_of(link, struct bpf_tracing_link, link);
WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog));
kfree(tr_link);
}
static const struct bpf_link_ops bpf_tracing_link_lops = {
.release = bpf_tracing_link_release,
};
static int bpf_tracing_prog_attach(struct bpf_prog *prog) static int bpf_tracing_prog_attach(struct bpf_prog *prog)
{ {
int tr_fd, err; struct bpf_tracing_link *link;
int link_fd, err;
if (prog->expected_attach_type != BPF_TRACE_FENTRY && if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
prog->expected_attach_type != BPF_TRACE_FEXIT && prog->expected_attach_type != BPF_TRACE_FEXIT &&
...@@ -2199,58 +2329,61 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) ...@@ -2199,58 +2329,61 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
goto out_put_prog; goto out_put_prog;
} }
link = kzalloc(sizeof(*link), GFP_USER);
if (!link) {
err = -ENOMEM;
goto out_put_prog;
}
bpf_link_init(&link->link, &bpf_tracing_link_lops, prog);
err = bpf_trampoline_link_prog(prog); err = bpf_trampoline_link_prog(prog);
if (err) if (err)
goto out_put_prog; goto out_free_link;
tr_fd = anon_inode_getfd("bpf-tracing-prog", &bpf_tracing_prog_fops, link_fd = bpf_link_new_fd(&link->link);
prog, O_CLOEXEC); if (link_fd < 0) {
if (tr_fd < 0) {
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog)); WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
err = tr_fd; err = link_fd;
goto out_put_prog; goto out_free_link;
} }
return tr_fd; return link_fd;
out_free_link:
kfree(link);
out_put_prog: out_put_prog:
bpf_prog_put(prog); bpf_prog_put(prog);
return err; return err;
} }
struct bpf_raw_tracepoint { struct bpf_raw_tp_link {
struct bpf_link link;
struct bpf_raw_event_map *btp; struct bpf_raw_event_map *btp;
struct bpf_prog *prog;
}; };
static int bpf_raw_tracepoint_release(struct inode *inode, struct file *filp) static void bpf_raw_tp_link_release(struct bpf_link *link)
{ {
struct bpf_raw_tracepoint *raw_tp = filp->private_data; struct bpf_raw_tp_link *raw_tp =
container_of(link, struct bpf_raw_tp_link, link);
if (raw_tp->prog) { bpf_probe_unregister(raw_tp->btp, raw_tp->link.prog);
bpf_probe_unregister(raw_tp->btp, raw_tp->prog);
bpf_prog_put(raw_tp->prog);
}
bpf_put_raw_tracepoint(raw_tp->btp); bpf_put_raw_tracepoint(raw_tp->btp);
kfree(raw_tp); kfree(raw_tp);
return 0;
} }
static const struct file_operations bpf_raw_tp_fops = { static const struct bpf_link_ops bpf_raw_tp_lops = {
.release = bpf_raw_tracepoint_release, .release = bpf_raw_tp_link_release,
.read = bpf_dummy_read,
.write = bpf_dummy_write,
}; };
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
static int bpf_raw_tracepoint_open(const union bpf_attr *attr) static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
{ {
struct bpf_raw_tracepoint *raw_tp; struct bpf_raw_tp_link *raw_tp;
struct bpf_raw_event_map *btp; struct bpf_raw_event_map *btp;
struct bpf_prog *prog; struct bpf_prog *prog;
const char *tp_name; const char *tp_name;
char buf[128]; char buf[128];
int tp_fd, err; int link_fd, err;
if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN)) if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
return -EINVAL; return -EINVAL;
...@@ -2302,21 +2435,20 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) ...@@ -2302,21 +2435,20 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
err = -ENOMEM; err = -ENOMEM;
goto out_put_btp; goto out_put_btp;
} }
bpf_link_init(&raw_tp->link, &bpf_raw_tp_lops, prog);
raw_tp->btp = btp; raw_tp->btp = btp;
raw_tp->prog = prog;
err = bpf_probe_register(raw_tp->btp, prog); err = bpf_probe_register(raw_tp->btp, prog);
if (err) if (err)
goto out_free_tp; goto out_free_tp;
tp_fd = anon_inode_getfd("bpf-raw-tracepoint", &bpf_raw_tp_fops, raw_tp, link_fd = bpf_link_new_fd(&raw_tp->link);
O_CLOEXEC); if (link_fd < 0) {
if (tp_fd < 0) {
bpf_probe_unregister(raw_tp->btp, prog); bpf_probe_unregister(raw_tp->btp, prog);
err = tp_fd; err = link_fd;
goto out_free_tp; goto out_free_tp;
} }
return tp_fd; return link_fd;
out_free_tp: out_free_tp:
kfree(raw_tp); kfree(raw_tp);
...@@ -3266,16 +3398,22 @@ static int bpf_task_fd_query(const union bpf_attr *attr, ...@@ -3266,16 +3398,22 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
if (err) if (err)
goto out; goto out;
if (file->f_op == &bpf_raw_tp_fops) { if (file->f_op == &bpf_link_fops) {
struct bpf_raw_tracepoint *raw_tp = file->private_data; struct bpf_link *link = file->private_data;
if (link->ops == &bpf_raw_tp_lops) {
struct bpf_raw_tp_link *raw_tp =
container_of(link, struct bpf_raw_tp_link, link);
struct bpf_raw_event_map *btp = raw_tp->btp; struct bpf_raw_event_map *btp = raw_tp->btp;
err = bpf_task_fd_query_copy(attr, uattr, err = bpf_task_fd_query_copy(attr, uattr,
raw_tp->prog->aux->id, raw_tp->link.prog->aux->id,
BPF_FD_TYPE_RAW_TRACEPOINT, BPF_FD_TYPE_RAW_TRACEPOINT,
btp->tp->name, 0, 0); btp->tp->name, 0, 0);
goto put_file; goto put_file;
} }
goto out_not_supp;
}
event = perf_get_event(file); event = perf_get_event(file);
if (!IS_ERR(event)) { if (!IS_ERR(event)) {
...@@ -3294,6 +3432,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr, ...@@ -3294,6 +3432,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
goto put_file; goto put_file;
} }
out_not_supp:
err = -ENOTSUPP; err = -ENOTSUPP;
put_file: put_file:
fput(file); fput(file);
......
...@@ -6931,6 +6931,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, ...@@ -6931,6 +6931,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
struct bpf_link { struct bpf_link {
int (*detach)(struct bpf_link *link); int (*detach)(struct bpf_link *link);
int (*destroy)(struct bpf_link *link); int (*destroy)(struct bpf_link *link);
char *pin_path; /* NULL, if not pinned */
int fd; /* hook FD, -1 if not applicable */
bool disconnected; bool disconnected;
}; };
...@@ -6960,26 +6962,109 @@ int bpf_link__destroy(struct bpf_link *link) ...@@ -6960,26 +6962,109 @@ int bpf_link__destroy(struct bpf_link *link)
err = link->detach(link); err = link->detach(link);
if (link->destroy) if (link->destroy)
link->destroy(link); link->destroy(link);
if (link->pin_path)
free(link->pin_path);
free(link); free(link);
return err; return err;
} }
struct bpf_link_fd { int bpf_link__fd(const struct bpf_link *link)
struct bpf_link link; /* has to be at the top of struct */ {
int fd; /* hook FD */ return link->fd;
}; }
const char *bpf_link__pin_path(const struct bpf_link *link)
{
return link->pin_path;
}
static int bpf_link__detach_fd(struct bpf_link *link)
{
return close(link->fd);
}
struct bpf_link *bpf_link__open(const char *path)
{
struct bpf_link *link;
int fd;
fd = bpf_obj_get(path);
if (fd < 0) {
fd = -errno;
pr_warn("failed to open link at %s: %d\n", path, fd);
return ERR_PTR(fd);
}
link = calloc(1, sizeof(*link));
if (!link) {
close(fd);
return ERR_PTR(-ENOMEM);
}
link->detach = &bpf_link__detach_fd;
link->fd = fd;
link->pin_path = strdup(path);
if (!link->pin_path) {
bpf_link__destroy(link);
return ERR_PTR(-ENOMEM);
}
return link;
}
int bpf_link__pin(struct bpf_link *link, const char *path)
{
int err;
if (link->pin_path)
return -EBUSY;
err = make_parent_dir(path);
if (err)
return err;
err = check_path(path);
if (err)
return err;
link->pin_path = strdup(path);
if (!link->pin_path)
return -ENOMEM;
if (bpf_obj_pin(link->fd, link->pin_path)) {
err = -errno;
zfree(&link->pin_path);
return err;
}
pr_debug("link fd=%d: pinned at %s\n", link->fd, link->pin_path);
return 0;
}
int bpf_link__unpin(struct bpf_link *link)
{
int err;
if (!link->pin_path)
return -EINVAL;
err = unlink(link->pin_path);
if (err != 0)
return -errno;
pr_debug("link fd=%d: unpinned from %s\n", link->fd, link->pin_path);
zfree(&link->pin_path);
return 0;
}
static int bpf_link__detach_perf_event(struct bpf_link *link) static int bpf_link__detach_perf_event(struct bpf_link *link)
{ {
struct bpf_link_fd *l = (void *)link;
int err; int err;
err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0); err = ioctl(link->fd, PERF_EVENT_IOC_DISABLE, 0);
if (err) if (err)
err = -errno; err = -errno;
close(l->fd); close(link->fd);
return err; return err;
} }
...@@ -6987,7 +7072,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, ...@@ -6987,7 +7072,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
int pfd) int pfd)
{ {
char errmsg[STRERR_BUFSIZE]; char errmsg[STRERR_BUFSIZE];
struct bpf_link_fd *link; struct bpf_link *link;
int prog_fd, err; int prog_fd, err;
if (pfd < 0) { if (pfd < 0) {
...@@ -7005,7 +7090,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, ...@@ -7005,7 +7090,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
link = calloc(1, sizeof(*link)); link = calloc(1, sizeof(*link));
if (!link) if (!link)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
link->link.detach = &bpf_link__detach_perf_event; link->detach = &bpf_link__detach_perf_event;
link->fd = pfd; link->fd = pfd;
if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
...@@ -7024,7 +7109,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, ...@@ -7024,7 +7109,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
libbpf_strerror_r(err, errmsg, sizeof(errmsg))); libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
return ERR_PTR(err); return ERR_PTR(err);
} }
return (struct bpf_link *)link; return link;
} }
/* /*
...@@ -7312,18 +7397,11 @@ static struct bpf_link *attach_tp(const struct bpf_sec_def *sec, ...@@ -7312,18 +7397,11 @@ static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
return link; return link;
} }
static int bpf_link__detach_fd(struct bpf_link *link)
{
struct bpf_link_fd *l = (void *)link;
return close(l->fd);
}
struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
const char *tp_name) const char *tp_name)
{ {
char errmsg[STRERR_BUFSIZE]; char errmsg[STRERR_BUFSIZE];
struct bpf_link_fd *link; struct bpf_link *link;
int prog_fd, pfd; int prog_fd, pfd;
prog_fd = bpf_program__fd(prog); prog_fd = bpf_program__fd(prog);
...@@ -7336,7 +7414,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, ...@@ -7336,7 +7414,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
link = calloc(1, sizeof(*link)); link = calloc(1, sizeof(*link));
if (!link) if (!link)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
link->link.detach = &bpf_link__detach_fd; link->detach = &bpf_link__detach_fd;
pfd = bpf_raw_tracepoint_open(tp_name, prog_fd); pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
if (pfd < 0) { if (pfd < 0) {
...@@ -7348,7 +7426,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, ...@@ -7348,7 +7426,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
return ERR_PTR(pfd); return ERR_PTR(pfd);
} }
link->fd = pfd; link->fd = pfd;
return (struct bpf_link *)link; return link;
} }
static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
...@@ -7362,7 +7440,7 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, ...@@ -7362,7 +7440,7 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
{ {
char errmsg[STRERR_BUFSIZE]; char errmsg[STRERR_BUFSIZE];
struct bpf_link_fd *link; struct bpf_link *link;
int prog_fd, pfd; int prog_fd, pfd;
prog_fd = bpf_program__fd(prog); prog_fd = bpf_program__fd(prog);
...@@ -7375,7 +7453,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) ...@@ -7375,7 +7453,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
link = calloc(1, sizeof(*link)); link = calloc(1, sizeof(*link));
if (!link) if (!link)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
link->link.detach = &bpf_link__detach_fd; link->detach = &bpf_link__detach_fd;
pfd = bpf_raw_tracepoint_open(NULL, prog_fd); pfd = bpf_raw_tracepoint_open(NULL, prog_fd);
if (pfd < 0) { if (pfd < 0) {
...@@ -7409,10 +7487,9 @@ struct bpf_link *bpf_program__attach(struct bpf_program *prog) ...@@ -7409,10 +7487,9 @@ struct bpf_link *bpf_program__attach(struct bpf_program *prog)
static int bpf_link__detach_struct_ops(struct bpf_link *link) static int bpf_link__detach_struct_ops(struct bpf_link *link)
{ {
struct bpf_link_fd *l = (void *)link;
__u32 zero = 0; __u32 zero = 0;
if (bpf_map_delete_elem(l->fd, &zero)) if (bpf_map_delete_elem(link->fd, &zero))
return -errno; return -errno;
return 0; return 0;
...@@ -7421,7 +7498,7 @@ static int bpf_link__detach_struct_ops(struct bpf_link *link) ...@@ -7421,7 +7498,7 @@ static int bpf_link__detach_struct_ops(struct bpf_link *link)
struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map) struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map)
{ {
struct bpf_struct_ops *st_ops; struct bpf_struct_ops *st_ops;
struct bpf_link_fd *link; struct bpf_link *link;
__u32 i, zero = 0; __u32 i, zero = 0;
int err; int err;
...@@ -7453,10 +7530,10 @@ struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map) ...@@ -7453,10 +7530,10 @@ struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map)
return ERR_PTR(err); return ERR_PTR(err);
} }
link->link.detach = bpf_link__detach_struct_ops; link->detach = bpf_link__detach_struct_ops;
link->fd = map->fd; link->fd = map->fd;
return (struct bpf_link *)link; return link;
} }
enum bpf_perf_event_ret enum bpf_perf_event_ret
......
...@@ -219,6 +219,11 @@ LIBBPF_API void bpf_program__unload(struct bpf_program *prog); ...@@ -219,6 +219,11 @@ LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
struct bpf_link; struct bpf_link;
LIBBPF_API struct bpf_link *bpf_link__open(const char *path);
LIBBPF_API int bpf_link__fd(const struct bpf_link *link);
LIBBPF_API const char *bpf_link__pin_path(const struct bpf_link *link);
LIBBPF_API int bpf_link__pin(struct bpf_link *link, const char *path);
LIBBPF_API int bpf_link__unpin(struct bpf_link *link);
LIBBPF_API void bpf_link__disconnect(struct bpf_link *link); LIBBPF_API void bpf_link__disconnect(struct bpf_link *link);
LIBBPF_API int bpf_link__destroy(struct bpf_link *link); LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
......
...@@ -238,5 +238,10 @@ LIBBPF_0.0.7 { ...@@ -238,5 +238,10 @@ LIBBPF_0.0.7 {
LIBBPF_0.0.8 { LIBBPF_0.0.8 {
global: global:
bpf_link__fd;
bpf_link__open;
bpf_link__pin;
bpf_link__pin_path;
bpf_link__unpin;
bpf_program__set_attach_target; bpf_program__set_attach_target;
} LIBBPF_0.0.7; } LIBBPF_0.0.7;
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020 Facebook */
#include <test_progs.h>
#include <sys/stat.h>
#include "test_link_pinning.skel.h"
static int duration = 0;
void test_link_pinning_subtest(struct bpf_program *prog,
struct test_link_pinning__bss *bss)
{
const char *link_pin_path = "/sys/fs/bpf/pinned_link_test";
struct stat statbuf = {};
struct bpf_link *link;
int err, i;
link = bpf_program__attach(prog);
if (CHECK(IS_ERR(link), "link_attach", "err: %ld\n", PTR_ERR(link)))
goto cleanup;
bss->in = 1;
usleep(1);
CHECK(bss->out != 1, "res_check1", "exp %d, got %d\n", 1, bss->out);
/* pin link */
err = bpf_link__pin(link, link_pin_path);
if (CHECK(err, "link_pin", "err: %d\n", err))
goto cleanup;
CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path1",
"exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
/* check that link was pinned */
err = stat(link_pin_path, &statbuf);
if (CHECK(err, "stat_link", "err %d errno %d\n", err, errno))
goto cleanup;
bss->in = 2;
usleep(1);
CHECK(bss->out != 2, "res_check2", "exp %d, got %d\n", 2, bss->out);
/* destroy link, pinned link should keep program attached */
bpf_link__destroy(link);
link = NULL;
bss->in = 3;
usleep(1);
CHECK(bss->out != 3, "res_check3", "exp %d, got %d\n", 3, bss->out);
/* re-open link from BPFFS */
link = bpf_link__open(link_pin_path);
if (CHECK(IS_ERR(link), "link_open", "err: %ld\n", PTR_ERR(link)))
goto cleanup;
CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path2",
"exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
/* unpin link from BPFFS, program still attached */
err = bpf_link__unpin(link);
if (CHECK(err, "link_unpin", "err: %d\n", err))
goto cleanup;
/* still active, as we have FD open now */
bss->in = 4;
usleep(1);
CHECK(bss->out != 4, "res_check4", "exp %d, got %d\n", 4, bss->out);
bpf_link__destroy(link);
link = NULL;
/* Validate it's finally detached.
* Actual detachment might get delayed a bit, so there is no reliable
* way to validate it immediately here, let's count up for long enough
* and see if eventually output stops being updated
*/
for (i = 5; i < 10000; i++) {
bss->in = i;
usleep(1);
if (bss->out == i - 1)
break;
}
CHECK(i == 10000, "link_attached", "got to iteration #%d\n", i);
cleanup:
if (!IS_ERR(link))
bpf_link__destroy(link);
}
void test_link_pinning(void)
{
struct test_link_pinning* skel;
skel = test_link_pinning__open_and_load();
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
return;
if (test__start_subtest("pin_raw_tp"))
test_link_pinning_subtest(skel->progs.raw_tp_prog, skel->bss);
if (test__start_subtest("pin_tp_btf"))
test_link_pinning_subtest(skel->progs.tp_btf_prog, skel->bss);
test_link_pinning__destroy(skel);
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020 Facebook */
#include <stdbool.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
int in = 0;
int out = 0;
SEC("raw_tp/sys_enter")
int raw_tp_prog(const void *ctx)
{
out = in;
return 0;
}
SEC("tp_btf/sys_enter")
int tp_btf_prog(const void *ctx)
{
out = in;
return 0;
}
char _license[] SEC("license") = "GPL";
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