2022-07-10 22:20:06

by Alan Maguire

[permalink] [raw]
Subject: [PATCH v5 bpf-next 0/2] bpf: add a ksym BPF iterator

a ksym BPF iterator would be useful as it would allow more flexible
interactions with kernel symbols than are currently supported; it could
for example create more efficient map representations for lookup,
speed up symbol resolution etc.

The idea was initially discussed here [1].

Changes since v4 [2]:

- add BPF_ITER_RESCHED to improve responsiveness (Hao, patch 1)
- remove pr_warn to be consistent with other iterators (Andrii, patch 1)
- add definitions to bpf_iter.h to ensure iter tests build on older
kernels (Andrii, patch 2)

Changes since v3 [3]:

- use late_initcall() to register iter; means we are both consistent
with other iters and can encapsulate all iter-specific code in
kallsyms.c in CONFIG_BPF_SYSCALL (Alexei, Yonghong, patch 1).

Changes since v2 [4]:

- set iter->show_value on initialization based on current creds
and use it in selftest to determine if we show values
(Yonghong, patches 1/2)
- inline iter registration into kallsyms_init (Yonghong, patch 1)

Changes since RFC [5]:

- change name of iterator (and associated structures/fields) to "ksym"
(Andrii, patches 1, 2)
- remove dependency on CONFIG_PROC_FS; it was used for other BPF
iterators, and I assumed it was needed because of seq ops but I
don't think it is required on digging futher (Andrii, patch 1)

[1] https://lore.kernel.org/all/YjRPZj6Z8vuLeEZo@krava/
[2] https://lore.kernel.org/bpf/[email protected]/
[3] https://lore.kernel.org/bpf/[email protected]
[4] https://lore.kernel.org/bpf/[email protected]/
[5] https://lore.kernel.org/all/[email protected]/

Alan Maguire (2):
bpf: add a ksym BPF iterator
selftests/bpf: add a ksym iter subtest

kernel/kallsyms.c | 91 +++++++++++++++++++++++
tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 16 ++++
tools/testing/selftests/bpf/progs/bpf_iter.h | 32 ++++++++
tools/testing/selftests/bpf/progs/bpf_iter_ksym.c | 74 ++++++++++++++++++
4 files changed, 213 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/bpf_iter_ksym.c

--
1.8.3.1


2022-07-10 22:22:43

by Alan Maguire

[permalink] [raw]
Subject: [PATCH v5 bpf-next 1/2] bpf: add a ksym BPF iterator

add a "ksym" iterator which provides access to a "struct kallsym_iter"
for each symbol. Intent is to support more flexible symbol parsing
as discussed in [1].

[1] https://lore.kernel.org/all/YjRPZj6Z8vuLeEZo@krava/

Suggested-by: Alexei Starovoitov <[email protected]>
Signed-off-by: Alan Maguire <[email protected]>
Acked-by: Yonghong Song <[email protected]>
---
kernel/kallsyms.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)

diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index fbdf8d3..79a8583 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bsearch.h>
+#include <linux/btf_ids.h>

