All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation
@ 2022-06-16 20:22 Jiri Olsa
  2022-06-16 20:22 ` [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code Jiri Olsa
  2022-06-24 20:40 ` [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation patchwork-bot+netdevbpf
  0 siblings, 2 replies; 6+ messages in thread
From: Jiri Olsa @ 2022-06-16 20:22 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers

hi,
sending change we discussed some time ago [1] to get rid of
some deprecated functions we use in perf prologue code.

Despite the gloomy discussion I think the final code does
not look that bad ;-)

This patchset removes following libbpf functions from perf:
  bpf_program__set_prep
  bpf_program__nth_fd
  struct bpf_prog_prep_result

v5 changes:
  - squashed patches together so we don't break bisection [Arnaldo]

v4 changes:
  - fix typo [Andrii]

v3 changes:
  - removed R0/R1 zero init in libbpf_prog_prepare_load_fn,
    because it's not needed [Andrii]
  - rebased/post on top of bpf-next/master which now has
    all the needed perf/core changes

v2 changes:
  - use fallback section prog handler, so we don't need to
    use section prefix [Andrii]
  - realloc prog->insns array in bpf_program__set_insns [Andrii]
  - squash patch 1 from previous version with
    bpf_program__set_insns change [Daniel]
  - patch 3 already merged [Arnaldo]
  - added more comments

thanks,
jirka


[1] https://lore.kernel.org/bpf/CAEf4BzaiBO3_617kkXZdYJ8hS8YF--ZLgapNbgeeEJ-pY0H88g@mail.gmail.com/
---
Jiri Olsa (1):
      perf tools: Rework prologue generation code

 tools/perf/util/bpf-loader.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 175 insertions(+), 29 deletions(-)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code
  2022-06-16 20:22 [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation Jiri Olsa
@ 2022-06-16 20:22 ` Jiri Olsa
  2022-06-20 12:43   ` Daniel Borkmann
  2022-06-24 18:59   ` Arnaldo Carvalho de Melo
  2022-06-24 20:40 ` [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation patchwork-bot+netdevbpf
  1 sibling, 2 replies; 6+ messages in thread
From: Jiri Olsa @ 2022-06-16 20:22 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers

Some functions we use for bpf prologue generation are going to be
deprecated. This change reworks current code not to use them.

We need to replace following functions/struct:
   bpf_program__set_prep
   bpf_program__nth_fd
   struct bpf_prog_prep_result

Currently we use bpf_program__set_prep to hook perf callback before
program is loaded and provide new instructions with the prologue.

We replace this function/ality by taking instructions for specific
program, attaching prologue to them and load such new ebpf programs
with prologue using separate bpf_prog_load calls (outside libbpf
load machinery).

Before we can take and use program instructions, we need libbpf to
actually load it. This way we get the final shape of its instructions
with all relocations and verifier adjustments).

There's one glitch though.. perf kprobe program already assumes
generated prologue code with proper values in argument registers,
so loading such program directly will fail in the verifier.

That's where the fallback pre-load handler fits in and prepends
the initialization code to the program. Once such program is loaded
we take its instructions, cut off the initialization code and prepend
the prologue.

I know.. sorry ;-)

To have access to the program when loading this patch adds support to
register 'fallback' section handler to take care of perf kprobe programs.
The fallback means that it handles any section definition besides the
ones that libbpf handles.

The handler serves two purposes:
  - allows perf programs to have special arguments in section name
  - allows perf to use pre-load callback where we can attach init
    code (zeroing all argument registers) to each perf program

Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/perf/util/bpf-loader.c | 204 ++++++++++++++++++++++++++++++-----
 1 file changed, 175 insertions(+), 29 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index f8ad581ea247..6bd7c288e820 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -9,6 +9,7 @@
 #include <linux/bpf.h>
 #include <bpf/libbpf.h>
 #include <bpf/bpf.h>
+#include <linux/filter.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -49,6 +50,7 @@ struct bpf_prog_priv {
 	struct bpf_insn *insns_buf;
 	int nr_types;
 	int *type_mapping;
+	int *prologue_fds;
 };
 
 struct bpf_perf_object {
@@ -56,6 +58,11 @@ struct bpf_perf_object {
 	struct bpf_object *obj;
 };
 
+struct bpf_preproc_result {
+	struct bpf_insn *new_insn_ptr;
+	int new_insn_cnt;
+};
+
 static LIST_HEAD(bpf_objects_list);
 static struct hashmap *bpf_program_hash;
 static struct hashmap *bpf_map_hash;
@@ -86,6 +93,7 @@ bpf_perf_object__next(struct bpf_perf_object *prev)
 	     (perf_obj) = (tmp), (tmp) = bpf_perf_object__next(tmp))
 
 static bool libbpf_initialized;
+static int libbpf_sec_handler;
 
 static int bpf_perf_object__add(struct bpf_object *obj)
 {
@@ -99,12 +107,76 @@ static int bpf_perf_object__add(struct bpf_object *obj)
 	return perf_obj ? 0 : -ENOMEM;
 }
 
