Skip to contentSkip to conversation

Registrating struct_ops types from modules

PATCH Superseded [email protected] 3 participants 34 messages Oct 30, 2023 – Nov 2, 2023

Subsystems (8)

v8 of 17 Superseded Latest is v17 →

lore
Share & tools
lore
#0 lore raw

v8 cover Registrating struct_ops types from modules

From: Kui-Feng Lee <[email protected]>

Given the current constraints of the current implementation, struct_ops cannot be registered dynamically. This presents a significant limitation for modules like coming fuse-bpf, which seeks to implement a new struct_ops type. To address this issue, a new API is introduced that allows the registration of new struct_ops types from modules.

Previously, struct_ops types were defined in bpf_struct_ops_types.h and collected as a static array. The new API lets callers add new struct_ops types dynamically. The static array has been removed and replaced by the per-btf struct_ops_tab.

The struct_ops subsystem relies on BTF to determine the layout of values in a struct_ops map and identify the subsystem that the struct_ops map registers to. However, the kernel BTF does not include the type information of struct_ops types defined by a module. The struct_ops subsystem requires knowledge of the corresponding module for a given struct_ops map and the utilization of BTF information from that module. We empower libbpf to determine the correct module for accessing the BTF information and pass an identity (FD) of the module btf to the kernel. The kernel looks up type information and registered struct_ops types directly from the given btf.

If a module exits while one or more struct_ops maps still refer to a struct_ops type defined by the module, it can lead to unforeseen complications. Therefore, it is crucial to ensure that a module remains intact as long as any struct_ops map is still linked to a struct_ops type defined by the module. To achieve this, every struct_ops map holds a reference to the module while being registered.

Changes from v7:

- Fix check_struct_ops_btf_id() to use attach btf if there is instead of btf_vmlinux.

Changes from v6:

- Change returned error code to -EINVAL for the case of bpf_try_get_module().

- Return an error code from bpf_struct_ops_init().

- Fix the dependency issue of testing_helpers.c and rcu_tasks_trace_gp.skel.h.

Changes from v5:

- As the 2nd patch, we introduce "bpf_struct_ops_desc". This change involves moving certain members of "bpf_struct_ops" to "bpf_struct_ops_desc", which becomes a part of "btf_struct_ops_tab". This ensures that these members remain accessible even when the owner module of a "bpf_struct_ops" is unloaded.

- Correct the order of arguments when calling
in the 3rd patch.

- Remove the owner argument from bpf_struct_ops_init_one(). Instead, callers should fill in st_ops->owner.

- Make sure to hold the owner module when calling bpf_struct_ops_find() and bpf_struct_ops_find_value() in the 6th patch.

- Merge the functions register_bpf_struct_ops_btf() and register_bpf_struct_ops() into a single function and relocate it to btf.c for better organization and clarity.

- Undo the name modifications made to find_kernel_btf_id() and find_ksym_btf_id() in the 8th patch.

Changes from v4:

- Fix the dependency between testing_helpers.o and rcu_tasks_trace_gp.skel.h.

Changes from v3:

- Fix according to the feedback for v3.

- Change of the order of arguments to make btf as the first argument.

- Use btf_try_get_module() instead of try_get_module() since the module pointed by st_ops->owner can gone while some one is still holding its btf.

- Move variables defined by BPF_STRUCT_OPS_COMMON_VALUE to struct bpf_struct_ops_common_value to validation easier.

- Register the struct_ops type defined by bpf_testmod in its init function.

- Rename field name to 'value_type_btf_obj_fd' to make it explicit.

- Fix leaking of btf objects on error.

- st_maps hold their modules to keep modules alive and prevent they from unloading.

- bpf_map of libbpf keeps mod_btf_fd instead of a pointer to module_btf.

- Do call_rcu_tasks_trace() in kern_sync_rcu() to ensure the bpf_testmod is unloaded properly. It uses rcu_tasks_trace_gp to trigger call_rcu_tasks_trace() in the kernel.

- Merge and reorder patches in a reasonable order.

Changes from v2:

- Remove struct_ops array, and add a per-btf (module) struct_ops_tab to collect registered struct_ops types.

- Validate value_type by checking member names and types.

---
v7: https://lore.kernel.org/all/[email protected]/
v6: https://lore.kernel.org/all/[email protected]/
v5: https://lore.kernel.org/all/[email protected]/
v4: https://lore.kernel.org/all/[email protected]/
v3: https://lore.kernel.org/all/[email protected]/
v2: https://lore.kernel.org/all/[email protected]/

Kui-Feng Lee (10):
bpf: refactory struct_ops type initialization to a function. bpf, net: introduce bpf_struct_ops_desc.
bpf: add struct_ops_tab to btf.
bpf: hold module for bpf_struct_ops_map.
bpf: validate value_type
bpf: pass attached BTF to the bpf_struct_ops subsystem bpf, net: switch to dynamic registration
libbpf: Find correct module BTFs for struct_ops maps and progs.
bpf: export btf_ctx_access to modules.
selftests/bpf: test case for register_bpf_struct_ops().

 include/linux/bpf.h                           |  51 +-
 include/linux/bpf_verifier.h                  |   1 +
 include/linux/btf.h                           |   9 +
 include/uapi/linux/bpf.h                      |   5 +
 kernel/bpf/bpf_struct_ops.c                   | 435 ++++++++++--------
 kernel/bpf/bpf_struct_ops_types.h             |  12 -
 kernel/bpf/btf.c                              | 113 ++++-
 kernel/bpf/syscall.c                          |   2 +-
 kernel/bpf/verifier.c                         |  25 +-
 net/bpf/bpf_dummy_struct_ops.c                |  23 +-
 net/ipv4/bpf_tcp_ca.c                         |  24 +-
 tools/include/uapi/linux/bpf.h                |   5 +
 tools/lib/bpf/bpf.c                           |   4 +-
 tools/lib/bpf/bpf.h                           |   5 +-
 tools/lib/bpf/libbpf.c                        |  50 +-
 tools/testing/selftests/bpf/Makefile          |   4 +-
 .../selftests/bpf/bpf_testmod/bpf_testmod.c   |  59 +++
 .../selftests/bpf/bpf_testmod/bpf_testmod.h   |   5 +
 .../bpf/prog_tests/test_struct_ops_module.c   |  39 ++
 .../selftests/bpf/progs/struct_ops_module.c   |  30 ++
 tools/testing/selftests/bpf/testing_helpers.c |  35 ++
 21 files changed, 697 insertions(+), 239 deletions(-)
 delete mode 100644 kernel/bpf/bpf_struct_ops_types.h
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
 create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_module.c
Signature · 1 line
2.34.1
#1 lore raw

PATCH v8 1/10 bpf: refactory struct_ops type initialization to a function.

From: Kui-Feng Lee <[email protected]>

Move the majority of the code to bpf_struct_ops_init_one(), which can then be utilized for the initialization of newly registered dynamically allocated struct_ops types in the following patches.

---
 kernel/bpf/bpf_struct_ops.c | 157 +++++++++++++++++++-----------------
 1 file changed, 83 insertions(+), 74 deletions(-)
kernel/bpf/bpf_struct_ops.c +83 -74
index db6176fb64dc..627cf1ea840a 100644
@@ -110,102 +110,111 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops = {
 
 static const struct btf_type *module_type;
 
-void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
+static void bpf_struct_ops_init_one(struct bpf_struct_ops *st_ops,
+				    struct btf *btf,
+				    struct bpf_verifier_log *log)
 {
-	s32 type_id, value_id, module_id;
 	const struct btf_member *member;
-	struct bpf_struct_ops *st_ops;
 	const struct btf_type *t;
+	s32 type_id, value_id;
 	char value_name[128];
 	const char *mname;
-	u32 i, j;
+	int i;
 
-	/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */
-#define BPF_STRUCT_OPS_TYPE(_name) BTF_TYPE_EMIT(struct bpf_struct_ops_##_name);
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
+	if (strlen(st_ops->name) + VALUE_PREFIX_LEN >=
+	    sizeof(value_name)) {
+		pr_warn("struct_ops name %s is too long\n",
+			st_ops->name);
+		return;
+	}
+	sprintf(value_name, "%s%s", VALUE_PREFIX, st_ops->name);
 
-	module_id = btf_find_by_name_kind(btf, "module", BTF_KIND_STRUCT);
-	if (module_id < 0) {
-		pr_warn("Cannot find struct module in btf_vmlinux\n");
+	value_id = btf_find_by_name_kind(btf, value_name,
+					 BTF_KIND_STRUCT);
+	if (value_id < 0) {
+		pr_warn("Cannot find struct %s in btf_vmlinux\n",
+			value_name);
 		return;
 	}
-	module_type = btf_type_by_id(btf, module_id);
 
-	for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) {
-		st_ops = bpf_struct_ops[i];
+	type_id = btf_find_by_name_kind(btf, st_ops->name,
+					BTF_KIND_STRUCT);
+	if (type_id < 0) {
+		pr_warn("Cannot find struct %s in btf_vmlinux\n",
+			st_ops->name);
+		return;
+	}
+	t = btf_type_by_id(btf, type_id);
+	if (btf_type_vlen(t) > BPF_STRUCT_OPS_MAX_NR_MEMBERS) {
+		pr_warn("Cannot support #%u members in struct %s\n",
+			btf_type_vlen(t), st_ops->name);
+		return;
+	}
 
-		if (strlen(st_ops->name) + VALUE_PREFIX_LEN >=
-		    sizeof(value_name)) {
-			pr_warn("struct_ops name %s is too long\n",
+	for_each_member(i, t, member) {
+		const struct btf_type *func_proto;
+
+		mname = btf_name_by_offset(btf, member->name_off);
+		if (!*mname) {
+			pr_warn("anon member in struct %s is not supported\n",
 				st_ops->name);
-			continue;
+			break;
 		}
-		sprintf(value_name, "%s%s", VALUE_PREFIX, st_ops->name);
 
-		value_id = btf_find_by_name_kind(btf, value_name,
-						 BTF_KIND_STRUCT);
-		if (value_id < 0) {
-			pr_warn("Cannot find struct %s in btf_vmlinux\n",
-				value_name);
-			continue;
+		if (__btf_member_bitfield_size(t, member)) {
+			pr_warn("bit field member %s in struct %s is not supported\n",
+				mname, st_ops->name);
+			break;
 		}
 
-		type_id = btf_find_by_name_kind(btf, st_ops->name,
-						BTF_KIND_STRUCT);
-		if (type_id < 0) {
-			pr_warn("Cannot find struct %s in btf_vmlinux\n",
-				st_ops->name);
-			continue;
-		}
-		t = btf_type_by_id(btf, type_id);
-		if (btf_type_vlen(t) > BPF_STRUCT_OPS_MAX_NR_MEMBERS) {
-			pr_warn("Cannot support #%u members in struct %s\n",
-				btf_type_vlen(t), st_ops->name);
-			continue;
+		func_proto = btf_type_resolve_func_ptr(btf,
+						       member->type,
+						       NULL);
+		if (func_proto &&
+		    btf_distill_func_proto(log, btf,
+					   func_proto, mname,
+					   &st_ops->func_models[i])) {
+			pr_warn("Error in parsing func ptr %s in struct %s\n",
+				mname, st_ops->name);
+			break;
 		}
+	}
 
-		for_each_member(j, t, member) {
-			const struct btf_type *func_proto;
+	if (i == btf_type_vlen(t)) {
+		if (st_ops->init(btf)) {
+			pr_warn("Error in init bpf_struct_ops %s\n",
+				st_ops->name);
+		} else {
+			st_ops->type_id = type_id;
+			st_ops->type = t;
+			st_ops->value_id = value_id;
+			st_ops->value_type = btf_type_by_id(btf,
+							    value_id);
+		}
+	}
+}
 
-			mname = btf_name_by_offset(btf, member->name_off);
-			if (!*mname) {
-				pr_warn("anon member in struct %s is not supported\n",
-					st_ops->name);
-				break;
-			}
+void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
+{
+	struct bpf_struct_ops *st_ops;
+	s32 module_id;
+	u32 i;
 
-			if (__btf_member_bitfield_size(t, member)) {
-				pr_warn("bit field member %s in struct %s is not supported\n",
-					mname, st_ops->name);
-				break;
-			}
+	/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */
+#define BPF_STRUCT_OPS_TYPE(_name) BTF_TYPE_EMIT(struct bpf_struct_ops_##_name);
+#include "bpf_struct_ops_types.h"
+#undef BPF_STRUCT_OPS_TYPE
 
-			func_proto = btf_type_resolve_func_ptr(btf,
-							       member->type,
-							       NULL);
-			if (func_proto &&
-			    btf_distill_func_proto(log, btf,
-						   func_proto, mname,
-						   &st_ops->func_models[j])) {
-				pr_warn("Error in parsing func ptr %s in struct %s\n",
-					mname, st_ops->name);
-				break;
-			}
-		}
+	module_id = btf_find_by_name_kind(btf, "module", BTF_KIND_STRUCT);
+	if (module_id < 0) {
+		pr_warn("Cannot find struct module in btf_vmlinux\n");
+		return;
+	}
+	module_type = btf_type_by_id(btf, module_id);
 
-		if (j == btf_type_vlen(t)) {
-			if (st_ops->init(btf)) {
-				pr_warn("Error in init bpf_struct_ops %s\n",
-					st_ops->name);
-			} else {
-				st_ops->type_id = type_id;
-				st_ops->type = t;
-				st_ops->value_id = value_id;
-				st_ops->value_type = btf_type_by_id(btf,
-								    value_id);
-			}
-		}
+	for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) {
+		st_ops = bpf_struct_ops[i];
+		bpf_struct_ops_init_one(st_ops, btf, log);
 	}
 }
 
Signature · 1 line
2.34.1
#2 lore raw

PATCH v8 2/10 bpf, net: introduce bpf_struct_ops_desc.

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Move some of members of bpf_struct_ops to bpf_struct_ops_desc. When we introduce the APIs to register new bpf_struct_ops types from modules, bpf_struct_ops may destroyed when the module is unloaded. Moving these members to bpf_struct_ops_desc make these data available even the module is unloaded.

---
 include/linux/bpf.h            | 13 ++++--
 kernel/bpf/bpf_struct_ops.c    | 76 +++++++++++++++++-----------------
 kernel/bpf/verifier.c          |  8 ++--
 net/bpf/bpf_dummy_struct_ops.c |  9 +++-
 net/ipv4/bpf_tcp_ca.c          |  8 +++-
 5 files changed, 68 insertions(+), 46 deletions(-)
include/linux/bpf.h +9 -4
index b4825d3cdb29..b55e27162df0 100644
@@ -1626,17 +1626,22 @@ struct bpf_struct_ops {
 	void (*unreg)(void *kdata);
 	int (*update)(void *kdata, void *old_kdata);
 	int (*validate)(void *kdata);
-	const struct btf_type *type;
-	const struct btf_type *value_type;
 	const char *name;
 	struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS];
+};
+
+struct bpf_struct_ops_desc {
+	struct bpf_struct_ops *st_ops;
+
+	const struct btf_type *type;
+	const struct btf_type *value_type;
 	u32 type_id;
 	u32 value_id;
 };
 
 #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
 #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
-const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id);
+const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id);
 void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
 bool bpf_struct_ops_get(const void *kdata);
 void bpf_struct_ops_put(const void *kdata);
@@ -1679,7 +1684,7 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
 			    union bpf_attr __user *uattr);
 #endif
 #else
-static inline const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id)
+static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
 {
 	return NULL;
 }
kernel/bpf/bpf_struct_ops.c +39 -37

227 lines not on this page · raw message

kernel/bpf/verifier.c +5 -3
index 857d76694517..290e3a7ee72f 100644
@@ -20081,6 +20081,7 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 {
 	const struct btf_type *t, *func_proto;
+	const struct bpf_struct_ops_desc *st_ops_desc;
 	const struct bpf_struct_ops *st_ops;
 	const struct btf_member *member;
 	struct bpf_prog *prog = env->prog;
@@ -20093,14 +20094,15 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 	}
 
 	btf_id = prog->aux->attach_btf_id;
