diff mbox series

[RFC,v8,09/20] bpf: Find special BTF fields in union

Message ID 20240510192412.3297104-10-amery.hung@bytedance.com (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series bpf qdisc | expand

Checks

Context Check Description
netdev/tree_selection success Guessing tree name failed - patch did not apply, async

Commit Message

Amery Hung May 10, 2024, 7:24 p.m. UTC
This patch looks into unions when parsing BTF. While we would like to
support adding a skb to bpf collections, the bpf graph node in sk_buff
will happen to be in a union due to space constraint. Therefore,

Signed-off-by: Amery Hung <amery.hung@bytedance.com>
---
 kernel/bpf/btf.c | 74 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 64 insertions(+), 10 deletions(-)

Comments

Amery Hung May 16, 2024, 11:37 p.m. UTC | #1
The implementation of supporting adding skb to collections is flaky as
Kui-Feng has pointed out in offline discussion. Basically, supporting
special BTF fields in unions needs more care.

I will defer patch 5-12 to another patchset after the first BPF Qdisc
patchset lands. While the performance of qdiscs implemented with the first
series will not be as good, this will make the patchset easier to review.
diff mbox series

Patch

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 37fb6143da79..25a5dc840ac3 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3305,7 +3305,7 @@  static int btf_find_struct(const struct btf *btf, const struct btf_type *t,
 			   u32 off, int sz, enum btf_field_type field_type,
 			   struct btf_field_info *info)
 {
-	if (!__btf_type_is_struct(t))
+	if (!btf_type_is_struct(t))
 		return BTF_FIELD_IGNORE;
 	if (t->size != sz)
 		return BTF_FIELD_IGNORE;
@@ -3497,6 +3497,24 @@  static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask,
 	return type;
 }
 
+static int btf_get_union_field_types(const struct btf *btf, const struct btf_type *u,
+				     u32 field_mask, u32 *seen_mask, int *align, int *sz)
+{
+	int i, field_type, field_types = 0;
+	const struct btf_member *member;
+	const struct btf_type *t;
+
+	for_each_member(i, u, member) {
+		t = btf_type_by_id(btf, member->type);
+		field_type = btf_get_field_type(__btf_name_by_offset(btf, t->name_off),
+						field_mask, seen_mask, align, sz);
+		if (field_type == 0 || field_type == BPF_KPTR_REF)
+			continue;
+		field_types = field_types | field_type;
+	}
+	return field_types;
+}
+
 #undef field_mask_test_name
 
 static int btf_find_struct_field(const struct btf *btf,
@@ -3512,8 +3530,12 @@  static int btf_find_struct_field(const struct btf *btf,
 		const struct btf_type *member_type = btf_type_by_id(btf,
 								    member->type);
 
-		field_type = btf_get_field_type(__btf_name_by_offset(btf, member_type->name_off),
-						field_mask, &seen_mask, &align, &sz);
+		field_type = BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION ?
+			btf_get_union_field_types(btf, member_type, field_mask,
+						  &seen_mask, &align, &sz) :
+			btf_get_field_type(__btf_name_by_offset(btf, member_type->name_off),
+					   field_mask, &seen_mask, &align, &sz);
+
 		if (field_type == 0)
 			continue;
 		if (field_type < 0)
@@ -3521,8 +3543,7 @@  static int btf_find_struct_field(const struct btf *btf,
 
 		off = __btf_member_bit_offset(t, member);
 		if (off % 8)
-			/* valid C code cannot generate such BTF */
-			return -EINVAL;
+			continue;
 		off /= 8;
 		if (off % align)
 			continue;
@@ -3737,6 +3758,20 @@  static int btf_parse_kptr(const struct btf *btf, struct btf_field *field,
 	return ret;
 }
 
+static const struct btf_type *
+btf_find_member_by_name(const struct btf *btf, const struct btf_type *t,
+			const char *member_name)
+{
+	const struct btf_member *member;
+	int i;
+
+	for_each_member(i, t, member) {
+		if (!strcmp(member_name, __btf_name_by_offset(btf, member->name_off)))
+			return btf_type_by_id(btf, member->type);
+	}
+	return NULL;
+}
+
 static int btf_parse_graph_root(struct btf_field *field,
 				struct btf_field_info *info,
 				const char *node_type_name,
@@ -3754,18 +3789,27 @@  static int btf_parse_graph_root(struct btf_field *field,
 	 * verify its type.
 	 */
 	for_each_member(i, t, member) {
-		if (strcmp(info->graph_root.node_name,
-			   __btf_name_by_offset(btf, member->name_off)))
+		const struct btf_type *member_type = btf_type_by_id(btf, member->type);
+
+		if (BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION) {
+			member_type = btf_find_member_by_name(btf, member_type,
+							      info->graph_root.node_name);
+			if (!member_type)
+				continue;
+		} else if (strcmp(info->graph_root.node_name,
+				  __btf_name_by_offset(btf, member->name_off))) {
 			continue;
+		}
+
 		/* Invalid BTF, two members with same name */
 		if (n)
 			return -EINVAL;
-		n = btf_type_by_id(btf, member->type);
+		n = member_type;
 		if (!__btf_type_is_struct(n))
 			return -EINVAL;
 		if (strcmp(node_type_name, __btf_name_by_offset(btf, n->name_off)))
 			return -EINVAL;
-		offset = __btf_member_bit_offset(n, member);
+		offset = __btf_member_bit_offset(member_type, member);
 		if (offset % 8)
 			return -EINVAL;
 		offset /= 8;
@@ -5440,7 +5484,7 @@  btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
 		const struct btf_member *member;
 		struct btf_struct_meta *type;
 		struct btf_record *record;
-		const struct btf_type *t;
+		const struct btf_type *t, *member_type;
 		int j, tab_cnt, id;
 
 		id = btf_is_base_kernel ?
@@ -5462,6 +5506,16 @@  btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
 		cond_resched();
 
 		for_each_member(j, t, member) {
+			member_type = btf_type_by_id(btf, member->type);
+			if (BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION) {
+				const struct btf_member *umember;
+				int k;
+
+				for_each_member(k, member_type, umember) {
+					if (btf_id_set_contains(&aof.set, umember->type))
+						goto parse;
+				}
+			}
 			if (btf_id_set_contains(&aof.set, member->type))
 				goto parse;
 		}