/*
* These will be re-linked against their real values
@@ -799,6 +800,96 @@ static int s_show(struct seq_file *m, void *p)
.show = s_show
};

+#ifdef CONFIG_BPF_SYSCALL
+
+struct bpf_iter__ksym {
+ __bpf_md_ptr(struct bpf_iter_meta *, meta);
+ __bpf_md_ptr(struct kallsym_iter *, ksym);
+};
+
+static int ksym_prog_seq_show(struct seq_file *m, bool in_stop)
+{
+ struct bpf_iter__ksym ctx;
+ struct bpf_iter_meta meta;
+ struct bpf_prog *prog;
+
+ meta.seq = m;
+ prog = bpf_iter_get_info(&meta, in_stop);
+ if (!prog)
+ return 0;
+
+ ctx.meta = &meta;
+ ctx.ksym = m ? m->private : NULL;
+ return bpf_iter_run_prog(prog, &ctx);
+}
+
+static int bpf_iter_ksym_seq_show(struct seq_file *m, void *p)
+{
+ return ksym_prog_seq_show(m, false);
+}
+
+static void bpf_iter_ksym_seq_stop(struct seq_file *m, void *p)
+{
+ if (!p)
+ (void) ksym_prog_seq_show(m, true);
+ else
+ s_stop(m, p);
+}
+
+static const struct seq_operations bpf_iter_ksym_ops = {
+ .start = s_start,
+ .next = s_next,
+ .stop = bpf_iter_ksym_seq_stop,
+ .show = bpf_iter_ksym_seq_show,
+};
+
+static int bpf_iter_ksym_init(void *priv_data, struct bpf_iter_aux_info *aux)
+{
+ struct kallsym_iter *iter = priv_data;
+
+ reset_iter(iter, 0);
+
+ /* cache here as in kallsyms_open() case; use current process
+ * credentials to tell BPF iterators if values should be shown.
+ */
+ iter->show_value = kallsyms_show_value(current_cred());
+
+ return 0;
+}
+
+DEFINE_BPF_ITER_FUNC(ksym, struct bpf_iter_meta *meta, struct kallsym_iter *ksym)
+
+static const struct bpf_iter_seq_info ksym_iter_seq_info = {
+ .seq_ops = &bpf_iter_ksym_ops,
+ .init_seq_private = bpf_iter_ksym_init,
+ .fini_seq_private = NULL,
+ .seq_priv_size = sizeof(struct kallsym_iter),
+};
+
+static struct bpf_iter_reg ksym_iter_reg_info = {
+ .target = "ksym",
+ .feature = BPF_ITER_RESCHED,
+ .ctx_arg_info_size = 1,
+ .ctx_arg_info = {
+ { offsetof(struct bpf_iter__ksym, ksym),
+ PTR_TO_BTF_ID_OR_NULL },
+ },
+ .seq_info = &ksym_iter_seq_info,
+};
+
+BTF_ID_LIST(btf_ksym_iter_id)
+BTF_ID(struct, kallsym_iter)
+
+static int __init bpf_ksym_iter_register(void)
+{
+ ksym_iter_reg_info.ctx_arg_info[0].btf_id = *btf_ksym_iter_id;
+ return bpf_iter_reg_target(&ksym_iter_reg_info);
+}
+
+late_initcall(bpf_ksym_iter_register);
+
+#endif /* CONFIG_BPF_SYSCALL */
+
static inline int kallsyms_for_perf(void)
{
#ifdef CONFIG_PERF_EVENTS
--
1.8.3.1

2022-07-10 22:25:34

by Alan Maguire

[permalink] [raw]
Subject: [PATCH v5 bpf-next 2/2] selftests/bpf: add a ksym iter subtest

add subtest verifying BPF ksym iter behaviour. The BPF ksym
iter program shows an example of dumping a format different to
/proc/kallsyms. It adds KIND and MAX_SIZE fields which represent the
kind of symbol (core kernel, module, ftrace, bpf, or kprobe) and
the maximum size the symbol can be. The latter is calculated from
the difference between current symbol value and the next symbol
value.

The key benefit for this iterator will likely be supporting in-kernel
data-gathering rather than dumping symbol details to userspace and
parsing the results.

Signed-off-by: Alan Maguire <[email protected]>
Acked-by: Yonghong Song <[email protected]>
---
tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 16 +++++
tools/testing/selftests/bpf/progs/bpf_iter.h | 32 ++++++++++
tools/testing/selftests/bpf/progs/bpf_iter_ksym.c | 74 +++++++++++++++++++++++
3 files changed, 122 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/bpf_iter_ksym.c

diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
index 7ff5fa9..a33874b 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
@@ -27,6 +27,7 @@
#include "bpf_iter_test_kern5.skel.h"
#include "bpf_iter_test_kern6.skel.h"
#include "bpf_iter_bpf_link.skel.h"
+#include "bpf_iter_ksym.skel.h"

static int duration;

@@ -1120,6 +1121,19 @@ static void test_link_iter(void)
bpf_iter_bpf_link__destroy(skel);
}

+static void test_ksym_iter(void)
+{
+ struct bpf_iter_ksym *skel;
+
+ skel = bpf_iter_ksym__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "bpf_iter_ksym__open_and_load"))
+ return;
+
+ do_dummy_read(skel->progs.dump_ksym);
+
+ bpf_iter_ksym__destroy(skel);
+}
+
#define CMP_BUFFER_SIZE 1024
static char task_vma_output[CMP_BUFFER_SIZE];
static char proc_maps_output[CMP_BUFFER_SIZE];
@@ -1267,4 +1281,6 @@ void test_bpf_iter(void)
test_buf_neg_offset();
if (test__start_subtest("link-iter"))
test_link_iter();
+ if (test__start_subtest("ksym"))
+ test_ksym_iter();
}
diff --git a/tools/testing/selftests/bpf/progs/bpf_iter.h b/tools/testing/selftests/bpf/progs/bpf_iter.h
index 97ec8bc..4b23a08 100644
--- a/tools/testing/selftests/bpf/progs/bpf_iter.h
+++ b/tools/testing/selftests/bpf/progs/bpf_iter.h
@@ -22,6 +22,8 @@
#define BTF_F_NONAME BTF_F_NONAME___not_used
#define BTF_F_PTR_RAW BTF_F_PTR_RAW___not_used
#define BTF_F_ZERO BTF_F_ZERO___not_used
+#define bpf_iter__ksym bpf_iter__ksym___not_used
+#define kallsym_iter kallsym_iter___not_used
#include "vmlinux.h"
#undef bpf_iter_meta
#undef bpf_iter__bpf_map
@@ -44,6 +46,8 @@
#undef BTF_F_NONAME
#undef BTF_F_PTR_RAW
#undef BTF_F_ZERO
+#undef bpf_iter__ksym
+#undef kallsym_iter

