Skip to content
Open
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
16 changes: 16 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;
struct bpf_map *st_ops_assoc;
};

struct bpf_prog {
Expand Down Expand Up @@ -2010,6 +2012,9 @@ 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_prog_assoc_struct_ops(struct bpf_prog *prog, struct bpf_map *map);
void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog);
void *bpf_prog_get_assoc_struct_ops(const struct bpf_prog_aux *aux);
u32 bpf_struct_ops_id(const void *kdata);

#ifdef CONFIG_NET
Expand Down Expand Up @@ -2057,6 +2062,17 @@ static inline int bpf_struct_ops_link_create(union bpf_attr *attr)
{
return -EOPNOTSUPP;
}
static inline int bpf_prog_assoc_struct_ops(struct bpf_prog *prog, struct bpf_map *map)
{
return -EOPNOTSUPP;
}
static inline void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog)
{
}
static inline void *bpf_prog_get_assoc_struct_ops(const struct bpf_prog_aux *aux)
{
return NULL;
}
static inline void bpf_map_struct_ops_info_fill(struct bpf_map_info *info, struct bpf_map *map)
{
}
Expand Down
17 changes: 17 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_PROG_ASSOC_STRUCT_OPS
* 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_PROG_ASSOC_STRUCT_OPS,
__MAX_BPF_CMD,
};

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

struct {
__u32 map_fd;
__u32 prog_fd;
__u32 flags;
} prog_assoc_struct_ops;

} __attribute__((aligned(8)));

/* The description below is an attempt at providing documentation to eBPF
Expand Down
70 changes: 70 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_prog_disassoc_struct_ops(st_map->links[i]->prog);
bpf_link_put(st_map->links[i]);
st_map->links[i] = NULL;
}
Expand Down Expand Up @@ -801,6 +802,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
goto reset_unlock;
}

err = bpf_prog_assoc_struct_ops(prog, &st_map->map);
if (err) {
bpf_prog_put(prog);
goto reset_unlock;
}

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

int bpf_prog_assoc_struct_ops(struct bpf_prog *prog, struct bpf_map *map)
{
guard(mutex)(&prog->aux->st_ops_assoc_mutex);

if (prog->aux->st_ops_assoc && prog->aux->st_ops_assoc != map) {
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
WRITE_ONCE(prog->aux->st_ops_assoc, BPF_PTR_POISON);
return 0;
}

return -EBUSY;
}

if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
bpf_map_inc(map);

WRITE_ONCE(prog->aux->st_ops_assoc, map);
return 0;
}

void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog)
{
struct bpf_map *map;

guard(mutex)(&prog->aux->st_ops_assoc_mutex);

map = READ_ONCE(prog->aux->st_ops_assoc);
if (!map || map == BPF_PTR_POISON)
return;

if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
bpf_map_put(map);

WRITE_ONCE(prog->aux->st_ops_assoc, NULL);
}

/*
* Get a pointer to the struct_ops struct (i.e., kdata) associated with a
* program.
*
* If the returned pointer is not NULL, it must points to a valid and
* initialized struct_ops. The struct_ops may or may not be attached.
* Kernel struct_ops implementers are responsible for tracking and checking
* the state of the struct_ops if the use case requires an attached struct_ops.
*/
void *bpf_prog_get_assoc_struct_ops(const struct bpf_prog_aux *aux)
{
struct bpf_struct_ops_map *st_map;
struct bpf_map *map;

map = READ_ONCE(aux->st_ops_assoc);
if (!map || map == BPF_PTR_POISON)
return NULL;

st_map = (struct bpf_struct_ops_map *)map;

if (smp_load_acquire(&st_map->kvalue.common.state) == BPF_STRUCT_OPS_STATE_INIT)
return NULL;

return &st_map->kvalue.data;
}
EXPORT_SYMBOL_GPL(bpf_prog_get_assoc_struct_ops);

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
3 changes: 3 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,7 @@ static void bpf_prog_free_deferred(struct work_struct *work)
#endif
bpf_free_used_maps(aux);
bpf_free_used_btfs(aux);
bpf_prog_disassoc_struct_ops(aux->prog);
if (bpf_prog_is_dev_bound(aux))
bpf_prog_dev_bound_destroy(aux->prog);
#ifdef CONFIG_PERF_EVENTS
Expand Down
46 changes: 46 additions & 0 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -6092,6 +6092,49 @@ static int prog_stream_read(union bpf_attr *attr)
return ret;
}

