A helper is added to allow seq file writing of kernel data structures using vmlinux BTF. Its signature is long bpf_seq_btf_write(struct seq_file *m, struct btf_ptr *ptr, u32 btf_ptr_size, u64 flags); Flags and struct btf_ptr definitions/use are identical to the bpf_btf_snprintf helper, and the helper returns 0 on success or a negative error value. Suggested-by: Alexei Starovoitov <alexei.starovoitov@xxxxxxxxx> Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- include/linux/btf.h | 3 ++ include/uapi/linux/bpf.h | 10 ++++++ kernel/bpf/btf.c | 17 +++++++--- kernel/trace/bpf_trace.c | 75 +++++++++++++++++++++++++++++++++--------- tools/include/uapi/linux/bpf.h | 10 ++++++ 5 files changed, 96 insertions(+), 19 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index 3e5cdc2..eed23a4 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -69,6 +69,9 @@ const struct btf_type *btf_type_id_size(const struct btf *btf, void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, struct seq_file *m); +int btf_type_seq_show_flags(const struct btf *btf, u32 type_id, void *obj, + struct seq_file *m, u64 flags); + /* * Copy len bytes of string representation of obj of BTF type_id into buf. * diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 9b89b67..c0815f1 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -3614,6 +3614,15 @@ struct bpf_stack_build_id { * The number of bytes that were written (or would have been * written if output had to be truncated due to string size), * or a negative error in cases of failure. + * + * long bpf_seq_btf_write(struct seq_file *m, struct btf_ptr *ptr, u32 ptr_size, u64 flags) + * Description + * Use BTF to write to seq_write a string representation of + * *ptr*->ptr, using *ptr*->type name or *ptr*->type_id as per + * bpf_btf_snprintf() above. *flags* are identical to those + * used for bpf_btf_snprintf. + * Return + * 0 on success or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3766,6 +3775,7 @@ struct bpf_stack_build_id { FN(d_path), \ FN(copy_from_user), \ FN(btf_snprintf), \ + FN(seq_btf_write), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 70f5b88..0902464 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5328,17 +5328,26 @@ static void btf_seq_show(struct btf_show *show, const char *fmt, ...) va_end(args); } -void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, - struct seq_file *m) +int btf_type_seq_show_flags(const struct btf *btf, u32 type_id, void *obj, + struct seq_file *m, u64 flags) { struct btf_show sseq; sseq.target = m; sseq.showfn = btf_seq_show; - sseq.flags = BTF_SHOW_NONAME | BTF_SHOW_COMPACT | BTF_SHOW_ZERO | - BTF_SHOW_UNSAFE; + sseq.flags = flags; btf_type_show(btf, type_id, obj, &sseq); + + return sseq.state.status; +} + +void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, + struct seq_file *m) +{ + (void) btf_type_seq_show_flags(btf, type_id, obj, m, + BTF_SHOW_NONAME | BTF_SHOW_COMPACT | + BTF_SHOW_ZERO | BTF_SHOW_UNSAFE); } struct btf_show_snprintf { diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index f171e03..eee36a8 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -71,6 +71,10 @@ static struct bpf_raw_event_map *bpf_get_raw_tracepoint_module(const char *name) u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); u64 bpf_get_stack(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); +static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size, + u64 flags, const struct btf **btf, + s32 *btf_id); + /** * trace_call_bpf - invoke BPF program * @call: tracepoint event @@ -780,6 +784,30 @@ struct bpf_seq_printf_buf { .btf_id = bpf_seq_write_btf_ids, }; +BPF_CALL_4(bpf_seq_btf_write, struct seq_file *, m, struct btf_ptr *, ptr, + u32, btf_ptr_size, u64, flags) +{ + const struct btf *btf; + s32 btf_id; + int ret; + + ret = bpf_btf_printf_prepare(ptr, btf_ptr_size, flags, &btf, &btf_id); + if (ret) + return ret; + + return btf_type_seq_show_flags(btf, btf_id, ptr->ptr, m, flags); +} + +static const struct bpf_func_proto bpf_seq_btf_write_proto = { + .func = bpf_seq_btf_write, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .btf_id = bpf_seq_write_btf_ids, +}; + static __always_inline int get_map_perf_counter(struct bpf_map *map, u64 flags, u64 *value, u64 *enabled, u64 *running) @@ -1151,15 +1179,14 @@ static bool bpf_d_path_allowed(const struct bpf_prog *prog) #define BTF_F_ALL (BTF_F_COMPACT | BTF_F_NONAME | \ BTF_F_PTR_RAW | BTF_F_ZERO) -BPF_CALL_5(bpf_btf_snprintf, char *, str, u32, str_size, struct btf_ptr *, ptr, - u32, btf_ptr_size, u64, flags) +static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size, + u64 flags, const struct btf **btf, + s32 *btf_id) { u8 btf_kind = BTF_KIND_TYPEDEF; char type_name[KSYM_NAME_LEN]; const struct btf_type *t; - const struct btf *btf; const char *btf_type; - s32 btf_id; int ret; if (unlikely(flags & ~(BTF_F_ALL))) @@ -1168,10 +1195,10 @@ static bool bpf_d_path_allowed(const struct bpf_prog *prog) if (btf_ptr_size != sizeof(struct btf_ptr)) return -EINVAL; - btf = bpf_get_btf_vmlinux(); + *btf = bpf_get_btf_vmlinux(); - if (IS_ERR_OR_NULL(btf)) - return PTR_ERR(btf); + if (IS_ERR_OR_NULL(*btf)) + return PTR_ERR(*btf); if (ptr->type != NULL) { ret = copy_from_kernel_nofault(type_name, ptr->type, @@ -1201,20 +1228,34 @@ static bool bpf_d_path_allowed(const struct bpf_prog *prog) * * Fall back to BTF_KIND_INT if this fails. */ - btf_id = btf_find_by_name_kind(btf, btf_type, btf_kind); - if (btf_id < 0) - btf_id = btf_find_by_name_kind(btf, btf_type, - BTF_KIND_INT); + *btf_id = btf_find_by_name_kind(*btf, btf_type, btf_kind); + if (*btf_id < 0) + *btf_id = btf_find_by_name_kind(*btf, btf_type, + BTF_KIND_INT); } else if (ptr->type_id > 0) - btf_id = ptr->type_id; + *btf_id = ptr->type_id; else return -EINVAL; - if (btf_id > 0) - t = btf_type_by_id(btf, btf_id); - if (btf_id <= 0 || !t) + if (*btf_id > 0) + t = btf_type_by_id(*btf, *btf_id); + if (*btf_id <= 0 || !t) return -ENOENT; + return 0; +} + +BPF_CALL_5(bpf_btf_snprintf, char *, str, u32, str_size, struct btf_ptr *, ptr, + u32, btf_ptr_size, u64, flags) +{ + const struct btf *btf; + s32 btf_id; + int ret; + + ret = bpf_btf_printf_prepare(ptr, btf_ptr_size, flags, &btf, &btf_id); + if (ret) + return ret; + return btf_type_snprintf_show(btf, btf_id, ptr->ptr, str, str_size, flags); } @@ -1715,6 +1756,10 @@ static void put_bpf_raw_tp_regs(void) return prog->expected_attach_type == BPF_TRACE_ITER ? &bpf_seq_write_proto : NULL; + case BPF_FUNC_seq_btf_write: + return prog->expected_attach_type == BPF_TRACE_ITER ? + &bpf_seq_btf_write_proto : + NULL; case BPF_FUNC_d_path: return &bpf_d_path_proto; default: diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 9b89b67..c0815f1 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -3614,6 +3614,15 @@ struct bpf_stack_build_id { * The number of bytes that were written (or would have been * written if output had to be truncated due to string size), * or a negative error in cases of failure. + * + * long bpf_seq_btf_write(struct seq_file *m, struct btf_ptr *ptr, u32 ptr_size, u64 flags) + * Description + * Use BTF to write to seq_write a string representation of + * *ptr*->ptr, using *ptr*->type name or *ptr*->type_id as per + * bpf_btf_snprintf() above. *flags* are identical to those + * used for bpf_btf_snprintf. + * Return + * 0 on success or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3766,6 +3775,7 @@ struct bpf_stack_build_id { FN(d_path), \ FN(copy_from_user), \ FN(btf_snprintf), \ + FN(seq_btf_write), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper -- 1.8.3.1