struct bpf_iter_meta {
struct seq_file *seq;
@@ -151,3 +155,31 @@ enum {
BTF_F_PTR_RAW = (1ULL << 2),
BTF_F_ZERO = (1ULL << 3),
};
+
+#ifndef KSYM_NAME_LEN
+#define KSYM_NAME_LEN 128
+#endif
+
+#ifndef MODULE_NAME_LEN
+#define MODULE_NAME_LEN (64 - sizeof(unsigned long))
+#endif
+
+struct kallsym_iter {
+ loff_t pos;
+ loff_t pos_arch_end;
+ loff_t pos_mod_end;
+ loff_t pos_ftrace_mod_end;
+ loff_t pos_bpf_end;
+ unsigned long value;
+ unsigned int nameoff; /* If iterating in core kernel symbols. */
+ char type;
+ char name[KSYM_NAME_LEN];
+ char module_name[MODULE_NAME_LEN];
+ int exported;
+ int show_value;
+};
+
+struct bpf_iter__ksym {
+ struct bpf_iter_meta *meta;
+ struct kallsym_iter *ksym;
+};
diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c b/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c
new file mode 100644
index 0000000..285c008
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022, Oracle and/or its affiliates. */
+#include "bpf_iter.h"
+#include <bpf/bpf_helpers.h>
+
+char _license[] SEC("license") = "GPL";
+
+unsigned long last_sym_value = 0;
+
+static inline char tolower(char c)
+{
+ if (c >= 'A' && c <= 'Z')
+ c += ('a' - 'A');
+ return c;
+}
+
+static inline char toupper(char c)
+{
+ if (c >= 'a' && c <= 'z')
+ c -= ('a' - 'A');
+ return c;
+}
+
+/* Dump symbols with max size; the latter is calculated by caching symbol N value
+ * and when iterating on symbol N+1, we can print max size of symbol N via
+ * address of N+1 - address of N.
+ */
+SEC("iter/ksym")
+int dump_ksym(struct bpf_iter__ksym *ctx)
+{
+ struct seq_file *seq = ctx->meta->seq;
+ struct kallsym_iter *iter = ctx->ksym;
+ __u32 seq_num = ctx->meta->seq_num;
+ unsigned long value;
+ char type;
+ int ret;
+
+ if (!iter)
+ return 0;
+
+ if (seq_num == 0) {
+ BPF_SEQ_PRINTF(seq, "ADDR TYPE NAME MODULE_NAME KIND MAX_SIZE\n");
+ return 0;
+ }
+ if (last_sym_value)
+ BPF_SEQ_PRINTF(seq, "0x%x\n", iter->value - last_sym_value);
+ else
+ BPF_SEQ_PRINTF(seq, "\n");
+
+ value = iter->show_value ? iter->value : 0;
+
+ last_sym_value = value;
+
+ type = iter->type;
+
+ if (iter->module_name[0]) {
+ type = iter->exported ? toupper(type) : tolower(type);
+ BPF_SEQ_PRINTF(seq, "0x%llx %c %s [ %s ] ",
+ value, type, iter->name, iter->module_name);
+ } else {
+ BPF_SEQ_PRINTF(seq, "0x%llx %c %s ", value, type, iter->name);
+ }
+ if (!iter->pos_arch_end || iter->pos_arch_end > iter->pos)
+ BPF_SEQ_PRINTF(seq, "CORE ");
+ else if (!iter->pos_mod_end || iter->pos_mod_end > iter->pos)
+ BPF_SEQ_PRINTF(seq, "MOD ");
+ else if (!iter->pos_ftrace_mod_end || iter->pos_ftrace_mod_end > iter->pos)
+ BPF_SEQ_PRINTF(seq, "FTRACE_MOD ");
+ else if (!iter->pos_bpf_end || iter->pos_bpf_end > iter->pos)
+ BPF_SEQ_PRINTF(seq, "BPF ");
+ else
+ BPF_SEQ_PRINTF(seq, "KPROBE ");
+ return 0;
+}
--
1.8.3.1

2022-07-11 00:35:13

by Yonghong Song

[permalink] [raw]
Subject: Re: [PATCH v5 bpf-next 2/2] selftests/bpf: add a ksym iter subtest



On 7/10/22 3:09 PM, Alan Maguire wrote:
> add subtest verifying BPF ksym iter behaviour. The BPF ksym
> iter program shows an example of dumping a format different to
> /proc/kallsyms. It adds KIND and MAX_SIZE fields which represent the
> kind of symbol (core kernel, module, ftrace, bpf, or kprobe) and
> the maximum size the symbol can be. The latter is calculated from
> the difference between current symbol value and the next symbol
> value.
>
> The key benefit for this iterator will likely be supporting in-kernel
> data-gathering rather than dumping symbol details to userspace and
> parsing the results.
>
> Signed-off-by: Alan Maguire <[email protected]>
> Acked-by: Yonghong Song <[email protected]>
> ---
> tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 16 +++++
> tools/testing/selftests/bpf/progs/bpf_iter.h | 32 ++++++++++
> tools/testing/selftests/bpf/progs/bpf_iter_ksym.c | 74 +++++++++++++++++++++++
> 3 files changed, 122 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/progs/bpf_iter_ksym.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
> index 7ff5fa9..a33874b 100644
> --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
> +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
> @@ -27,6 +27,7 @@
> #include "bpf_iter_test_kern5.skel.h"
> #include "bpf_iter_test_kern6.skel.h"
> #include "bpf_iter_bpf_link.skel.h"
> +#include "bpf_iter_ksym.skel.h"
>
> static int duration;
>
> @@ -1120,6 +1121,19 @@ static void test_link_iter(void)
> bpf_iter_bpf_link__destroy(skel);
> }
>
> +static void test_ksym_iter(void)
> +{
> + struct bpf_iter_ksym *skel;
> +
> + skel = bpf_iter_ksym__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "bpf_iter_ksym__open_and_load"))
> + return;
> +
> + do_dummy_read(skel->progs.dump_ksym);
> +
> + bpf_iter_ksym__destroy(skel);
> +}
> +
> #define CMP_BUFFER_SIZE 1024
> static char task_vma_output[CMP_BUFFER_SIZE];
> static char proc_maps_output[CMP_BUFFER_SIZE];
> @@ -1267,4 +1281,6 @@ void test_bpf_iter(void)
> test_buf_neg_offset();
> if (test__start_subtest("link-iter"))
> test_link_iter();
> + if (test__start_subtest("ksym"))
> + test_ksym_iter();
> }
> diff --git a/tools/testing/selftests/bpf/progs/bpf_iter.h b/tools/testing/selftests/bpf/progs/bpf_iter.h
> index 97ec8bc..4b23a08 100644
> --- a/tools/testing/selftests/bpf/progs/bpf_iter.h
> +++ b/tools/testing/selftests/bpf/progs/bpf_iter.h
> @@ -22,6 +22,8 @@
> #define BTF_F_NONAME BTF_F_NONAME___not_used
> #define BTF_F_PTR_RAW BTF_F_PTR_RAW___not_used
> #define BTF_F_ZERO BTF_F_ZERO___not_used
> +#define bpf_iter__ksym bpf_iter__ksym___not_used
> +#define kallsym_iter kallsym_iter___not_used

