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
139 changes: 139 additions & 0 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -9793,6 +9793,145 @@ __u32 bpf_program__line_info_cnt(const struct bpf_program *prog)
return prog->line_info_cnt;
}

static bool map_type_contains_progs(enum bpf_map_type type)
{
return type == BPF_MAP_TYPE_PROG_ARRAY ||
type == BPF_MAP_TYPE_DEVMAP ||
type == BPF_MAP_TYPE_CPUMAP;
}

/*
* Clone program-containing maps to avoid owner compatibility conflicts.
* PROG_ARRAY, DEVMAP, and CPUMAP maps track the owner program type; loading
* programs with different attributes (e.g. sleepable vs non-sleepable) against
* the same map fails. Create fresh maps and patch instruction references.
*/
static int clone_prog_maps(struct bpf_object *obj, struct bpf_insn *insns,
size_t insns_cnt, int *temp_fds)
{
int i, j, old_fd, new_fd, num_fds = 0;

for (i = 0; i < obj->nr_maps; i++) {
struct bpf_map *map = &obj->maps[i];

if (!map_type_contains_progs(map->def.type))
continue;

old_fd = map->fd;
if (old_fd < 0)
continue;

new_fd = bpf_map_create(map->def.type, map->name,
map->def.key_size, map->def.value_size,
map->def.max_entries, NULL);
if (new_fd < 0)
return -errno;

for (j = 0; j < insns_cnt; j++) {
if (insns[j].code != (BPF_LD | BPF_IMM | BPF_DW))
continue;
if (insns[j].src_reg != BPF_PSEUDO_MAP_FD &&
insns[j].src_reg != BPF_PSEUDO_MAP_VALUE)
continue;
if (insns[j].imm == old_fd)
insns[j].imm = new_fd;
}

temp_fds[num_fds++] = new_fd;
}

return num_fds;
}

int bpf_prog_clone(struct bpf_program *prog, struct bpf_prog_load_opts *opts)
{
LIBBPF_OPTS(bpf_prog_load_opts, local_opts);
struct bpf_insn *insns = NULL;
int temp_fds[256], num_fds;
struct bpf_object *obj;
int i, err, fd;

if (!prog)
return libbpf_err(-EINVAL);

obj = prog->obj;
if (!obj || obj->state < OBJ_PREPARED)
return libbpf_err(-EINVAL);

if (!opts)
opts = &local_opts;

/* Fill unset fields from prepared program/object */
if (!opts->expected_attach_type)
opts->expected_attach_type = prog->expected_attach_type;
if (!opts->attach_btf_id)
opts->attach_btf_id = prog->attach_btf_id;
if (!opts->attach_btf_obj_fd)
opts->attach_btf_obj_fd = prog->attach_btf_obj_fd;
if (!opts->attach_prog_fd)
opts->attach_prog_fd = prog->attach_prog_fd;
if (!opts->prog_flags)
opts->prog_flags = prog->prog_flags;
if (!opts->prog_ifindex)
opts->prog_ifindex = prog->prog_ifindex;
if (!opts->kern_version)
opts->kern_version = obj->kern_version;
if (!opts->fd_array)
opts->fd_array = obj->fd_array;
if (!opts->token_fd)
opts->token_fd = obj->token_fd;
if (opts->token_fd)
opts->prog_flags |= BPF_F_TOKEN_FD;

/* BTF func/line info */
if (obj->btf && btf__fd(obj->btf) >= 0) {
if (!opts->prog_btf_fd)
opts->prog_btf_fd = btf__fd(obj->btf);

if (!opts->func_info) {
opts->func_info = prog->func_info;
opts->func_info_cnt = prog->func_info_cnt;
opts->func_info_rec_size = prog->func_info_rec_size;
}
if (!opts->line_info) {
opts->line_info = prog->line_info;
opts->line_info_cnt = prog->line_info_cnt;
opts->line_info_rec_size = prog->line_info_rec_size;
}
}

/* Resolve BTF attach targets, set sleepable/XDP flags, etc. */
if (prog->sec_def && prog->sec_def->prog_prepare_load_fn) {
err = prog->sec_def->prog_prepare_load_fn(prog, opts,
prog->sec_def->cookie);
if (err)
return libbpf_err(err);
}

/* Clone program-containing maps to avoid owner compatibility issues */
insns = malloc(prog->insns_cnt * sizeof(*insns));
if (!insns)
return libbpf_err(-ENOMEM);

memcpy(insns, prog->insns, prog->insns_cnt * sizeof(*insns));

num_fds = clone_prog_maps(obj, insns, prog->insns_cnt, temp_fds);
if (num_fds < 0) {
err = num_fds;
free(insns);
return libbpf_err(err);
}

fd = bpf_prog_load(prog->type, prog->name, obj->license,
insns, prog->insns_cnt, opts);

for (i = 0; i < num_fds; i++)
close(temp_fds[i]);

free(insns);
return libbpf_err(fd);
}