-	st_ops = bpf_struct_ops_find(btf_id);
-	if (!st_ops) {
+	st_ops_desc = bpf_struct_ops_find(btf_id);
+	if (!st_ops_desc) {
 		verbose(env, "attach_btf_id %u is not a supported struct\n",
 			btf_id);
 		return -ENOTSUPP;
 	}
+	st_ops = st_ops_desc->st_ops;
 
-	t = st_ops->type;
+	t = st_ops_desc->type;
 	member_idx = prog->expected_attach_type;
 	if (member_idx >= btf_type_vlen(t)) {
 		verbose(env, "attach to invalid member idx %u of struct %s\n",
net/bpf/bpf_dummy_struct_ops.c +8 -1
index 5918d1b32e19..ffa224053a6c 100644
@@ -17,6 +17,8 @@ struct bpf_dummy_ops_test_args {
 	struct bpf_dummy_ops_state state;
 };
 
+static struct btf *bpf_dummy_ops_btf;
+
 static struct bpf_dummy_ops_test_args *
 dummy_ops_init_args(const union bpf_attr *kattr, unsigned int nr)
 {
@@ -85,9 +87,13 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
 	void *image = NULL;
 	unsigned int op_idx;
 	int prog_ret;
+	u32 type_id;
 	int err;
 
-	if (prog->aux->attach_btf_id != st_ops->type_id)
+	type_id = btf_find_by_name_kind(bpf_dummy_ops_btf,
+					bpf_bpf_dummy_ops.name,
+					BTF_KIND_STRUCT);
+	if (prog->aux->attach_btf_id != type_id)
 		return -EOPNOTSUPP;
 
 	func_proto = prog->aux->attach_func_proto;
@@ -143,6 +149,7 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
 
 static int bpf_dummy_init(struct btf *btf)
 {
+	bpf_dummy_ops_btf = btf;
 	return 0;
 }
 
net/ipv4/bpf_tcp_ca.c +7 -1
index 39dcccf0f174..3c8b76578a2a 100644
@@ -20,6 +20,7 @@ static u32 unsupported_ops[] = {
 
 static const struct btf_type *tcp_sock_type;
 static u32 tcp_sock_id, sock_id;
+static const struct btf_type *tcp_congestion_ops_type;
 
 static int bpf_tcp_ca_init(struct btf *btf)
 {
@@ -36,6 +37,11 @@ static int bpf_tcp_ca_init(struct btf *btf)
 	tcp_sock_id = type_id;
 	tcp_sock_type = btf_type_by_id(btf, tcp_sock_id);
 
+	type_id = btf_find_by_name_kind(btf, "tcp_congestion_ops", BTF_KIND_STRUCT);
+	if (type_id < 0)
+		return -EINVAL;
+	tcp_congestion_ops_type = btf_type_by_id(btf, type_id);
+
 	return 0;
 }
 
@@ -149,7 +155,7 @@ static u32 prog_ops_moff(const struct bpf_prog *prog)
 	u32 midx;
 
 	midx = prog->expected_attach_type;
-	t = bpf_tcp_congestion_ops.type;
+	t = tcp_congestion_ops_type;
 	m = &btf_type_member(t)[midx];
 
 	return __btf_member_bit_offset(t, m) / 8;
Signature · 1 line
2.34.1
#3 lore raw

Re: [PATCH bpf-next v8 02/10] bpf, net: introduce bpf_struct_ops_desc.

Quoted 3 lines · On 10/30/23 12:28 PM, [email protected] wrote:

+static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
struct btf *btf,
struct bpf_verifier_log *log)

nit. I think this should be renamed to bpf_struct_ops_desc_init() instead. It is initializing 'struct bpf_struct_ops_desc' now.

#4 lore raw

Re: [PATCH bpf-next v8 02/10] bpf, net: introduce bpf_struct_ops_desc.

Quoted 2 lines · Martin KaFai Lau

nit. I think this should be renamed to bpf_

Sure! It looks better.

#5 lore raw

PATCH v8 3/10 bpf: add struct_ops_tab to btf.

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Maintain a registry of registered struct_ops types in the per-btf (module) struct_ops_tab. This registry allows for easy lookup of struct_ops types that are registered by a specific module.

Every struct_ops type should have an associated module BTF to provide type information since we are going to allow modules to define and register new struct_ops types. Once this change is made, the bpf_struct_ops subsystem knows where to look up type info with just a bpf_struct_ops.

The subsystem looks up struct_ops types from a given module BTF although it is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than btf_vmlinux can be used as well.

---
 include/linux/bpf.h         |  5 +--
 include/linux/btf.h         |  8 +++++
 kernel/bpf/bpf_struct_ops.c | 28 ++++++++-------
 kernel/bpf/btf.c            | 71 +++++++++++++++++++++++++++++++++++++
 kernel/bpf/verifier.c       |  2 +-
 5 files changed, 99 insertions(+), 15 deletions(-)
include/linux/bpf.h +3 -2
index b55e27162df0..77dd9a522d55 100644
@@ -1633,6 +1633,7 @@ struct bpf_struct_ops {
 struct bpf_struct_ops_desc {
 	struct bpf_struct_ops *st_ops;
 
+	struct btf *btf;
 	const struct btf_type *type;
 	const struct btf_type *value_type;
 	u32 type_id;
@@ -1641,7 +1642,7 @@ struct bpf_struct_ops_desc {
 
 #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
 #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id);
+const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id);
 void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
 bool bpf_struct_ops_get(const void *kdata);
 void bpf_struct_ops_put(const void *kdata);
@@ -1684,7 +1685,7 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
 			    union bpf_attr __user *uattr);
 #endif
 #else
-static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id)
 {
 	return NULL;
 }
include/linux/btf.h +8 -0
index c2231c64d60b..933a227573da 100644
@@ -572,4 +572,12 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type
 	return btf_type_is_struct(t);
 }
 
+struct bpf_struct_ops;
+struct bpf_struct_ops_desc;
+
+struct bpf_struct_ops_desc *
+btf_add_struct_ops(struct bpf_struct_ops *st_ops);
+const struct bpf_struct_ops_desc *
+btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
+
 #endif
kernel/bpf/bpf_struct_ops.c +16 -12
index e35d6321a2f8..0bc21a39257d 100644
@@ -186,6 +186,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 			pr_warn("Error in init bpf_struct_ops %s\n",
 				st_ops->name);
 		} else {
+			st_ops_desc->btf = btf;
 			st_ops_desc->type_id = type_id;
 			st_ops_desc->type = t;
 			st_ops_desc->value_id = value_id;
@@ -222,7 +223,7 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
 extern struct btf *btf_vmlinux;
 
 static const struct bpf_struct_ops_desc *
-bpf_struct_ops_find_value(u32 value_id)
+bpf_struct_ops_find_value(struct btf *btf, u32 value_id)
 {
 	unsigned int i;
 
@@ -237,7 +238,8 @@ bpf_struct_ops_find_value(u32 value_id)
 	return NULL;
 }
 
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+const struct bpf_struct_ops_desc *
+bpf_struct_ops_find(struct btf *btf, u32 type_id)
 {
 	unsigned int i;
 
@@ -317,7 +319,7 @@ static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map)
 	}
 }
 
-static int check_zero_holes(const struct btf_type *t, void *data)
+static int check_zero_holes(const struct btf *btf, const struct btf_type *t, void *data)
 {
 	const struct btf_member *member;
 	u32 i, moff, msize, prev_mend = 0;
@@ -329,8 +331,8 @@ static int check_zero_holes(const struct btf_type *t, void *data)
 		    memchr_inv(data + prev_mend, 0, moff - prev_mend))
 			return -EINVAL;
 
-		mtype = btf_type_by_id(btf_vmlinux, member->type);
-		mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
+		mtype = btf_type_by_id(btf, member->type);
+		mtype = btf_resolve_size(btf, mtype, &msize);
 		if (IS_ERR(mtype))
 			return PTR_ERR(mtype);
 		prev_mend = moff + msize;
@@ -397,12 +399,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 	if (*(u32 *)key != 0)
 		return -E2BIG;
 
-	err = check_zero_holes(st_ops_desc->value_type, value);
+	err = check_zero_holes(st_ops_desc->btf, st_ops_desc->value_type, value);
 	if (err)
 		return err;
 
 	uvalue = value;
-	err = check_zero_holes(t, uvalue->data);
+	err = check_zero_holes(st_ops_desc->btf, t, uvalue->data);
 	if (err)
 		return err;
 
@@ -437,7 +439,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		u32 moff;
 
 		moff = __btf_member_bit_offset(t, member) / 8;
-		ptype = btf_type_resolve_ptr(btf_vmlinux, member->type, NULL);
+		ptype = btf_type_resolve_ptr(st_ops_desc->btf, member->type, NULL);
 		if (ptype == module_type) {
 			if (*(void **)(udata + moff))
 				goto reset_unlock;
@@ -462,8 +464,8 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		if (!ptype || !btf_type_is_func_proto(ptype)) {
 			u32 msize;
 
-			mtype = btf_type_by_id(btf_vmlinux, member->type);
-			mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
+			mtype = btf_type_by_id(st_ops_desc->btf, member->type);
+			mtype = btf_resolve_size(st_ops_desc->btf, mtype, &msize);
 			if (IS_ERR(mtype)) {
 				err = PTR_ERR(mtype);
 				goto reset_unlock;
@@ -602,6 +604,7 @@ static long bpf_struct_ops_map_delete_elem(struct bpf_map *map, void *key)
 static void bpf_struct_ops_map_seq_show_elem(struct bpf_map *map, void *key,
 					     struct seq_file *m)
 {
+	struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
 	void *value;
 	int err;
 
@@ -611,7 +614,8 @@ static void bpf_struct_ops_map_seq_show_elem(struct bpf_map *map, void *key,
 
 	err = bpf_struct_ops_map_sys_lookup_elem(map, key, value);
 	if (!err) {
-		btf_type_seq_show(btf_vmlinux, map->btf_vmlinux_value_type_id,
+		btf_type_seq_show(st_map->st_ops_desc->btf,
+				  map->btf_vmlinux_value_type_id,
 				  value, m);
 		seq_puts(m, "\n");
 	}
@@ -673,7 +677,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
 	struct bpf_map *map;
 	int ret;
 
-	st_ops_desc = bpf_struct_ops_find_value(attr->btf_vmlinux_value_type_id);
+	st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, attr->btf_vmlinux_value_type_id);
 	if (!st_ops_desc)
 		return ERR_PTR(-ENOTSUPP);
 
kernel/bpf/btf.c +71 -0

97 lines not on this page · raw message

kernel/bpf/verifier.c +1 -1
index 290e3a7ee72f..bdd166cab977 100644
@@ -20094,7 +20094,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 	}
 
 	btf_id = prog->aux->attach_btf_id;
-	st_ops_desc = bpf_struct_ops_find(btf_id);
+	st_ops_desc = bpf_struct_ops_find(btf_vmlinux, btf_id);
 	if (!st_ops_desc) {
 		verbose(env, "attach_btf_id %u is not a supported struct\n",
 			btf_id);
Signature · 1 line
2.34.1
#6 lore raw

Re: [PATCH bpf-next v8 03/10] bpf: add struct_ops_tab to btf.

Quoted 10 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Maintain a registry of registered struct_ops types in the per-btf (module) struct_ops_tab. This registry allows for easy lookup of struct_ops types that are registered by a specific module.

Every struct_ops type should have an associated module BTF to provide type information since we are going to allow modules to define and register new struct_ops types. Once this change is made, the bpf_struct_ops subsystem knows where to look up type info with just a bpf_struct_ops.

I think this part needs better description. I found it hard to parse. In particular:

...
the "bpf_struct_ops" subsystem
knows where to look up type info with just
a "bpf_struct_ops"
...

May be something like:

It is a preparation work for supporting kernel module struct_ops in a latter patch. Each struct_ops will be registered under its own kernel module btf and will be stored in the newly added btf->struct_ops_tab. The bpf verifier and bpf syscall (e.g. prog and map cmd) can find the struct_ops and its btf type/size/id... information from btf->struct_ops_tab.

Quoted 4 lines

The subsystem looks up struct_ops types from a given module BTF although it is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than btf_vmlinux can be used as well.

I think this describes about the "struct btf *btf" argument change in this patch. This seems unrelated to the "add struct_ops_tab to btf" change. Can it be in its own preparation patch?

[ ... ]

Quoted 13 lines
kernel/bpf/bpf_struct_ops.c +1 -0
index e35d6321a2f8..0bc21a39257d 100644
@@ -186,6 +186,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
  			pr_warn("Error in init bpf_struct_ops %s\n",
  				st_ops->name);
  		} else {
+			st_ops_desc->btf = btf;
  			st_ops_desc->type_id = type_id;
  			st_ops_desc->type = t;
  			st_ops_desc->value_id = value_id;

kernel/bpf/bpf_struct_ops.c void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log) · line 226

Quoted 5 lines
kernel/bpf/bpf_struct_ops.c +1 -1
@@ -222,7 +223,7 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
  extern struct btf *btf_vmlinux;
 
  static const struct bpf_struct_ops_desc *
-bpf_struct_ops_find_value(u32 value_id)
+bpf_struct_ops_find_value(struct btf *btf, u32 value_id)

The "!btf_vmlinux" check a few lines below should also be changed to "!btf". I think I had commented on a similar point in v5.

kernel/bpf/bpf_struct_ops.c bpf_struct_ops_find_value(u32 value_id) · line 242

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +2 -1
  {
  	unsigned int i;
 
@@ -237,7 +238,8 @@ bpf_struct_ops_find_value(u32 value_id)
  	return NULL;
  }
 
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+const struct bpf_struct_ops_desc *
+bpf_struct_ops_find(struct btf *btf, u32 type_id)

same here.

Quoted 3 lines
kernel/bpf/bpf_struct_ops.c +0 -0
  {
  	unsigned int i;
 

[ ... ]

Quoted 21 lines
kernel/bpf/bpf_struct_ops.c +21 -0
+static struct bpf_struct_ops_desc *
+btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops)
+{
+	struct btf_struct_ops_tab *tab, *new_tab;
+	int i;
+
+	if (!btf)
+		return ERR_PTR(-ENOENT);
+
+	/* Assume this function is called for a module when the module is
+	 * loading.
+	 */
+
+	tab = btf->struct_ops_tab;
+	if (!tab) {
+		tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
+			      GFP_KERNEL);
+		if (!tab)
+			return ERR_PTR(-ENOMEM);
+		tab->capacity = 4;
+		btf->struct_ops_tab = tab;

kernel/bpf/bpf_struct_ops.c

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +10 -0
+	}
+
+	for (i = 0; i < tab->cnt; i++)
+		if (tab->ops[i].st_ops == st_ops)
+			return ERR_PTR(-EEXIST);
+
+	if (tab->cnt == tab->capacity) {
+		new_tab = krealloc(tab, sizeof(*tab) +
+				   sizeof(struct bpf_struct_ops *) *
+				   tab->capacity * 2, GFP_KERNEL);

nit. Use a similar offsetof() like a few lines above.

kernel/bpf/bpf_struct_ops.c

Quoted 8 lines
kernel/bpf/bpf_struct_ops.c +8 -0
+		if (!new_tab)
+			return ERR_PTR(-ENOMEM);
+		tab = new_tab;
+		tab->capacity *= 2;
+		btf->struct_ops_tab = tab;
+	}
+
+	btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;

nit. s/btf->struct_ops_tab/tab/

Quoted 3 lines
kernel/bpf/bpf_struct_ops.c +3 -0
+
+	return &btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt++];
+}
#7 lore raw

Re: [PATCH bpf-next v8 03/10] bpf: add struct_ops_tab to btf.

Quoted 20 lines · Martin KaFai Lau
Quoted 10 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Maintain a registry of registered struct_ops types in the per-btf (module) struct_ops_tab. This registry allows for easy lookup of struct_ops types that are registered by a specific module.

Every struct_ops type should have an associated module BTF to provide type information since we are going to allow modules to define and register new struct_ops types. Once this change is made, the bpf_struct_ops subsystem knows where to look up type info with just a bpf_struct_ops.

I think this part needs better description. I found it hard to parse. In particular:

...
  the "bpf_struct_ops" subsystem
       knows where to look up type info with just a "bpf_struct_ops"
...

Quoted 3 lines

May be something like:

It is a preparation work for supporting kernel module struct_ops in a latter patch. Each struct_ops will be registered under its own kernel module btf and will be stored in the newly added btf->struct_ops_tab. The bpf verifier and bpf syscall (e.g. prog and map cmd) can find the struct_ops and its btf type/size/id... information from btf->struct_ops_tab.

Got it!

Quoted 8 lines
Quoted 5 lines

The subsystem looks up struct_ops types from a given module BTF although it is always btf_vmlinux now. Once start using struct_ops_tab, btfs other than btf_vmlinux can be used as well.

I think this describes about the "struct btf *btf" argument change in this patch. This seems unrelated to the "add struct_ops_tab to btf" change. Can it be in its own preparation patch?

Sure!

Quoted 15 lines

[ ... ]

Quoted 13 lines
kernel/bpf/bpf_struct_ops.c +1 -0
index e35d6321a2f8..0bc21a39257d 100644
@@ -186,6 +186,7 @@ static void bpf_struct_ops_init_one(struct 
 bpf_struct_ops_desc *st_ops_desc,
               pr_warn("Error in init bpf_struct_ops %s\n",
                   st_ops->name);
           } else {
+            st_ops_desc->btf = btf;
               st_ops_desc->type_id = type_id;
               st_ops_desc->type = t;

              st_ops_desc->value_id = value_id;

Quoted 8 lines

kernel/bpf/bpf_struct_ops.c void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log) · line 226

Quoted 6 lines
kernel/bpf/bpf_struct_ops.c +0 -0
@@ -222,7 +223,7 @@ void bpf_struct_ops_init(struct btf *btf, struct 
 bpf_verifier_log *log)
   extern struct btf *btf_vmlinux;

  static const struct bpf_struct_ops_desc *

kernel/bpf/bpf_struct_ops.c +1 -1
-bpf_struct_ops_find_value(u32 value_id)
+bpf_struct_ops_find_value(struct btf *btf, u32 value_id)

The "!btf_vmlinux" check a few lines below should also be changed to "!btf". I think I had commented on a similar point in v5.

At this patch, btf is still always btf_vmlinux until the patch 6 and 7. I will move these changes from the patch 6 and 7 to here or the new patch mentioned above.

Quoted 12 lines

kernel/bpf/bpf_struct_ops.c bpf_struct_ops_find_value(u32 value_id) · line 241

Quoted 9 lines

  {
      unsigned int i;

kernel/bpf/bpf_struct_ops.c +2 -1
@@ -237,7 +238,8 @@ bpf_struct_ops_find_value(u32 value_id)
       return NULL;
   }
-const struct bpf_struct_ops_desc *bpf_struct_ops_find(u32 type_id)
+const struct bpf_struct_ops_desc *
+bpf_struct_ops_find(struct btf *btf, u32 type_id)

same here.

Got it!

Quoted 30 lines
Quoted 2 lines
kernel/bpf/bpf_struct_ops.c +0 -0
   {
       unsigned int i;

[ ... ]

Quoted 25 lines
kernel/bpf/bpf_struct_ops.c +25 -0
+static struct bpf_struct_ops_desc *
+btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops)
+{
+    struct btf_struct_ops_tab *tab, *new_tab;
+    int i;
+
+    if (!btf)
+        return ERR_PTR(-ENOENT);
+
+    /* Assume this function is called for a module when the module is
+     * loading.
+     */
+
+    tab = btf->struct_ops_tab;
+    if (!tab) {
+        tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
+                  GFP_KERNEL);
+        if (!tab)
+            return ERR_PTR(-ENOMEM);
+        tab->capacity = 4;
+        btf->struct_ops_tab = tab;
+    }
+
+    for (i = 0; i < tab->cnt; i++)
+        if (tab->ops[i].st_ops == st_ops)
Quoted 8 lines
Quoted 6 lines

+            return ERR_PTR(-EEXIST);
+
+    if (tab->cnt == tab->capacity) {
+        new_tab = krealloc(tab, sizeof(*tab) + +                   sizeof(struct bpf_struct_ops *) * +                   tab->capacity * 2, GFP_KERNEL);

nit. Use a similar offsetof() like a few lines above.

Sure!

Quoted 10 lines
Quoted 9 lines

+        if (!new_tab)
+            return ERR_PTR(-ENOMEM);
+        tab = new_tab;
+        tab->capacity *= 2;
+        btf->struct_ops_tab = tab;
+    }
+
+    btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops;

Quoted 2 lines

nit. s/btf->struct_ops_tab/tab/

Sure!

Quoted 4 lines
Quoted 3 lines
kernel/bpf/bpf_struct_ops.c +3 -0
+
+    return &btf->struct_ops_tab->ops[btf->struct_ops_tab->cnt++];
+}
#8 lore raw

PATCH v8 4/10 bpf: hold module for bpf_struct_ops_map.

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

To ensure that a module remains accessible whenever a struct_ops object of a struct_ops type provided by the module is still in use.

---
 include/linux/bpf.h         |  1 +
 include/linux/btf.h         |  2 +-
 kernel/bpf/bpf_struct_ops.c | 70 ++++++++++++++++++++++++++++++-------
 3 files changed, 60 insertions(+), 13 deletions(-)
include/linux/bpf.h +1 -0
index 77dd9a522d55..c993df3cf699 100644
@@ -1626,6 +1626,7 @@ struct bpf_struct_ops {
 	void (*unreg)(void *kdata);
 	int (*update)(void *kdata, void *old_kdata);
 	int (*validate)(void *kdata);
+	struct module *owner;
 	const char *name;
 	struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS];
 };
include/linux/btf.h +1 -1
index 933a227573da..a8813605f2f6 100644
@@ -576,7 +576,7 @@ struct bpf_struct_ops;
 struct bpf_struct_ops_desc;
 
 struct bpf_struct_ops_desc *
-btf_add_struct_ops(struct bpf_struct_ops *st_ops);
+btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops);
 const struct bpf_struct_ops_desc *
 btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
 
