Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,8 @@ struct bpf_prog_aux {
struct rcu_head rcu;
};
struct bpf_stream stream[2];
struct mutex st_ops_assoc_mutex;
void *st_ops_assoc;
};

struct bpf_prog {
Expand Down Expand Up @@ -2010,6 +2012,8 @@ static inline void bpf_module_put(const void *data, struct module *owner)
module_put(owner);
}
int bpf_struct_ops_link_create(union bpf_attr *attr);
int bpf_struct_ops_assoc_prog(struct bpf_map *map, struct bpf_prog *prog);
void bpf_struct_ops_disassoc_prog(struct bpf_prog *prog);
u32 bpf_struct_ops_id(const void *kdata);

#ifdef CONFIG_NET
Expand Down Expand Up @@ -2057,6 +2061,13 @@ static inline int bpf_struct_ops_link_create(union bpf_attr *attr)
{
return -EOPNOTSUPP;
}
static inline void bpf_struct_ops_assoc_prog(struct bpf_map *map, struct bpf_prog *prog)
{
return -EOPNOTSUPP;
}
static inline void bpf_struct_ops_disassoc_prog(struct bpf_prog *prog)
{
}
static inline void bpf_map_struct_ops_info_fill(struct bpf_map_info *info, struct bpf_map *map)
{
}
Expand Down
16 changes: 16 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,16 @@ union bpf_iter_link_info {
* Number of bytes read from the stream on success, or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* BPF_STRUCT_OPS_ASSOCIATE_PROG
* Description
* Associate a BPF program with a struct_ops map. The struct_ops
* map is identified by *map_fd* and the BPF program is
* identified by *prog_fd*.
*
* Return
* 0 on success or -1 if an error occurred (in which case,
* *errno* is set appropriately).
*
* NOTES
* eBPF objects (maps and programs) can be shared between processes.
*
Expand Down Expand Up @@ -974,6 +984,7 @@ enum bpf_cmd {
BPF_PROG_BIND_MAP,
BPF_TOKEN_CREATE,
BPF_PROG_STREAM_READ_BY_FD,
BPF_STRUCT_OPS_ASSOCIATE_PROG,
__MAX_BPF_CMD,
};

Expand Down Expand Up @@ -1890,6 +1901,11 @@ union bpf_attr {
__u32 prog_fd;
} prog_stream_read;

struct {
__u32 map_fd;
__u32 prog_fd;
} struct_ops_assoc_prog;

} __attribute__((aligned(8)));

/* The description below is an attempt at providing documentation to eBPF
Expand Down
32 changes: 32 additions & 0 deletions kernel/bpf/bpf_struct_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map)
for (i = 0; i < st_map->funcs_cnt; i++) {
if (!st_map->links[i])
break;
bpf_struct_ops_disassoc_prog(st_map->links[i]->prog);
bpf_link_put(st_map->links[i]);
st_map->links[i] = NULL;
}
Expand Down Expand Up @@ -801,6 +802,11 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
goto reset_unlock;
}

/* Don't stop a program from being reused. prog->aux->st_ops_assoc
* will point to the first struct_ops kdata.
*/
bpf_struct_ops_assoc_prog(&st_map->map, prog);

link = kzalloc(sizeof(*link), GFP_USER);
if (!link) {
bpf_prog_put(prog);
Expand Down Expand Up @@ -1394,6 +1400,32 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
return err;
}

int bpf_struct_ops_assoc_prog(struct bpf_map *map, struct bpf_prog *prog)
{
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
void *kdata = &st_map->kvalue.data;
int ret = 0;

mutex_lock(&prog->aux->st_ops_assoc_mutex);

if (prog->aux->st_ops_assoc && prog->aux->st_ops_assoc != kdata) {
ret = -EBUSY;
goto out;
}

prog->aux->st_ops_assoc = kdata;
out:
mutex_unlock(&prog->aux->st_ops_assoc_mutex);
return ret;
}

void bpf_struct_ops_disassoc_prog(struct bpf_prog *prog)
{
mutex_lock(&prog->aux->st_ops_assoc_mutex);
prog->aux->st_ops_assoc = NULL;
mutex_unlock(&prog->aux->st_ops_assoc_mutex);
}

