Skip to content

Commit

Permalink
Merge branch 'link_detach'
Browse files Browse the repository at this point in the history
Andrii Nakryiko says:

====================
This patch set adds new BPF link operation, LINK_DETACH, allowing processes
with BPF link FD to force-detach it from respective BPF hook, similarly how
BPF link is auto-detached when such BPF hook (e.g., cgroup, net_device, netns,
etc) is removed. This facility allows admin to forcefully undo BPF link
attachment, while process that created BPF link in the first place is left
intact.

Once force-detached, BPF link stays valid in the kernel as long as there is at
least one FD open against it. It goes into defunct state, just like
auto-detached BPF link.

bpftool also got `link detach` command to allow triggering this in
non-programmatic fashion.

v1->v2:
- improve error reporting in `bpftool link detach` (Song).
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed Aug 2, 2020
2 parents 4939b28 + e85f99a commit 5a6b1a2
Show file tree
Hide file tree
Showing 20 changed files with 208 additions and 34 deletions.
1 change: 1 addition & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ struct bpf_link {
struct bpf_link_ops {
void (*release)(struct bpf_link *link);
void (*dealloc)(struct bpf_link *link);
int (*detach)(struct bpf_link *link);
int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
struct bpf_prog *old_prog);
void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
Expand Down
5 changes: 5 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ enum bpf_cmd {
BPF_LINK_GET_NEXT_ID,
BPF_ENABLE_STATS,
BPF_ITER_CREATE,
BPF_LINK_DETACH,
};

enum bpf_map_type {
Expand Down Expand Up @@ -634,6 +635,10 @@ union bpf_attr {
__u32 old_prog_fd;
} link_update;

struct {
__u32 link_fd;
} link_detach;

struct { /* struct used by BPF_ENABLE_STATS command */
__u32 type;
} enable_stats;
Expand Down
15 changes: 14 additions & 1 deletion kernel/bpf/cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ static void bpf_cgroup_link_release(struct bpf_link *link)
{
struct bpf_cgroup_link *cg_link =
container_of(link, struct bpf_cgroup_link, link);
struct cgroup *cg;

/* link might have been auto-detached by dying cgroup already,
* in that case our work is done here
Expand All @@ -832,8 +833,12 @@ static void bpf_cgroup_link_release(struct bpf_link *link)
WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link,
cg_link->type));

cg = cg_link->cgroup;
cg_link->cgroup = NULL;

mutex_unlock(&cgroup_mutex);
cgroup_put(cg_link->cgroup);

cgroup_put(cg);
}

static void bpf_cgroup_link_dealloc(struct bpf_link *link)
Expand All @@ -844,6 +849,13 @@ static void bpf_cgroup_link_dealloc(struct bpf_link *link)
kfree(cg_link);
}

static int bpf_cgroup_link_detach(struct bpf_link *link)
{
bpf_cgroup_link_release(link);

return 0;
}

static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
struct seq_file *seq)
{
Expand Down Expand Up @@ -883,6 +895,7 @@ static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
static const struct bpf_link_ops bpf_cgroup_link_lops = {
.release = bpf_cgroup_link_release,
.dealloc = bpf_cgroup_link_dealloc,
.detach = bpf_cgroup_link_detach,
.update_prog = cgroup_bpf_replace,
.show_fdinfo = bpf_cgroup_link_show_fdinfo,
.fill_link_info = bpf_cgroup_link_fill_link_info,
Expand Down
8 changes: 8 additions & 0 deletions kernel/bpf/net_namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,16 @@ static void bpf_netns_link_release(struct bpf_link *link)
bpf_prog_array_free(old_array);

out_unlock:
net_link->net = NULL;
mutex_unlock(&netns_bpf_mutex);
}

static int bpf_netns_link_detach(struct bpf_link *link)
{
bpf_netns_link_release(link);
return 0;
}

static void bpf_netns_link_dealloc(struct bpf_link *link)
{
struct bpf_netns_link *net_link =
Expand Down Expand Up @@ -228,6 +235,7 @@ static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
static const struct bpf_link_ops bpf_netns_link_ops = {
.release = bpf_netns_link_release,
.dealloc = bpf_netns_link_dealloc,
.detach = bpf_netns_link_detach,
.update_prog = bpf_netns_link_update_prog,
.fill_link_info = bpf_netns_link_fill_info,
.show_fdinfo = bpf_netns_link_show_fdinfo,
Expand Down
26 changes: 26 additions & 0 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -3991,6 +3991,29 @@ static int link_update(union bpf_attr *attr)
return ret;
}

#define BPF_LINK_DETACH_LAST_FIELD link_detach.link_fd

static int link_detach(union bpf_attr *attr)
{
struct bpf_link *link;
int ret;

if (CHECK_ATTR(BPF_LINK_DETACH))
return -EINVAL;

link = bpf_link_get_from_fd(attr->link_detach.link_fd);
if (IS_ERR(link))
return PTR_ERR(link);

if (link->ops->detach)
ret = link->ops->detach(link);
else
ret = -EOPNOTSUPP;

bpf_link_put(link);
return ret;
}

static int bpf_link_inc_not_zero(struct bpf_link *link)
{
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
Expand Down Expand Up @@ -4240,6 +4263,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
case BPF_ITER_CREATE:
err = bpf_iter_create(&attr);
break;
case BPF_LINK_DETACH:
err = link_detach(&attr);
break;
default:
err = -EINVAL;
break;
Expand Down
11 changes: 10 additions & 1 deletion net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -8979,12 +8979,20 @@ static void bpf_xdp_link_release(struct bpf_link *link)
/* if racing with net_device's tear down, xdp_link->dev might be
* already NULL, in which case link was already auto-detached
*/
if (xdp_link->dev)
if (xdp_link->dev) {
WARN_ON(dev_xdp_detach_link(xdp_link->dev, NULL, xdp_link));
xdp_link->dev = NULL;
}

rtnl_unlock();
}

static int bpf_xdp_link_detach(struct bpf_link *link)
{
bpf_xdp_link_release(link);
return 0;
}

static void bpf_xdp_link_dealloc(struct bpf_link *link)
{
struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link);
Expand Down Expand Up @@ -9066,6 +9074,7 @@ static int bpf_xdp_link_update(struct bpf_link *link, struct bpf_prog *new_prog,
static const struct bpf_link_ops bpf_xdp_link_lops = {
.release = bpf_xdp_link_release,
.dealloc = bpf_xdp_link_dealloc,
.detach = bpf_xdp_link_detach,
.show_fdinfo = bpf_xdp_link_show_fdinfo,
.fill_link_info = bpf_xdp_link_fill_link_info,
.update_prog = bpf_xdp_link_update,
Expand Down
8 changes: 8 additions & 0 deletions tools/bpf/bpftool/Documentation/bpftool-link.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ LINK COMMANDS

| **bpftool** **link { show | list }** [*LINK*]
| **bpftool** **link pin** *LINK* *FILE*
| **bpftool** **link detach *LINK*
| **bpftool** **link help**
|
| *LINK* := { **id** *LINK_ID* | **pinned** *FILE* }
Expand Down Expand Up @@ -49,6 +50,13 @@ DESCRIPTION
contain a dot character ('.'), which is reserved for future
extensions of *bpffs*.

**bpftool link detach** *LINK*
Force-detach link *LINK*. BPF link and its underlying BPF
program will stay valid, but they will be detached from the
respective BPF hook and BPF link will transition into
a defunct state until last open file descriptor for that
link is closed.

**bpftool link help**
Print short help message.

Expand Down
4 changes: 2 additions & 2 deletions tools/bpf/bpftool/bash-completion/bpftool
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ _bpftool()
;;
link)
case $command in
show|list|pin)
show|list|pin|detach)
case $prev in
id)
_bpftool_get_link_ids
Expand All @@ -1139,7 +1139,7 @@ _bpftool()
COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
return 0
;;
pin)
pin|detach)
if [[ $prev == "$command" ]]; then
COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
else
Expand Down
37 changes: 36 additions & 1 deletion tools/bpf/bpftool/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ static const char * const link_type_name[] = {

static int link_parse_fd(int *argc, char ***argv)
{
int fd;

if (is_prefix(**argv, "id")) {
unsigned int id;
char *endptr;
Expand All @@ -35,7 +37,10 @@ static int link_parse_fd(int *argc, char ***argv)
}
NEXT_ARGP();

return bpf_link_get_fd_by_id(id);
fd = bpf_link_get_fd_by_id(id);
if (fd < 0)
p_err("failed to get link with ID %d: %s", id, strerror(errno));
return fd;
} else if (is_prefix(**argv, "pinned")) {
char *path;

Expand Down Expand Up @@ -316,6 +321,34 @@ static int do_pin(int argc, char **argv)
return err;
}

static int do_detach(int argc, char **argv)
{
int err, fd;

if (argc != 2) {
p_err("link specifier is invalid or missing\n");
return 1;
}

fd = link_parse_fd(&argc, &argv);
if (fd < 0)
return 1;

err = bpf_link_detach(fd);
if (err)
err = -errno;
close(fd);
if (err) {
p_err("failed link detach: %s", strerror(-err));
return 1;
}

if (json_output)
jsonw_null(json_wtr);

return 0;
}

static int do_help(int argc, char **argv)
{
if (json_output) {
Expand All @@ -326,6 +359,7 @@ static int do_help(int argc, char **argv)
fprintf(stderr,
"Usage: %1$s %2$s { show | list } [LINK]\n"
" %1$s %2$s pin LINK FILE\n"
" %1$s %2$s detach LINK\n"
" %1$s %2$s help\n"
"\n"
" " HELP_SPEC_LINK "\n"
Expand All @@ -341,6 +375,7 @@ static const struct cmd cmds[] = {
{ "list", do_show },
{ "help", do_help },
{ "pin", do_pin },
{ "detach", do_detach },
{ 0 }
};

Expand Down
5 changes: 5 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ enum bpf_cmd {
BPF_LINK_GET_NEXT_ID,
BPF_ENABLE_STATS,
BPF_ITER_CREATE,
BPF_LINK_DETACH,
};

enum bpf_map_type {
Expand Down Expand Up @@ -634,6 +635,10 @@ union bpf_attr {
__u32 old_prog_fd;
} link_update;

struct {
__u32 link_fd;
} link_detach;

struct { /* struct used by BPF_ENABLE_STATS command */
__u32 type;
} enable_stats;
Expand Down
10 changes: 10 additions & 0 deletions tools/lib/bpf/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,16 @@ int bpf_link_create(int prog_fd, int target_fd,
return sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr));
}

int bpf_link_detach(int link_fd)
{
union bpf_attr attr;

memset(&attr, 0, sizeof(attr));
attr.link_detach.link_fd = link_fd;

return sys_bpf(BPF_LINK_DETACH, &attr, sizeof(attr));
}

int bpf_link_update(int link_fd, int new_prog_fd,
const struct bpf_link_update_opts *opts)
{
Expand Down
2 changes: 2 additions & 0 deletions tools/lib/bpf/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
enum bpf_attach_type attach_type,
const struct bpf_link_create_opts *opts);

LIBBPF_API int bpf_link_detach(int link_fd);

struct bpf_link_update_opts {
size_t sz; /* size of this struct for forward/backward compatibility */
__u32 flags; /* extra flags */
Expand Down
5 changes: 5 additions & 0 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7748,6 +7748,11 @@ struct bpf_link *bpf_link__open(const char *path)
return link;
}

int bpf_link__detach(struct bpf_link *link)
{
return bpf_link_detach(link->fd) ? -errno : 0;
}

int bpf_link__pin(struct bpf_link *link, const char *path)
{
int err;
Expand Down
1 change: 1 addition & 0 deletions tools/lib/bpf/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ LIBBPF_API int bpf_link__unpin(struct bpf_link *link);
LIBBPF_API int bpf_link__update_program(struct bpf_link *link,
struct bpf_program *prog);
LIBBPF_API void bpf_link__disconnect(struct bpf_link *link);
LIBBPF_API int bpf_link__detach(struct bpf_link *link);
LIBBPF_API int bpf_link__destroy(struct bpf_link *link);

LIBBPF_API struct bpf_link *
Expand Down
2 changes: 2 additions & 0 deletions tools/lib/bpf/libbpf.map
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ LIBBPF_0.0.9 {

LIBBPF_0.1.0 {
global:
bpf_link__detach;
bpf_link_detach;
bpf_map__ifindex;
bpf_map__key_size;
bpf_map__map_flags;
Expand Down
20 changes: 19 additions & 1 deletion tools/testing/selftests/bpf/prog_tests/cgroup_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <test_progs.h>
#include "cgroup_helpers.h"
#include "testing_helpers.h"
#include "test_cgroup_link.skel.h"

static __u32 duration = 0;
Expand Down Expand Up @@ -37,7 +38,8 @@ void test_cgroup_link(void)
int last_cg = ARRAY_SIZE(cgs) - 1, cg_nr = ARRAY_SIZE(cgs);
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, link_upd_opts);
struct bpf_link *links[ARRAY_SIZE(cgs)] = {}, *tmp_link;
__u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags;
__u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags, prog_id;
struct bpf_link_info info;
int i = 0, err, prog_fd;
bool detach_legacy = false;

Expand Down Expand Up @@ -219,6 +221,22 @@ void test_cgroup_link(void)
/* BPF programs should still get called */
ping_and_check(0, cg_nr);

prog_id = link_info_prog_id(links[0], &info);
CHECK(prog_id == 0, "link_info", "failed\n");
CHECK(info.cgroup.cgroup_id == 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id);

err = bpf_link__detach(links[0]);
if (CHECK(err, "link_detach", "failed %d\n", err))
goto cleanup;

/* cgroup_id should be zero in link_info */
prog_id = link_info_prog_id(links[0], &info);
CHECK(prog_id == 0, "link_info", "failed\n");
CHECK(info.cgroup.cgroup_id != 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id);

/* First BPF program shouldn't be called anymore */
ping_and_check(0, cg_nr - 1);

/* leave cgroup and remove them, don't detach programs */
cleanup_cgroup_environment();

Expand Down
Loading

0 comments on commit 5a6b1a2

Please sign in to comment.