kernel/bpf/bpf_struct_ops.c +58 -12

169 lines not on this page · raw message

Signature · 1 line
2.34.1
#9 lore raw

Re: [PATCH bpf-next v8 04/10] bpf: hold module for bpf_struct_ops_map.

Quoted 65 lines · On 10/30/23 12:28 PM, [email protected] wrote:
kernel/bpf/bpf_struct_ops.c +20 -12
@@ -681,9 +697,17 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
  	if (!st_ops_desc)
  		return ERR_PTR(-ENOTSUPP);
 
+	if (st_ops_desc->btf != btf_vmlinux) {
+		mod = btf_try_get_module(st_ops_desc->btf);
+		if (!mod)
+			return ERR_PTR(-EINVAL);
+	}
+
  	vt = st_ops_desc->value_type;
-	if (attr->value_size != vt->size)
-		return ERR_PTR(-EINVAL);
+	if (attr->value_size != vt->size) {
+		ret = -EINVAL;
+		goto errout;
+	}
 
  	t = st_ops_desc->type;
 
@@ -694,17 +718,17 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
  		(vt->size - sizeof(struct bpf_struct_ops_value));
 
  	st_map = bpf_map_area_alloc(st_map_size, NUMA_NO_NODE);
-	if (!st_map)
-		return ERR_PTR(-ENOMEM);
+	if (!st_map) {
+		ret = -ENOMEM;
+		goto errout;
+	}
 
  	st_map->st_ops_desc = st_ops_desc;
  	map = &st_map->map;
 
  	ret = bpf_jit_charge_modmem(PAGE_SIZE);
-	if (ret) {
-		__bpf_struct_ops_map_free(map);
-		return ERR_PTR(ret);
-	}
+	if (ret)
+		goto errout_free;
 
  	st_map->image = bpf_jit_alloc_exec(PAGE_SIZE);
  	if (!st_map->image) {
@@ -713,23 +737,32 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
  		 * here.
  		 */
  		bpf_jit_uncharge_modmem(PAGE_SIZE);
-		__bpf_struct_ops_map_free(map);
-		return ERR_PTR(-ENOMEM);
+		ret = -ENOMEM;
+		goto errout_free;
  	}
  	st_map->uvalue = bpf_map_area_alloc(vt->size, NUMA_NO_NODE);
  	st_map->links =
  		bpf_map_area_alloc(btf_type_vlen(t) * sizeof(struct bpf_links *),
  				   NUMA_NO_NODE);
  	if (!st_map->uvalue || !st_map->links) {
-		__bpf_struct_ops_map_free(map);
-		return ERR_PTR(-ENOMEM);
+		ret = -ENOMEM;
+		goto errout_free;
  	}
 
  	mutex_init(&st_map->lock);

kernel/bpf/bpf_struct_ops.c static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) · line 762

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +6 -0
  	set_vm_flush_reset_perms(st_map->image);
  	bpf_map_init_from_attr(map, attr);
 
+	module_put(mod);
+
  	return map;
+
+errout_free:
+	__bpf_struct_ops_map_free(map);
+	btf = NULL;		/* has been released */

btf is not defined. I don't think this patch compiles. Something from a latter patch?

Quoted 4 lines
kernel/bpf/bpf_struct_ops.c +3 -0
+errout:
+	module_put(mod);
+	return ERR_PTR(ret);
  }
#10 lore raw

Re: [PATCH bpf-next v8 04/10] bpf: hold module for bpf_struct_ops_map.

Quoted 65 lines · Martin KaFai Lau
Quoted 64 lines · On 10/30/23 12:28 PM, [email protected] wrote:
kernel/bpf/bpf_struct_ops.c +18 -10
@@ -681,9 +697,17 @@ static struct bpf_map 
 *bpf_struct_ops_map_alloc(union bpf_attr *attr)
       if (!st_ops_desc)
           return ERR_PTR(-ENOTSUPP);
+    if (st_ops_desc->btf != btf_vmlinux) {
+        mod = btf_try_get_module(st_ops_desc->btf);
+        if (!mod)
+            return ERR_PTR(-EINVAL);
+    }
+
       vt = st_ops_desc->value_type;
-    if (attr->value_size != vt->size)
-        return ERR_PTR(-EINVAL);
+    if (attr->value_size != vt->size) {
+        ret = -EINVAL;
+        goto errout;
+    }
       t = st_ops_desc->type;
@@ -694,17 +718,17 @@ static struct bpf_map 
 *bpf_struct_ops_map_alloc(union bpf_attr *attr)
           (vt->size - sizeof(struct bpf_struct_ops_value));
       st_map = bpf_map_area_alloc(st_map_size, NUMA_NO_NODE);
-    if (!st_map)
-        return ERR_PTR(-ENOMEM);
+    if (!st_map) {
+        ret = -ENOMEM;
+        goto errout;
+    }
       st_map->st_ops_desc = st_ops_desc;
       map = &st_map->map;
       ret = bpf_jit_charge_modmem(PAGE_SIZE);
-    if (ret) {
-        __bpf_struct_ops_map_free(map);
-        return ERR_PTR(ret);
-    }
+    if (ret)
+        goto errout_free;
       st_map->image = bpf_jit_alloc_exec(PAGE_SIZE);
       if (!st_map->image) {
@@ -713,23 +737,32 @@ static struct bpf_map 
 *bpf_struct_ops_map_alloc(union bpf_attr *attr)
            * here.
            */
           bpf_jit_uncharge_modmem(PAGE_SIZE);
-        __bpf_struct_ops_map_free(map);
-        return ERR_PTR(-ENOMEM);
+        ret = -ENOMEM;
+        goto errout_free;
       }
       st_map->uvalue = bpf_map_area_alloc(vt->size, NUMA_NO_NODE);

      st_map->links =
          bpf_map_area_alloc(btf_type_vlen(t) * sizeof(struct bpf_links *), NUMA_NO_NODE);
      if (!st_map->uvalue || !st_map->links) {
-        __bpf_struct_ops_map_free(map);
-        return ERR_PTR(-ENOMEM);
+        ret = -ENOMEM;
+        goto errout_free;
      }
      mutex_init(&st_map->lock); set_vm_flush_reset_perms(st_map->image); bpf_map_init_from_attr(map, attr);
+    module_put(mod);
+

Quoted 8 lines

kernel/bpf/bpf_struct_ops.c static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) · line 762

Quoted 5 lines

      return map;

kernel/bpf/bpf_struct_ops.c +4 -0
+
+errout_free:
+    __bpf_struct_ops_map_free(map);
+    btf = NULL;        /* has been released */

btf is not defined. I don't think this patch compiles. Something from a latter patch?

Yes, it is defined in the next patch. I will fix it.

Quoted 6 lines
Quoted 5 lines

+errout:
+    module_put(mod);
+    return ERR_PTR(ret);
  }

#11 lore raw

PATCH v8 5/10 bpf: validate value_type

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

A value_type should consist of three components: refcnt, state, and data. refcnt and state has been move to struct bpf_struct_ops_common_value to make it easier to check the value type.

---
 kernel/bpf/bpf_struct_ops.c | 86 ++++++++++++++++++++++++++++---------
 1 file changed, 65 insertions(+), 21 deletions(-)
kernel/bpf/bpf_struct_ops.c +65 -21
index b0b8cf9ed057..256516aba632 100644
@@ -20,9 +20,11 @@ enum bpf_struct_ops_state {
 	BPF_STRUCT_OPS_STATE_READY,
 };
 
-#define BPF_STRUCT_OPS_COMMON_VALUE			\
-	refcount_t refcnt;				\
-	enum bpf_struct_ops_state state
+struct bpf_struct_ops_common_value {
+	refcount_t refcnt;
+	enum bpf_struct_ops_state state;
+};
+#define BPF_STRUCT_OPS_COMMON_VALUE struct bpf_struct_ops_common_value common
 
 struct bpf_struct_ops_value {
 	BPF_STRUCT_OPS_COMMON_VALUE;
@@ -109,6 +111,38 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops = {
 };
 
 static const struct btf_type *module_type;
+static const struct btf_type *common_value_type;
+
+static bool is_valid_value_type(struct btf *btf, s32 value_id,
+				const struct btf_type *type,
+				const char *value_name)
+{
+	const struct btf_member *member;
+	const struct btf_type *vt, *mt;
+
+	vt = btf_type_by_id(btf, value_id);
+	if (btf_vlen(vt) != 2) {
+		pr_warn("The number of %s's members should be 2, but we get %d\n",
+			value_name, btf_vlen(vt));
+		return false;
+	}
+	member = btf_type_member(vt);
+	mt = btf_type_by_id(btf, member->type);
+	if (mt != common_value_type) {
+		pr_warn("The first member of %s should be bpf_struct_ops_common_value\n",
+			value_name);
+		return false;
+	}
+	member++;
+	mt = btf_type_by_id(btf, member->type);
+	if (mt != type) {
+		pr_warn("The second member of %s should be %s\n",
+			value_name, btf_name_by_offset(btf, type->name_off));
+		return false;
+	}
+
+	return true;
+}
 
 static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 				    struct btf *btf,
@@ -130,14 +164,6 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 	}
 	sprintf(value_name, "%s%s", VALUE_PREFIX, st_ops->name);
 
