All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor
@ 2021-10-25 22:45 Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 1/4] libbpf: fix off-by-one bug in bpf_core_apply_relo() Andrii Nakryiko
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2021-10-25 22:45 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Add libbpf APIs to access BPF program instructions. Both before and after
libbpf processing (before and after bpf_object__load()). This allows to
inspect what's going on with BPF program assembly instructions as libbpf
performs its processing magic.

But in more practical terms, this allows to do a no-brainer BPF program
cloning, which is something you need when working with fentry/fexit BPF
programs to be able to attach the same BPF program code to multiple kernel
functions. Currently, kernel needs multiple copies of BPF programs, each
loaded with its own target BTF ID. retsnoop is one such example that
previously had to rely on bpf_program__set_prep() API to hijack program
instructions ([0] for before and after).

Speaking of bpf_program__set_prep() API and the whole concept of
multiple-instance BPF programs in libbpf, all that is scheduled for
deprecation in v0.7. It doesn't work well, it's cumbersome, and it will become
more broken as libbpf adds more functionality. So deprecate and remove it in
libbpf 1.0. It doesn't seem to be used by anyone anyways (except for that
retsnoop hack, which is now much cleaner with new APIs as can be seen in [0]).

  [0] https://github.com/anakryiko/retsnoop/pull/1

Andrii Nakryiko (4):
  libbpf: fix off-by-one bug in bpf_core_apply_relo()
  libbpf: add ability to fetch bpf_program's underlying instructions
  libbpf: deprecate multi-instance bpf_program APIs
  libbpf: deprecate ambiguously-named bpf_program__size() API

 tools/lib/bpf/libbpf.c   | 36 ++++++++++++++++++++++-----------
 tools/lib/bpf/libbpf.h   | 43 +++++++++++++++++++++++++++++++++++++---
 tools/lib/bpf/libbpf.map |  2 ++
 3 files changed, 66 insertions(+), 15 deletions(-)

-- 
2.30.2


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

* [PATCH bpf-next 1/4] libbpf: fix off-by-one bug in bpf_core_apply_relo()
  2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
@ 2021-10-25 22:45 ` Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 2/4] libbpf: add ability to fetch bpf_program's underlying instructions Andrii Nakryiko
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2021-10-25 22:45 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Fix instruction index validity check which has off-by-one error.

Fixes: 3ee4f5335511 ("libbpf: Split bpf_core_apply_relo() into bpf_program independent helper.")
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 604abe00785f..e27a249d46fb 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -5405,7 +5405,7 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
 	 * relocated, so it's enough to just subtract in-section offset
 	 */
 	insn_idx = insn_idx - prog->sec_insn_off;
-	if (insn_idx > prog->insns_cnt)
+	if (insn_idx >= prog->insns_cnt)
 		return -EINVAL;
 	insn = &prog->insns[insn_idx];
 
-- 
2.30.2


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

* [PATCH bpf-next 2/4] libbpf: add ability to fetch bpf_program's underlying instructions
  2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 1/4] libbpf: fix off-by-one bug in bpf_core_apply_relo() Andrii Nakryiko
@ 2021-10-25 22:45 ` Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 3/4] libbpf: deprecate multi-instance bpf_program APIs Andrii Nakryiko
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2021-10-25 22:45 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Add APIs providing read-only access to bpf_program BPF instructions ([0]).
This is useful for diagnostics purposes, but it also allows a cleaner
support for cloning BPF programs after libbpf did all the FD resolution
and CO-RE relocations, subprog instructions appending, etc. Currently,
cloning BPF program is possible only through hijacking a half-broken
bpf_program__set_prep() API, which doesn't really work well for anything
but most primitive programs. For instance, set_prep() API doesn't allow
adjusting BPF program load parameters which are necessary for loading
fentry/fexit BPF programs (the case where BPF program cloning is
a necessity if doing some sort of mass-attachment functionality).

Given bpf_program__set_prep() API is set to be deprecated, having
a cleaner alternative is a must. libbpf internally already keeps track
of linear array of struct bpf_insn, so it's not hard to expose it. The
only gotcha is that libbpf previously freed instructions array during
bpf_object load time, which would make this API much less useful overall,
because in between bpf_object__open() and bpf_object__load() a lot of
changes to instructions are done by libbpf.

So this patch makes libbpf hold onto prog->insns array even after BPF
program loading. I think this is a small price for added functionality
and improved introspection of BPF program code.