void bpf_map_struct_ops_info_fill(struct bpf_map_info *info, struct bpf_map *map)
{
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
Expand Down
6 changes: 6 additions & 0 deletions kernel/bpf/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
mutex_init(&fp->aux->used_maps_mutex);
mutex_init(&fp->aux->ext_mutex);
mutex_init(&fp->aux->dst_mutex);
mutex_init(&fp->aux->st_ops_assoc_mutex);

#ifdef CONFIG_BPF_SYSCALL
bpf_prog_stream_init(fp);
Expand Down Expand Up @@ -286,6 +287,7 @@ void __bpf_prog_free(struct bpf_prog *fp)
if (fp->aux) {
mutex_destroy(&fp->aux->used_maps_mutex);
mutex_destroy(&fp->aux->dst_mutex);
mutex_destroy(&fp->aux->st_ops_assoc_mutex);
kfree(fp->aux->poke_tab);
kfree(fp->aux);
}
Expand Down Expand Up @@ -2875,6 +2877,10 @@ static void bpf_prog_free_deferred(struct work_struct *work)
#endif
bpf_free_used_maps(aux);
bpf_free_used_btfs(aux);
if (aux->st_ops_assoc) {
bpf_struct_ops_put(aux->st_ops_assoc);
bpf_struct_ops_disassoc_prog(aux->prog);
}
if (bpf_prog_is_dev_bound(aux))
bpf_prog_dev_bound_destroy(aux->prog);
#ifdef CONFIG_PERF_EVENTS
Expand Down
38 changes: 38 additions & 0 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -6092,6 +6092,41 @@ static int prog_stream_read(union bpf_attr *attr)
return ret;
}

#define BPF_STRUCT_OPS_ASSOCIATE_PROG_LAST_FIELD struct_ops_assoc_prog.prog_fd

static int struct_ops_assoc_prog(union bpf_attr *attr)
{
struct bpf_prog *prog;
struct bpf_map *map;
int ret;

if (CHECK_ATTR(BPF_STRUCT_OPS_ASSOCIATE_PROG))
return -EINVAL;

prog = bpf_prog_get(attr->struct_ops_assoc_prog.prog_fd);
if (IS_ERR(prog))
return PTR_ERR(prog);

map = bpf_map_get(attr->struct_ops_assoc_prog.map_fd);
if (IS_ERR(map)) {
ret = PTR_ERR(map);
goto out;
}

if (map->map_type != BPF_MAP_TYPE_STRUCT_OPS ||
prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
ret = -EINVAL;
goto out;
}

ret = bpf_struct_ops_assoc_prog(map, prog);
out:
if (ret && !IS_ERR(map))
bpf_map_put(map);
bpf_prog_put(prog);
return ret;
}