-	value_id = btf_find_by_name_kind(btf, value_name,
-					 BTF_KIND_STRUCT);
-	if (value_id < 0) {
-		pr_warn("Cannot find struct %s in btf_vmlinux\n",
-			value_name);
-		return;
-	}
-
 	type_id = btf_find_by_name_kind(btf, st_ops->name,
 					BTF_KIND_STRUCT);
 	if (type_id < 0) {
@@ -152,6 +178,16 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 		return;
 	}
 
+	value_id = btf_find_by_name_kind(btf, value_name,
+					 BTF_KIND_STRUCT);
+	if (value_id < 0) {
+		pr_warn("Cannot find struct %s in btf_vmlinux\n",
+			value_name);
+		return;
+	}
+	if (!is_valid_value_type(btf, value_id, t, value_name))
+		return;
+
 	for_each_member(i, t, member) {
 		const struct btf_type *func_proto;
 
@@ -199,7 +235,7 @@ static void bpf_struct_ops_init_one(struct bpf_struct_ops_desc *st_ops_desc,
 void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
 {
 	struct bpf_struct_ops_desc *st_ops_desc;
-	s32 module_id;
+	s32 module_id, common_value_id;
 	u32 i;
 
 	/* Ensure BTF type is emitted for "struct bpf_struct_ops_##_name" */
@@ -213,6 +249,14 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log)
 		return;
 	}
 	module_type = btf_type_by_id(btf, module_id);
+	common_value_id = btf_find_by_name_kind(btf,
+						"bpf_struct_ops_common_value",
+						BTF_KIND_STRUCT);
+	if (common_value_id < 0) {
+		pr_warn("Cannot find struct common_value in btf_vmlinux\n");
+		return;
+	}
+	common_value_type = btf_type_by_id(btf, common_value_id);
 
 	for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) {
 		st_ops_desc = &bpf_struct_ops[i];
@@ -277,7 +321,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
 
 	kvalue = &st_map->kvalue;
 	/* Pair with smp_store_release() during map_update */
-	state = smp_load_acquire(&kvalue->state);
+	state = smp_load_acquire(&kvalue->common.state);
 	if (state == BPF_STRUCT_OPS_STATE_INIT) {
 		memset(value, 0, map->value_size);
 		return 0;
@@ -288,7 +332,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
 	 */
 	uvalue = value;
 	memcpy(uvalue, st_map->uvalue, map->value_size);
-	uvalue->state = state;
+	uvalue->common.state = state;
 
 	/* This value offers the user space a general estimate of how
 	 * many sockets are still utilizing this struct_ops for TCP
@@ -296,7 +340,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
 	 * should sufficiently meet our present goals.
 	 */
 	refcnt = atomic64_read(&map->refcnt) - atomic64_read(&map->usercnt);
-	refcount_set(&uvalue->refcnt, max_t(s64, refcnt, 0));
+	refcount_set(&uvalue->common.refcnt, max_t(s64, refcnt, 0));
 
 	return 0;
 }
@@ -409,7 +453,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 	if (err)
 		return err;
 
-	if (uvalue->state || refcount_read(&uvalue->refcnt))
+	if (uvalue->common.state || refcount_read(&uvalue->common.refcnt))
 		return -EINVAL;
 
 	tlinks = kcalloc(BPF_TRAMP_MAX, sizeof(*tlinks), GFP_KERNEL);
@@ -421,7 +465,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 
 	mutex_lock(&st_map->lock);
 
-	if (kvalue->state != BPF_STRUCT_OPS_STATE_INIT) {
+	if (kvalue->common.state != BPF_STRUCT_OPS_STATE_INIT) {
 		err = -EBUSY;
 		goto unlock;
 	}
@@ -542,7 +586,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		 *
 		 * Pair with smp_load_acquire() during lookup_elem().
 		 */
-		smp_store_release(&kvalue->state, BPF_STRUCT_OPS_STATE_READY);
+		smp_store_release(&kvalue->common.state, BPF_STRUCT_OPS_STATE_READY);
 		goto unlock;
 	}
 
@@ -560,7 +604,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
 		 * It ensures the above udata updates (e.g. prog->aux->id)
 		 * can be seen once BPF_STRUCT_OPS_STATE_INUSE is set.
 		 */
-		smp_store_release(&kvalue->state, BPF_STRUCT_OPS_STATE_INUSE);
+		smp_store_release(&kvalue->common.state, BPF_STRUCT_OPS_STATE_INUSE);
 		/* Hold the owner module until the struct_ops is
 		 * unregistered
 		 */
@@ -596,7 +640,7 @@ static long bpf_struct_ops_map_delete_elem(struct bpf_map *map, void *key)
 	if (st_map->map.map_flags & BPF_F_LINK)
 		return -EOPNOTSUPP;
 
-	prev_state = cmpxchg(&st_map->kvalue.state,
+	prev_state = cmpxchg(&st_map->kvalue.common.state,
 			     BPF_STRUCT_OPS_STATE_INUSE,
 			     BPF_STRUCT_OPS_STATE_TOBEFREE);
 	switch (prev_state) {
@@ -828,7 +872,7 @@ static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
 	return map->map_type == BPF_MAP_TYPE_STRUCT_OPS &&
 		map->map_flags & BPF_F_LINK &&
 		/* Pair with smp_store_release() during map_update */
-		smp_load_acquire(&st_map->kvalue.state) == BPF_STRUCT_OPS_STATE_READY;
+		smp_load_acquire(&st_map->kvalue.common.state) == BPF_STRUCT_OPS_STATE_READY;
 }
 
 static void bpf_struct_ops_map_link_dealloc(struct bpf_link *link)
Signature · 1 line
2.34.1
#12 lore raw

PATCH v8 6/10 bpf: pass attached BTF to the bpf_struct_ops subsystem

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Giving a BTF, the bpf_struct_ops knows the right place to look up type info associated with a type ID. This enables a user space program to load a struct_ops object linked to a struct_ops type defined by a module, by providing the module BTF (fd).

The bpf_prog includes attach_btf in aux which is passed along with the bpf_attr when loading the program. The purpose of attach_btf is to determine the btf type of attach_btf_id. The attach_btf_id is then used to identify the traced function for a trace program. In the case of struct_ops programs, it is used to identify the struct_ops type of the struct_ops object that a program is attached to.

---
 include/linux/bpf_verifier.h   |  1 +
 include/uapi/linux/bpf.h       |  5 +++++
 kernel/bpf/bpf_struct_ops.c    | 32 +++++++++++++++++++++++++-------
 kernel/bpf/syscall.c           |  2 +-
 kernel/bpf/verifier.c          | 19 ++++++++++++++++---
 tools/include/uapi/linux/bpf.h |  5 +++++
 6 files changed, 53 insertions(+), 11 deletions(-)
include/linux/bpf_verifier.h +1 -0
index 24213a99cc79..c1461342f19e 100644
@@ -598,6 +598,7 @@ struct bpf_verifier_env {
 	u32 prev_insn_idx;
 	struct bpf_prog *prog;		/* eBPF program being verified */
 	const struct bpf_verifier_ops *ops;
+	struct module *attach_btf_mod;	/* The owner module of prog->aux->attach_btf */
 	struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
 	int stack_size;			/* number of states to be processed */
 	bool strict_alignment;		/* perform strict pointer alignment checks */
include/uapi/linux/bpf.h +5 -0
index 0f6cdf52b1da..fd20c52606b2 100644
@@ -1398,6 +1398,11 @@ union bpf_attr {
 		 * to using 5 hash functions).
 		 */
 		__u64	map_extra;
+
+		__u32   value_type_btf_obj_fd;	/* fd pointing to a BTF
+						 * type data for
+						 * btf_vmlinux_value_type_id.
+						 */
 	};
 
 	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
kernel/bpf/bpf_struct_ops.c +25 -7
index 256516aba632..db2bbba50e38 100644
@@ -694,6 +694,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
 		bpf_jit_uncharge_modmem(PAGE_SIZE);
 	}
 	bpf_map_area_free(st_map->uvalue);
+	btf_put(st_map->st_ops_desc->btf);
 	bpf_map_area_free(st_map);
 }
 
@@ -735,16 +736,31 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
 	const struct btf_type *t, *vt;
 	struct module *mod = NULL;
 	struct bpf_map *map;
+	struct btf *btf;
 	int ret;
 
-	st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, attr->btf_vmlinux_value_type_id);
-	if (!st_ops_desc)
-		return ERR_PTR(-ENOTSUPP);
+	if (attr->value_type_btf_obj_fd) {
+		/* The map holds btf for its whole life time. */
+		btf = btf_get_by_fd(attr->value_type_btf_obj_fd);
+		if (IS_ERR(btf))
+			return ERR_PTR(PTR_ERR(btf));
+
+		if (btf != btf_vmlinux) {
+			mod = btf_try_get_module(btf);
+			if (!mod) {
+				ret = -EINVAL;
+				goto errout;
+			}
+		}
+	} else {
+		btf = btf_vmlinux;
+		btf_get(btf);
+	}
 
-	if (st_ops_desc->btf != btf_vmlinux) {
-		mod = btf_try_get_module(st_ops_desc->btf);
-		if (!mod)
-			return ERR_PTR(-EINVAL);
+	st_ops_desc = bpf_struct_ops_find_value(btf, attr->btf_vmlinux_value_type_id);
+	if (!st_ops_desc) {
+		ret = -ENOTSUPP;
+		goto errout;
 	}
 
 	vt = st_ops_desc->value_type;
@@ -805,7 +821,9 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
 	__bpf_struct_ops_map_free(map);
 	btf = NULL;		/* has been released */
 errout:
+	btf_put(btf);
 	module_put(mod);
+
 	return ERR_PTR(ret);
 }
 
kernel/bpf/syscall.c +1 -1
index 0ed286b8a0f0..974651fe2bee 100644
@@ -1096,7 +1096,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
 	return ret;
 }
 
-#define BPF_MAP_CREATE_LAST_FIELD map_extra
+#define BPF_MAP_CREATE_LAST_FIELD value_type_btf_obj_fd
 /* called via syscall */
 static int map_create(union bpf_attr *attr)
 {
kernel/bpf/verifier.c +16 -3
index bdd166cab977..20d6d9665983 100644
@@ -20086,6 +20086,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 	const struct btf_member *member;
 	struct bpf_prog *prog = env->prog;
 	u32 btf_id, member_idx;
+	struct btf *btf;
 	const char *mname;
 
 	if (!prog->gpl_compatible) {
@@ -20093,8 +20094,18 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 		return -EINVAL;
 	}
 
+	btf = prog->aux->attach_btf;
+	if (btf != btf_vmlinux) {
+		/* Make sure st_ops is valid through the lifetime of env */
+		env->attach_btf_mod = btf_try_get_module(btf);
+		if (!env->attach_btf_mod) {
+			verbose(env, "owner module of btf is not found\n");
+			return -ENOTSUPP;
+		}
+	}
+
 	btf_id = prog->aux->attach_btf_id;
-	st_ops_desc = bpf_struct_ops_find(btf_vmlinux, btf_id);
+	st_ops_desc = bpf_struct_ops_find(btf, btf_id);
 	if (!st_ops_desc) {
 		verbose(env, "attach_btf_id %u is not a supported struct\n",
 			btf_id);
@@ -20111,8 +20122,8 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 	}
 
 	member = &btf_type_member(t)[member_idx];
-	mname = btf_name_by_offset(btf_vmlinux, member->name_off);
-	func_proto = btf_type_resolve_func_ptr(btf_vmlinux, member->type,
+	mname = btf_name_by_offset(btf, member->name_off);
+	func_proto = btf_type_resolve_func_ptr(btf, member->type,
 					       NULL);
 	if (!func_proto) {
 		verbose(env, "attach to invalid member %s(@idx %u) of struct %s\n",
@@ -20805,6 +20816,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
 		env->prog->expected_attach_type = 0;
 
 	*prog = env->prog;
+
+	module_put(env->attach_btf_mod);
 err_unlock:
 	if (!is_priv)
 		mutex_unlock(&bpf_verifier_lock);
tools/include/uapi/linux/bpf.h +5 -0
index 0f6cdf52b1da..fd20c52606b2 100644
@@ -1398,6 +1398,11 @@ union bpf_attr {
 		 * to using 5 hash functions).
 		 */
 		__u64	map_extra;
+
+		__u32   value_type_btf_obj_fd;	/* fd pointing to a BTF
+						 * type data for
+						 * btf_vmlinux_value_type_id.
+						 */
 	};
 
 	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
Signature · 1 line
2.34.1
#13 lore raw

Re: [PATCH bpf-next v8 06/10] bpf: pass attached BTF to the bpf_struct_ops subsystem

Quoted 6 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Giving a BTF, the bpf_struct_ops knows the right place to look up type info associated with a type ID. This enables a user space program to load a struct_ops object linked to a struct_ops type defined by a module, by providing the module BTF (fd).

This describes about the struct_ops map creation change (by adding value_type_btf_obj_fd)? It could be described more clearly in the commit message, like specify the value_type_btf_obj_fd addition and how it is used in the struct_ops map creation.

Quoted 7 lines

The bpf_prog includes attach_btf in aux which is passed along with the bpf_attr when loading the program. The purpose of attach_btf is to determine the btf type of attach_btf_id. The attach_btf_id is then used to identify the traced function for a trace program. In the case of struct_ops programs, it is used to identify the struct_ops type of the struct_ops object that a program is attached to.

Does attach_btf_obj_fd also work?

[ ... ]

Quoted 14 lines
kernel/bpf/bpf_struct_ops.c +1 -0
index 256516aba632..db2bbba50e38 100644
@@ -694,6 +694,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
  		bpf_jit_uncharge_modmem(PAGE_SIZE);
  	}
  	bpf_map_area_free(st_map->uvalue);
+	btf_put(st_map->st_ops_desc->btf);
  	bpf_map_area_free(st_map);
  }
 
@@ -735,16 +736,31 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
  	const struct btf_type *t, *vt;

kernel/bpf/bpf_struct_ops.c static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) · line 743

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +3 -3
  	struct module *mod = NULL;
  	struct bpf_map *map;
+	struct btf *btf;
  	int ret;
 
-	st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, attr->btf_vmlinux_value_type_id);
-	if (!st_ops_desc)
-		return ERR_PTR(-ENOTSUPP);
+	if (attr->value_type_btf_obj_fd) {
+		/* The map holds btf for its whole life time. */

It took me a while to parse this comment and connect it with the btf_put(st_map->st_ops_desc->btf) in the __bpf_struct_ops_map_free() above.

It is now like "btf" owns "struct_ops_desc" which also stores a pointer pointing back to itself, like "btf->struct_ops_desc->btf". The struct_ops_desc->btf was not initialized by st_map but st_map will increment its refcount much later.

Can btf be directly stored in the st_map->btf instead and map_alloc holds the refcnt of st_map->btf and btf_put(st_map->btf) in map_free?

Quoted 27 lines
kernel/bpf/bpf_struct_ops.c +19 -4
+		btf = btf_get_by_fd(attr->value_type_btf_obj_fd);
+		if (IS_ERR(btf))
+			return ERR_PTR(PTR_ERR(btf));
+
+		if (btf != btf_vmlinux) {
+			mod = btf_try_get_module(btf);
+			if (!mod) {
+				ret = -EINVAL;
+				goto errout;
+			}
+		}
+	} else {
+		btf = btf_vmlinux;
+		btf_get(btf);
+	}
 