+static void *program_priv(const struct bpf_program *prog)
+{
+	void *priv;
+
+	if (IS_ERR_OR_NULL(bpf_program_hash))
+		return NULL;
+	if (!hashmap__find(bpf_program_hash, prog, &priv))
+		return NULL;
+	return priv;
+}
+
+static struct bpf_insn prologue_init_insn[] = {
+	BPF_MOV64_IMM(BPF_REG_2, 0),
+	BPF_MOV64_IMM(BPF_REG_3, 0),
+	BPF_MOV64_IMM(BPF_REG_4, 0),
+	BPF_MOV64_IMM(BPF_REG_5, 0),
+};
+
+static int libbpf_prog_prepare_load_fn(struct bpf_program *prog,
+				       struct bpf_prog_load_opts *opts __maybe_unused,
+				       long cookie __maybe_unused)
+{
+	size_t init_size_cnt = ARRAY_SIZE(prologue_init_insn);
+	size_t orig_insn_cnt, insn_cnt, init_size, orig_size;
+	struct bpf_prog_priv *priv = program_priv(prog);
+	const struct bpf_insn *orig_insn;
+	struct bpf_insn *insn;
+
+	if (IS_ERR_OR_NULL(priv)) {
+		pr_debug("bpf: failed to get private field\n");
+		return -BPF_LOADER_ERRNO__INTERNAL;
+	}
+
+	if (!priv->need_prologue)
+		return 0;
+
+	/* prepend initialization code to program instructions */
+	orig_insn = bpf_program__insns(prog);
+	orig_insn_cnt = bpf_program__insn_cnt(prog);
+	init_size = init_size_cnt * sizeof(*insn);
+	orig_size = orig_insn_cnt * sizeof(*insn);
+
+	insn_cnt = orig_insn_cnt + init_size_cnt;
+	insn = malloc(insn_cnt * sizeof(*insn));
+	if (!insn)
+		return -ENOMEM;
+
+	memcpy(insn, prologue_init_insn, init_size);
+	memcpy((char *) insn + init_size, orig_insn, orig_size);
+	bpf_program__set_insns(prog, insn, insn_cnt);
+	return 0;
+}
+
 static int libbpf_init(void)
 {
+	LIBBPF_OPTS(libbpf_prog_handler_opts, handler_opts,
+		.prog_prepare_load_fn = libbpf_prog_prepare_load_fn,
+	);
+
 	if (libbpf_initialized)
 		return 0;
 
 	libbpf_set_print(libbpf_perf_print);
+	libbpf_sec_handler = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_KPROBE,
+							  0, &handler_opts);
+	if (libbpf_sec_handler < 0) {
+		pr_debug("bpf: failed to register libbpf section handler: %d\n",
+			 libbpf_sec_handler);
+		return -BPF_LOADER_ERRNO__INTERNAL;
+	}
 	libbpf_initialized = true;
 	return 0;
 }
@@ -188,14 +260,31 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 	return obj;
 }
 