#define SEC_DEF(sec_pfx, ptype, atype, flags, ...) { \
.sec = (char *)sec_pfx, \
.prog_type = BPF_PROG_TYPE_##ptype, \
Expand Down
18 changes: 18 additions & 0 deletions tools/lib/bpf/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,24 @@ LIBBPF_API int libbpf_register_prog_handler(const char *sec,
*/
LIBBPF_API int libbpf_unregister_prog_handler(int handler_id);

/**
* @brief **bpf_prog_clone()** loads a single BPF program from a prepared
* BPF object into the kernel, returning its file descriptor.
*
* The BPF object must have been previously prepared with
* **bpf_object__prepare()**. If @opts is provided, any non-zero field
* overrides the defaults derived from the program/object internals.
* If @opts is NULL, all fields are populated automatically.
*
* The returned FD is owned by the caller and must be closed with close().
*
* @param prog BPF program from a prepared object
* @param opts Optional load options; non-zero fields override defaults
* @return program FD (>= 0) on success; negative error code on failure
*/
LIBBPF_API int bpf_prog_clone(struct bpf_program *prog,
struct bpf_prog_load_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 @@ -452,6 +452,7 @@ LIBBPF_1.7.0 {
bpf_map__set_exclusive_program;
bpf_map__exclusive_program;
bpf_prog_assoc_struct_ops;
bpf_prog_clone;
bpf_program__assoc_struct_ops;
btf__permute;
} LIBBPF_1.6.0;
104 changes: 44 additions & 60 deletions tools/testing/selftests/bpf/veristat.c
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,7 @@ static void mask_unrelated_struct_ops_progs(struct bpf_object *obj,
}
}

static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
static void fixup_obj_maps(struct bpf_object *obj)
{
struct bpf_map *map;

Expand All @@ -1251,15 +1251,23 @@ static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const ch
case BPF_MAP_TYPE_INODE_STORAGE:
case BPF_MAP_TYPE_CGROUP_STORAGE:
case BPF_MAP_TYPE_CGRP_STORAGE:
break;
case BPF_MAP_TYPE_STRUCT_OPS:
mask_unrelated_struct_ops_progs(obj, map, prog);
break;
default:
if (bpf_map__max_entries(map) == 0)
bpf_map__set_max_entries(map, 1);
}
}
}

