On Thu, Aug 14, 2025 at 8:46 PM Andrii Nakryiko <andrii.nakryiko@xxxxxxxxx> wrote: > > On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@xxxxxxxxxx> wrote: > > > > Implement setters and getters that allow map to be registers as > > typo: registered > > > exclusive to the specified program. The registration should be done > > before the exclusive program is loaded. > > > > Signed-off-by: KP Singh <kpsingh@xxxxxxxxxx> > > --- > > tools/lib/bpf/bpf.c | 4 ++- > > tools/lib/bpf/bpf.h | 4 ++- > > tools/lib/bpf/libbpf.c | 66 ++++++++++++++++++++++++++++++++++++++++ > > tools/lib/bpf/libbpf.h | 18 +++++++++++ > > tools/lib/bpf/libbpf.map | 2 ++ > > 5 files changed, 92 insertions(+), 2 deletions(-) > > > > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c > > index ab40dbf9f020..6a08a1559237 100644 > > --- a/tools/lib/bpf/bpf.c > > +++ b/tools/lib/bpf/bpf.c > > @@ -172,7 +172,7 @@ int bpf_map_create(enum bpf_map_type map_type, > > __u32 max_entries, > > const struct bpf_map_create_opts *opts) > > { > > - const size_t attr_sz = offsetofend(union bpf_attr, map_token_fd); > > + const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash); > > union bpf_attr attr; > > int fd; > > > > @@ -203,6 +203,8 @@ int bpf_map_create(enum bpf_map_type map_type, > > attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0); > > > > attr.map_token_fd = OPTS_GET(opts, token_fd, 0); > > + attr.excl_prog_hash = ptr_to_u64(OPTS_GET(opts, excl_prog_hash, NULL)); > > + attr.excl_prog_hash_size = OPTS_GET(opts, excl_prog_hash_size, 0); > > > > fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz); > > return libbpf_err_errno(fd); > > diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h > > index 7252150e7ad3..675a09bb7d2f 100644 > > --- a/tools/lib/bpf/bpf.h > > +++ b/tools/lib/bpf/bpf.h > > @@ -54,9 +54,11 @@ struct bpf_map_create_opts { > > __s32 value_type_btf_obj_fd; > > > > __u32 token_fd; > > + __u32 excl_prog_hash_size; > > leaving a gap here, can you please reorder and have hash first, > followed by size? done > > > + const void *excl_prog_hash; > > size_t :0; > > }; > > -#define bpf_map_create_opts__last_field token_fd > > +#define bpf_map_create_opts__last_field excl_prog_hash > > > > LIBBPF_API int bpf_map_create(enum bpf_map_type map_type, > > const char *map_name, > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c > > index 0bb3d71dcd9f..ed3294f69271 100644 > > --- a/tools/lib/bpf/libbpf.c > > +++ b/tools/lib/bpf/libbpf.c > > @@ -499,6 +499,7 @@ struct bpf_program { > > __u32 line_info_rec_size; > > __u32 line_info_cnt; > > __u32 prog_flags; > > + __u8 hash[SHA256_DIGEST_LENGTH]; > > }; > > > > struct bpf_struct_ops { > > @@ -578,6 +579,7 @@ struct bpf_map { > > bool autocreate; > > bool autoattach; > > __u64 map_extra; > > + struct bpf_program *excl_prog; > > }; > > > > enum extern_type { > > @@ -4488,6 +4490,43 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx) > > } > > } > > > > +static int bpf_program__compute_hash(struct bpf_program *prog) > > nit: this is not an API, so please don't use double underscores. > Something like bpf_prog_compute_hash() should do. ack. > > > +{ > > + struct bpf_insn *purged; > > + int i, err; > > + > > + purged = calloc(1, BPF_INSN_SZ * prog->insns_cnt); > > we had some patch fixing similar argument misuse issue, so I'd rather > have calloc(prog->insns_cnt, BPF_INSN_SZ), if you don't mind > I don't mind :) > > + if (!purged) > > + return -ENOMEM; > > + > > + /* If relocations have been done, the map_fd needs to be > > + * discarded for the digest calculation. > > + */ > > + for (i = 0; i < prog->insns_cnt; i++) { > > + purged[i] = prog->insns[i]; > > + if (purged[i].code == (BPF_LD | BPF_IMM | BPF_DW) && > > + (purged[i].src_reg == BPF_PSEUDO_MAP_FD || > > + purged[i].src_reg == BPF_PSEUDO_MAP_VALUE)) { > > + purged[i].imm = 0; > > + i++; > > + if (i >= prog->insns_cnt || > > + prog->insns[i].code != 0 || > > + prog->insns[i].dst_reg != 0 || > > + prog->insns[i].src_reg != 0 || > > + prog->insns[i].off != 0) { > > + err = -EINVAL; > > + goto out; > > + } > > + purged[i] = prog->insns[i]; > > + purged[i].imm = 0; > > + } > > + } > > + err = libbpf_sha256(purged, prog->insns_cnt * sizeof(struct bpf_insn), prog->hash, SHA256_DIGEST_LENGTH); > > too long, wrap before prog->hash? done > > > +out: > > + free(purged); > > + return err; > > +} > > + > > static int bpf_program__record_reloc(struct bpf_program *prog, > > struct reloc_desc *reloc_desc, > > __u32 insn_idx, const char *sym_name, > > @@ -5227,6 +5266,18 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b > > create_attr.token_fd = obj->token_fd; > > if (obj->token_fd) > > create_attr.map_flags |= BPF_F_TOKEN_FD; > > + if (map->excl_prog) { > > + if (map->excl_prog->obj->state == OBJ_LOADED) { > > + pr_warn("exclusive program already loaded\n"); > > + return libbpf_err(-EINVAL); > > + } > > unnecessary check, maps are always created before programs, so if > map->excl_prog belongs to the same bpf_object (and it should), then we > implicitly have a guarantee it's not yet created. So please drop. Removed. > > > + err = bpf_program__compute_hash(map->excl_prog); > > + if (err) > > + return err; > > + > > + create_attr.excl_prog_hash = map->excl_prog->hash; > > + create_attr.excl_prog_hash_size = SHA256_DIGEST_LENGTH; > > + } > > > > if (bpf_map__is_struct_ops(map)) { > > create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id; > > @@ -10517,6 +10568,21 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd) > > return 0; > > } > > > > +int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog) > > +{ > > + if (map_is_created(map)) { > > + pr_warn("exclusive programs must be set before map creation\n"); > > + return libbpf_err(-EINVAL); > > + } > > should we worry about someone providing a bpf_program that doesn't > belong to the same bpf_object that map belongs to? it's easy to check, > just compare map->obj and prog->obj Seems logical, added the check. > > + map->excl_prog = prog; > > + return 0; > > +} > > + > > +struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map) > > libbpf getters don't have "get_" prefix, so just bpf_map__exclusive_program() updated. > > > +{ > > + return map->excl_prog; > > +} > > + > > static struct bpf_map * > > __bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i) > > { > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h > > index 455a957cb702..ddaf58c8a298 100644 > > --- a/tools/lib/bpf/libbpf.h > > +++ b/tools/lib/bpf/libbpf.h > > @@ -1266,7 +1266,25 @@ LIBBPF_API int bpf_map__lookup_and_delete_elem(const struct bpf_map *map, > > */ > > LIBBPF_API int bpf_map__get_next_key(const struct bpf_map *map, > > const void *cur_key, void *next_key, size_t key_sz); > > +/** > > + * @brief **bpf_map__set_exclusive_program()** sets map to be exclusive to the > > + * to the specified program. The program must not be loaded yet. > > typo: "to the" duplicated fixed. > > Also, I think the more important restriction is that the map should > not have been created yet (so this has to be called between opening > and prepare/load steps, just like setting read-only global variables). > This by implication will mean that the program is not loaded either, > as we'll restrict bpf_program to be from the same bpf_object (which > you can mention as well for clarity). > How about? /** * @brief **bpf_map__set_exclusive_program()** sets a map to be exclusive to the * specified program. This must be called *before* the map is created. * * @param map BPF map to make exclusive. * @param prog BPF program to be the exclusive user of the map. Must belong * to the same bpf_object as the map. * @return 0 on success; a negative error code otherwise. * * This function must be called after the BPF object is opened but before * it is loaded. Once the object is loaded, only the specified program * will be able to access the map's contents. */ > > + * @param map BPF map to make exclusive. > > + * @param prog BPF program to be the exclusive user of the map. > > + * @return 0 on success; a negative error code otherwise. > > + * > > + * Once a map is made exclusive, only the specified program can access its > > + * contents. > > + */ > > +LIBBPF_API int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog); > > > > +/** > > + * @brief **bpf_map__get_exclusive_program()** returns the exclusive program > > + * that is registered with the map (if any). > > + * @param map BPF map to which the exclusive program is registered. > > + * @return the registered exclusive program. > > + */ > > +LIBBPF_API struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map); > > struct bpf_xdp_set_link_opts { > > size_t sz; > > int old_fd; > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map > > index d7bd463e7017..a5c5d0f2db5c 100644 > > --- a/tools/lib/bpf/libbpf.map > > +++ b/tools/lib/bpf/libbpf.map > > @@ -436,6 +436,8 @@ LIBBPF_1.6.0 { > > bpf_linker__add_buf; > > bpf_linker__add_fd; > > bpf_linker__new_fd; > > + bpf_map__set_exclusive_program; > > + bpf_map__get_exclusive_program; > > we are in LIBBPF_1.7.0 now, so please move done > > pw-bot: cr > > > > bpf_object__prepare; > > bpf_prog_stream_read; > > bpf_program__attach_cgroup_opts; > > -- > > 2.43.0 > >