On Wed, 2022-04-20 at 10:37 -0700, Andrii Nakryiko wrote: > On Fri, Apr 15, 2022 at 9:30 PM Kui-Feng Lee <kuifeng@xxxxxx> wrote: > > > > Replace struct bpf_tramp_progs with struct bpf_tramp_links to > > collect > > struct bpf_tramp_link(s) for a trampoline. struct bpf_tramp_link > > extends bpf_link to act as a linked list node. > > > > arch_prepare_bpf_trampoline() accepts a struct bpf_tramp_links to > > collects all bpf_tramp_link(s) that a trampoline should call. > > > > Change BPF trampoline and bpf_struct_ops to pass bpf_tramp_links > > instead of bpf_tramp_progs. > > > > Signed-off-by: Kui-Feng Lee <kuifeng@xxxxxx> > > --- > > arch/x86/net/bpf_jit_comp.c | 36 +++++++++-------- > > include/linux/bpf.h | 36 +++++++++++------ > > include/linux/bpf_types.h | 1 + > > include/uapi/linux/bpf.h | 1 + > > kernel/bpf/bpf_struct_ops.c | 69 ++++++++++++++++++++++-------- > > -- > > kernel/bpf/syscall.c | 23 ++++------- > > kernel/bpf/trampoline.c | 73 +++++++++++++++++++----------- > > ---- > > net/bpf/bpf_dummy_struct_ops.c | 37 ++++++++++++++--- > > tools/bpf/bpftool/link.c | 1 + > > tools/include/uapi/linux/bpf.h | 1 + > > 10 files changed, 175 insertions(+), 103 deletions(-) > > > > [...] > > > @@ -385,6 +399,7 @@ static int > > bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, > > for_each_member(i, t, member) { > > const struct btf_type *mtype, *ptype; > > struct bpf_prog *prog; > > + struct bpf_tramp_link *link; > > u32 moff; > > > > moff = __btf_member_bit_offset(t, member) / 8; > > @@ -438,16 +453,26 @@ static int > > bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, > > err = PTR_ERR(prog); > > goto reset_unlock; > > } > > - st_map->progs[i] = prog; > > > > if (prog->type != BPF_PROG_TYPE_STRUCT_OPS || > > prog->aux->attach_btf_id != st_ops->type_id || > > prog->expected_attach_type != i) { > > + bpf_prog_put(prog); > > err = -EINVAL; > > goto reset_unlock; > > } > > > > - err = bpf_struct_ops_prepare_trampoline(tprogs, > > prog, > > + link = kzalloc(sizeof(*link), GFP_USER); > > seems like you are leaking this link and all the links allocated in > previous successful iterations of this loop? In the block of reset_unlok, it calls bpf_struct_ops_map_put_progs() to release all links in st_map including all links of previous iterations. > > > + if (!link) { > > + bpf_prog_put(prog); > > + err = -ENOMEM; > > + goto reset_unlock; > > + } > > + bpf_link_init(&link->link, > > BPF_LINK_TYPE_STRUCT_OPS, > > + &bpf_struct_ops_link_lops, prog); > > + st_map->links[i] = &link->link; > > + > > + err = bpf_struct_ops_prepare_trampoline(tlinks, > > link, > > &st_ops- > > >func_models[i], > > image, > > image_end); > > if (err < 0) > > @@ -490,7 +515,7 @@ static int > > bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, > > memset(uvalue, 0, map->value_size); > > memset(kvalue, 0, map->value_size); > > unlock: > > - kfree(tprogs); > > + kfree(tlinks); > > so you'll need to free those links inside tlinks (or wherever else > they are stored) All links are in st_maps. They will be free by bpf_struct_ops_map_put_progs(). Does that make sense? > > > mutex_unlock(&st_map->lock); > > return err; > > } > > @@ -545,9 +570,9 @@ static void bpf_struct_ops_map_free(struct > > bpf_map *map) > > { > > struct bpf_struct_ops_map *st_map = (struct > > bpf_struct_ops_map *)map; > > > > - if (st_map->progs) > > + if (st_map->links) > > bpf_struct_ops_map_put_progs(st_map); > > - bpf_map_area_free(st_map->progs); > > + bpf_map_area_free(st_map->links); > > bpf_jit_free_exec(st_map->image); > > bpf_map_area_free(st_map->uvalue); > > bpf_map_area_free(st_map); > > [...] > > > @@ -105,10 +120,20 @@ int bpf_struct_ops_test_run(struct bpf_prog > > *prog, const union bpf_attr *kattr, > > } > > set_vm_flush_reset_perms(image); > > > > + link = kzalloc(sizeof(*link), GFP_USER); > > + if (!link) { > > + err = -ENOMEM; > > + goto out; > > + } > > + /* prog doesn't take the ownership of the reference from > > caller */ > > + bpf_prog_inc(prog); > > + bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, > > &bpf_struct_ops_link_lops, prog); > > + > > op_idx = prog->expected_attach_type; > > - err = bpf_struct_ops_prepare_trampoline(tprogs, prog, > > + err = bpf_struct_ops_prepare_trampoline(tlinks, link, > > &st_ops- > > >func_models[op_idx], > > image, image + > > PAGE_SIZE); > > + > > nit: no need for extra empty line here Got it! > > > if (err < 0) > > goto out; > > > > @@ -124,7 +149,9 @@ int bpf_struct_ops_test_run(struct bpf_prog > > *prog, const union bpf_attr *kattr, > > out: > > kfree(args); > > bpf_jit_free_exec(image); > > - kfree(tprogs); > > + if (link) > > + bpf_link_put(&link->link); > > you never to bpf_link_prime() and bpf_link_settle() for these "pseudo > links" for struct_ops, so there is no need for bpf_link_put(), it can > be just bpf_link_free(), right? agree. > > > + kfree(tlinks); > > return err; > > } > > > > diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c > > index 8fb0116f9136..6353a789322b 100644 > > --- a/tools/bpf/bpftool/link.c > > +++ b/tools/bpf/bpftool/link.c > > @@ -23,6 +23,7 @@ static const char * const link_type_name[] = { > > [BPF_LINK_TYPE_XDP] = "xdp", > > [BPF_LINK_TYPE_PERF_EVENT] = "perf_event", > > [BPF_LINK_TYPE_KPROBE_MULTI] = "kprobe_multi", > > + [BPF_LINK_TYPE_STRUCT_OPS] = "struct_ops", > > }; > > > > static struct hashmap *link_table; > > diff --git a/tools/include/uapi/linux/bpf.h > > b/tools/include/uapi/linux/bpf.h > > index d14b10b85e51..a4f557338af7 100644 > > --- a/tools/include/uapi/linux/bpf.h > > +++ b/tools/include/uapi/linux/bpf.h > > @@ -1013,6 +1013,7 @@ enum bpf_link_type { > > BPF_LINK_TYPE_XDP = 6, > > BPF_LINK_TYPE_PERF_EVENT = 7, > > BPF_LINK_TYPE_KPROBE_MULTI = 8, > > + BPF_LINK_TYPE_STRUCT_OPS = 9, > > > > MAX_BPF_LINK_TYPE, > > }; > > -- > > 2.30.2 > >