See retsnoop PR ([1]) for how it can be used in practice and code
savings compared to relying on bpf_program__set_prep().

  [0] Closes: https://github.com/libbpf/libbpf/issues/298
  [1] https://github.com/anakryiko/retsnoop/pull/1

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.c   | 12 ++++++++++--
 tools/lib/bpf/libbpf.h   | 36 ++++++++++++++++++++++++++++++++++--
 tools/lib/bpf/libbpf.map |  2 ++
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e27a249d46fb..dc86ad24dfcb 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -6653,8 +6653,6 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 out:
 	if (err)
 		pr_warn("failed to load program '%s'\n", prog->name);
-	zfree(&prog->insns);
-	prog->insns_cnt = 0;
 	return libbpf_err(err);
 }
 
@@ -8143,6 +8141,16 @@ size_t bpf_program__size(const struct bpf_program *prog)
 	return prog->insns_cnt * BPF_INSN_SZ;
 }
 
+const struct bpf_insn *bpf_program__insns(const struct bpf_program *prog)
+{
+	return prog->insns;
+}
+
+size_t bpf_program__insn_cnt(const struct bpf_program *prog)
+{
+	return prog->insns_cnt;
+}
+
 int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
 			  bpf_program_prep_t prep)
 {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 89ca9c83ed4e..c6bcc5b98906 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -226,6 +226,40 @@ LIBBPF_API int bpf_program__set_autoload(struct bpf_program *prog, bool autoload
 /* returns program size in bytes */
 LIBBPF_API size_t bpf_program__size(const struct bpf_program *prog);
 
+struct bpf_insn;
+
+/**
+ * @brief **bpf_program__insns()** gives read-only access to BPF program's
+ * underlying BPF instructions.
+ * @param prog BPF program for which to return instructions
+ * @return a pointer to an array of BPF instructions that belong to the
+ * specified BPF program
+ *
+ * Returned pointer is always valid and not NULL. Number of `struct bpf_insn`
+ * pointed to can be fetched using **bpf_program__insn_cnt()** API.
+ *
+ * Keep in mind, libbpf can modify and append/delete BPF program's
+ * instructions as it processes BPF object file and prepares everything for
+ * uploading into the kernel. So depending on the point in BPF object
+ * lifetime, **bpf_program__insns()** can return different sets of
+ * instructions. As an example, during BPF object load phase BPF program
+ * instructions will be CO-RE-relocated, BPF subprograms instructions will be
+ * appended, ldimm64 instructions will have FDs embedded, etc. So instructions
+ * returned before **bpf_object__load()** and after it might be quite
+ * different.
+ */
+LIBBPF_API const struct bpf_insn *bpf_program__insns(const struct bpf_program *prog);
+/**
+ * @brief **bpf_program__insn_cnt()** returns number of `struct bpf_insn`'s
+ * that form specified BPF program.
+ * @param prog BPF program for which to return number of BPF instructions
+ *
+ * See **bpf_program__insns()** documentation for notes on how libbpf can
+ * change instructions and their count during different phases of
+ * **bpf_object** lifetime.
+ */
+LIBBPF_API size_t bpf_program__insn_cnt(const struct bpf_program *prog);
+
 LIBBPF_API int bpf_program__load(struct bpf_program *prog, char *license,
 				 __u32 kern_version);
 LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
@@ -365,8 +399,6 @@ LIBBPF_API struct bpf_link *
 bpf_program__attach_iter(const struct bpf_program *prog,
 			 const struct bpf_iter_attach_opts *opts);
 
-struct bpf_insn;
-
 /*
  * Libbpf allows callers to adjust BPF programs before being loaded
  * into kernel. One program in an object file can be transformed into
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 116964a29e44..15239c05659c 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -393,6 +393,8 @@ LIBBPF_0.6.0 {
 		bpf_object__next_program;
 		bpf_object__prev_map;
 		bpf_object__prev_program;
+		bpf_program__insn_cnt;
+		bpf_program__insns;
 		btf__add_btf;
 		btf__add_decl_tag;
 		btf__raw_data;
-- 
2.30.2


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

* [PATCH bpf-next 3/4] libbpf: deprecate multi-instance bpf_program APIs
  2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 1/4] libbpf: fix off-by-one bug in bpf_core_apply_relo() Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 2/4] libbpf: add ability to fetch bpf_program's underlying instructions Andrii Nakryiko
@ 2021-10-25 22:45 ` Andrii Nakryiko
  2021-10-25 22:45 ` [PATCH bpf-next 4/4] libbpf: deprecate ambiguously-named bpf_program__size() API Andrii Nakryiko
  2021-10-26  1:38 ` [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Alexei Starovoitov
  4 siblings, 0 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2021-10-25 22:45 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Schedule deprecation of a set of APIs that are related to multi-instance
bpf_programs:
  - bpf_program__set_prep() ([0]);
  - bpf_program__{set,unset}_instance() ([1]);
  - bpf_program__nth_fd().

These APIs are obscure, very niche, and don't seem to be used much in
practice. bpf_program__set_prep() is pretty useless for anything but the
simplest BPF programs, as it doesn't allow to adjust BPF program load
attributes, among other things. In short, it already bitrotted and will
bitrot some more if not removed.

With bpf_program__insns() API, which gives access to post-processed BPF
program instructions of any given entry-point BPF program, it's now
possible to do whatever necessary adjustments were possible with
set_prep() API before, but also more. Given any such use case is
automatically an advanced use case, requiring users to stick to
low-level bpf_prog_load() APIs and managing their own prog FDs is
reasonable.

  [0] Closes: https://github.com/libbpf/libbpf/issues/299
  [1] Closes: https://github.com/libbpf/libbpf/issues/300

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.c | 22 +++++++++++++---------
 tools/lib/bpf/libbpf.h |  6 +++++-
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index dc86ad24dfcb..4b2158657507 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -7358,8 +7358,7 @@ static int check_path(const char *path)
 	return err;
 }
 
-int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
-			      int instance)
+static int bpf_program_pin_instance(struct bpf_program *prog, const char *path, int instance)
 {
 	char *cp, errmsg[STRERR_BUFSIZE];
 	int err;
@@ -7394,8 +7393,7 @@ int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
 	return 0;
 }
 
-int bpf_program__unpin_instance(struct bpf_program *prog, const char *path,
-				int instance)
+static int bpf_program_unpin_instance(struct bpf_program *prog, const char *path, int instance)
 {
 	int err;
 
@@ -7423,6 +7421,12 @@ int bpf_program__unpin_instance(struct bpf_program *prog, const char *path,
 	return 0;
 }
 
+__attribute__((alias("bpf_program_pin_instance")))
+int bpf_object__pin_instance(struct bpf_program *prog, const char *path, int instance);
+
+__attribute__((alias("bpf_program_unpin_instance")))
+int bpf_program__unpin_instance(struct bpf_program *prog, const char *path, int instance);
+
 int bpf_program__pin(struct bpf_program *prog, const char *path)
 {
 	int i, err;
@@ -7447,7 +7451,7 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
 
 	if (prog->instances.nr == 1) {
 		/* don't create subdirs when pinning single instance */
-		return bpf_program__pin_instance(prog, path, 0);
+		return bpf_program_pin_instance(prog, path, 0);
 	}
 
 	for (i = 0; i < prog->instances.nr; i++) {
@@ -7463,7 +7467,7 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
 			goto err_unpin;
 		}
 
-		err = bpf_program__pin_instance(prog, buf, i);
+		err = bpf_program_pin_instance(prog, buf, i);
 		if (err)
 			goto err_unpin;
 	}
