From: Alexei Starovoitov <ast@xxxxxxxxxx> Given BPF program's BTF perform a linear search through kernel BTFs for a possible candidate. Then wire the result into bpf_core_apply_relo_insn(). Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> --- kernel/bpf/btf.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 9bb1247346ce..e04b5e669d12 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -25,6 +25,7 @@ #include <linux/kobject.h> #include <linux/sysfs.h> #include <net/sock.h> +#include "../tools/lib/bpf/relo_core.h" /* BTF (BPF Type Format) is the meta data format which describes * the data types of BPF program/map. Hence, it basically focus @@ -6370,15 +6371,159 @@ size_t bpf_core_essential_name_len(const char *name) return n; } +static void bpf_core_free_cands(struct bpf_core_cand_list *cands) +{ + kfree(cands->cands); + kfree(cands); +} + +static int bpf_core_add_cands(struct bpf_core_cand *local_cand, + size_t local_essent_len, + const struct btf *targ_btf, + int targ_start_id, + struct bpf_core_cand_list *cands) +{ + struct bpf_core_cand *new_cands, *cand; + const struct btf_type *t; + const char *targ_name; + size_t targ_essent_len; + int n, i; + + n = btf_nr_types(targ_btf); + for (i = targ_start_id; i < n; i++) { + t = btf__type_by_id(targ_btf, i); + if (btf_kind(t) != btf_kind(local_cand->t)) + continue; + + targ_name = btf_name_by_offset(targ_btf, t->name_off); + if (str_is_empty(targ_name)) + continue; + + targ_essent_len = bpf_core_essential_name_len(targ_name); + if (targ_essent_len != local_essent_len) + continue; + + if (strncmp(local_cand->name, targ_name, local_essent_len) != 0) + continue; + +/* printk("CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s\n", + local_cand->id, btf_type_str(local_cand->t), + local_cand->name, i, btf_type_str(t), targ_name);*/ + new_cands = krealloc(cands->cands, + (cands->len + 1) * sizeof(*cands->cands), GFP_KERNEL); + if (!new_cands) + return -ENOMEM; + + cand = &new_cands[cands->len]; + cand->btf = targ_btf; + cand->t = t; + cand->name = targ_name; + cand->id = i; + + cands->cands = new_cands; + cands->len++; + } + return 0; +} + +static struct bpf_core_cand_list * +bpf_core_find_cands(const struct btf *local_btf, u32 local_type_id) +{ + struct bpf_core_cand local_cand = {}; + struct bpf_core_cand_list *cands; + const struct btf *main_btf; + size_t local_essent_len; + struct btf *mod_btf; + int err; + int id; + + local_cand.btf = local_btf; + local_cand.t = btf__type_by_id(local_btf, local_type_id); + if (!local_cand.t) + return ERR_PTR(-EINVAL); + + local_cand.name = btf_name_by_offset(local_btf, local_cand.t->name_off); + if (str_is_empty(local_cand.name)) + return ERR_PTR(-EINVAL); + local_essent_len = bpf_core_essential_name_len(local_cand.name); + + cands = kcalloc(1, sizeof(*cands), GFP_KERNEL); + if (!cands) + return ERR_PTR(-ENOMEM); + + /* Attempt to find target candidates in vmlinux BTF first */ + main_btf = bpf_get_btf_vmlinux(); + err = bpf_core_add_cands(&local_cand, local_essent_len, main_btf, 1, cands); + if (err) + goto err_out; + + /* if vmlinux BTF has any candidate, don't got for module BTFs */ + if (cands->len) + return cands; + + /* If candidate is not found in vmlinux's BTF then search in module's BTFs */ + spin_lock_bh(&btf_idr_lock); + idr_for_each_entry(&btf_idr, mod_btf, id) { + if (!btf_is_module(mod_btf)) + continue; + /* linear search could be slow hence unlock/lock + * the IDR to avoiding holding it for too long + */ + btf_get(mod_btf); + spin_unlock_bh(&btf_idr_lock); + err = bpf_core_add_cands(&local_cand, local_essent_len, + mod_btf, + btf_nr_types(main_btf), + cands); + if (err) + btf_put(mod_btf); + goto err_out; + spin_lock_bh(&btf_idr_lock); + btf_put(mod_btf); + } + spin_unlock_bh(&btf_idr_lock); + + return cands; +err_out: + bpf_core_free_cands(cands); + return ERR_PTR(err); +} + BPF_CALL_5(bpf_core_apply_relo, int, btf_fd, struct bpf_core_relo_desc *, relo, int, relo_sz, void *, insn, int, flags) { + struct bpf_core_cand_list *cands = NULL; + struct bpf_core_relo core_relo = {}; struct btf *btf; - long ret; + int err; if (flags) return -EINVAL; - return -EOPNOTSUPP; + + if (sizeof(*relo) != relo_sz) + return -EINVAL; + btf = btf_get_by_fd(btf_fd); + if (IS_ERR(btf)) + return PTR_ERR(btf); + if (btf_is_kernel(btf)) { + btf_put(btf); + return -EACCES; + } + if (relo->kind != BPF_CORE_TYPE_ID_LOCAL) { + cands = bpf_core_find_cands(btf, relo->type_id); + if (IS_ERR(cands)) { + btf_put(btf); + printk("target candidate search failed for %d\n", + relo->type_id); + return PTR_ERR(cands); + } + } + core_relo.type_id = relo->type_id; + core_relo.access_str_off = relo->access_str_off; + core_relo.kind = relo->kind; + err = bpf_core_apply_relo_insn("prog_name", insn, 0, &core_relo, 0, btf, cands); + btf_put(btf); + return 0; } const struct bpf_func_proto bpf_core_apply_relo_proto = { -- 2.30.2