static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
{
struct bpf_map *map;

bpf_object__for_each_map(map, obj) {
if (bpf_map__type(map) == BPF_MAP_TYPE_STRUCT_OPS)
mask_unrelated_struct_ops_progs(obj, map, prog);
}

/* SEC(freplace) programs can't be loaded with veristat as is,
* but we can try guessing their target program's expected type by
Expand Down Expand Up @@ -1608,6 +1616,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
const char *base_filename = basename(strdupa(filename));
const char *prog_name = bpf_program__name(prog);
long mem_peak_a, mem_peak_b, mem_peak = -1;
LIBBPF_OPTS(bpf_prog_load_opts, opts);
char *buf;
int buf_sz, log_level;
struct verif_stats *stats;
Expand Down Expand Up @@ -1647,9 +1656,6 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
}
verif_log_buf[0] = '\0';

bpf_program__set_log_buf(prog, buf, buf_sz);
bpf_program__set_log_level(prog, log_level);

/* increase chances of successful BPF object loading */
fixup_obj(obj, prog, base_filename);

Expand All @@ -1658,15 +1664,21 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
if (env.force_reg_invariants)
bpf_program__set_flags(prog, bpf_program__flags(prog) | BPF_F_TEST_REG_INVARIANTS);

err = bpf_object__prepare(obj);
if (!err) {
cgroup_err = reset_stat_cgroup();
mem_peak_a = cgroup_memory_peak();
err = bpf_object__load(obj);
mem_peak_b = cgroup_memory_peak();
if (!cgroup_err && mem_peak_a >= 0 && mem_peak_b >= 0)
mem_peak = mem_peak_b - mem_peak_a;
opts.log_buf = buf;
opts.log_size = buf_sz;
opts.log_level = log_level;

cgroup_err = reset_stat_cgroup();
mem_peak_a = cgroup_memory_peak();
fd = bpf_prog_clone(prog, &opts);
if (fd < 0) {
err = fd;
fprintf(stderr, "Failed to load program %s %d\n", prog_name, err);
}
mem_peak_b = cgroup_memory_peak();
if (!cgroup_err && mem_peak_a >= 0 && mem_peak_b >= 0)
mem_peak = mem_peak_b - mem_peak_a;

env.progs_processed++;

stats->file_name = strdup(base_filename);
Expand All @@ -1678,7 +1690,6 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
stats->stats[MEMORY_PEAK] = mem_peak < 0 ? -1 : mem_peak / (1024 * 1024);

memset(&info, 0, info_len);
fd = bpf_program__fd(prog);
if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) {
stats->stats[JITED_SIZE] = info.jited_prog_len;
if (env.dump_mode & DUMP_JITED)
Expand All @@ -1699,7 +1710,8 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf

if (verif_log_buf != buf)
free(buf);

if (fd > 0)
close(fd);
return 0;
}

Expand Down Expand Up @@ -2182,8 +2194,8 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i
static int process_obj(const char *filename)
{
const char *base_filename = basename(strdupa(filename));
struct bpf_object *obj = NULL, *tobj;
struct bpf_program *prog, *tprog, *lprog;
struct bpf_object *obj = NULL;
struct bpf_program *prog;
libbpf_print_fn_t old_libbpf_print_fn;
LIBBPF_OPTS(bpf_object_open_opts, opts);
int err = 0, prog_cnt = 0;
Expand Down Expand Up @@ -2222,51 +2234,26 @@ static int process_obj(const char *filename)
env.files_processed++;

bpf_object__for_each_program(prog, obj) {
bpf_program__set_autoload(prog, true);
prog_cnt++;
}

if (prog_cnt == 1) {
prog = bpf_object__next_program(obj, NULL);
bpf_program__set_autoload(prog, true);
err = set_global_vars(obj, env.presets, env.npresets);
if (err) {
fprintf(stderr, "Failed to set global variables %d\n", err);
goto cleanup;
}
process_prog(filename, obj, prog);
fixup_obj_maps(obj);

err = set_global_vars(obj, env.presets, env.npresets);
if (err) {
fprintf(stderr, "Failed to set global variables %d\n", err);
goto cleanup;
}

bpf_object__for_each_program(prog, obj) {
const char *prog_name = bpf_program__name(prog);

tobj = bpf_object__open_file(filename, &opts);
if (!tobj) {
err = -errno;
fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
goto cleanup;
}

err = set_global_vars(tobj, env.presets, env.npresets);
if (err) {
fprintf(stderr, "Failed to set global variables %d\n", err);
goto cleanup;
}

lprog = NULL;
bpf_object__for_each_program(tprog, tobj) {
const char *tprog_name = bpf_program__name(tprog);

if (strcmp(prog_name, tprog_name) == 0) {
bpf_program__set_autoload(tprog, true);
lprog = tprog;
} else {
bpf_program__set_autoload(tprog, false);
}
}
err = bpf_object__prepare(obj);
if (err) {
fprintf(stderr, "Failed to prepare BPF object for loading %d\n", err);
goto cleanup;
}

process_prog(filename, tobj, lprog);
bpf_object__close(tobj);
bpf_object__for_each_program(prog, obj) {
process_prog(filename, obj, prog);
}

cleanup:
Expand Down Expand Up @@ -3264,17 +3251,14 @@ static int handle_verif_mode(void)
create_stat_cgroup();
for (i = 0; i < env.filename_cnt; i++) {
err = process_obj(env.filenames[i]);
if (err) {
if (err)
fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err);
goto out;
}
}

qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats);

output_prog_stats();

out:
destroy_stat_cgroup();
return err;
}
Expand Down
Loading