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: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
abbc61a5f2
@ -1056,6 +1056,19 @@ extern int sysctl_unprivileged_bpf_disabled;
|
||||
int bpf_map_new_fd(struct bpf_map *map, int flags);
|
||||
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_get_user(const char __user *pathname, int flags);
|
||||
|
||||
|
@ -25,6 +25,7 @@ enum bpf_type {
|
||||
BPF_TYPE_UNSPEC = 0,
|
||||
BPF_TYPE_PROG,
|
||||
BPF_TYPE_MAP,
|
||||
BPF_TYPE_LINK,
|
||||
};
|
||||
|
||||
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:
|
||||
bpf_map_inc_with_uref(raw);
|
||||
break;
|
||||
case BPF_TYPE_LINK:
|
||||
bpf_link_inc(raw);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
@ -53,6 +57,9 @@ static void bpf_any_put(void *raw, enum bpf_type type)
|
||||
case BPF_TYPE_MAP:
|
||||
bpf_map_put_with_uref(raw);
|
||||
break;
|
||||
case BPF_TYPE_LINK:
|
||||
bpf_link_put(raw);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
@ -63,20 +70,32 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type)
|
||||
{
|
||||
void *raw;
|
||||
|
||||
*type = BPF_TYPE_MAP;
|
||||
raw = bpf_map_get_with_uref(ufd);
|
||||
if (IS_ERR(raw)) {
|
||||
*type = BPF_TYPE_PROG;
|
||||
raw = bpf_prog_get(ufd);
|
||||
if (!IS_ERR(raw)) {
|
||||
*type = BPF_TYPE_MAP;
|
||||
return raw;
|
||||
}
|
||||
|
||||
return raw;
|
||||
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 ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static const struct inode_operations bpf_dir_iops;
|
||||
|
||||
static const struct inode_operations bpf_prog_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,
|
||||
const struct inode *dir,
|
||||
@ -114,6 +133,8 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)
|
||||
*type = BPF_TYPE_PROG;
|
||||
else if (inode->i_op == &bpf_map_iops)
|
||||
*type = BPF_TYPE_MAP;
|
||||
else if (inode->i_op == &bpf_link_iops)
|
||||
*type = BPF_TYPE_LINK;
|
||||
else
|
||||
return -EACCES;
|
||||
|
||||
@ -335,6 +356,12 @@ static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)
|
||||
&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 *
|
||||
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,
|
||||
case BPF_TYPE_MAP:
|
||||
ret = vfs_mkobj(dentry, mode, bpf_mkmap, raw);
|
||||
break;
|
||||
case BPF_TYPE_LINK:
|
||||
ret = vfs_mkobj(dentry, mode, bpf_mklink, raw);
|
||||
break;
|
||||
default:
|
||||
ret = -EPERM;
|
||||
}
|
||||
@ -487,6 +517,8 @@ int bpf_obj_get_user(const char __user *pathname, int flags)
|
||||
ret = bpf_prog_new_fd(raw);
|
||||
else if (type == BPF_TYPE_MAP)
|
||||
ret = bpf_map_new_fd(raw, f_flags);
|
||||
else if (type == BPF_TYPE_LINK)
|
||||
ret = bpf_link_new_fd(raw);
|
||||
else
|
||||
return -ENOENT;
|
||||
|
||||
@ -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)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (inode->i_op == &bpf_link_iops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (inode->i_op != &bpf_prog_iops)
|
||||
return ERR_PTR(-EACCES);
|
||||
|
||||
|
@ -2173,24 +2173,154 @@ static int bpf_obj_get(const union bpf_attr *attr)
|
||||
attr->file_flags);
|
||||
}
|
||||
|
||||
static int bpf_tracing_prog_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct bpf_prog *prog = filp->private_data;
|
||||
struct bpf_link {
|
||||
atomic64_t refcnt;
|
||||
const struct bpf_link_ops *ops;
|
||||
struct bpf_prog *prog;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
|
||||
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
|
||||
struct bpf_prog *prog)
|
||||
{
|
||||
atomic64_set(&link->refcnt, 1);
|
||||
link->ops = ops;
|
||||
link->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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static const struct file_operations bpf_tracing_prog_fops = {
|
||||
.release = bpf_tracing_prog_release,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
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,
|
||||
.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)
|
||||
{
|
||||
int tr_fd, err;
|
||||
struct bpf_tracing_link *link;
|
||||
int link_fd, err;
|
||||
|
||||
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
|
||||
prog->expected_attach_type != BPF_TRACE_FEXIT &&
|
||||
@ -2199,58 +2329,61 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
err = bpf_trampoline_link_prog(prog);
|
||||
if (err)
|
||||
goto out_put_prog;
|
||||
|
||||
tr_fd = anon_inode_getfd("bpf-tracing-prog", &bpf_tracing_prog_fops,
|
||||
prog, O_CLOEXEC);
|
||||
if (tr_fd < 0) {
|
||||
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
|
||||
err = tr_fd;
|
||||
link = kzalloc(sizeof(*link), GFP_USER);
|
||||
if (!link) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_prog;
|
||||
}
|
||||
return tr_fd;
|
||||
bpf_link_init(&link->link, &bpf_tracing_link_lops, prog);
|
||||
|
||||
err = bpf_trampoline_link_prog(prog);
|
||||
if (err)
|
||||
goto out_free_link;
|
||||
|
||||
link_fd = bpf_link_new_fd(&link->link);
|
||||
if (link_fd < 0) {
|
||||
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
|
||||
err = link_fd;
|
||||
goto out_free_link;
|
||||
}
|
||||
return link_fd;
|
||||
|
||||
out_free_link:
|
||||
kfree(link);
|
||||
out_put_prog:
|
||||
bpf_prog_put(prog);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct bpf_raw_tracepoint {
|
||||
struct bpf_raw_tp_link {
|
||||
struct bpf_link link;
|
||||
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->prog);
|
||||
bpf_prog_put(raw_tp->prog);
|
||||
}
|
||||
bpf_probe_unregister(raw_tp->btp, raw_tp->link.prog);
|
||||
bpf_put_raw_tracepoint(raw_tp->btp);
|
||||
kfree(raw_tp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations bpf_raw_tp_fops = {
|
||||
.release = bpf_raw_tracepoint_release,
|
||||
.read = bpf_dummy_read,
|
||||
.write = bpf_dummy_write,
|
||||
static const struct bpf_link_ops bpf_raw_tp_lops = {
|
||||
.release = bpf_raw_tp_link_release,
|
||||
};
|
||||
|
||||
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
|
||||
|
||||
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_prog *prog;
|
||||
const char *tp_name;
|
||||
char buf[128];
|
||||
int tp_fd, err;
|
||||
int link_fd, err;
|
||||
|
||||
if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
|
||||
return -EINVAL;
|
||||
@ -2302,21 +2435,20 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
err = -ENOMEM;
|
||||
goto out_put_btp;
|
||||
}
|
||||
bpf_link_init(&raw_tp->link, &bpf_raw_tp_lops, prog);
|
||||
raw_tp->btp = btp;
|
||||
raw_tp->prog = prog;
|
||||
|
||||
err = bpf_probe_register(raw_tp->btp, prog);
|
||||
if (err)
|
||||
goto out_free_tp;
|
||||
|
||||
tp_fd = anon_inode_getfd("bpf-raw-tracepoint", &bpf_raw_tp_fops, raw_tp,
|
||||
O_CLOEXEC);
|
||||
if (tp_fd < 0) {
|
||||
link_fd = bpf_link_new_fd(&raw_tp->link);
|
||||
if (link_fd < 0) {
|
||||
bpf_probe_unregister(raw_tp->btp, prog);
|
||||
err = tp_fd;
|
||||
err = link_fd;
|
||||
goto out_free_tp;
|
||||
}
|
||||
return tp_fd;
|
||||
return link_fd;
|
||||
|
||||
out_free_tp:
|
||||
kfree(raw_tp);
|
||||
@ -3266,15 +3398,21 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (file->f_op == &bpf_raw_tp_fops) {
|
||||
struct bpf_raw_tracepoint *raw_tp = file->private_data;
|
||||
struct bpf_raw_event_map *btp = raw_tp->btp;
|
||||
if (file->f_op == &bpf_link_fops) {
|
||||
struct bpf_link *link = file->private_data;
|
||||
|
||||
err = bpf_task_fd_query_copy(attr, uattr,
|
||||
raw_tp->prog->aux->id,
|
||||
BPF_FD_TYPE_RAW_TRACEPOINT,
|
||||
btp->tp->name, 0, 0);
|
||||
goto put_file;
|
||||
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;
|
||||
|
||||
err = bpf_task_fd_query_copy(attr, uattr,
|
||||
raw_tp->link.prog->aux->id,
|
||||
BPF_FD_TYPE_RAW_TRACEPOINT,
|
||||
btp->tp->name, 0, 0);
|
||||
goto put_file;
|
||||
}
|
||||
goto out_not_supp;
|
||||
}
|
||||
|
||||
event = perf_get_event(file);
|
||||
@ -3294,6 +3432,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
|
||||
goto put_file;
|
||||
}
|
||||
|
||||
out_not_supp:
|
||||
err = -ENOTSUPP;
|
||||
put_file:
|
||||
fput(file);
|
||||
|
@ -6931,6 +6931,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
|
||||
struct bpf_link {
|
||||
int (*detach)(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;
|
||||
};
|
||||
|
||||
@ -6960,26 +6962,109 @@ int bpf_link__destroy(struct bpf_link *link)
|
||||
err = link->detach(link);
|
||||
if (link->destroy)
|
||||
link->destroy(link);
|
||||
if (link->pin_path)
|
||||
free(link->pin_path);
|
||||
free(link);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct bpf_link_fd {
|
||||
struct bpf_link link; /* has to be at the top of struct */
|
||||
int fd; /* hook FD */
|
||||
};
|
||||
int bpf_link__fd(const struct bpf_link *link)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct bpf_link_fd *l = (void *)link;
|
||||
int err;
|
||||
|
||||
err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||
err = ioctl(link->fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||
if (err)
|
||||
err = -errno;
|
||||
|
||||
close(l->fd);
|
||||
close(link->fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -6987,7 +7072,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
|
||||
int pfd)
|
||||
{
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
struct bpf_link_fd *link;
|
||||
struct bpf_link *link;
|
||||
int prog_fd, err;
|
||||
|
||||
if (pfd < 0) {
|
||||
@ -7005,7 +7090,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
|
||||
link = calloc(1, sizeof(*link));
|
||||
if (!link)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
link->link.detach = &bpf_link__detach_perf_event;
|
||||
link->detach = &bpf_link__detach_perf_event;
|
||||
link->fd = pfd;
|
||||
|
||||
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,
|
||||
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
return (struct bpf_link *)link;
|
||||
return link;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -7312,18 +7397,11 @@ out:
|
||||
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,
|
||||
const char *tp_name)
|
||||
{
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
struct bpf_link_fd *link;
|
||||
struct bpf_link *link;
|
||||
int prog_fd, pfd;
|
||||
|
||||
prog_fd = bpf_program__fd(prog);
|
||||
@ -7336,7 +7414,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
|
||||
link = calloc(1, sizeof(*link));
|
||||
if (!link)
|
||||
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);
|
||||
if (pfd < 0) {
|
||||
@ -7348,7 +7426,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
|
||||
return ERR_PTR(pfd);
|
||||
}
|
||||
link->fd = pfd;
|
||||
return (struct bpf_link *)link;
|
||||
return link;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
struct bpf_link_fd *link;
|
||||
struct bpf_link *link;
|
||||
int prog_fd, pfd;
|
||||
|
||||
prog_fd = bpf_program__fd(prog);
|
||||
@ -7375,7 +7453,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
|
||||
link = calloc(1, sizeof(*link));
|
||||
if (!link)
|
||||
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);
|
||||
if (pfd < 0) {
|
||||
@ -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)
|
||||
{
|
||||
struct bpf_link_fd *l = (void *)link;
|
||||
__u32 zero = 0;
|
||||
|
||||
if (bpf_map_delete_elem(l->fd, &zero))
|
||||
if (bpf_map_delete_elem(link->fd, &zero))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
@ -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_struct_ops *st_ops;
|
||||
struct bpf_link_fd *link;
|
||||
struct bpf_link *link;
|
||||
__u32 i, zero = 0;
|
||||
int err;
|
||||
|
||||
@ -7453,10 +7530,10 @@ struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
link->link.detach = bpf_link__detach_struct_ops;
|
||||
link->detach = bpf_link__detach_struct_ops;
|
||||
link->fd = map->fd;
|
||||
|
||||
return (struct bpf_link *)link;
|
||||
return link;
|
||||
}
|
||||
|
||||
enum bpf_perf_event_ret
|
||||
|
@ -219,6 +219,11 @@ LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
|
||||
|
||||
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 int bpf_link__destroy(struct bpf_link *link);
|
||||
|
||||
|
@ -238,5 +238,10 @@ LIBBPF_0.0.7 {
|
||||
|
||||
LIBBPF_0.0.8 {
|
||||
global:
|
||||
bpf_link__fd;
|
||||
bpf_link__open;
|
||||
bpf_link__pin;
|
||||
bpf_link__pin_path;
|
||||
bpf_link__unpin;
|
||||
bpf_program__set_attach_target;
|
||||
} LIBBPF_0.0.7;
|
||||
|
105
tools/testing/selftests/bpf/prog_tests/link_pinning.c
Normal file
105
tools/testing/selftests/bpf/prog_tests/link_pinning.c
Normal file
@ -0,0 +1,105 @@
|
||||
// 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);
|
||||
}
|
25
tools/testing/selftests/bpf/progs/test_link_pinning.c
Normal file
25
tools/testing/selftests/bpf/progs/test_link_pinning.c
Normal file
@ -0,0 +1,25 @@
|
||||
// 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";
|
Loading…
x
Reference in New Issue
Block a user