@@ -7481,7 +7485,7 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
 		else if (len >= PATH_MAX)
 			continue;
 
-		bpf_program__unpin_instance(prog, buf, i);
+		bpf_program_unpin_instance(prog, buf, i);
 	}
 
 	rmdir(path);
@@ -7509,7 +7513,7 @@ int bpf_program__unpin(struct bpf_program *prog, const char *path)
 
 	if (prog->instances.nr == 1) {
 		/* don't create subdirs when pinning single instance */
-		return bpf_program__unpin_instance(prog, path, 0);
+		return bpf_program_unpin_instance(prog, path, 0);
 	}
 
 	for (i = 0; i < prog->instances.nr; i++) {
@@ -7522,7 +7526,7 @@ int bpf_program__unpin(struct bpf_program *prog, const char *path)
 		else if (len >= PATH_MAX)
 			return libbpf_err(-ENAMETOOLONG);
 
-		err = bpf_program__unpin_instance(prog, buf, i);
+		err = bpf_program_unpin_instance(prog, buf, i);
 		if (err)
 			return err;
 	}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index c6bcc5b98906..449eeed80be6 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -263,9 +263,11 @@ LIBBPF_API size_t bpf_program__insn_cnt(const struct bpf_program *prog);
 LIBBPF_API int bpf_program__load(struct bpf_program *prog, char *license,
 				 __u32 kern_version);
 LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
+LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
 					 const char *path,
 					 int instance);
+LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 LIBBPF_API int bpf_program__unpin_instance(struct bpf_program *prog,
 					   const char *path,
 					   int instance);