There is no need to do kallsym_iter. This structure
should appear in vmlinux.h for all supporting bpf kernels.

> #include "vmlinux.h"
> #undef bpf_iter_meta
> #undef bpf_iter__bpf_map
> @@ -44,6 +46,8 @@
> #undef BTF_F_NONAME
> #undef BTF_F_PTR_RAW
> #undef BTF_F_ZERO
> +#undef bpf_iter__ksym
> +#undef kallsym_iter
>
> struct bpf_iter_meta {
> struct seq_file *seq;
> @@ -151,3 +155,31 @@ enum {
> BTF_F_PTR_RAW = (1ULL << 2),
> BTF_F_ZERO = (1ULL << 3),
> };
> +
> +#ifndef KSYM_NAME_LEN
> +#define KSYM_NAME_LEN 128
> +#endif
> +
> +#ifndef MODULE_NAME_LEN
> +#define MODULE_NAME_LEN (64 - sizeof(unsigned long))
> +#endif
> +
> +struct kallsym_iter {
> + loff_t pos;
> + loff_t pos_arch_end;
> + loff_t pos_mod_end;
> + loff_t pos_ftrace_mod_end;
> + loff_t pos_bpf_end;
> + unsigned long value;
> + unsigned int nameoff; /* If iterating in core kernel symbols. */
> + char type;
> + char name[KSYM_NAME_LEN];
> + char module_name[MODULE_NAME_LEN];
> + int exported;
> + int show_value;
> +};
> +
> +struct bpf_iter__ksym {
> + struct bpf_iter_meta *meta;
> + struct kallsym_iter *ksym;
> +};
[...]