-	if (st_ops_desc->btf != btf_vmlinux) {
-		mod = btf_try_get_module(st_ops_desc->btf);
-		if (!mod)
-			return ERR_PTR(-EINVAL);
+	st_ops_desc = bpf_struct_ops_find_value(btf, attr->btf_vmlinux_value_type_id);
+	if (!st_ops_desc) {
+		ret = -ENOTSUPP;
+		goto errout;
  	}
 
  	vt = st_ops_desc->value_type;
#14 lore raw

Re: [PATCH bpf-next v8 06/10] bpf: pass attached BTF to the bpf_struct_ops subsystem

Quoted 9 lines · Martin KaFai Lau
Quoted 6 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Giving a BTF, the bpf_struct_ops knows the right place to look up type info associated with a type ID. This enables a user space program to load a struct_ops object linked to a struct_ops type defined by a module, by providing the module BTF (fd).

This describes about the struct_ops map creation change (by adding value_type_btf_obj_fd)? It could be described more clearly in the commit message, like specify the value_type_btf_obj_fd addition and how it is used in the struct_ops map creation.

I will rephrase this part as the following words.

Every kernel module has its BTF, comprising information on types defined in the module. The BTF fd (attr->value_type_btf_obj_fd) passed from userspace helps the bpf_struct_ops to lookup type information and description of the struct_ops type, which is necessary for parsing the layout of map element values and registering maps. The descriptions are looked up by matching a type id (attr->btf_vmlinux_value_type_id) against bpf_struct_ops_desc(s) defined in a BTF. If a struct_ops type is defined in a module, the bpf_struct_ops needs to know the module BTF to lookup the bpf_struct_ops_desc.

Quoted 10 lines
Quoted 8 lines

The bpf_prog includes attach_btf in aux which is passed along with the bpf_attr when loading the program. The purpose of attach_btf is to determine the btf type of attach_btf_id. The attach_btf_id is then used to identify the traced function for a trace program. In the case of struct_ops programs, it is used to identify the struct_ops type of the struct_ops object that a program is attached to.

Does attach_btf_obj_fd also work?

aux->attach_btf is from attach_btf_obj_fd, being set by bpf_prog_load(). I will rephrase it to make it clear.

The bpf_prog includes attach_btf in aux which is passed along with the bpf_attr (attr->attach_btf_obj_fd) when loading the program.

Quoted 31 lines

[ ... ]

Quoted 15 lines
kernel/bpf/bpf_struct_ops.c +1 -0
index 256516aba632..db2bbba50e38 100644
@@ -694,6 +694,7 @@ static void __bpf_struct_ops_map_free(struct 
 bpf_map *map)
           bpf_jit_uncharge_modmem(PAGE_SIZE);
       }
       bpf_map_area_free(st_map->uvalue);
+    btf_put(st_map->st_ops_desc->btf);
       bpf_map_area_free(st_map);
   }
@@ -735,16 +736,31 @@ static struct bpf_map 
 *bpf_struct_ops_map_alloc(union bpf_attr *attr)
       const struct btf_type *t, *vt;

kernel/bpf/bpf_struct_ops.c static struct bpf_map · line 744

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +3 -3
       struct module *mod = NULL;
       struct bpf_map *map;
+    struct btf *btf;
       int ret;
-    st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, 
 attr->btf_vmlinux_value_type_id);
-    if (!st_ops_desc)
-        return ERR_PTR(-ENOTSUPP);
+    if (attr->value_type_btf_obj_fd) {
+        /* The map holds btf for its whole life time. */

It took me a while to parse this comment and connect it with the btf_put(st_map->st_ops_desc->btf) in the __bpf_struct_ops_map_free() above.

Quoted 3 lines

It is now like "btf" owns "struct_ops_desc" which also stores a pointer pointing back to itself, like "btf->struct_ops_desc->btf". The struct_ops_desc->btf was not initialized by st_map but st_map will increment its refcount much later.

Can btf be directly stored in the st_map->btf instead and map_alloc holds the refcnt of st_map->btf and btf_put(st_map->btf) in map_free?

This topic has been discussed several times.
I will add a pointer to btf in st_map to make
people happy :)

Quoted 29 lines
Quoted 28 lines
kernel/bpf/bpf_struct_ops.c +19 -4
+        btf = btf_get_by_fd(attr->value_type_btf_obj_fd);
+        if (IS_ERR(btf))
+            return ERR_PTR(PTR_ERR(btf));
+
+        if (btf != btf_vmlinux) {
+            mod = btf_try_get_module(btf);
+            if (!mod) {
+                ret = -EINVAL;
+                goto errout;
+            }
+        }
+    } else {
+        btf = btf_vmlinux;
+        btf_get(btf);
+    }
-    if (st_ops_desc->btf != btf_vmlinux) {
-        mod = btf_try_get_module(st_ops_desc->btf);
-        if (!mod)
-            return ERR_PTR(-EINVAL);
+    st_ops_desc = bpf_struct_ops_find_value(btf, 
 attr->btf_vmlinux_value_type_id);
+    if (!st_ops_desc) {
+        ret = -ENOTSUPP;
+        goto errout;
       }
       vt = st_ops_desc->value_type;
#15 lore raw

PATCH v8 7/10 bpf, net: switch to dynamic registration

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Replace the static list of struct_ops types with per-btf struct_ops_tab to enable dynamic registration.

Both bpf_dummy_ops and bpf_tcp_ca now utilize the registration function instead of being listed in bpf_struct_ops_types.h.

---
 include/linux/bpf.h               |  36 ++++++--
 include/linux/btf.h               |   5 +-
 kernel/bpf/bpf_struct_ops.c       | 140 +++++++++---------------------
 kernel/bpf/bpf_struct_ops_types.h |  12 ---
 kernel/bpf/btf.c                  |  41 ++++++++-
 net/bpf/bpf_dummy_struct_ops.c    |  14 ++-
 net/ipv4/bpf_tcp_ca.c             |  16 +++-
 7 files changed, 140 insertions(+), 124 deletions(-)
 delete mode 100644 kernel/bpf/bpf_struct_ops_types.h
include/linux/bpf.h +31 -5
index c993df3cf699..9d7105ff06db 100644
@@ -1644,7 +1644,6 @@ struct bpf_struct_ops_desc {
 #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
 #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
 const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id);
-void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
 bool bpf_struct_ops_get(const void *kdata);
 void bpf_struct_ops_put(const void *kdata);
 int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
@@ -1690,10 +1689,6 @@ static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *
 {
 	return NULL;
 }
-static inline void bpf_struct_ops_init(struct btf *btf,
-				       struct bpf_verifier_log *log)
-{
-}
 static inline bool bpf_try_module_get(const void *data, struct module *owner)
 {
 	return try_module_get(owner);
@@ -3232,4 +3227,35 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
 	return prog->aux->func_idx != 0;
 }
 
+int register_bpf_struct_ops(struct bpf_struct_ops *st_ops);
+
+enum bpf_struct_ops_state {
+	BPF_STRUCT_OPS_STATE_INIT,
+	BPF_STRUCT_OPS_STATE_INUSE,
+	BPF_STRUCT_OPS_STATE_TOBEFREE,
+	BPF_STRUCT_OPS_STATE_READY,
+};
+
+struct bpf_struct_ops_common_value {
+	refcount_t refcnt;
+	enum bpf_struct_ops_state state;
+};
+
+/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is
+ * the map's value exposed to the userspace and its btf-type-id is
+ * stored at the map->btf_vmlinux_value_type_id.
+ *
+ */
+#define DEFINE_STRUCT_OPS_VALUE_TYPE(_name)			\
+extern struct bpf_struct_ops bpf_##_name;			\
+								\
+struct bpf_struct_ops_##_name {					\
+	struct bpf_struct_ops_common_value common;		\
+	struct _name data ____cacheline_aligned_in_smp;		\
+}
+
+extern int bpf_struct_ops_init(struct bpf_struct_ops_desc *st_ops_desc,
+			       struct btf *btf,
+			       struct bpf_verifier_log *log);
+
 #endif /* _LINUX_BPF_H */
include/linux/btf.h +3 -2
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
 #include <uapi/linux/bpf.h>
 
 #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);	\
+		((void)(struct bpf_struct_ops_##type *)0); }
 #define BTF_TYPE_EMIT_ENUM(enum_val) ((void)enum_val)
 
 /* These need to be macros, as the expressions are used in assembler input */
@@ -201,6 +203,7 @@ u32 btf_obj_id(const struct btf *btf);
 bool btf_is_kernel(const struct btf *btf);
 bool btf_is_module(const struct btf *btf);
 struct module *btf_try_get_module(const struct btf *btf);
+struct btf *btf_get_module_btf(const struct module *module);
 u32 btf_nr_types(const struct btf *btf);
 bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
 			   const struct btf_member *m,
@@ -575,8 +578,6 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type
 struct bpf_struct_ops;
 struct bpf_struct_ops_desc;
 
-struct bpf_struct_ops_desc *
-btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops);
 const struct bpf_struct_ops_desc *
 btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
 
kernel/bpf/bpf_struct_ops.c +43 -97

262 lines not on this page · raw message

kernel/bpf/bpf_struct_ops_types.h deleted +0 -12
deleted file mode 100644
index 5678a9ddf817..000000000000
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* internal file - do not include directly */
-
-#ifdef CONFIG_BPF_JIT
-#ifdef CONFIG_NET
-BPF_STRUCT_OPS_TYPE(bpf_dummy_ops)
-#endif
-#ifdef CONFIG_INET
-#include <net/tcp.h>
-BPF_STRUCT_OPS_TYPE(tcp_congestion_ops)
-#endif
-#endif
kernel/bpf/btf.c +38 -3

56 lines not on this page · raw message

net/bpf/bpf_dummy_struct_ops.c +12 -2
index ffa224053a6c..148a5851c4fa 100644
@@ -7,7 +7,7 @@
 #include <linux/bpf.h>
 #include <linux/btf.h>
 
-extern struct bpf_struct_ops bpf_bpf_dummy_ops;
+static struct bpf_struct_ops bpf_bpf_dummy_ops;
 
 /* A common type for test_N with return value in bpf_dummy_ops */
 typedef int (*dummy_ops_test_ret_fn)(struct bpf_dummy_ops_state *state, ...);
@@ -223,11 +223,13 @@ static int bpf_dummy_reg(void *kdata)
 	return -EOPNOTSUPP;
 }
 
+DEFINE_STRUCT_OPS_VALUE_TYPE(bpf_dummy_ops);
+
 static void bpf_dummy_unreg(void *kdata)
 {
 }
 