static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
{
union bpf_attr attr;
Expand Down Expand Up @@ -6231,6 +6266,9 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
case BPF_PROG_STREAM_READ_BY_FD:
err = prog_stream_read(&attr);
break;
case BPF_STRUCT_OPS_ASSOCIATE_PROG:
err = struct_ops_assoc_prog(&attr);
break;
default:
err = -EINVAL;
break;
Expand Down
3 changes: 1 addition & 2 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -21889,8 +21889,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,

if (!bpf_jit_supports_far_kfunc_call())
insn->imm = BPF_CALL_IMM(desc->addr);
if (insn->off)
return 0;

if (desc->func_id == special_kfunc_list[KF_bpf_obj_new_impl] ||
desc->func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) {
struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta;
Expand Down
16 changes: 16 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,16 @@ union bpf_iter_link_info {
* Number of bytes read from the stream on success, or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* BPF_STRUCT_OPS_ASSOCIATE_PROG
* Description
* Associate a BPF program with a struct_ops map. The struct_ops
* map is identified by *map_fd* and the BPF program is
* identified by *prog_fd*.
*
* Return
* 0 on success or -1 if an error occurred (in which case,
* *errno* is set appropriately).
*
* NOTES
* eBPF objects (maps and programs) can be shared between processes.
*
Expand Down Expand Up @@ -974,6 +984,7 @@ enum bpf_cmd {
BPF_PROG_BIND_MAP,
BPF_TOKEN_CREATE,
BPF_PROG_STREAM_READ_BY_FD,
BPF_STRUCT_OPS_ASSOCIATE_PROG,
__MAX_BPF_CMD,
};

Expand Down Expand Up @@ -1890,6 +1901,11 @@ union bpf_attr {
__u32 prog_fd;
} prog_stream_read;

struct {
__u32 map_fd;
__u32 prog_fd;
} struct_ops_assoc_prog;

} __attribute__((aligned(8)));

/* The description below is an attempt at providing documentation to eBPF
Expand Down
18 changes: 18 additions & 0 deletions tools/lib/bpf/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1397,3 +1397,21 @@ int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len,
err = sys_bpf(BPF_PROG_STREAM_READ_BY_FD, &attr, attr_sz);
return libbpf_err_errno(err);
}

int bpf_struct_ops_associate_prog(int map_fd, int prog_fd,
struct bpf_struct_ops_associate_prog_opts *opts)
{
const size_t attr_sz = offsetofend(union bpf_attr, struct_ops_assoc_prog);
union bpf_attr attr;
int err;

if (!OPTS_VALID(opts, bpf_struct_ops_associate_prog_opts))
return libbpf_err(-EINVAL);

memset(&attr, 0, attr_sz);
attr.struct_ops_assoc_prog.map_fd = map_fd;
attr.struct_ops_assoc_prog.prog_fd = prog_fd;

err = sys_bpf(BPF_STRUCT_OPS_ASSOCIATE_PROG, &attr, attr_sz);
return libbpf_err_errno(err);
}
19 changes: 19 additions & 0 deletions tools/lib/bpf/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,25 @@ struct bpf_prog_stream_read_opts {
LIBBPF_API int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len,
struct bpf_prog_stream_read_opts *opts);

struct bpf_struct_ops_associate_prog_opts {
size_t sz;
size_t :0;
};
#define bpf_struct_ops_associate_prog_opts__last_field sz
/**
* @brief **bpf_struct_ops_associate_prog** associate a BPF program with a
* struct_ops map.
*
* @param map_fd FD for the struct_ops map to be associated with a BPF progam
* @param prog_fd FD for the BPF program
* @param opts optional options, can be NULL
*
* @return 0 on success; negative error code, otherwise (errno is also set to
* the error code)
*/
LIBBPF_API int bpf_struct_ops_associate_prog(int map_fd, int prog_fd,
struct bpf_struct_ops_associate_prog_opts *opts);

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
1 change: 1 addition & 0 deletions tools/lib/bpf/libbpf.map
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,5 @@ LIBBPF_1.7.0 {
global:
bpf_map__set_exclusive_program;
bpf_map__exclusive_program;
bpf_struct_ops_associate_prog;
} LIBBPF_1.6.0;
76 changes: 76 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0

#include <test_progs.h>
#include "struct_ops_assoc.skel.h"

static void test_st_ops_assoc(void)
{
int sys_enter_prog_a_fd, sys_enter_prog_b_fd;
int syscall_prog_a_fd, syscall_prog_b_fd;
struct struct_ops_assoc *skel = NULL;
int err, pid, map_a_fd, map_b_fd;

skel = struct_ops_assoc__open_and_load();
if (!ASSERT_OK_PTR(skel, "struct_ops_assoc__open"))
goto out;

sys_enter_prog_a_fd = bpf_program__fd(skel->progs.sys_enter_prog_a);
sys_enter_prog_b_fd = bpf_program__fd(skel->progs.sys_enter_prog_b);
syscall_prog_a_fd = bpf_program__fd(skel->progs.syscall_prog_a);
syscall_prog_b_fd = bpf_program__fd(skel->progs.syscall_prog_b);
map_a_fd = bpf_map__fd(skel->maps.st_ops_map_a);
map_b_fd = bpf_map__fd(skel->maps.st_ops_map_b);

err = bpf_struct_ops_associate_prog(map_a_fd, syscall_prog_a_fd, NULL);
if (!ASSERT_OK(err, "bpf_struct_ops_associate_prog"))
goto out;

err = bpf_struct_ops_associate_prog(map_a_fd, sys_enter_prog_a_fd, NULL);
if (!ASSERT_OK(err, "bpf_struct_ops_associate_prog"))
goto out;

err = bpf_struct_ops_associate_prog(map_b_fd, syscall_prog_b_fd, NULL);
if (!ASSERT_OK(err, "bpf_struct_ops_associate_prog"))
goto out;

err = bpf_struct_ops_associate_prog(map_b_fd, sys_enter_prog_b_fd, NULL);
if (!ASSERT_OK(err, "bpf_struct_ops_associate_prog"))
goto out;

/* sys_enter_prog_a already associated with map_a */
err = bpf_struct_ops_associate_prog(map_b_fd, sys_enter_prog_a_fd, NULL);
if (!ASSERT_ERR(err, "bpf_struct_ops_associate_prog"))
goto out;

err = struct_ops_assoc__attach(skel);
if (!ASSERT_OK(err, "struct_ops_assoc__attach"))
goto out;

/* run tracing prog that calls .test_1 and checks return */
pid = getpid();
skel->bss->test_pid = pid;
sys_gettid();
skel->bss->test_pid = 0;

ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");

/* run syscall_prog that calls .test_1 and checks return */
err = bpf_prog_test_run_opts(syscall_prog_a_fd, NULL);
ASSERT_OK(err, "bpf_prog_test_run_opts");

err = bpf_prog_test_run_opts(syscall_prog_b_fd, NULL);
ASSERT_OK(err, "bpf_prog_test_run_opts");

ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err");
ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err");

out:
struct_ops_assoc__destroy(skel);
}

void test_struct_ops_assoc(void)
{
if (test__start_subtest("st_ops_assoc"))
test_st_ops_assoc();
}
Loading
Loading