+static void close_prologue_programs(struct bpf_prog_priv *priv)
+{
+	struct perf_probe_event *pev;
+	int i, fd;
+
+	if (!priv->need_prologue)
+		return;
+	pev = &priv->pev;
+	for (i = 0; i < pev->ntevs; i++) {
+		fd = priv->prologue_fds[i];
+		if (fd != -1)
+			close(fd);
+	}
+}
+
 static void
 clear_prog_priv(const struct bpf_program *prog __maybe_unused,
 		void *_priv)
 {
 	struct bpf_prog_priv *priv = _priv;
 
+	close_prologue_programs(priv);
 	cleanup_perf_probe_events(&priv->pev, 1);
 	zfree(&priv->insns_buf);
+	zfree(&priv->prologue_fds);
 	zfree(&priv->type_mapping);
 	zfree(&priv->sys_name);
 	zfree(&priv->evt_name);
@@ -243,17 +332,6 @@ static bool ptr_equal(const void *key1, const void *key2,
 	return key1 == key2;
 }
 
-static void *program_priv(const struct bpf_program *prog)
-{
-	void *priv;
-
-	if (IS_ERR_OR_NULL(bpf_program_hash))
-		return NULL;
-	if (!hashmap__find(bpf_program_hash, prog, &priv))
-		return NULL;
-	return priv;
-}
-
 static int program_set_priv(struct bpf_program *prog, void *priv)
 {
 	void *old_priv;
@@ -558,8 +636,8 @@ static int bpf__prepare_probe(void)
 
 static int
 preproc_gen_prologue(struct bpf_program *prog, int n,
-		     struct bpf_insn *orig_insns, int orig_insns_cnt,
-		     struct bpf_prog_prep_result *res)
+		     const struct bpf_insn *orig_insns, int orig_insns_cnt,
+		     struct bpf_preproc_result *res)
 {
 	struct bpf_prog_priv *priv = program_priv(prog);
 	struct probe_trace_event *tev;
@@ -607,7 +685,6 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
 
 	res->new_insn_ptr = buf;
 	res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
-	res->pfd = NULL;
 	return 0;
 
 errout:
@@ -715,7 +792,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
 	struct bpf_prog_priv *priv = program_priv(prog);
 	struct perf_probe_event *pev;
 	bool need_prologue = false;
-	int err, i;
+	int i;
 
 	if (IS_ERR_OR_NULL(priv)) {
 		pr_debug("Internal error when hook preprocessor\n");
@@ -753,6 +830,13 @@ static int hook_load_preprocessor(struct bpf_program *prog)
 		return -ENOMEM;
 	}
 
+	priv->prologue_fds = malloc(sizeof(int) * pev->ntevs);
+	if (!priv->prologue_fds) {
+		pr_debug("Not enough memory: alloc prologue fds failed\n");
+		return -ENOMEM;
+	}
+	memset(priv->prologue_fds, -1, sizeof(int) * pev->ntevs);
+
 	priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
 	if (!priv->type_mapping) {
 		pr_debug("Not enough memory: alloc type_mapping failed\n");
@@ -761,13 +845,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
 	memset(priv->type_mapping, -1,
 	       sizeof(int) * pev->ntevs);
 
-	err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
-	if (err)
-		return err;
-
-	err = bpf_program__set_prep(prog, priv->nr_types,
-				    preproc_gen_prologue);
-	return err;
+	return map_prologue(pev, priv->type_mapping, &priv->nr_types);
 }
 
 int bpf__probe(struct bpf_object *obj)
@@ -874,6 +952,77 @@ int bpf__unprobe(struct bpf_object *obj)
 	return ret;
 }
 
+static int bpf_object__load_prologue(struct bpf_object *obj)
+{
+	int init_cnt = ARRAY_SIZE(prologue_init_insn);
+	const struct bpf_insn *orig_insns;
+	struct bpf_preproc_result res;
+	struct perf_probe_event *pev;
+	struct bpf_program *prog;
+	int orig_insns_cnt;
+
+	bpf_object__for_each_program(prog, obj) {
+		struct bpf_prog_priv *priv = program_priv(prog);
+		int err, i, fd;
+
+		if (IS_ERR_OR_NULL(priv)) {
+			pr_debug("bpf: failed to get private field\n");
+			return -BPF_LOADER_ERRNO__INTERNAL;
+		}
+
+		if (!priv->need_prologue)
+			continue;
+
+		/*
+		 * For each program that needs prologue we do following:
+		 *
+		 * - take its current instructions and use them
+		 *   to generate the new code with prologue
+		 * - load new instructions with bpf_prog_load
+		 *   and keep the fd in prologue_fds
+		 * - new fd will be used in bpf__foreach_event
+		 *   to connect this program with perf evsel
+		 */
+		orig_insns = bpf_program__insns(prog);
+		orig_insns_cnt = bpf_program__insn_cnt(prog);
+
+		pev = &priv->pev;
+		for (i = 0; i < pev->ntevs; i++) {
+			/*
+			 * Skipping artificall prologue_init_insn instructions
+			 * (init_cnt), so the prologue can be generated instead
+			 * of them.
+			 */
+			err = preproc_gen_prologue(prog, i,
+						   orig_insns + init_cnt,
+						   orig_insns_cnt - init_cnt,
+						   &res);
+			if (err)
+				return err;
+
+			fd = bpf_prog_load(bpf_program__get_type(prog),
+					   bpf_program__name(prog), "GPL",
+					   res.new_insn_ptr,
+					   res.new_insn_cnt, NULL);
+			if (fd < 0) {
+				char bf[128];
+
+				libbpf_strerror(-errno, bf, sizeof(bf));
+				pr_debug("bpf: load objects with prologue failed: err=%d: (%s)\n",
+					 -errno, bf);
+				return -errno;
+			}
+			priv->prologue_fds[i] = fd;
+		}
+		/*
+		 * We no longer need the original program,
+		 * we can unload it.
+		 */
+		bpf_program__unload(prog);
+	}
+	return 0;
+}
+
 int bpf__load(struct bpf_object *obj)
 {
 	int err;
@@ -885,7 +1034,7 @@ int bpf__load(struct bpf_object *obj)
 		pr_debug("bpf: load objects failed: err=%d: (%s)\n", err, bf);
 		return err;
 	}
-	return 0;
+	return bpf_object__load_prologue(obj);
 }
 
 int bpf__foreach_event(struct bpf_object *obj,
@@ -920,13 +1069,10 @@ int bpf__foreach_event(struct bpf_object *obj,
 		for (i = 0; i < pev->ntevs; i++) {
 			tev = &pev->tevs[i];
 
-			if (priv->need_prologue) {
-				int type = priv->type_mapping[i];
-
-				fd = bpf_program__nth_fd(prog, type);
-			} else {
+			if (priv->need_prologue)
+				fd = priv->prologue_fds[i];
+			else
 				fd = bpf_program__fd(prog);
-			}
 
 			if (fd < 0) {
 				pr_debug("bpf: failed to get file descriptor\n");
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code
  2022-06-16 20:22 ` [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code Jiri Olsa
@ 2022-06-20 12:43   ` Daniel Borkmann
  2022-08-01 17:44     ` Arnaldo Carvalho de Melo
  2022-06-24 18:59   ` Arnaldo Carvalho de Melo
  1 sibling, 1 reply; 6+ messages in thread
From: Daniel Borkmann @ 2022-06-20 12:43 UTC (permalink / raw)
  To: Jiri Olsa, Arnaldo Carvalho de Melo, Alexei Starovoitov, Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers

On 6/16/22 10:22 PM, Jiri Olsa wrote:
> Some functions we use for bpf prologue generation are going to be
> deprecated. This change reworks current code not to use them.
> 
> We need to replace following functions/struct:
>     bpf_program__set_prep
>     bpf_program__nth_fd
>     struct bpf_prog_prep_result
> 
> Currently we use bpf_program__set_prep to hook perf callback before
> program is loaded and provide new instructions with the prologue.
> 
> We replace this function/ality by taking instructions for specific
> program, attaching prologue to them and load such new ebpf programs
> with prologue using separate bpf_prog_load calls (outside libbpf
> load machinery).
> 
> Before we can take and use program instructions, we need libbpf to
> actually load it. This way we get the final shape of its instructions
> with all relocations and verifier adjustments).
> 
> There's one glitch though.. perf kprobe program already assumes
> generated prologue code with proper values in argument registers,
> so loading such program directly will fail in the verifier.
> 
> That's where the fallback pre-load handler fits in and prepends
> the initialization code to the program. Once such program is loaded
> we take its instructions, cut off the initialization code and prepend
> the prologue.
> 
> I know.. sorry ;-)
> 
> To have access to the program when loading this patch adds support to
> register 'fallback' section handler to take care of perf kprobe programs.
> The fallback means that it handles any section definition besides the
> ones that libbpf handles.
> 
> The handler serves two purposes:
>    - allows perf programs to have special arguments in section name
>    - allows perf to use pre-load callback where we can attach init
>      code (zeroing all argument registers) to each perf program
> 
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>

Hey Arnaldo, if you get a chance, please take a look.

Thanks a lot,
Daniel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code
  2022-06-16 20:22 ` [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code Jiri Olsa
  2022-06-20 12:43   ` Daniel Borkmann
@ 2022-06-24 18:59   ` Arnaldo Carvalho de Melo
  1 sibling, 0 replies; 6+ messages in thread
From: Arnaldo Carvalho de Melo @ 2022-06-24 18:59 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers

Em Thu, Jun 16, 2022 at 10:22:14PM +0200, Jiri Olsa escreveu:
> Some functions we use for bpf prologue generation are going to be
> deprecated. This change reworks current code not to use them.
> 
> We need to replace following functions/struct:
>    bpf_program__set_prep
>    bpf_program__nth_fd
>    struct bpf_prog_prep_result

So this is a combination of those two patches, ok:

Before:

[root@quaco ~]# perf -v
perf version 5.19.rc3.g3af6e6d06631
[root@quaco ~]# perf test bpf
 40: LLVM search and compile                                         :
 40.1: Basic BPF llvm compile                                        : Ok
 40.3: Compile source for BPF prologue generation                    : Ok
 40.4: Compile source for BPF relocation                             : Ok
 42: BPF filter                                                      :
 42.1: Basic BPF filtering                                           : Ok
 42.2: BPF pinning                                                   : Ok
 42.3: BPF prologue generation                                       : Ok
 63: Test libpfm4 support                                            :
 96: perf stat --bpf-counters test                                   : Ok
[root@quaco ~]#

After:
[acme@quaco perf-urgent]$ git log --oneline -5
44d22a24d0d49588 (HEAD -> perf/urgent) perf tools: Rework prologue generation code
3af6e6d0663196e7 (five/perf/urgent) perf stat: Enable ignore_missing_thread
c3259c85b333af5e perf inject: Adjust output data offset for backward compatibility
8e6445010f7bffbb perf trace beauty: Fix generation of errno id->str table on ALT Linux
57a438eb2aef4f1f perf build-id: Fix caching files with a wrong build ID
[acme@quaco perf-urgent]$ 
[root@quaco ~]# perf -v
perf version 5.19.rc3.g44d22a24d0d4
[root@quaco ~]# perf test bpf
 40: LLVM search and compile                                         :
 40.1: Basic BPF llvm compile                                        : Ok
 40.3: Compile source for BPF prologue generation                    : Ok
 40.4: Compile source for BPF relocation                             : Ok
 42: BPF filter                                                      :
 42.1: Basic BPF filtering                                           : Ok
 42.2: BPF pinning                                                   : Ok
 42.3: BPF prologue generation                                       : Ok
 63: Test libpfm4 support                                            :
 96: perf stat --bpf-counters test                                   : Ok
[root@quaco ~]#

And 'perf test' with the augmented_raw_syscalls.c eBPF support also
continues to work:

[root@quaco ~]# perf trace -e /home/acme/git/perf/tools/perf/examples/bpf/augmented_raw_syscalls.c,open*,connect* --max-events 20
     0.000 systemd-oomd/948 openat(dfd: CWD, filename: "/proc/meminfo", flags: RDONLY|CLOEXEC) = 12
     0.151 abrt-dump-jour/1338 openat(dfd: CWD, filename: "/var/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDONLY|CLOEXEC|NONBLOCK) = 31
     0.096 abrt-dump-jour/1336 openat(dfd: CWD, filename: "/var/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDONLY|CLOEXEC|NONBLOCK) = 31
     0.092 abrt-dump-jour/1337 openat(dfd: CWD, filename: "/var/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDONLY|CLOEXEC|NONBLOCK) = 31
     9.024 isc-net-0000/1108 connect(fd: 512, uservaddr: { .family: INET6, port: 53, addr: 2406:8600:f03f:1f8::1003 }, addrlen: 28) = -1 ENETUNREACH (Network is unreachable)
     9.728 isc-net-0000/1108 connect(fd: 513, uservaddr: { .family: INET6, port: 53, addr: 2406:2000:1d0::7961:686f:6f21 }, addrlen: 28) = -1 ENETUNREACH (Network is unreachable)
     9.811 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/comm", flags: RDONLY|CLOEXEC) ...
    10.061 isc-net-0000/1108 connect(fd: 514, uservaddr: { .family: INET6, port: 53, addr: 2001:4998:1c0::7961:686f:6f21 }, addrlen: 28) = -1 ENETUNREACH (Network is unreachable)
     9.811 systemd-journa/712  ... [continued]: openat())             = 66
    10.401 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/cmdline", flags: RDONLY|CLOEXEC|NOCTTY) = 66
    10.438 isc-net-0000/1108 connect(fd: 513, uservaddr: { .family: INET6, port: 53, addr: 2001:4998:1b0::7961:686f:6f21 }, addrlen: 28) = -1 ENETUNREACH (Network is unreachable)
    10.803 isc-net-0000/1108 connect(fd: 514, uservaddr: { .family: INET, port: 53, addr: 98.138.11.157 }, addrlen: 16) = 0
    10.851 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/status", flags: RDONLY|CLOEXEC|NOCTTY) = 66
    11.165 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/sessionid", flags: RDONLY|CLOEXEC) = 66
    11.420 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/loginuid", flags: RDONLY|CLOEXEC) = 66
    11.631 systemd-journa/712 openat(dfd: CWD, filename: "/proc/1107/cgroup", flags: RDONLY|CLOEXEC) = 66
    11.903 systemd-journa/712 openat(dfd: CWD, filename: "/run/systemd/units/log-extra-fields:named.service", flags: RDONLY|CLOEXEC) = -1 ENOENT (No such file or directory)
    12.085 systemd-journa/712 openat(dfd: CWD, filename: "/run/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDWR|CLOEXEC|NONBLOCK) = -1 ENOENT (No such file or directory)
    12.849 systemd-journa/712 openat(dfd: CWD, filename: "/run/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDWR|CLOEXEC|NONBLOCK) = -1 ENOENT (No such file or directory)
    13.141 systemd-journa/712 openat(dfd: CWD, filename: "/run/log/journal/d6a97235307247e09f13f326fb607e3c/system.journal", flags: RDWR|CLOEXEC|NONBLOCK) = -1 ENOENT (No such file or directory)
[root@quaco ~]#

[root@quaco ~]# grep -A2 SEC /home/acme/git/perf/tools/perf/examples/bpf/augmented_raw_syscalls.c
SEC("!raw_syscalls:unaugmented")
int syscall_unaugmented(struct syscall_enter_args *args)
{
--
 * These will be tail_called from SEC("raw_syscalls:sys_enter"), so will find in
 * augmented_args_tmp what was read by that raw_syscalls:sys_enter and go
 * on from there, reading the first syscall arg as a string, i.e. open's
--
SEC("!syscalls:sys_enter_connect")
int sys_enter_connect(struct syscall_enter_args *args)
{
--
SEC("!syscalls:sys_enter_sendto")
int sys_enter_sendto(struct syscall_enter_args *args)
{
--
SEC("!syscalls:sys_enter_open")
int sys_enter_open(struct syscall_enter_args *args)
{
--
SEC("!syscalls:sys_enter_openat")
int sys_enter_openat(struct syscall_enter_args *args)
{
--
SEC("!syscalls:sys_enter_rename")
int sys_enter_rename(struct syscall_enter_args *args)
{
--
SEC("!syscalls:sys_enter_renameat")
int sys_enter_renameat(struct syscall_enter_args *args)
{
--
SEC("raw_syscalls:sys_enter")
int sys_enter(struct syscall_enter_args *args)
{
--
SEC("raw_syscalls:sys_exit")
int sys_exit(struct syscall_exit_args *args)
{
[root@quaco ~]#


Thanks, you can have my:

Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Stronger than an:

Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>

- Arnaldo
 
> Currently we use bpf_program__set_prep to hook perf callback before
> program is loaded and provide new instructions with the prologue.
> 
> We replace this function/ality by taking instructions for specific
> program, attaching prologue to them and load such new ebpf programs
> with prologue using separate bpf_prog_load calls (outside libbpf
> load machinery).
> 
> Before we can take and use program instructions, we need libbpf to
> actually load it. This way we get the final shape of its instructions
> with all relocations and verifier adjustments).
> 
> There's one glitch though.. perf kprobe program already assumes
> generated prologue code with proper values in argument registers,
> so loading such program directly will fail in the verifier.
> 
> That's where the fallback pre-load handler fits in and prepends
> the initialization code to the program. Once such program is loaded
> we take its instructions, cut off the initialization code and prepend
> the prologue.
> 
> I know.. sorry ;-)
> 
> To have access to the program when loading this patch adds support to
> register 'fallback' section handler to take care of perf kprobe programs.
> The fallback means that it handles any section definition besides the
> ones that libbpf handles.
> 
> The handler serves two purposes:
>   - allows perf programs to have special arguments in section name
>   - allows perf to use pre-load callback where we can attach init
>     code (zeroing all argument registers) to each perf program
> 
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
>  tools/perf/util/bpf-loader.c | 204 ++++++++++++++++++++++++++++++-----
>  1 file changed, 175 insertions(+), 29 deletions(-)
> 
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index f8ad581ea247..6bd7c288e820 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -9,6 +9,7 @@
>  #include <linux/bpf.h>
>  #include <bpf/libbpf.h>
>  #include <bpf/bpf.h>
> +#include <linux/filter.h>
>  #include <linux/err.h>
>  #include <linux/kernel.h>
>  #include <linux/string.h>
> @@ -49,6 +50,7 @@ struct bpf_prog_priv {
>  	struct bpf_insn *insns_buf;
>  	int nr_types;
>  	int *type_mapping;
> +	int *prologue_fds;
>  };
>  
>  struct bpf_perf_object {
> @@ -56,6 +58,11 @@ struct bpf_perf_object {
>  	struct bpf_object *obj;
>  };
>  
> +struct bpf_preproc_result {
> +	struct bpf_insn *new_insn_ptr;
> +	int new_insn_cnt;
> +};
> +
>  static LIST_HEAD(bpf_objects_list);
>  static struct hashmap *bpf_program_hash;
>  static struct hashmap *bpf_map_hash;
> @@ -86,6 +93,7 @@ bpf_perf_object__next(struct bpf_perf_object *prev)
>  	     (perf_obj) = (tmp), (tmp) = bpf_perf_object__next(tmp))
>  
>  static bool libbpf_initialized;
> +static int libbpf_sec_handler;
>  
>  static int bpf_perf_object__add(struct bpf_object *obj)
>  {
> @@ -99,12 +107,76 @@ static int bpf_perf_object__add(struct bpf_object *obj)
>  	return perf_obj ? 0 : -ENOMEM;
>  }
>  
> +static void *program_priv(const struct bpf_program *prog)
> +{
> +	void *priv;
> +
> +	if (IS_ERR_OR_NULL(bpf_program_hash))
> +		return NULL;
> +	if (!hashmap__find(bpf_program_hash, prog, &priv))
> +		return NULL;
> +	return priv;
> +}
> +
> +static struct bpf_insn prologue_init_insn[] = {
> +	BPF_MOV64_IMM(BPF_REG_2, 0),
> +	BPF_MOV64_IMM(BPF_REG_3, 0),
> +	BPF_MOV64_IMM(BPF_REG_4, 0),
> +	BPF_MOV64_IMM(BPF_REG_5, 0),
> +};
> +
> +static int libbpf_prog_prepare_load_fn(struct bpf_program *prog,
> +				       struct bpf_prog_load_opts *opts __maybe_unused,
> +				       long cookie __maybe_unused)
> +{
> +	size_t init_size_cnt = ARRAY_SIZE(prologue_init_insn);
> +	size_t orig_insn_cnt, insn_cnt, init_size, orig_size;
> +	struct bpf_prog_priv *priv = program_priv(prog);
> +	const struct bpf_insn *orig_insn;
> +	struct bpf_insn *insn;
> +
> +	if (IS_ERR_OR_NULL(priv)) {
> +		pr_debug("bpf: failed to get private field\n");
> +		return -BPF_LOADER_ERRNO__INTERNAL;
> +	}
> +
> +	if (!priv->need_prologue)
> +		return 0;
> +
> +	/* prepend initialization code to program instructions */
> +	orig_insn = bpf_program__insns(prog);
> +	orig_insn_cnt = bpf_program__insn_cnt(prog);
> +	init_size = init_size_cnt * sizeof(*insn);
> +	orig_size = orig_insn_cnt * sizeof(*insn);
> +
> +	insn_cnt = orig_insn_cnt + init_size_cnt;
> +	insn = malloc(insn_cnt * sizeof(*insn));
> +	if (!insn)
> +		return -ENOMEM;
> +
> +	memcpy(insn, prologue_init_insn, init_size);
> +	memcpy((char *) insn + init_size, orig_insn, orig_size);
> +	bpf_program__set_insns(prog, insn, insn_cnt);
> +	return 0;
> +}
> +
>  static int libbpf_init(void)
>  {
> +	LIBBPF_OPTS(libbpf_prog_handler_opts, handler_opts,
> +		.prog_prepare_load_fn = libbpf_prog_prepare_load_fn,
> +	);
> +
>  	if (libbpf_initialized)
>  		return 0;
>  
>  	libbpf_set_print(libbpf_perf_print);
> +	libbpf_sec_handler = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_KPROBE,
> +							  0, &handler_opts);
> +	if (libbpf_sec_handler < 0) {
> +		pr_debug("bpf: failed to register libbpf section handler: %d\n",
> +			 libbpf_sec_handler);
> +		return -BPF_LOADER_ERRNO__INTERNAL;
> +	}
>  	libbpf_initialized = true;
>  	return 0;
>  }
> @@ -188,14 +260,31 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
>  	return obj;
>  }
>  
> +static void close_prologue_programs(struct bpf_prog_priv *priv)
> +{
> +	struct perf_probe_event *pev;
> +	int i, fd;
> +
> +	if (!priv->need_prologue)
> +		return;
> +	pev = &priv->pev;
> +	for (i = 0; i < pev->ntevs; i++) {
> +		fd = priv->prologue_fds[i];
> +		if (fd != -1)
> +			close(fd);
> +	}
> +}
> +
>  static void
>  clear_prog_priv(const struct bpf_program *prog __maybe_unused,
>  		void *_priv)
>  {
>  	struct bpf_prog_priv *priv = _priv;
>  
> +	close_prologue_programs(priv);
>  	cleanup_perf_probe_events(&priv->pev, 1);
>  	zfree(&priv->insns_buf);
> +	zfree(&priv->prologue_fds);
>  	zfree(&priv->type_mapping);
>  	zfree(&priv->sys_name);
>  	zfree(&priv->evt_name);
> @@ -243,17 +332,6 @@ static bool ptr_equal(const void *key1, const void *key2,
>  	return key1 == key2;
>  }
>  
> -static void *program_priv(const struct bpf_program *prog)
> -{
> -	void *priv;
> -
> -	if (IS_ERR_OR_NULL(bpf_program_hash))
> -		return NULL;
> -	if (!hashmap__find(bpf_program_hash, prog, &priv))
> -		return NULL;
> -	return priv;
> -}
> -
>  static int program_set_priv(struct bpf_program *prog, void *priv)
>  {
>  	void *old_priv;
> @@ -558,8 +636,8 @@ static int bpf__prepare_probe(void)
>  
>  static int
>  preproc_gen_prologue(struct bpf_program *prog, int n,
> -		     struct bpf_insn *orig_insns, int orig_insns_cnt,
> -		     struct bpf_prog_prep_result *res)
> +		     const struct bpf_insn *orig_insns, int orig_insns_cnt,
> +		     struct bpf_preproc_result *res)
>  {
>  	struct bpf_prog_priv *priv = program_priv(prog);
>  	struct probe_trace_event *tev;
> @@ -607,7 +685,6 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
>  
>  	res->new_insn_ptr = buf;
>  	res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
> -	res->pfd = NULL;
>  	return 0;
>  
>  errout:
> @@ -715,7 +792,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
>  	struct bpf_prog_priv *priv = program_priv(prog);
>  	struct perf_probe_event *pev;
>  	bool need_prologue = false;
> -	int err, i;
> +	int i;
>  
>  	if (IS_ERR_OR_NULL(priv)) {
>  		pr_debug("Internal error when hook preprocessor\n");
> @@ -753,6 +830,13 @@ static int hook_load_preprocessor(struct bpf_program *prog)
>  		return -ENOMEM;
>  	}
>  
> +	priv->prologue_fds = malloc(sizeof(int) * pev->ntevs);
> +	if (!priv->prologue_fds) {
> +		pr_debug("Not enough memory: alloc prologue fds failed\n");
> +		return -ENOMEM;
> +	}
> +	memset(priv->prologue_fds, -1, sizeof(int) * pev->ntevs);
> +
>  	priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
>  	if (!priv->type_mapping) {
>  		pr_debug("Not enough memory: alloc type_mapping failed\n");
> @@ -761,13 +845,7 @@ static int hook_load_preprocessor(struct bpf_program *prog)
>  	memset(priv->type_mapping, -1,
>  	       sizeof(int) * pev->ntevs);
>  
> -	err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
> -	if (err)
> -		return err;
> -
> -	err = bpf_program__set_prep(prog, priv->nr_types,
> -				    preproc_gen_prologue);
> -	return err;
> +	return map_prologue(pev, priv->type_mapping, &priv->nr_types);
>  }
>  
>  int bpf__probe(struct bpf_object *obj)
> @@ -874,6 +952,77 @@ int bpf__unprobe(struct bpf_object *obj)
>  	return ret;
>  }
>  
> +static int bpf_object__load_prologue(struct bpf_object *obj)
> +{
> +	int init_cnt = ARRAY_SIZE(prologue_init_insn);
> +	const struct bpf_insn *orig_insns;
> +	struct bpf_preproc_result res;
> +	struct perf_probe_event *pev;
> +	struct bpf_program *prog;
> +	int orig_insns_cnt;
> +
> +	bpf_object__for_each_program(prog, obj) {
> +		struct bpf_prog_priv *priv = program_priv(prog);
> +		int err, i, fd;
> +
> +		if (IS_ERR_OR_NULL(priv)) {
> +			pr_debug("bpf: failed to get private field\n");
> +			return -BPF_LOADER_ERRNO__INTERNAL;
> +		}
> +
> +		if (!priv->need_prologue)
> +			continue;
> +
> +		/*
> +		 * For each program that needs prologue we do following:
> +		 *
> +		 * - take its current instructions and use them
> +		 *   to generate the new code with prologue
> +		 * - load new instructions with bpf_prog_load
> +		 *   and keep the fd in prologue_fds
> +		 * - new fd will be used in bpf__foreach_event
> +		 *   to connect this program with perf evsel
> +		 */
> +		orig_insns = bpf_program__insns(prog);
> +		orig_insns_cnt = bpf_program__insn_cnt(prog);
> +
> +		pev = &priv->pev;
> +		for (i = 0; i < pev->ntevs; i++) {
> +			/*
> +			 * Skipping artificall prologue_init_insn instructions
> +			 * (init_cnt), so the prologue can be generated instead
> +			 * of them.
> +			 */
> +			err = preproc_gen_prologue(prog, i,
> +						   orig_insns + init_cnt,
> +						   orig_insns_cnt - init_cnt,
> +						   &res);
> +			if (err)
> +				return err;
> +
> +			fd = bpf_prog_load(bpf_program__get_type(prog),
> +					   bpf_program__name(prog), "GPL",
> +					   res.new_insn_ptr,
> +					   res.new_insn_cnt, NULL);
> +			if (fd < 0) {
> +				char bf[128];
> +
> +				libbpf_strerror(-errno, bf, sizeof(bf));
> +				pr_debug("bpf: load objects with prologue failed: err=%d: (%s)\n",
> +					 -errno, bf);
> +				return -errno;
> +			}
> +			priv->prologue_fds[i] = fd;
> +		}
> +		/*
> +		 * We no longer need the original program,
> +		 * we can unload it.
> +		 */
> +		bpf_program__unload(prog);
> +	}
> +	return 0;
> +}
> +
>  int bpf__load(struct bpf_object *obj)
>  {
>  	int err;
> @@ -885,7 +1034,7 @@ int bpf__load(struct bpf_object *obj)
>  		pr_debug("bpf: load objects failed: err=%d: (%s)\n", err, bf);
>  		return err;
>  	}
> -	return 0;
> +	return bpf_object__load_prologue(obj);
>  }
>  
>  int bpf__foreach_event(struct bpf_object *obj,
> @@ -920,13 +1069,10 @@ int bpf__foreach_event(struct bpf_object *obj,
>  		for (i = 0; i < pev->ntevs; i++) {
>  			tev = &pev->tevs[i];
>  
> -			if (priv->need_prologue) {
> -				int type = priv->type_mapping[i];
> -
> -				fd = bpf_program__nth_fd(prog, type);
> -			} else {
> +			if (priv->need_prologue)
> +				fd = priv->prologue_fds[i];
> +			else
>  				fd = bpf_program__fd(prog);
> -			}
>  
>  			if (fd < 0) {
>  				pr_debug("bpf: failed to get file descriptor\n");
> -- 
> 2.35.3

-- 

- Arnaldo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation
  2022-06-16 20:22 [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation Jiri Olsa
  2022-06-16 20:22 ` [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code Jiri Olsa
@ 2022-06-24 20:40 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2022-06-24 20:40 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: acme, ast, daniel, andrii, linux-perf-users, netdev, bpf, mingo,
	namhyung, alexander.shishkin, a.p.zijlstra, kafai,
	songliubraving, yhs, john.fastabend, irogers

Hello:

This patch was applied to bpf/bpf-next.git (master)
by Andrii Nakryiko <andrii@kernel.org>:

On Thu, 16 Jun 2022 22:22:13 +0200 you wrote:
> hi,
> sending change we discussed some time ago [1] to get rid of
> some deprecated functions we use in perf prologue code.
> 
> Despite the gloomy discussion I think the final code does
> not look that bad ;-)
> 
> [...]

Here is the summary with links:
  - [PATCHv5,bpf-next,1/1] perf tools: Rework prologue generation code
    https://git.kernel.org/bpf/bpf-next/c/b168852eb8ef

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code
  2022-06-20 12:43   ` Daniel Borkmann
@ 2022-08-01 17:44     ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 6+ messages in thread
From: Arnaldo Carvalho de Melo @ 2022-08-01 17:44 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Jiri Olsa, Alexei Starovoitov, Andrii Nakryiko, linux-perf-users,
	netdev, bpf, Ingo Molnar, Namhyung Kim, Alexander Shishkin,
	Peter Zijlstra, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, Ian Rogers

Em Mon, Jun 20, 2022 at 02:43:22PM +0200, Daniel Borkmann escreveu:
> On 6/16/22 10:22 PM, Jiri Olsa wrote:
> > Some functions we use for bpf prologue generation are going to be
> > deprecated. This change reworks current code not to use them.
> > 
> > We need to replace following functions/struct:
> >     bpf_program__set_prep
> >     bpf_program__nth_fd
> >     struct bpf_prog_prep_result
> > 
> > Currently we use bpf_program__set_prep to hook perf callback before
> > program is loaded and provide new instructions with the prologue.
> > 
> > We replace this function/ality by taking instructions for specific
> > program, attaching prologue to them and load such new ebpf programs
> > with prologue using separate bpf_prog_load calls (outside libbpf
> > load machinery).
> > 
> > Before we can take and use program instructions, we need libbpf to
> > actually load it. This way we get the final shape of its instructions
> > with all relocations and verifier adjustments).
> > 
> > There's one glitch though.. perf kprobe program already assumes
> > generated prologue code with proper values in argument registers,
> > so loading such program directly will fail in the verifier.
> > 
> > That's where the fallback pre-load handler fits in and prepends
> > the initialization code to the program. Once such program is loaded
> > we take its instructions, cut off the initialization code and prepend
> > the prologue.
> > 
> > I know.. sorry ;-)
> > 
> > To have access to the program when loading this patch adds support to
> > register 'fallback' section handler to take care of perf kprobe programs.
> > The fallback means that it handles any section definition besides the
> > ones that libbpf handles.
> > 
> > The handler serves two purposes:
> >    - allows perf programs to have special arguments in section name
> >    - allows perf to use pre-load callback where we can attach init
> >      code (zeroing all argument registers) to each perf program
> > 
> > Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> > Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> 
> Hey Arnaldo, if you get a chance, please take a look.

Sry, applied.

- Arnaldo

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-08-01 17:44 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-16 20:22 [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation Jiri Olsa
2022-06-16 20:22 ` [PATCHv5 bpf-next 1/1] perf tools: Rework prologue generation code Jiri Olsa
2022-06-20 12:43   ` Daniel Borkmann
2022-08-01 17:44     ` Arnaldo Carvalho de Melo
2022-06-24 18:59   ` Arnaldo Carvalho de Melo
2022-06-24 20:40 ` [PATCHv5 bpf-next 0/1] perf tools: Fix prologue generation patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.