-struct bpf_struct_ops bpf_bpf_dummy_ops = {
+static struct bpf_struct_ops bpf_bpf_dummy_ops = {
 	.verifier_ops = &bpf_dummy_verifier_ops,
 	.init = bpf_dummy_init,
 	.check_member = bpf_dummy_ops_check_member,
@@ -235,4 +237,12 @@ struct bpf_struct_ops bpf_bpf_dummy_ops = {
 	.reg = bpf_dummy_reg,
 	.unreg = bpf_dummy_unreg,
 	.name = "bpf_dummy_ops",
+	.owner = THIS_MODULE,
 };
+
+static int __init bpf_dummy_struct_ops_init(void)
+{
+	BTF_STRUCT_OPS_TYPE_EMIT(bpf_dummy_ops);
+	return register_bpf_struct_ops(&bpf_bpf_dummy_ops);
+}
+late_initcall(bpf_dummy_struct_ops_init);
net/ipv4/bpf_tcp_ca.c +13 -3
index 3c8b76578a2a..b36a19274e5b 100644
@@ -12,7 +12,7 @@
 #include <net/bpf_sk_storage.h>
 
 /* "extern" is to avoid sparse warning.  It is only used in bpf_struct_ops.c. */
-extern struct bpf_struct_ops bpf_tcp_congestion_ops;
+static struct bpf_struct_ops bpf_tcp_congestion_ops;
 
 static u32 unsupported_ops[] = {
 	offsetof(struct tcp_congestion_ops, get_info),
@@ -277,7 +277,9 @@ static int bpf_tcp_ca_validate(void *kdata)
 	return tcp_validate_congestion_control(kdata);
 }
 
-struct bpf_struct_ops bpf_tcp_congestion_ops = {
+DEFINE_STRUCT_OPS_VALUE_TYPE(tcp_congestion_ops);
+
+static struct bpf_struct_ops bpf_tcp_congestion_ops = {
 	.verifier_ops = &bpf_tcp_ca_verifier_ops,
 	.reg = bpf_tcp_ca_reg,
 	.unreg = bpf_tcp_ca_unreg,
@@ -287,10 +289,18 @@ struct bpf_struct_ops bpf_tcp_congestion_ops = {
 	.init = bpf_tcp_ca_init,
 	.validate = bpf_tcp_ca_validate,
 	.name = "tcp_congestion_ops",
+	.owner = THIS_MODULE,
 };
 
 static int __init bpf_tcp_ca_kfunc_init(void)
 {
-	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &bpf_tcp_ca_kfunc_set);
+	int ret;
+
+	BTF_STRUCT_OPS_TYPE_EMIT(tcp_congestion_ops);
+
+	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &bpf_tcp_ca_kfunc_set);
+	ret = ret ?: register_bpf_struct_ops(&bpf_tcp_congestion_ops);
+
+	return ret;
 }
 late_initcall(bpf_tcp_ca_kfunc_init);
Signature · 1 line
2.34.1
#16 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 85 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Replace the static list of struct_ops types with per-btf struct_ops_tab to enable dynamic registration.

Both bpf_dummy_ops and bpf_tcp_ca now utilize the registration function instead of being listed in bpf_struct_ops_types.h.

---
  include/linux/bpf.h               |  36 ++++++--
  include/linux/btf.h               |   5 +-
  kernel/bpf/bpf_struct_ops.c       | 140 +++++++++---------------------
  kernel/bpf/bpf_struct_ops_types.h |  12 ---
  kernel/bpf/btf.c                  |  41 ++++++++-
  net/bpf/bpf_dummy_struct_ops.c    |  14 ++-
  net/ipv4/bpf_tcp_ca.c             |  16 +++-
  7 files changed, 140 insertions(+), 124 deletions(-)

delete mode 100644 kernel/bpf/bpf_struct_ops_types.h

include/linux/bpf.h +31 -5
index c993df3cf699..9d7105ff06db 100644
@@ -1644,7 +1644,6 @@ struct bpf_struct_ops_desc {
  #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
  #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
  const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id);
-void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
  bool bpf_struct_ops_get(const void *kdata);
  void bpf_struct_ops_put(const void *kdata);
  int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
@@ -1690,10 +1689,6 @@ static inline const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *
  {
  	return NULL;
  }
-static inline void bpf_struct_ops_init(struct btf *btf,
-				       struct bpf_verifier_log *log)
-{
-}
  static inline bool bpf_try_module_get(const void *data, struct module *owner)
  {
  	return try_module_get(owner);
@@ -3232,4 +3227,35 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
  	return prog->aux->func_idx != 0;
  }
 
+int register_bpf_struct_ops(struct bpf_struct_ops *st_ops);
+
+enum bpf_struct_ops_state {
+	BPF_STRUCT_OPS_STATE_INIT,
+	BPF_STRUCT_OPS_STATE_INUSE,
+	BPF_STRUCT_OPS_STATE_TOBEFREE,
+	BPF_STRUCT_OPS_STATE_READY,
+};
+
+struct bpf_struct_ops_common_value {
+	refcount_t refcnt;
+	enum bpf_struct_ops_state state;
+};
+
+/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is
+ * the map's value exposed to the userspace and its btf-type-id is
+ * stored at the map->btf_vmlinux_value_type_id.
+ *
+ */
+#define DEFINE_STRUCT_OPS_VALUE_TYPE(_name)			\
+extern struct bpf_struct_ops bpf_##_name;			\
+								\
+struct bpf_struct_ops_##_name {					\
+	struct bpf_struct_ops_common_value common;		\
+	struct _name data ____cacheline_aligned_in_smp;		\
+}
+
+extern int bpf_struct_ops_init(struct bpf_struct_ops_desc *st_ops_desc,
+			       struct btf *btf,
+			       struct bpf_verifier_log *log);
+
  #endif /* _LINUX_BPF_H */
include/linux/btf.h +0 -0
index a8813605f2f6..954536431e0b 100644

include/linux/btf.h line 15

Quoted 4 lines
include/linux/btf.h +1 -0
@@ -12,6 +12,8 @@
  #include <uapi/linux/bpf.h>
 
  #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);	\

((void)(struct type *)0); is new. Why is it needed?

Quoted 35 lines
include/linux/btf.h +2 -2
+		((void)(struct bpf_struct_ops_##type *)0); }
  #define BTF_TYPE_EMIT_ENUM(enum_val) ((void)enum_val)
 
  /* These need to be macros, as the expressions are used in assembler input */
@@ -201,6 +203,7 @@ u32 btf_obj_id(const struct btf *btf);
  bool btf_is_kernel(const struct btf *btf);
  bool btf_is_module(const struct btf *btf);
  struct module *btf_try_get_module(const struct btf *btf);
+struct btf *btf_get_module_btf(const struct module *module);
  u32 btf_nr_types(const struct btf *btf);
  bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
  			   const struct btf_member *m,
@@ -575,8 +578,6 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type
  struct bpf_struct_ops;
  struct bpf_struct_ops_desc;
 
-struct bpf_struct_ops_desc *
-btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops);
  const struct bpf_struct_ops_desc *
  btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);
 
kernel/bpf/bpf_struct_ops.c +0 -6
index db2bbba50e38..f3ec72be9c63 100644
@@ -13,21 +13,8 @@
  #include <linux/btf_ids.h>
  #include <linux/rcupdate_wait.h>
 
-enum bpf_struct_ops_state {
-	BPF_STRUCT_OPS_STATE_INIT,
-	BPF_STRUCT_OPS_STATE_INUSE,
-	BPF_STRUCT_OPS_STATE_TOBEFREE,
-	BPF_STRUCT_OPS_STATE_READY,
-};

kernel/bpf/bpf_struct_ops.c line 17

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +1 -8
-
-struct bpf_struct_ops_common_value {
-	refcount_t refcnt;
-	enum bpf_struct_ops_state state;
-};
-#define BPF_STRUCT_OPS_COMMON_VALUE struct bpf_struct_ops_common_value common
-
  struct bpf_struct_ops_value {
-	BPF_STRUCT_OPS_COMMON_VALUE;
+	struct bpf_struct_ops_common_value common;

This cleanup is good. It should have been done together in patch 5 instead when refcnt and state were grouped into a new 'struct bpf_struct_ops_common_value'.

Quoted 40 lines
kernel/bpf/bpf_struct_ops.c +0 -29
  	char data[] ____cacheline_aligned_in_smp;
  };
 
@@ -72,35 +59,6 @@ static DEFINE_MUTEX(update_mutex);
  #define VALUE_PREFIX "bpf_struct_ops_"
  #define VALUE_PREFIX_LEN (sizeof(VALUE_PREFIX) - 1)
 
-/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is
- * the map's value exposed to the userspace and its btf-type-id is
- * stored at the map->btf_vmlinux_value_type_id.
- *
- */
-#define BPF_STRUCT_OPS_TYPE(_name)				\
-extern struct bpf_struct_ops bpf_##_name;			\
-								\
-struct bpf_struct_ops_##_name {						\
-	BPF_STRUCT_OPS_COMMON_VALUE;				\
-	struct _name data ____cacheline_aligned_in_smp;		\
-};
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-
-enum {
-#define BPF_STRUCT_OPS_TYPE(_name) BPF_STRUCT_OPS_TYPE_##_name,
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-	__NR_BPF_STRUCT_OPS_TYPE,
-};
-
-static struct bpf_struct_ops_desc bpf_struct_ops[] = {
-#define BPF_STRUCT_OPS_TYPE(_name)				\
-	[BPF_STRUCT_OPS_TYPE_##_name] = { .st_ops = &bpf_##_name },
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-};
-
  const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = {
  };
 

kernel/bpf/bpf_struct_ops.c const struct bpf_prog_ops bpf_struct_ops_prog_ops = { · line 73

Quoted 8 lines
kernel/bpf/bpf_struct_ops.c +3 -2
@@ -110,13 +68,22 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops = {
  #endif
  };
 
-static const struct btf_type *module_type;
-static const struct btf_type *common_value_type;
+BTF_ID_LIST(st_ops_ids)
+BTF_ID(struct, module)
+BTF_ID(struct, bpf_struct_ops_common_value)

This should have been done in a separated patch immediately after patch 1. The patch 7 has unrelated changes/cleanups like this and the above BPF_STRUCT_OPS_COMMON_VALUE which could have been done earlier as preparation patches instead of packing them together with the main change here: "switch to dynamic registration". The commit message for the BTF_ID_LIST changes could be like: "A preparation to completely retire the bpf_struct_ops_init() function in the latter patch...".

kernel/bpf/bpf_struct_ops.c const struct bpf_prog_ops bpf_struct_ops_prog_ops = { · line 77

Quoted 4 lines
kernel/bpf/bpf_struct_ops.c +4 -0
+
+enum {
+	idx_module_id,
+	idx_st_ops_common_value_id,

nit. upper case to stay consistent with other similar usages.

Quoted 2 lines
kernel/bpf/bpf_struct_ops.c +2 -0
+};
+

[ ... ]

kernel/bpf/bpf_struct_ops.c

Quoted 8 lines
kernel/bpf/bpf_struct_ops.c +8 -0
+int register_bpf_struct_ops(struct bpf_struct_ops *st_ops)
+{
+	struct bpf_struct_ops_desc *desc;
+	struct bpf_verifier_log *log;
+	struct btf *btf;
+	int err = 0;
+
+	if (st_ops == NULL)

NULL check is not needed. caller will never do that. If it really wanted to try, other values would have similar effect.

Quoted 11 lines
kernel/bpf/bpf_struct_ops.c +11 -0
+		return -EINVAL;
+
+	btf = btf_get_module_btf(st_ops->owner);
+	if (!btf)
+		return -EINVAL;
+
+	log = kzalloc(sizeof(*log), GFP_KERNEL | __GFP_NOWARN);
+	if (!log) {
+		err = -ENOMEM;
+		goto errout;
+	}

kernel/bpf/bpf_struct_ops.c

Quoted 10 lines
kernel/bpf/bpf_struct_ops.c +10 -0
+
+	log->level = BPF_LOG_KERNEL;
+
+	desc = btf_add_struct_ops(btf, st_ops);
+	if (IS_ERR(desc)) {
+		err = PTR_ERR(desc);
+		goto errout;
+	}
+
+	err = bpf_struct_ops_init(desc, btf, log);

When bpf_struct_ops_init() returns err, desc is in btf_struct_ops_tab but it is in an uninitialized state. May be do the bpf_struct_ops_init() in btf_add_struct_ops() and only increment struct_ops_tab->cnt when everything is correct.

Quoted 8 lines
kernel/bpf/bpf_struct_ops.c +8 -0
+
+errout:
+	kfree(log);
+	btf_put(btf);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(register_bpf_struct_ops);
#17 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 84 lines · Martin KaFai Lau
Quoted 83 lines · On 10/30/23 12:28 PM, [email protected] wrote:

From: Kui-Feng Lee <[email protected]>

Replace the static list of struct_ops types with per-btf struct_ops_tab to enable dynamic registration.

Both bpf_dummy_ops and bpf_tcp_ca now utilize the registration function instead of being listed in bpf_struct_ops_types.h.

---
  include/linux/bpf.h               |  36 ++++++-- include/linux/btf.h               |   5 +- kernel/bpf/bpf_struct_ops.c       | 140 +++++++++--------------------- kernel/bpf/bpf_struct_ops_types.h |  12 --- kernel/bpf/btf.c                  |  41 ++++++++- net/bpf/bpf_dummy_struct_ops.c    |  14 ++- net/ipv4/bpf_tcp_ca.c             |  16 +++-

  7 files changed, 140 insertions(+), 124 deletions(-)

  delete mode 100644 kernel/bpf/bpf_struct_ops_types.h

include/linux/bpf.h +0 -5
index c993df3cf699..9d7105ff06db 100644
@@ -1644,7 +1644,6 @@ struct bpf_struct_ops_desc {
   #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
   #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
   const struct bpf_struct_ops_desc *bpf_struct_ops_find(struct btf *btf, u32 type_id);
-void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log);
   bool bpf_struct_ops_get(const void *kdata);
   void bpf_struct_ops_put(const void *kdata);
   int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
@@ -1690,10 +1689,6 @@ static inline const struct bpf_struct_ops_desc 
 *bpf_struct_ops_find(struct btf *
   {
       return NULL;
   }
-static inline void bpf_struct_ops_init(struct btf *btf,
-                       struct bpf_verifier_log *log)
-{
-}
   static inline bool bpf_try_module_get(const void *data, struct module *owner)
   {

      return try_module_get(owner);

include/linux/bpf.h +31 -0
@@ -3232,4 +3227,35 @@ static inline bool bpf_is_subprog(const struct 
 bpf_prog *prog)
       return prog->aux->func_idx != 0;
   }
+int register_bpf_struct_ops(struct bpf_struct_ops *st_ops);
+
+enum bpf_struct_ops_state {
+    BPF_STRUCT_OPS_STATE_INIT,
+    BPF_STRUCT_OPS_STATE_INUSE,
+    BPF_STRUCT_OPS_STATE_TOBEFREE,
+    BPF_STRUCT_OPS_STATE_READY,
+};
+
+struct bpf_struct_ops_common_value {
+    refcount_t refcnt;
+    enum bpf_struct_ops_state state;
+};
+
+/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is
+ * the map's value exposed to the userspace and its btf-type-id is
+ * stored at the map->btf_vmlinux_value_type_id.
+ *
+ */
+#define DEFINE_STRUCT_OPS_VALUE_TYPE(_name)            \
+extern struct bpf_struct_ops bpf_##_name;            \
+                                \
+struct bpf_struct_ops_##_name {                    \
+    struct bpf_struct_ops_common_value common;        \
+    struct _name data ____cacheline_aligned_in_smp;        \
+}
+
+extern int bpf_struct_ops_init(struct bpf_struct_ops_desc *st_ops_desc,
+                   struct btf *btf,
+                   struct bpf_verifier_log *log);
+
   #endif /* _LINUX_BPF_H */
include/linux/btf.h +0 -0
index a8813605f2f6..954536431e0b 100644
Quoted 8 lines

include/linux/btf.h line 14

Quoted 6 lines
include/linux/btf.h +1 -0
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module. For example, modules usually don't use value types of struct_ops directly.

Quoted 38 lines
Quoted 38 lines
include/linux/btf.h +2 -0
+        ((void)(struct bpf_struct_ops_##type *)0); }
   #define BTF_TYPE_EMIT_ENUM(enum_val) ((void)enum_val)
   /* These need to be macros, as the expressions are used in assembler input */
@@ -201,6 +203,7 @@ u32 btf_obj_id(const struct btf *btf);
   bool btf_is_kernel(const struct btf *btf);
   bool btf_is_module(const struct btf *btf);
   struct module *btf_try_get_module(const struct btf *btf);
+struct btf *btf_get_module_btf(const struct module *module);
   u32 btf_nr_types(const struct btf *btf);
   bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
                  const struct btf_member *m,
@@ -575,8 +578,6 @@ static inline bool btf_type_is_struct_ptr(struct 

btf *btf, const struct btf_type
  struct bpf_struct_ops;
  struct bpf_struct_ops_desc;

include/linux/btf.h +0 -2
-struct bpf_struct_ops_desc *
-btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops);

  const struct bpf_struct_ops_desc * btf_get_struct_ops(struct btf *btf, u32 *ret_cnt);

kernel/bpf/bpf_struct_ops.c +0 -11
index db2bbba50e38..f3ec72be9c63 100644
@@ -13,21 +13,8 @@
   #include <linux/btf_ids.h>
   #include <linux/rcupdate_wait.h>
-enum bpf_struct_ops_state {
-    BPF_STRUCT_OPS_STATE_INIT,
-    BPF_STRUCT_OPS_STATE_INUSE,
-    BPF_STRUCT_OPS_STATE_TOBEFREE,
-    BPF_STRUCT_OPS_STATE_READY,
-};
-
-struct bpf_struct_ops_common_value {
-    refcount_t refcnt;
-    enum bpf_struct_ops_state state;
-};
Quoted 8 lines
Quoted 6 lines
kernel/bpf/bpf_struct_ops.c +0 -1
-#define BPF_STRUCT_OPS_COMMON_VALUE struct 

bpf_struct_ops_common_value common
-
  struct bpf_struct_ops_value {
-    BPF_STRUCT_OPS_COMMON_VALUE;
+    struct bpf_struct_ops_common_value common;

This cleanup is good. It should have been done together in patch 5 instead when refcnt and state were grouped into a new 'struct bpf_struct_ops_common_value'.

Got it!

Quoted 40 lines
Quoted 40 lines

      char data[] ____cacheline_aligned_in_smp;
  };

kernel/bpf/bpf_struct_ops.c +0 -29
@@ -72,35 +59,6 @@ static DEFINE_MUTEX(update_mutex);
   #define VALUE_PREFIX "bpf_struct_ops_"
   #define VALUE_PREFIX_LEN (sizeof(VALUE_PREFIX) - 1)
-/* bpf_struct_ops_##_name (e.g. bpf_struct_ops_tcp_congestion_ops) is
- * the map's value exposed to the userspace and its btf-type-id is
- * stored at the map->btf_vmlinux_value_type_id.
- *
- */
-#define BPF_STRUCT_OPS_TYPE(_name)                \
-extern struct bpf_struct_ops bpf_##_name;            \
-                                \
-struct bpf_struct_ops_##_name {                        \
-    BPF_STRUCT_OPS_COMMON_VALUE;                \
-    struct _name data ____cacheline_aligned_in_smp;        \
-};
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-
-enum {
-#define BPF_STRUCT_OPS_TYPE(_name) BPF_STRUCT_OPS_TYPE_##_name,
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-    __NR_BPF_STRUCT_OPS_TYPE,
-};
-
-static struct bpf_struct_ops_desc bpf_struct_ops[] = {
-#define BPF_STRUCT_OPS_TYPE(_name)                \
-    [BPF_STRUCT_OPS_TYPE_##_name] = { .st_ops = &bpf_##_name },
-#include "bpf_struct_ops_types.h"
-#undef BPF_STRUCT_OPS_TYPE
-};
-
   const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = {
   };
@@ -110,13 +68,22 @@ const struct bpf_prog_ops bpf_struct_ops_prog_ops 
 = {
   #endif
Quoted 9 lines

kernel/bpf/bpf_struct_ops.c const struct bpf_prog_ops bpf_struct_ops_prog_ops = { · line 73

Quoted 6 lines
kernel/bpf/bpf_struct_ops.c +3 -2
   };
-static const struct btf_type *module_type;
-static const struct btf_type *common_value_type;
+BTF_ID_LIST(st_ops_ids)
+BTF_ID(struct, module)
+BTF_ID(struct, bpf_struct_ops_common_value)

This should have been done in a separated patch immediately after patch 1. The patch 7 has unrelated changes/cleanups like this and the above BPF_STRUCT_OPS_COMMON_VALUE which could have been done earlier as preparation patches instead of packing them together with the main change here: "switch to dynamic registration". The commit message for the BTF_ID_LIST changes could be like: "A preparation to completely retire the bpf_struct_ops_init() function in the latter patch...".

Got it!

Quoted 13 lines

kernel/bpf/bpf_struct_ops.c const struct bpf_prog_ops bpf_struct_ops_prog_ops = { · line 77

Quoted 4 lines
kernel/bpf/bpf_struct_ops.c +4 -0
+
+enum {
+    idx_module_id,
+    idx_st_ops_common_value_id,

nit. upper case to stay consistent with other similar usages.

Quoted 2 lines
kernel/bpf/bpf_struct_ops.c +2 -0
+};
+

[ ... ]

Quoted 10 lines

kernel/bpf/bpf_struct_ops.c

Quoted 8 lines
kernel/bpf/bpf_struct_ops.c +8 -0
+int register_bpf_struct_ops(struct bpf_struct_ops *st_ops)
+{
+    struct bpf_struct_ops_desc *desc;
+    struct bpf_verifier_log *log;
+    struct btf *btf;
+    int err = 0;
+
+    if (st_ops == NULL)

NULL check is not needed. caller will never do that. If it really wanted to try, other values would have similar effect.

Ok!

Quoted 16 lines
Quoted 16 lines

+        return -EINVAL;

kernel/bpf/bpf_struct_ops.c +14 -0
+
+    btf = btf_get_module_btf(st_ops->owner);
+    if (!btf)
+        return -EINVAL;
+
+    log = kzalloc(sizeof(*log), GFP_KERNEL | __GFP_NOWARN);
+    if (!log) {
+        err = -ENOMEM;
+        goto errout;
+    }
+
+    log->level = BPF_LOG_KERNEL;
+
+    desc = btf_add_struct_ops(btf, st_ops);
Quoted 8 lines
Quoted 6 lines

+    if (IS_ERR(desc)) {
+        err = PTR_ERR(desc);
+        goto errout;
+    }
+
+    err = bpf_struct_ops_init(desc, btf, log);

When bpf_struct_ops_init() returns err, desc is in btf_struct_ops_tab but it is in an uninitialized state. May be do the bpf_struct_ops_init() in btf_add_struct_ops() and only increment struct_ops_tab->cnt when everything is correct.

agree

Quoted 11 lines
Quoted 9 lines
kernel/bpf/bpf_struct_ops.c +8 -0
+
+errout:
+    kfree(log);
+    btf_put(btf);
+
+    return err;
+}
+EXPORT_SYMBOL_GPL(register_bpf_struct_ops);
#18 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 11 lines · Kui-Feng Lee
Quoted 10 lines

include/linux/btf.h line 14

Quoted 8 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

#19 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 12 lines · Martin KaFai Lau
Quoted 11 lines · Kui-Feng Lee
Quoted 10 lines

include/linux/btf.h line 14

Quoted 8 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 6 lines
Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line, the module developer will fail to load a struct_ops map of the dummy
type. This line is added to avoid this awful situation.

#20 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

In reply to Martin KaFai Lau
Quoted 12 lines · Martin KaFai Lau
Quoted 11 lines · Kui-Feng Lee
Quoted 10 lines

include/linux/btf.h line 14

Quoted 8 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 6 lines
Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line, the module developer will fail to load a struct_ops map of the dummy
type. This line is added to avoid this awful situation.

#21 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 15 lines · Kui-Feng Lee
Quoted 12 lines · Martin KaFai Lau
Quoted 11 lines · Kui-Feng Lee
Quoted 10 lines

include/linux/btf.h line 14

Quoted 8 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line,

Other than bpf_dummy_ops, after reg(), the struct_ops->func() must be used somewhere in the kernel or module. Like tcp must be using the tcp_congestion_ops after reg(). bpf_dummy_ops is very special and probably should be moved out to bpf_testmod somehow but this is for later. Even bpf_dummy_ops does not have an issue now. Why it is needed after the kmod support change?

or it is a preemptive addition to be future proof only?

Addition is fine if it is required to work. I am trying to understand why this new addition is needed after the kmod support change. The reason why this is needed after the kmod support change is not obvious from looking at the code. The commit message didn't mention why and what broke after this kmod change. If someone wants to clean it up a few months later, we will need to figure out why it was added in the first place.

Quoted 3 lines

the module developer will fail to load a struct_ops map of the dummy type. This line is added to avoid this awful situation.

#22 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 23 lines · Martin KaFai Lau
Quoted 22 lines · Kui-Feng Lee
Quoted 19 lines · Martin KaFai Lau
Quoted 12 lines · Kui-Feng Lee
Quoted 11 lines

include/linux/btf.h line 15

Quoted 9 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type 
 *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

Quoted 8 lines
Quoted 2 lines

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line,

Other than bpf_dummy_ops, after reg(), the struct_ops->func() must be used somewhere in the kernel or module. Like tcp must be using the tcp_congestion_ops after reg(). bpf_dummy_ops is very special and probably should be moved out to bpf_testmod somehow but this is for later. Even bpf_dummy_ops does not have an issue now. Why it is needed after the kmod support change?

or it is a preemptive addition to be future proof only?

Addition is fine if it is required to work. I am trying to understand why this new addition is needed after the kmod support change. The reason why this is needed after the kmod support change is not obvious from looking at the code. The commit message didn't mention why and what broke after this kmod change. If someone wants to clean it up a few months later, we will need to figure out why it was added in the first place.

It is a future proof.
What do you think if I add a comment in the code?

Quoted 6 lines
Quoted 5 lines

the module developer will fail to load a struct_ops map of the dummy type. This line is added to avoid this awful situation.

#23 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 30 lines · Kui-Feng Lee
Quoted 27 lines · Martin KaFai Lau
Quoted 15 lines · Kui-Feng Lee
Quoted 12 lines · Martin KaFai Lau
Quoted 11 lines · Kui-Feng Lee
Quoted 10 lines

include/linux/btf.h line 14

Quoted 8 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line,

Other than bpf_dummy_ops, after reg(), the struct_ops->func() must be used somewhere in the kernel or module. Like tcp must be using the tcp_congestion_ops after reg(). bpf_dummy_ops is very special and probably should be moved out to bpf_testmod somehow but this is for later. Even bpf_dummy_ops does not have an issue now. Why it is needed after the kmod support change?

Quoted 7 lines
Quoted 3 lines

or it is a preemptive addition to be future proof only?

Addition is fine if it is required to work. I am trying to understand why this new addition is needed after the kmod support change. The reason why this is needed after the kmod support change is not obvious from looking at the code. The commit message didn't mention why and what broke after this kmod change. If someone wants to clean it up a few months later, we will need to figure out why it was added in the first place.

It is a future proof.
What do you think if I add a comment in the code?

If it is not required to work, I prefer not adding it to avoid confusion and avoid future cleanup temptation. Even the artificial bpf_dummy_ops does not need it, so not enough reason to introduce this code redundancy.

Switch topic.
While we are on a new macro topic, I think a new macro will be useful to emit the value type and register_bpf_struct_ops together. wdyt?

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

the module developer will fail to load a struct_ops map of the dummy type. This line is added to avoid this awful situation.

#24 lore raw

Re: [PATCH bpf-next v8 07/10] bpf, net: switch to dynamic registration

Quoted 34 lines · Martin KaFai Lau
Quoted 33 lines · Kui-Feng Lee
Quoted 30 lines · Martin KaFai Lau
Quoted 16 lines · Kui-Feng Lee
Quoted 13 lines · Martin KaFai Lau
Quoted 12 lines · Kui-Feng Lee
Quoted 11 lines

include/linux/btf.h line 15

Quoted 9 lines
include/linux/btf.h +1 -0
index a8813605f2f6..954536431e0b 100644
@@ -12,6 +12,8 @@
   #include <uapi/linux/bpf.h>
   #define BTF_TYPE_EMIT(type) ((void)(type *)0)
+#define BTF_STRUCT_OPS_TYPE_EMIT(type) {((void)(struct type 
 *)0);    \

((void)(struct type *)0); is new. Why is it needed?

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

This is a trick of BTF to force compiler generate type info for the given type. Without trick, compiler may skip these types if these type are not used at all in the module.  For example, modules usually don't use value types of struct_ops directly.

It is not the value type and value type emit is understood. It is the struct_ops type itself and it is new addition in this patchset afaict. The value type emit is in the next line which was cut out from the context here.

I mean both of them are required.
In the case of a dummy implementation, struct_ops type itself properly never being used, only being declared by the module. Without this line,

Other than bpf_dummy_ops, after reg(), the struct_ops->func() must be used somewhere in the kernel or module. Like tcp must be using the tcp_congestion_ops after reg(). bpf_dummy_ops is very special and probably should be moved out to bpf_testmod somehow but this is for later. Even bpf_dummy_ops does not have an issue now. Why it is needed after the kmod support change?

or it is a preemptive addition to be future proof only?

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Addition is fine if it is required to work. I am trying to understand why this new addition is needed after the kmod support change. The reason why this is needed after the kmod support change is not obvious from looking at the code. The commit message didn't mention why and what broke after this kmod change. If someone wants to clean it up a few months later, we will need to figure out why it was added in the first place.

It is a future proof.
What do you think if I add a comment in the code?

If it is not required to work, I prefer not adding it to avoid confusion and avoid future cleanup temptation. Even the artificial bpf_dummy_ops does not need it, so not enough reason to introduce this code redundancy.

Got it!

Quoted 3 lines

Switch topic.
While we are on a new macro topic, I think a new macro will be useful to emit the value type and register_bpf_struct_ops together. wdyt?

Like this?

#define REGISTER_STRUCT_OPS(st_type, st_ops) { \
        BTF_STRUCT_OPS_TYPE_EMIT(st_type);  \
        register_bpf_struct_ops(st_ops); } while(0)
static int bpf_testmod_init(void) {
    ....
    REGISTER_STRUCT_OPS(bpf_testmod_ops, &bpf_bpf_testmod_ops);
    ....
}

or you like something more aggressive

#define REGISTER_STRUCT_OPS(st_type) { \
        BTF_STRUCT_OPS_TYPE_EMIT(st_type);  \
        register_bpf_struct_ops(&bpf_##st_type); } while(0)
Quoted 9 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

the module developer will fail to load a struct_ops map of the dummy type. This line is added to avoid this awful situation.

#25 lore raw

PATCH v8 8/10 libbpf: Find correct module BTFs for struct_ops maps and progs.

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Locate the module BTFs for struct_ops maps and progs and pass them to the kernel. This ensures that the kernel correctly resolves type IDs from the appropriate module BTFs.

For the map of a struct_ops object, the FD of the module BTF is set to bpf_map to keep a reference to the module BTF. The FD is passed to the kernel as value_type_btf_obj_fd when the struct_ops object is loaded.

For a bpf_struct_ops prog, attach_btf_obj_fd of bpf_prog is the FD of a module BTF in the kernel.

---
 tools/lib/bpf/bpf.c    |  4 +++-
 tools/lib/bpf/bpf.h    |  5 ++++-
 tools/lib/bpf/libbpf.c | 50 ++++++++++++++++++++++++++++--------------
 3 files changed, 41 insertions(+), 18 deletions(-)
tools/lib/bpf/bpf.c +3 -1
index 9dc9625651dc..b133acfe08fb 100644
@@ -169,7 +169,8 @@ int bpf_map_create(enum bpf_map_type map_type,
 		   __u32 max_entries,
 		   const struct bpf_map_create_opts *opts)
 {
-	const size_t attr_sz = offsetofend(union bpf_attr, map_extra);
+	const size_t attr_sz = offsetofend(union bpf_attr,
+					   value_type_btf_obj_fd);
 	union bpf_attr attr;
 	int fd;
 
@@ -191,6 +192,7 @@ int bpf_map_create(enum bpf_map_type map_type,
 	attr.btf_key_type_id = OPTS_GET(opts, btf_key_type_id, 0);
 	attr.btf_value_type_id = OPTS_GET(opts, btf_value_type_id, 0);
 	attr.btf_vmlinux_value_type_id = OPTS_GET(opts, btf_vmlinux_value_type_id, 0);
+	attr.value_type_btf_obj_fd = OPTS_GET(opts, value_type_btf_obj_fd, 0);
 
 	attr.inner_map_fd = OPTS_GET(opts, inner_map_fd, 0);
 	attr.map_flags = OPTS_GET(opts, map_flags, 0);
tools/lib/bpf/bpf.h +4 -1
index d0f53772bdc0..ffdd81c0196a 100644
@@ -51,8 +51,11 @@ struct bpf_map_create_opts {
 
 	__u32 numa_node;
 	__u32 map_ifindex;
+
+	__u32 value_type_btf_obj_fd;
+	size_t:0;
 };
-#define bpf_map_create_opts__last_field map_ifindex
+#define bpf_map_create_opts__last_field value_type_btf_obj_fd
 
 LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
 			      const char *map_name,
tools/lib/bpf/libbpf.c +34 -16

139 lines not on this page · raw message

Signature · 1 line
2.34.1
#26 lore raw

PATCH v8 9/10 bpf: export btf_ctx_access to modules.

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

The module requires the use of btf_ctx_access() to invoke bpf_tracing_btf_ctx_access() from a module. This function is valuable for implementing validation functions that ensure proper access to ctx.

---
 kernel/bpf/btf.c | 1 +
 1 file changed, 1 insertion(+)
kernel/bpf/btf.c +1 -0
index 57d2114927e4..38f0611ee2f2 100644
@@ -6139,6 +6139,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
 		__btf_name_by_offset(btf, t->name_off));
 	return true;
 }
+EXPORT_SYMBOL_GPL(btf_ctx_access);
 
 enum bpf_struct_walk_result {
 	/* < 0 error */
Signature · 1 line
2.34.1
#27 lore raw

PATCH v8 10/10 selftests/bpf: test case for register_bpf_struct_ops().

In reply to [email protected]

From: Kui-Feng Lee <[email protected]>

Create a new struct_ops type called bpf_testmod_ops within the bpf_testmod module. When a struct_ops object is registered, the bpf_testmod module will invoke test_2 from the module.

---
 tools/testing/selftests/bpf/Makefile          |  4 +-
 .../selftests/bpf/bpf_testmod/bpf_testmod.c   | 59 +++++++++++++++++++
 .../selftests/bpf/bpf_testmod/bpf_testmod.h   |  5 ++
 .../bpf/prog_tests/test_struct_ops_module.c   | 39 ++++++++++++
 .../selftests/bpf/progs/struct_ops_module.c   | 30 ++++++++++
 tools/testing/selftests/bpf/testing_helpers.c | 35 +++++++++++
 6 files changed, 171 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
 create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_module.c
tools/testing/selftests/bpf/Makefile +3 -1
index 9c27b67bc7b1..1b02e3fdaf21 100644
@@ -458,7 +458,8 @@ TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o,	\
 				 $$(notdir $$(wildcard $(TRUNNER_TESTS_DIR)/*.c)))
 TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,		\
 				 $$(filter %.c,$(TRUNNER_EXTRA_SOURCES)))
-TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES))
+TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES)) \
+	$$(TRUNNER_OUTPUT)$(if $$(TRUNNER_OUTPUT),/)rcu_tasks_trace_gp.skel.h
 TRUNNER_TESTS_HDR := $(TRUNNER_TESTS_DIR)/tests.h
 TRUNNER_BPF_SRCS := $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c))
 TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.bpf.o, $$(TRUNNER_BPF_SRCS))
@@ -724,6 +725,7 @@ $(OUTPUT)/uprobe_multi: uprobe_multi.c
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
 
+
 EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)	\
 	prog_tests/tests.h map_tests/tests.h verifier/tests.h		\
 	feature bpftool							\
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +59 -0

81 lines not on this page · raw message

tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h +5 -0
index f32793efe095..ca5435751c79 100644
@@ -28,4 +28,9 @@ struct bpf_iter_testmod_seq {
 	int cnt;
 };
 
+struct bpf_testmod_ops {
+	int (*test_1)(void);
+	int (*test_2)(int a, int b);
+};
+
 #endif /* _BPF_TESTMOD_H */
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +39 -0
new file mode 100644
index 000000000000..3a00dc294583
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+
+static void test_regular_load(void)
+{
+	struct struct_ops_module *skel;
+	struct bpf_link *link;
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	int err;
+
+	skel = struct_ops_module__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+		return;
+	err = struct_ops_module__load(skel);
+	if (!ASSERT_OK(err, "struct_ops_module_load"))
+		return;
+
+	link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+	ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+	/* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */
+	ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");
+
+	bpf_link__destroy(link);
+
+	struct_ops_module__destroy(skel);
+}
+
+void serial_test_struct_ops_module(void)
+{
+	if (test__start_subtest("regular_load"))
+		test_regular_load();
+}
+
tools/testing/selftests/bpf/progs/struct_ops_module.c new file +30 -0
new file mode 100644
index 000000000000..cb305d04342f
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "../bpf_testmod/bpf_testmod.h"
+
+char _license[] SEC("license") = "GPL";
+
+int test_2_result = 0;
+
+SEC("struct_ops/test_1")
+int BPF_PROG(test_1)
+{
+	return 0xdeadbeef;
+}
+
+SEC("struct_ops/test_2")
+int BPF_PROG(test_2, int a, int b)
+{
+	test_2_result = a + b;
+	return a + b;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops testmod_1 = {
+	.test_1 = (void *)test_1,
+	.test_2 = (void *)test_2,
+};
+
tools/testing/selftests/bpf/testing_helpers.c +35 -0
index 8d994884c7b4..05870cd62458 100644
@@ -10,6 +10,7 @@
 #include "test_progs.h"
 #include "testing_helpers.h"
 #include <linux/membarrier.h>
+#include "rcu_tasks_trace_gp.skel.h"
 
 int parse_num_list(const char *s, bool **num_set, int *num_set_len)
 {
@@ -380,10 +381,44 @@ int load_bpf_testmod(bool verbose)
 	return 0;
 }
 
+/* This function will trigger call_rcu_tasks_trace() in the kernel */
+static int kern_sync_rcu_tasks_trace(void)
+{
+	struct rcu_tasks_trace_gp *rcu;
+	time_t start;
+	long gp_seq;
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+
+	rcu = rcu_tasks_trace_gp__open_and_load();
+	if (IS_ERR(rcu))
+		return -EFAULT;
+	if (rcu_tasks_trace_gp__attach(rcu))
+		return -EFAULT;
+
+	gp_seq = READ_ONCE(rcu->bss->gp_seq);
+
+	if (bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace),
+				   &opts))
+		return -EFAULT;
+	if (opts.retval != 0)
+		return -EFAULT;
+
+	start = time(NULL);
+	while ((start + 2) > time(NULL) &&
+	       gp_seq == READ_ONCE(rcu->bss->gp_seq))
+		sched_yield();
+
+	rcu_tasks_trace_gp__destroy(rcu);
+
+	return 0;
+}
+
 /*
  * Trigger synchronize_rcu() in kernel.
  */
 int kern_sync_rcu(void)
 {
+	if (kern_sync_rcu_tasks_trace())
+		return -EFAULT;
 	return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0);
 }
Signature · 1 line
2.34.1
#28 lore raw

Re: [PATCH bpf-next v8 10/10] selftests/bpf: test case for register_bpf_struct_ops().

Quoted 33 lines · On 10/30/23 12:28 PM, [email protected] wrote:
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +27 -0
new file mode 100644
index 000000000000..3a00dc294583
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+
+static void test_regular_load(void)
+{
+	struct struct_ops_module *skel;
+	struct bpf_link *link;
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	int err;
+
+	skel = struct_ops_module__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+		return;
+	err = struct_ops_module__load(skel);
+	if (!ASSERT_OK(err, "struct_ops_module_load"))
+		return;
+
+	link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+	ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+	/* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */
+	ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 37

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +10 -0
+
+	bpf_link__destroy(link);
+
+	struct_ops_module__destroy(skel);
+}
+
+void serial_test_struct_ops_module(void)
+{
+	if (test__start_subtest("regular_load"))
+		test_regular_load();

Could it also add some negative tests, e.g. missing 'struct bpf_struct_ops_common_value', reg() when the module is gone...etc.

[ ... ]

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 39

Quoted 2 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +2 -0
+/* This function will trigger call_rcu_tasks_trace() in the kernel */
+static int kern_sync_rcu_tasks_trace(void)

With patch 4, is it still needed?

Quoted 39 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +32 -0
+{
+	struct rcu_tasks_trace_gp *rcu;
+	time_t start;
+	long gp_seq;
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+
+	rcu = rcu_tasks_trace_gp__open_and_load();
+	if (IS_ERR(rcu))
+		return -EFAULT;
+	if (rcu_tasks_trace_gp__attach(rcu))
+		return -EFAULT;
+
+	gp_seq = READ_ONCE(rcu->bss->gp_seq);
+
+	if (bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace),
+				   &opts))
+		return -EFAULT;
+	if (opts.retval != 0)
+		return -EFAULT;
+
+	start = time(NULL);
+	while ((start + 2) > time(NULL) &&
+	       gp_seq == READ_ONCE(rcu->bss->gp_seq))
+		sched_yield();
+
+	rcu_tasks_trace_gp__destroy(rcu);
+
+	return 0;
+}
+
  /*
   * Trigger synchronize_rcu() in kernel.
   */
  int kern_sync_rcu(void)
  {
+	if (kern_sync_rcu_tasks_trace())
+		return -EFAULT;
  	return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0);
  }
#29 lore raw

Re: [PATCH bpf-next v8 10/10] selftests/bpf: test case for register_bpf_struct_ops().

Quoted 47 lines · Martin KaFai Lau
Quoted 33 lines · On 10/30/23 12:28 PM, [email protected] wrote:
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +27 -0
new file mode 100644
index 000000000000..3a00dc294583
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+
+static void test_regular_load(void)
+{
+    struct struct_ops_module *skel;
+    struct bpf_link *link;
+    DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+    int err;
+
+    skel = struct_ops_module__open_opts(&opts);
+    if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+        return;
+    err = struct_ops_module__load(skel);
+    if (!ASSERT_OK(err, "struct_ops_module_load"))
+        return;
+
+    link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+    ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+    /* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */
+    ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 37

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +10 -0
+
+    bpf_link__destroy(link);
+
+    struct_ops_module__destroy(skel);
+}
+
+void serial_test_struct_ops_module(void)
+{
+    if (test__start_subtest("regular_load"))
+        test_regular_load();

Could it also add some negative tests, e.g. missing 'struct bpf_struct_ops_common_value', reg() when the module is gone...etc.

Quoted 6 lines

[ ... ]

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c

Quoted 2 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +2 -0
+/* This function will trigger call_rcu_tasks_trace() in the kernel */
+static int kern_sync_rcu_tasks_trace(void)

With patch 4, is it still needed?

Patch 4 shortens time of holding the module, but it still can happen since bpf_link_put() is performed asynchronously.

Quoted 42 lines
Quoted 41 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +15 -0
+{
+    struct rcu_tasks_trace_gp *rcu;
+    time_t start;
+    long gp_seq;
+    LIBBPF_OPTS(bpf_test_run_opts, opts);
+
+    rcu = rcu_tasks_trace_gp__open_and_load();
+    if (IS_ERR(rcu))
+        return -EFAULT;
+    if (rcu_tasks_trace_gp__attach(rcu))
+        return -EFAULT;
+
+    gp_seq = READ_ONCE(rcu->bss->gp_seq);
+
+    if 

(bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace), +                   &opts))
+        return -EFAULT;
+    if (opts.retval != 0)
+        return -EFAULT;

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +11 -0
+
+    start = time(NULL);
+    while ((start + 2) > time(NULL) &&
+           gp_seq == READ_ONCE(rcu->bss->gp_seq))
+        sched_yield();
+
+    rcu_tasks_trace_gp__destroy(rcu);
+
+    return 0;
+}
+

  /*
   * Trigger synchronize_rcu() in kernel.
   */
  int kern_sync_rcu(void)
  {
+    if (kern_sync_rcu_tasks_trace())
+        return -EFAULT;
      return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); }

#30 lore raw

Re: [PATCH bpf-next v8 10/10] selftests/bpf: test case for register_bpf_struct_ops().

Quoted 52 lines · Kui-Feng Lee
Quoted 49 lines · Martin KaFai Lau
Quoted 33 lines · On 10/30/23 12:28 PM, [email protected] wrote:
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +27 -0
new file mode 100644
index 000000000000..3a00dc294583
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+
+static void test_regular_load(void)
+{
+    struct struct_ops_module *skel;
+    struct bpf_link *link;
+    DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+    int err;
+
+    skel = struct_ops_module__open_opts(&opts);
+    if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+        return;
+    err = struct_ops_module__load(skel);
+    if (!ASSERT_OK(err, "struct_ops_module_load"))
+        return;
+
+    link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+    ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+    /* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */
+    ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 37

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +10 -0
+
+    bpf_link__destroy(link);
+
+    struct_ops_module__destroy(skel);
+}
+
+void serial_test_struct_ops_module(void)
+{
+    if (test__start_subtest("regular_load"))
+        test_regular_load();

Could it also add some negative tests, e.g. missing 'struct bpf_struct_ops_common_value', reg() when the module is gone...etc.

[ ... ]

Quoted 7 lines
Quoted 4 lines

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 39

Quoted 2 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +2 -0
+/* This function will trigger call_rcu_tasks_trace() in the kernel */
+static int kern_sync_rcu_tasks_trace(void)

With patch 4, is it still needed?

Patch 4 shortens time of holding the module, but it still can happen since bpf_link_put() is performed asynchronously.

Is the link pinned to a file that triggers bpf_link_put()? Otherwise, close() should reach bpf_link_put_direct() which is synchronous.

Even if it went through bpf_link_put(), rcu_tasks_trace_gp is very specific to the bpf sleepable tracing prog. Is it the correct one to wait?

Quoted 43 lines
Quoted 43 lines
Quoted 42 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +15 -0
+{
+    struct rcu_tasks_trace_gp *rcu;
+    time_t start;
+    long gp_seq;
+    LIBBPF_OPTS(bpf_test_run_opts, opts);
+
+    rcu = rcu_tasks_trace_gp__open_and_load();
+    if (IS_ERR(rcu))
+        return -EFAULT;
+    if (rcu_tasks_trace_gp__attach(rcu))
+        return -EFAULT;
+
+    gp_seq = READ_ONCE(rcu->bss->gp_seq);
+
+    if 

(bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace), +                   &opts))
+        return -EFAULT;
+    if (opts.retval != 0)
+        return -EFAULT;

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +11 -0
+
+    start = time(NULL);
+    while ((start + 2) > time(NULL) &&
+           gp_seq == READ_ONCE(rcu->bss->gp_seq))
+        sched_yield();
+
+    rcu_tasks_trace_gp__destroy(rcu);
+
+    return 0;
+}
+

  /*
   * Trigger synchronize_rcu() in kernel.
   */
  int kern_sync_rcu(void)
  {
+    if (kern_sync_rcu_tasks_trace())
+        return -EFAULT;
      return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); }

#31 lore raw

Re: [PATCH bpf-next v8 10/10] selftests/bpf: test case for register_bpf_struct_ops().

Quoted 59 lines · Martin KaFai Lau
Quoted 58 lines · Kui-Feng Lee
Quoted 54 lines · Martin KaFai Lau
Quoted 34 lines · On 10/30/23 12:28 PM, [email protected] wrote:
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +26 -0
new file mode 100644
index 000000000000..3a00dc294583
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+
+static void test_regular_load(void)
+{
+    struct struct_ops_module *skel;
+    struct bpf_link *link;
+    DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+    int err;
+
+    skel = struct_ops_module__open_opts(&opts);
+    if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+        return;
+    err = struct_ops_module__load(skel);
+    if (!ASSERT_OK(err, "struct_ops_module_load"))
+        return;
+
+    link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+    ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+    /* test_2() will be called from bpf_dummy_reg() in 

bpf_testmod.c */
+    ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c line 37

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +10 -0
+
+    bpf_link__destroy(link);
+
+    struct_ops_module__destroy(skel);
+}
+
+void serial_test_struct_ops_module(void)
+{
+    if (test__start_subtest("regular_load"))
+        test_regular_load();

Could it also add some negative tests, e.g. missing 'struct bpf_struct_ops_common_value', reg() when the module is gone...etc.

[ ... ]

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c

Quoted 2 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +2 -0
+/* This function will trigger call_rcu_tasks_trace() in the kernel */
+static int kern_sync_rcu_tasks_trace(void)

With patch 4, is it still needed?

Quoted 7 lines
Quoted 2 lines

Patch 4 shortens time of holding the module, but it still can happen since bpf_link_put() is performed asynchronously.

Is the link pinned to a file that triggers bpf_link_put()? Otherwise, close() should reach bpf_link_put_direct() which is synchronous.

Even if it went through bpf_link_put(), rcu_tasks_trace_gp is very specific to the bpf sleepable tracing prog. Is it the correct one to wait?

You are right! We don't test pinned link. I will remove this part.

Quoted 45 lines
Quoted 44 lines
Quoted 44 lines
Quoted 43 lines
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +15 -0
+{
+    struct rcu_tasks_trace_gp *rcu;
+    time_t start;
+    long gp_seq;
+    LIBBPF_OPTS(bpf_test_run_opts, opts);
+
+    rcu = rcu_tasks_trace_gp__open_and_load();
+    if (IS_ERR(rcu))
+        return -EFAULT;
+    if (rcu_tasks_trace_gp__attach(rcu))
+        return -EFAULT;
+
+    gp_seq = READ_ONCE(rcu->bss->gp_seq);
+
+    if 

(bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace), +                   &opts))
+        return -EFAULT;
+    if (opts.retval != 0)
+        return -EFAULT;

tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c new file +11 -0
+
+    start = time(NULL);
+    while ((start + 2) > time(NULL) &&
+           gp_seq == READ_ONCE(rcu->bss->gp_seq))
+        sched_yield();
+
+    rcu_tasks_trace_gp__destroy(rcu);
+
+    return 0;
+}
+

  /*
   * Trigger synchronize_rcu() in kernel.
   */
  int kern_sync_rcu(void)
  {
+    if (kern_sync_rcu_tasks_trace())
+        return -EFAULT;
      return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); }