#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.prog_fd

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

if (CHECK_ATTR(BPF_PROG_ASSOC_STRUCT_OPS))
return -EINVAL;

if (attr->prog_assoc_struct_ops.flags)
return -EINVAL;

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

if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
ret = -EINVAL;
goto put_prog;
}

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

if (map->map_type != BPF_MAP_TYPE_STRUCT_OPS) {
ret = -EINVAL;
goto put_map;
}

ret = bpf_prog_assoc_struct_ops(prog, map);

put_map:
bpf_map_put(map);
put_prog:
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 +6274,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_PROG_ASSOC_STRUCT_OPS:
err = prog_assoc_struct_ops(&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
17 changes: 17 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_PROG_ASSOC_STRUCT_OPS
* 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_PROG_ASSOC_STRUCT_OPS,
__MAX_BPF_CMD,
};

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

struct {
__u32 map_fd;
__u32 prog_fd;
__u32 flags;
} prog_assoc_struct_ops;

} __attribute__((aligned(8)));

/* The description below is an attempt at providing documentation to eBPF
Expand Down
19 changes: 19 additions & 0 deletions tools/lib/bpf/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1397,3 +1397,22 @@ 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_prog_assoc_struct_ops(int prog_fd, int map_fd,
struct bpf_prog_assoc_struct_ops_opts *opts)
{
const size_t attr_sz = offsetofend(union bpf_attr, prog_assoc_struct_ops);
union bpf_attr attr;
int err;

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

memset(&attr, 0, attr_sz);
attr.prog_assoc_struct_ops.map_fd = map_fd;
attr.prog_assoc_struct_ops.prog_fd = prog_fd;
attr.prog_assoc_struct_ops.flags = OPTS_GET(opts, flags, 0);

err = sys_bpf(BPF_PROG_ASSOC_STRUCT_OPS, &attr, attr_sz);
return libbpf_err_errno(err);
}
21 changes: 21 additions & 0 deletions tools/lib/bpf/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,27 @@ 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_prog_assoc_struct_ops_opts {
size_t sz;
__u32 flags;
size_t :0;
};
#define bpf_prog_assoc_struct_ops_opts__last_field flags

/**
* @brief **bpf_prog_assoc_struct_ops** associates a BPF program with a
* struct_ops map.
*
* @param prog_fd FD for the BPF program
* @param map_fd FD for the struct_ops map to be associated with 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_prog_assoc_struct_ops(int prog_fd, int map_fd,
struct bpf_prog_assoc_struct_ops_opts *opts);

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
30 changes: 30 additions & 0 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -13895,6 +13895,36 @@ int bpf_program__set_attach_target(struct bpf_program *prog,
return 0;
}

int bpf_program__assoc_struct_ops(struct bpf_program *prog, struct bpf_map *map,
struct bpf_prog_assoc_struct_ops_opts *opts)
{
int prog_fd;

prog_fd = bpf_program__fd(prog);
if (prog_fd < 0) {
pr_warn("prog '%s': can't associate BPF program without FD (was it loaded?)\n",
prog->name);
return -EINVAL;
}

if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
pr_warn("prog '%s': can't associate struct_ops program\n", prog->name);
return -EINVAL;
}

if (map->fd < 0) {
pr_warn("map '%s': can't associate BPF map without FD (was it created?)\n", map->name);
return -EINVAL;
}

if (!bpf_map__is_struct_ops(map)) {
pr_warn("map '%s': can't associate non-struct_ops map\n", map->name);
return -EINVAL;
}

return bpf_prog_assoc_struct_ops(prog_fd, map->fd, opts);
}

int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz)
{
int err = 0, n, len, start, end = -1;
Expand Down
Loading
Loading