@@ -427,7 +429,7 @@ bpf_program__attach_iter(const struct bpf_program *prog,
  * one instance. In this case bpf_program__fd(prog) is equal to
  * bpf_program__nth_fd(prog, 0).
  */
-
+LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__insns() for getting bpf_program instructions")
 struct bpf_prog_prep_result {
 	/*
 	 * If not NULL, load new instruction array.
@@ -456,9 +458,11 @@ typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
 				  struct bpf_insn *insns, int insns_cnt,
 				  struct bpf_prog_prep_result *res);
 
+LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__insns() for getting bpf_program instructions")
 LIBBPF_API int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
 				     bpf_program_prep_t prep);
 
+LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n);
 
 /*
-- 
2.30.2


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

* [PATCH bpf-next 4/4] libbpf: deprecate ambiguously-named bpf_program__size() API
  2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
                   ` (2 preceding siblings ...)
  2021-10-25 22:45 ` [PATCH bpf-next 3/4] libbpf: deprecate multi-instance bpf_program APIs Andrii Nakryiko
@ 2021-10-25 22:45 ` Andrii Nakryiko
  2021-10-26  1:38 ` [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Alexei Starovoitov
  4 siblings, 0 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2021-10-25 22:45 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

The name of the API doesn't convey clearly that this size is in number
of bytes (there needed to be a separate comment to make this clear in
libbpf.h). Further, measuring the size of BPF program in bytes is not
exactly the best fit, because BPF programs always consist of 8-byte
instructions. As such, bpf_program__insn_cnt() is a better alternative
in pretty much any imaginable case.

So schedule bpf_program__size() deprecation starting from v0.7 and it
will be removed in libbpf 1.0.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 449eeed80be6..e1900819bfab 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -224,6 +224,7 @@ LIBBPF_API bool bpf_program__autoload(const struct bpf_program *prog);
 LIBBPF_API int bpf_program__set_autoload(struct bpf_program *prog, bool autoload);
 
 /* returns program size in bytes */
+LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__insn_cnt() instead")
 LIBBPF_API size_t bpf_program__size(const struct bpf_program *prog);
 
 struct bpf_insn;
-- 
2.30.2


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

* Re: [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor
  2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
                   ` (3 preceding siblings ...)
  2021-10-25 22:45 ` [PATCH bpf-next 4/4] libbpf: deprecate ambiguously-named bpf_program__size() API Andrii Nakryiko
@ 2021-10-26  1:38 ` Alexei Starovoitov
  4 siblings, 0 replies; 6+ messages in thread
From: Alexei Starovoitov @ 2021-10-26  1:38 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, ast, daniel, kernel-team

On Mon, Oct 25, 2021 at 03:45:27PM -0700, Andrii Nakryiko wrote:
> Add libbpf APIs to access BPF program instructions. Both before and after
> libbpf processing (before and after bpf_object__load()). This allows to
> inspect what's going on with BPF program assembly instructions as libbpf
> performs its processing magic.
> 
> But in more practical terms, this allows to do a no-brainer BPF program
> cloning, which is something you need when working with fentry/fexit BPF
> programs to be able to attach the same BPF program code to multiple kernel
> functions. Currently, kernel needs multiple copies of BPF programs, each
> loaded with its own target BTF ID. retsnoop is one such example that
> previously had to rely on bpf_program__set_prep() API to hijack program
> instructions ([0] for before and after).
> 
> Speaking of bpf_program__set_prep() API and the whole concept of
> multiple-instance BPF programs in libbpf, all that is scheduled for
> deprecation in v0.7. It doesn't work well, it's cumbersome, and it will become
> more broken as libbpf adds more functionality. So deprecate and remove it in
> libbpf 1.0. It doesn't seem to be used by anyone anyways (except for that
> retsnoop hack, which is now much cleaner with new APIs as can be seen in [0]).
> 
>   [0] https://github.com/anakryiko/retsnoop/pull/1

Applied, Thanks

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

end of thread, other threads:[~2021-10-26  1:38 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-25 22:45 [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Andrii Nakryiko
2021-10-25 22:45 ` [PATCH bpf-next 1/4] libbpf: fix off-by-one bug in bpf_core_apply_relo() Andrii Nakryiko
2021-10-25 22:45 ` [PATCH bpf-next 2/4] libbpf: add ability to fetch bpf_program's underlying instructions Andrii Nakryiko
2021-10-25 22:45 ` [PATCH bpf-next 3/4] libbpf: deprecate multi-instance bpf_program APIs Andrii Nakryiko
2021-10-25 22:45 ` [PATCH bpf-next 4/4] libbpf: deprecate ambiguously-named bpf_program__size() API Andrii Nakryiko
2021-10-26  1:38 ` [PATCH bpf-next 0/4] libbpf: add bpf_program__insns() accessor Alexei Starovoitov

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.