@@ -264,6 +264,7 @@ int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
attr.line_info_rec_size = load_attr->line_info_rec_size;
attr.line_info_cnt = load_attr->line_info_cnt;
attr.line_info = ptr_to_u64(load_attr->line_info);
+ attr.fd_array = ptr_to_u64(load_attr->fd_array);
if (load_attr->name)
memcpy(attr.prog_name, load_attr->name,
@@ -419,6 +419,12 @@ struct extern_desc {
/* local btf_id of the ksym extern's type. */
__u32 type_id;
+ /* offset to be patched in for insn->off,
+ * this is 0 for btf_vmlinux, and index + 1
+ * for module BTF, where index is BTF index in
+ * obj->fd_array
+ */
+ __s16 offset;
} ksym;
};
};
@@ -515,6 +521,10 @@ struct bpf_object {
void *priv;
bpf_object_clear_priv_t clear_priv;
+ int *fd_array;
+ size_t fd_cap_cnt;
+ int nr_fds;
+
char path[];
};
#define obj_elf_valid(o) ((o)->efile.elf)
@@ -5333,6 +5343,7 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
ext = &obj->externs[relo->sym_off];
insn[0].src_reg = BPF_PSEUDO_KFUNC_CALL;
insn[0].imm = ext->ksym.kernel_btf_id;
+ insn[0].off = ext->ksym.offset;
break;
case RELO_SUBPROG_ADDR:
if (insn[0].src_reg != BPF_PSEUDO_FUNC) {
@@ -6127,6 +6138,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
}
load_attr.log_level = prog->log_level;
load_attr.prog_flags = prog->prog_flags;
+ load_attr.fd_array = prog->obj->fd_array;
if (prog->obj->gen_loader) {
bpf_gen__prog_load(prog->obj->gen_loader, &load_attr,
@@ -6729,9 +6741,44 @@ static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
}
if (kern_btf != obj->btf_vmlinux) {
- pr_warn("extern (func ksym) '%s': function in kernel module is not supported\n",
- ext->name);
- return -ENOTSUP;
+ int index = -1;
+
+ if (!obj->fd_array) {
+ obj->fd_array = calloc(8, sizeof(*obj->fd_array));
+ if (!obj->fd_array)
+ return -ENOMEM;
+ obj->fd_cap_cnt = 8;
+ }
+
+ for (int i = 0; i < obj->nr_fds; i++) {
+ if (obj->fd_array[i] == kern_btf_fd) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ if (obj->nr_fds == obj->fd_cap_cnt) {
+ ret = libbpf_ensure_mem((void **)&obj->fd_array,
+ &obj->fd_cap_cnt, sizeof(int),
+ obj->fd_cap_cnt + 1);
+ if (ret)
+ return ret;
+ }
+
+ index = obj->nr_fds;
+ obj->fd_array[obj->nr_fds++] = kern_btf_fd;
+ }
+
+ if (index >= INT16_MAX) {
+ /* insn->off is s16 */
+ pr_warn("extern (func ksym) '%s': module btf fd index too big\n",
+ ext->name);
+ return -E2BIG;
+ }
+ ext->ksym.offset = index + 1;
+ } else {
+ ext->ksym.offset = 0;
}
kern_func = btf__type_by_id(kern_btf, kfunc_id);
@@ -6907,6 +6954,9 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
err = bpf_gen__finish(obj->gen_loader);
}
+ /* clean up fd_array */
+ zfree(&obj->fd_array);
+
/* clean up module BTFs */
for (i = 0; i < obj->btf_module_cnt; i++) {
close(obj->btf_modules[i].fd);
@@ -289,6 +289,7 @@ struct bpf_prog_load_params {
__u32 log_level;
char *log_buf;
size_t log_buf_sz;
+ int *fd_array;
};
int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr);
This patch adds libbpf support for kernel module function call support. The fd_array parameter is used during BPF program load is used to pass module BTFs referenced by the program. insn->off is set to index into this array + 1, because insn->off as 0 is reserved for btf_vmlinux. The kernel subtracts 1 from insn->off when indexing into env->fd_array. We try to use existing insn->off for a module, since the kernel limits the maximum distinct module BTFs for kfuncs to 256, and also because index must never exceed the maximum allowed value that can fit in insn->off (INT16_MAX). In the future, if kernel interprets signed offset as unsigned for kfunc calls, this limit can be increased to UINT16_MAX. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> --- tools/lib/bpf/bpf.c | 1 + tools/lib/bpf/libbpf.c | 56 +++++++++++++++++++++++++++++++-- tools/lib/bpf/libbpf_internal.h | 1 + 3 files changed, 55 insertions(+), 3 deletions(-)