Skip to content

Commit 47d25ca

Browse files
anakryikoKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
libbpf: improve BTF dedup handling of "identical" BTF types
BTF dedup has a strong assumption that compiler with deduplicate identical types within any given compilation unit (i.e., .c file). This property is used when establishing equilvalence of two subgraphs of types. Unfortunately, this property doesn't always holds in practice. We've seen cases of having truly identical structs, unions, array definitions, and, most recently, even pointers to the same type being duplicated within CU. Previously, we mitigated this on a case-by-case basis, adding a few simple heuristics for validating that two BTF types (having two different type IDs) are structurally the same. But this approach scales poorly, and we can have more weird cases come up in the future. So let's take a half-step back, and implement a bit more generic structural equivalence check, recursively. We still limit it to reasonable depth to avoid long reference loops. Depth-wise limiting of potentially cyclical graph isn't great, but as I mentioned below doesn't seem to be detrimental performance-wise. We can always improve this in the future with per-type visited markers, if necessary. Performance-wise this doesn't seem too affect vmlinux BTF dedup, which makes sense because this logic kicks in not so frequently and only if we already established a canonical candidate type match, but suddenly find a different (but probably identical) type. On the other hand, this seems to help to reduce duplication across many kernel modules. In my local test, I had 639 kernel module built. Overall .BTF sections size goes down from 41MB bytes down to 5MB (!), which is pretty impressive for such a straightforward piece of logic added. But it would be nice to validate independently just in case my bash and Python-fu is broken. Signed-off-by: Andrii Nakryiko <[email protected]> Reviewed-by: Alan Maguire <[email protected]>
1 parent 6bce87f commit 47d25ca

File tree

1 file changed

+89
-48
lines changed

1 file changed

+89
-48
lines changed

tools/lib/bpf/btf.c

+89-48
Original file line numberDiff line numberDiff line change
@@ -4356,59 +4356,109 @@ static inline __u16 btf_fwd_kind(struct btf_type *t)
43564356
return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
43574357
}
43584358

