mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Merge tag 'bpf-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov: - Convert selftests/bpf/test_tc_edt and test_tc_tunnel from .sh to test_progs runner (Alexis Lothoré) - Convert selftests/bpf/test_xsk to test_progs runner (Bastien Curutchet) - Replace bpf memory allocator with kmalloc_nolock() in bpf_local_storage (Amery Hung), and in bpf streams and range tree (Puranjay Mohan) - Introduce support for indirect jumps in BPF verifier and x86 JIT (Anton Protopopov) and arm64 JIT (Puranjay Mohan) - Remove runqslower bpf tool (Hoyeon Lee) - Fix corner cases in the verifier to close several syzbot reports (Eduard Zingerman, KaFai Wan) - Several improvements in deadlock detection in rqspinlock (Kumar Kartikeya Dwivedi) - Implement "jmp" mode for BPF trampoline and corresponding DYNAMIC_FTRACE_WITH_JMP. It improves "fexit" program type performance from 80 M/s to 136 M/s. With Steven's Ack. (Menglong Dong) - Add ability to test non-linear skbs in BPF_PROG_TEST_RUN (Paul Chaignon) - Do not let BPF_PROG_TEST_RUN emit invalid GSO types to stack (Daniel Borkmann) - Generalize buildid reader into bpf_dynptr (Mykyta Yatsenko) - Optimize bpf_map_update_elem() for map-in-map types (Ritesh Oedayrajsingh Varma) - Introduce overwrite mode for BPF ring buffer (Xu Kuohai) * tag 'bpf-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (169 commits) bpf: optimize bpf_map_update_elem() for map-in-map types bpf: make kprobe_multi_link_prog_run always_inline selftests/bpf: do not hardcode target rate in test_tc_edt BPF program selftests/bpf: remove test_tc_edt.sh selftests/bpf: integrate test_tc_edt into test_progs selftests/bpf: rename test_tc_edt.bpf.c section to expose program type selftests/bpf: Add success stats to rqspinlock stress test rqspinlock: Precede non-head waiter queueing with AA check rqspinlock: Disable spinning for trylock fallback rqspinlock: Use trylock fallback when per-CPU rqnode is busy rqspinlock: Perform AA checks immediately rqspinlock: Enclose lock/unlock within lock entry acquisitions bpf: Remove runqslower tool selftests/bpf: Remove usage of lsm/file_alloc_security in selftest bpf: Disable file_alloc_security hook bpf: check for insn arrays in check_ptr_alignment bpf: force BPF_F_RDONLY_PROG on insn array creation bpf: Fix exclusive map memory leak selftests/bpf: Make CS length configurable for rqspinlock stress test selftests/bpf: Add lock wait time stats to rqspinlock stress test ...
This commit is contained in:
@@ -190,6 +190,7 @@ static const char * const map_type_name[] = {
|
||||
[BPF_MAP_TYPE_USER_RINGBUF] = "user_ringbuf",
|
||||
[BPF_MAP_TYPE_CGRP_STORAGE] = "cgrp_storage",
|
||||
[BPF_MAP_TYPE_ARENA] = "arena",
|
||||
[BPF_MAP_TYPE_INSN_ARRAY] = "insn_array",
|
||||
};
|
||||
|
||||
static const char * const prog_type_name[] = {
|
||||
@@ -369,6 +370,7 @@ enum reloc_type {
|
||||
RELO_EXTERN_CALL,
|
||||
RELO_SUBPROG_ADDR,
|
||||
RELO_CORE,
|
||||
RELO_INSN_ARRAY,
|
||||
};
|
||||
|
||||
struct reloc_desc {
|
||||
@@ -379,7 +381,16 @@ struct reloc_desc {
|
||||
struct {
|
||||
int map_idx;
|
||||
int sym_off;
|
||||
int ext_idx;
|
||||
/*
|
||||
* The following two fields can be unionized, as the
|
||||
* ext_idx field is used for extern symbols, and the
|
||||
* sym_size is used for jump tables, which are never
|
||||
* extern
|
||||
*/
|
||||
union {
|
||||
int ext_idx;
|
||||
int sym_size;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -421,6 +432,11 @@ struct bpf_sec_def {
|
||||
libbpf_prog_attach_fn_t prog_attach_fn;
|
||||
};
|
||||
|
||||
struct bpf_light_subprog {
|
||||
__u32 sec_insn_off;
|
||||
__u32 sub_insn_off;
|
||||
};
|
||||
|
||||
/*
|
||||
* bpf_prog should be a better name but it has been used in
|
||||
* linux/filter.h.
|
||||
@@ -494,6 +510,9 @@ struct bpf_program {
|
||||
__u32 line_info_cnt;
|
||||
__u32 prog_flags;
|
||||
__u8 hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
struct bpf_light_subprog *subprogs;
|
||||
__u32 subprog_cnt;
|
||||
};
|
||||
|
||||
struct bpf_struct_ops {
|
||||
@@ -667,6 +686,7 @@ struct elf_state {
|
||||
int symbols_shndx;
|
||||
bool has_st_ops;
|
||||
int arena_data_shndx;
|
||||
int jumptables_data_shndx;
|
||||
};
|
||||
|
||||
struct usdt_manager;
|
||||
@@ -738,6 +758,16 @@ struct bpf_object {
|
||||
void *arena_data;
|
||||
size_t arena_data_sz;
|
||||
|
||||
void *jumptables_data;
|
||||
size_t jumptables_data_sz;
|
||||
|
||||
struct {
|
||||
struct bpf_program *prog;
|
||||
int sym_off;
|
||||
int fd;
|
||||
} *jumptable_maps;
|
||||
size_t jumptable_map_cnt;
|
||||
|
||||
struct kern_feature_cache *feat_cache;
|
||||
char *token_path;
|
||||
int token_fd;
|
||||
@@ -764,6 +794,7 @@ void bpf_program__unload(struct bpf_program *prog)
|
||||
|
||||
zfree(&prog->func_info);
|
||||
zfree(&prog->line_info);
|
||||
zfree(&prog->subprogs);
|
||||
}
|
||||
|
||||
static void bpf_program__exit(struct bpf_program *prog)
|
||||
@@ -2996,7 +3027,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
|
||||
|
||||
scn = elf_sec_by_idx(obj, obj->efile.btf_maps_shndx);
|
||||
data = elf_sec_data(obj, scn);
|
||||
if (!scn || !data) {
|
||||
if (!data) {
|
||||
pr_warn("elf: failed to get %s map definitions for %s\n",
|
||||
MAPS_ELF_SEC, obj->path);
|
||||
return -EINVAL;
|
||||
@@ -3942,6 +3973,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
||||
} else if (strcmp(name, ARENA_SEC) == 0) {
|
||||
obj->efile.arena_data = data;
|
||||
obj->efile.arena_data_shndx = idx;
|
||||
} else if (strcmp(name, JUMPTABLES_SEC) == 0) {
|
||||
obj->jumptables_data = malloc(data->d_size);
|
||||
if (!obj->jumptables_data)
|
||||
return -ENOMEM;
|
||||
memcpy(obj->jumptables_data, data->d_buf, data->d_size);
|
||||
obj->jumptables_data_sz = data->d_size;
|
||||
obj->efile.jumptables_data_shndx = idx;
|
||||
} else {
|
||||
pr_info("elf: skipping unrecognized data section(%d) %s\n",
|
||||
idx, name);
|
||||
@@ -4634,6 +4672,16 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* jump table data relocation */
|
||||
if (shdr_idx == obj->efile.jumptables_data_shndx) {
|
||||
reloc_desc->type = RELO_INSN_ARRAY;
|
||||
reloc_desc->insn_idx = insn_idx;
|
||||
reloc_desc->map_idx = -1;
|
||||
reloc_desc->sym_off = sym->st_value;
|
||||
reloc_desc->sym_size = sym->st_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generic map reference relocation */
|
||||
if (type == LIBBPF_MAP_UNSPEC) {
|
||||
if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
|
||||
@@ -6144,6 +6192,157 @@ static void poison_kfunc_call(struct bpf_program *prog, int relo_idx,
|
||||
insn->imm = POISON_CALL_KFUNC_BASE + ext_idx;
|
||||
}
|
||||
|
||||
static int find_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < obj->jumptable_map_cnt; i++) {
|
||||
/*
|
||||
* This might happen that same offset is used for two different
|
||||
* programs (as jump tables can be the same). However, for
|
||||
* different programs different maps should be created.
|
||||
*/
|
||||
if (obj->jumptable_maps[i].sym_off == sym_off &&
|
||||
obj->jumptable_maps[i].prog == prog)
|
||||
return obj->jumptable_maps[i].fd;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int add_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off, int map_fd)
|
||||
{
|
||||
size_t cnt = obj->jumptable_map_cnt;
|
||||
size_t size = sizeof(obj->jumptable_maps[0]);
|
||||
void *tmp;
|
||||
|
||||
tmp = libbpf_reallocarray(obj->jumptable_maps, cnt + 1, size);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
obj->jumptable_maps = tmp;
|
||||
obj->jumptable_maps[cnt].prog = prog;
|
||||
obj->jumptable_maps[cnt].sym_off = sym_off;
|
||||
obj->jumptable_maps[cnt].fd = map_fd;
|
||||
obj->jumptable_map_cnt++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_subprog_idx(struct bpf_program *prog, int insn_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = prog->subprog_cnt - 1; i >= 0; i--) {
|
||||
if (insn_idx >= prog->subprogs[i].sub_insn_off)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int create_jt_map(struct bpf_object *obj, struct bpf_program *prog, struct reloc_desc *relo)
|
||||
{
|
||||
const __u32 jt_entry_size = 8;
|
||||
int sym_off = relo->sym_off;
|
||||
int jt_size = relo->sym_size;
|
||||
__u32 max_entries = jt_size / jt_entry_size;
|
||||
__u32 value_size = sizeof(struct bpf_insn_array_value);
|
||||
struct bpf_insn_array_value val = {};
|
||||
int subprog_idx;
|
||||
int map_fd, err;
|
||||
__u64 insn_off;
|
||||
__u64 *jt;
|
||||
__u32 i;
|
||||
|
||||
map_fd = find_jt_map(obj, prog, sym_off);
|
||||
if (map_fd >= 0)
|
||||
return map_fd;
|
||||
|
||||
if (sym_off % jt_entry_size) {
|
||||
pr_warn("map '.jumptables': jumptable start %d should be multiple of %u\n",
|
||||
sym_off, jt_entry_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (jt_size % jt_entry_size) {
|
||||
pr_warn("map '.jumptables': jumptable size %d should be multiple of %u\n",
|
||||
jt_size, jt_entry_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map_fd = bpf_map_create(BPF_MAP_TYPE_INSN_ARRAY, ".jumptables",
|
||||
4, value_size, max_entries, NULL);
|
||||
if (map_fd < 0)
|
||||
return map_fd;
|
||||
|
||||
if (!obj->jumptables_data) {
|
||||
pr_warn("map '.jumptables': ELF file is missing jump table data\n");
|
||||
err = -EINVAL;
|
||||
goto err_close;
|
||||
}
|
||||
if (sym_off + jt_size > obj->jumptables_data_sz) {
|
||||
pr_warn("map '.jumptables': jumptables_data size is %zd, trying to access %d\n",
|
||||
obj->jumptables_data_sz, sym_off + jt_size);
|
||||
err = -EINVAL;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
subprog_idx = -1; /* main program */
|
||||
if (relo->insn_idx < 0 || relo->insn_idx >= prog->insns_cnt) {
|
||||
pr_warn("map '.jumptables': invalid instruction index %d\n", relo->insn_idx);
|
||||
err = -EINVAL;
|
||||
goto err_close;
|
||||
}
|
||||
if (prog->subprogs)
|
||||
subprog_idx = find_subprog_idx(prog, relo->insn_idx);
|
||||
|
||||
jt = (__u64 *)(obj->jumptables_data + sym_off);
|
||||
for (i = 0; i < max_entries; i++) {
|
||||
/*
|
||||
* The offset should be made to be relative to the beginning of
|
||||
* the main function, not the subfunction.
|
||||
*/
|
||||
insn_off = jt[i]/sizeof(struct bpf_insn);
|
||||
if (subprog_idx >= 0) {
|
||||
insn_off -= prog->subprogs[subprog_idx].sec_insn_off;
|
||||
insn_off += prog->subprogs[subprog_idx].sub_insn_off;
|
||||
} else {
|
||||
insn_off -= prog->sec_insn_off;
|
||||
}
|
||||
|
||||
/*
|
||||
* LLVM-generated jump tables contain u64 records, however
|
||||
* should contain values that fit in u32.
|
||||
*/
|
||||
if (insn_off > UINT32_MAX) {
|
||||
pr_warn("map '.jumptables': invalid jump table value 0x%llx at offset %d\n",
|
||||
(long long)jt[i], sym_off + i * jt_entry_size);
|
||||
err = -EINVAL;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
val.orig_off = insn_off;
|
||||
err = bpf_map_update_elem(map_fd, &i, &val, 0);
|
||||
if (err)
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
err = bpf_map_freeze(map_fd);
|
||||
if (err)
|
||||
goto err_close;
|
||||
|
||||
err = add_jt_map(obj, prog, sym_off, map_fd);
|
||||
if (err)
|
||||
goto err_close;
|
||||
|
||||
return map_fd;
|
||||
|
||||
err_close:
|
||||
close(map_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Relocate data references within program code:
|
||||
* - map references;
|
||||
* - global variable references;
|
||||
@@ -6235,6 +6434,20 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
|
||||
case RELO_CORE:
|
||||
/* will be handled by bpf_program_record_relos() */
|
||||
break;
|
||||
case RELO_INSN_ARRAY: {
|
||||
int map_fd;
|
||||
|
||||
map_fd = create_jt_map(obj, prog, relo);
|
||||
if (map_fd < 0) {
|
||||
pr_warn("prog '%s': relo #%d: can't create jump table: sym_off %u\n",
|
||||
prog->name, i, relo->sym_off);
|
||||
return map_fd;
|
||||
}
|
||||
insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
|
||||
insn->imm = map_fd;
|
||||
insn->off = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_warn("prog '%s': relo #%d: bad relo type %d\n",
|
||||
prog->name, i, relo->type);
|
||||
@@ -6432,36 +6645,62 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int save_subprog_offsets(struct bpf_program *main_prog, struct bpf_program *subprog)
|
||||
{
|
||||
size_t size = sizeof(main_prog->subprogs[0]);
|
||||
int cnt = main_prog->subprog_cnt;
|
||||
void *tmp;
|
||||
|
||||
tmp = libbpf_reallocarray(main_prog->subprogs, cnt + 1, size);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
main_prog->subprogs = tmp;
|
||||
main_prog->subprogs[cnt].sec_insn_off = subprog->sec_insn_off;
|
||||
main_prog->subprogs[cnt].sub_insn_off = subprog->sub_insn_off;
|
||||
main_prog->subprog_cnt++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main_prog,
|
||||
struct bpf_program *subprog)
|
||||
{
|
||||
struct bpf_insn *insns;
|
||||
size_t new_cnt;
|
||||
int err;
|
||||
struct bpf_insn *insns;
|
||||
size_t new_cnt;
|
||||
int err;
|
||||
|
||||
subprog->sub_insn_off = main_prog->insns_cnt;
|
||||
subprog->sub_insn_off = main_prog->insns_cnt;
|
||||
|
||||
new_cnt = main_prog->insns_cnt + subprog->insns_cnt;
|
||||
insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns));
|
||||
if (!insns) {
|
||||
pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
main_prog->insns = insns;
|
||||
main_prog->insns_cnt = new_cnt;
|
||||
new_cnt = main_prog->insns_cnt + subprog->insns_cnt;
|
||||
insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns));
|
||||
if (!insns) {
|
||||
pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
main_prog->insns = insns;
|
||||
main_prog->insns_cnt = new_cnt;
|
||||
|
||||
memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns,
|
||||
subprog->insns_cnt * sizeof(*insns));
|
||||
memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns,
|
||||
subprog->insns_cnt * sizeof(*insns));
|
||||
|
||||
pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n",
|
||||
main_prog->name, subprog->insns_cnt, subprog->name);
|
||||
pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n",
|
||||
main_prog->name, subprog->insns_cnt, subprog->name);
|
||||
|
||||
/* The subprog insns are now appended. Append its relos too. */
|
||||
err = append_subprog_relos(main_prog, subprog);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
/* The subprog insns are now appended. Append its relos too. */
|
||||
err = append_subprog_relos(main_prog, subprog);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = save_subprog_offsets(main_prog, subprog);
|
||||
if (err) {
|
||||
pr_warn("prog '%s': failed to add subprog offsets: %s\n",
|
||||
main_prog->name, errstr(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -9228,6 +9467,13 @@ void bpf_object__close(struct bpf_object *obj)
|
||||
|
||||
zfree(&obj->arena_data);
|
||||
|
||||
zfree(&obj->jumptables_data);
|
||||
obj->jumptables_data_sz = 0;
|
||||
|
||||
for (i = 0; i < obj->jumptable_map_cnt; i++)
|
||||
close(obj->jumptable_maps[i].fd);
|
||||
zfree(&obj->jumptable_maps);
|
||||
|
||||
free(obj);
|
||||
}
|
||||
|
||||
@@ -13854,8 +14100,8 @@ int bpf_program__set_attach_target(struct bpf_program *prog,
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (attach_prog_fd && !attach_func_name) {
|
||||
/* remember attach_prog_fd and let bpf_program__load() find
|
||||
* BTF ID during the program load
|
||||
/* Store attach_prog_fd. The BTF ID will be resolved later during
|
||||
* the normal object/program load phase.
|
||||
*/
|
||||
prog->attach_prog_fd = attach_prog_fd;
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user