#32 lore raw
In reply to [email protected]
Quoted 19 lines · On 10/30/23 12:28 PM, [email protected] wrote:

Changes from v5:

- As the 2nd patch, we introduce "bpf_struct_ops_desc". This change involves moving certain members of "bpf_struct_ops" to "bpf_struct_ops_desc", which becomes a part of "btf_struct_ops_tab". This ensures that these members remain accessible even when the owner module of a "bpf_struct_ops" is unloaded.

- Correct the order of arguments when calling
in the 3rd patch.

- Remove the owner argument from bpf_struct_ops_init_one(). Instead,

    callers should fill in st_ops->owner.

- Make sure to hold the owner module when calling bpf_struct_ops_find() and bpf_struct_ops_find_value() in the 6th patch.

Quoted 5 lines

- Merge the functions register_bpf_struct_ops_btf() and register_bpf_struct_ops() into a single function and relocate it to btf.c for better organization and clarity.

- Undo the name modifications made to find_kernel_btf_id() and

The find_kernel_attach_btf_id name change is still in patch 8. tbh, I don't have a strong preference on the name here. Could it be in a separate patch for naming cleanup or at least mention it in the commit message?

#33 lore raw
Quoted 20 lines · Martin KaFai Lau
Quoted 19 lines · On 10/30/23 12:28 PM, [email protected] wrote:

Changes from v5:

  - As the 2nd patch, we introduce "bpf_struct_ops_desc". This change involves moving certain members of "bpf_struct_ops" to "bpf_struct_ops_desc", which becomes a part of "btf_struct_ops_tab". This ensures that these members remain accessible even when the owner module of a "bpf_struct_ops" is unloaded.

  - Correct the order of arguments when calling
     in the 3rd patch.

  - Remove the owner argument from bpf_struct_ops_init_one(). Instead, callers should fill in st_ops->owner.

  - Make sure to hold the owner module when calling bpf_struct_ops_find() and bpf_struct_ops_find_value() in the 6th patch.

Quoted 9 lines
Quoted 5 lines

  - Merge the functions register_bpf_struct_ops_btf() and register_bpf_struct_ops() into a single function and relocate it to btf.c for better organization and clarity.

  - Undo the name modifications made to find_kernel_btf_id() and

The find_kernel_attach_btf_id name change is still in patch 8. tbh, I don't have a strong preference on the name here. Could it be in a separate patch for naming cleanup or at least mention it in the commit message?

Sure! I will remove it.