4359-
/* Check if given two types are identical ARRAY definitions */
4360-
static bool btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2)
4359+
static bool btf_dedup_identical_types(struct btf_dedup *d, __u32 id1, __u32 id2, int depth)
43614360
{
43624361
struct btf_type *t1, *t2;
4362+
int k1, k2;
4363+
recur:
4364+
if (depth <= 0)
4365+
return false;
43634366

43644367
t1 = btf_type_by_id(d->btf, id1);
43654368
t2 = btf_type_by_id(d->btf, id2);
4366-
if (!btf_is_array(t1) || !btf_is_array(t2))
4369+
4370+
k1 = btf_kind(t1);
4371+
k2 = btf_kind(t2);
4372+
if (k1 != k2)
43674373
return false;
43684374

4369-
return btf_equal_array(t1, t2);
4370-
}
4375+
switch (k1) {
4376+
case BTF_KIND_UNKN: /* VOID */
4377+
return true;
4378+
case BTF_KIND_INT:
4379+
return btf_equal_int_tag(t1, t2);
4380+
case BTF_KIND_ENUM:
4381+
case BTF_KIND_ENUM64:
4382+
return btf_compat_enum(t1, t2);
4383+
case BTF_KIND_FWD:
4384+
case BTF_KIND_FLOAT:
4385+
return btf_equal_common(t1, t2);
4386+
case BTF_KIND_CONST:
4387+
case BTF_KIND_VOLATILE:
4388+
case BTF_KIND_RESTRICT:
4389+
case BTF_KIND_PTR:
4390+
case BTF_KIND_TYPEDEF:
4391+
case BTF_KIND_FUNC:
4392+
case BTF_KIND_TYPE_TAG:
4393+
if (t1->info != t2->info || t1->name_off != t2->name_off)
4394+
return false;
4395+
id1 = t1->type;
4396+
id2 = t2->type;
4397+
goto recur;
4398+
case BTF_KIND_ARRAY: {
4399+
struct btf_array *a1, *a2;
43714400

4372-
/* Check if given two types are identical STRUCT/UNION definitions */
4373-
static bool btf_dedup_identical_structs(struct btf_dedup *d, __u32 id1, __u32 id2)
4374-
{
4375-
const struct btf_member *m1, *m2;
4376-
struct btf_type *t1, *t2;
4377-
int n, i;
4401+
if (!btf_compat_array(t1, t2))
4402+
return false;
43784403

4379-
t1 = btf_type_by_id(d->btf, id1);
4380-
t2 = btf_type_by_id(d->btf, id2);
4404+
a1 = btf_array(t1);
4405+
a2 = btf_array(t1);
43814406

4382-
if (!btf_is_composite(t1) || btf_kind(t1) != btf_kind(t2))
4383-
return false;
4407+
if (a1->index_type != a2->index_type &&
4408+
!btf_dedup_identical_types(d, a1->index_type, a2->index_type, depth - 1))
4409+
return false;
43844410

4385-
if (!btf_shallow_equal_struct(t1, t2))
4386-
return false;
4411+
if (a1->type != a2->type &&
4412+
!btf_dedup_identical_types(d, a1->type, a2->type, depth - 1))
4413+
return false;
43874414

4388-
m1 = btf_members(t1);
4389-
m2 = btf_members(t2);
4390-
for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) {
4391-
if (m1->type != m2->type &&
4392-
!btf_dedup_identical_arrays(d, m1->type, m2->type) &&
4393-
!btf_dedup_identical_structs(d, m1->type, m2->type))
4415+
return true;
4416+
}
4417+
case BTF_KIND_STRUCT:
4418+
case BTF_KIND_UNION: {
4419+
const struct btf_member *m1, *m2;
4420+
int i, n;
4421+
4422+
if (!btf_shallow_equal_struct(t1, t2))
43944423
return false;
4424+
4425+
m1 = btf_members(t1);
4426+
m2 = btf_members(t2);
4427+
for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) {
4428+
if (m1->type == m2->type)
4429+
continue;
4430+
if (!btf_dedup_identical_types(d, m1->type, m2->type, depth - 1))
4431+
return false;
4432+
}
4433+
return true;
43954434
}
4396-
return true;
4397-
}
4435+
case BTF_KIND_FUNC_PROTO: {
4436+
const struct btf_param *p1, *p2;
4437+
int i, n;
43984438

4399-
static bool btf_dedup_identical_ptrs(struct btf_dedup *d, __u32 id1, __u32 id2)
4400-
{
4401-
struct btf_type *t1, *t2;
4439+
if (!btf_compat_fnproto(t1, t2))
4440+
return false;
44024441

4403-
t1 = btf_type_by_id(d->btf, id1);
4404-
t2 = btf_type_by_id(d->btf, id2);
4442+
if (t1->type != t2->type &&
4443+
!btf_dedup_identical_types(d, t1->type, t2->type, depth - 1))
4444+
return false;
44054445

4406-
if (!btf_is_ptr(t1) || !btf_is_ptr(t2))
4446+
p1 = btf_params(t1);
4447+
p2 = btf_params(t2);
4448+
for (i = 0, n = btf_vlen(t1); i < n; i++, p1++, p2++) {
4449+
if (p1->type == p2->type)
4450+
continue;
4451+
if (!btf_dedup_identical_types(d, p1->type, p2->type, depth - 1))
4452+
return false;
4453+
}
4454+
return true;
4455+
}
4456+
default:
44074457
return false;
4408-
4409-
return t1->type == t2->type;
4458+
}
44104459
}
44114460

4461+
44124462
/*
44134463
* Check equivalence of BTF type graph formed by candidate struct/union (we'll
44144464
* call it "candidate graph" in this description for brevity) to a type graph
@@ -4527,22 +4577,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
45274577
* different fields within the *same* struct. This breaks type
45284578
* equivalence check, which makes an assumption that candidate
45294579
* types sub-graph has a consistent and deduped-by-compiler
4530-
* types within a single CU. So work around that by explicitly
4531-
* allowing identical array types here.
4580+
* types within a single CU. And similar situation can happen
4581+
* with struct/union sometimes, and event with pointers.
4582+
* So accommodate cases like this doing a structural
4583+
* comparison recursively, but avoiding being stuck in endless
4584+
* loops by limiting the depth up to which we check.
45324585
*/
4533-
if (btf_dedup_identical_arrays(d, hypot_type_id, cand_id))
4534-
return 1;
4535-
/* It turns out that similar situation can happen with
4536-
* struct/union sometimes, sigh... Handle the case where
4537-
* structs/unions are exactly the same, down to the referenced
4538-
* type IDs. Anything more complicated (e.g., if referenced
4539-
* types are different, but equivalent) is *way more*
4540-
* complicated and requires a many-to-many equivalence mapping.
4541-
*/
4542-
if (btf_dedup_identical_structs(d, hypot_type_id, cand_id))
4543-
return 1;
4544-
/* A similar case is again observed for PTRs. */
4545-
if (btf_dedup_identical_ptrs(d, hypot_type_id, cand_id))
4586+
if (btf_dedup_identical_types(d, hypot_type_id, cand_id, 16))
45464587
return 1;
45474588
return 0;
45484589
}

0 commit comments

Comments
 (0)