Skip to content

Commit 2f1683c

Browse files
Donglin PengKernel Patches Daemon
authored andcommitted
libbpf: Optimize type lookup with binary search for sorted BTF
This patch introduces binary search optimization for BTF type lookups when the BTF instance contains sorted types. The optimization significantly improves performance when searching for types in large BTF instances with sorted type names. For unsorted BTF or when nr_sorted_types is zero, the implementation falls back to the original linear search algorithm. Cc: Eduard Zingerman <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Alan Maguire <[email protected]> Cc: Song Liu <[email protected]> Cc: Xiaoqin Zhang <[email protected]> Signed-off-by: Donglin Peng <[email protected]> Signed-off-by: Donglin Peng <[email protected]>
1 parent 5359bf0 commit 2f1683c

File tree

1 file changed

+122
-23
lines changed

1 file changed

+122
-23
lines changed

tools/lib/bpf/btf.c

Lines changed: 122 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626

2727
#define BTF_MAX_NR_TYPES 0x7fffffffU
2828
#define BTF_MAX_STR_OFFSET 0x7fffffffU
29+
/*
30+
* sort verification occurs lazily upon first btf_find_type_by_name_kind() call
31+
*/
32+
#define BTF_NEED_SORT_CHECK ((__u32)-1)
2933

3034
static struct btf_type btf_void;
3135

@@ -92,6 +96,16 @@ struct btf {
9296
* - for split BTF counts number of types added on top of base BTF.
9397
*/
9498
__u32 nr_types;
99+
/* number of sorted and named types in this BTF instance:
100+
* - doesn't include special [0] void type;
101+
* - for split BTF counts number of sorted and named types added on
102+
* top of base BTF.
103+
* - BTF_NEED_SORT_CHECK value indicates sort validation will be performed
104+
* on first call to btf_find_type_by_name_kind.
105+
* - zero value indicates applied sorting check with unsorted BTF or no
106+
* named types.
107+
*/
108+
__u32 nr_sorted_types;
95109
/* if not NULL, points to the base BTF on top of which the current
96110
* split BTF is based
97111
*/
@@ -897,44 +911,126 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id)
897911
return type_id;
898912
}
899913

900-
__s32 btf__find_by_name(const struct btf *btf, const char *type_name)
914+
/* Performs binary search within specified type ID range to find the leftmost
915+
* BTF type matching the given name. The search assumes types are sorted by
916+
* name in lexicographical order within the specified range.
917+
*
918+
* Return: Type ID of leftmost matching type, or -ENOENT if not found
919+
*/
920+
static __s32 btf_find_type_by_name_bsearch(const struct btf *btf, const char *name,
921+
__s32 start_id, __s32 end_id)
901922
{
902-
__u32 i, nr_types = btf__type_cnt(btf);
923+
const struct btf_type *t;
924+
const char *tname;
925+
__s32 l, r, m, lmost = -ENOENT;
926+
int ret;
927+
928+
l = start_id;
929+
r = end_id;
930+
while (l <= r) {
931+
m = l + (r - l) / 2;
932+
t = btf_type_by_id(btf, m);
933+
tname = btf__str_by_offset(btf, t->name_off);
934+
ret = strcmp(tname, name);
935+
if (ret < 0) {
936+
l = m + 1;
937+
} else {
938+
if (ret == 0)
939+
lmost = m;
940+
r = m - 1;
941+
}
942+
}
903943

904-
if (!strcmp(type_name, "void"))
905-
return 0;
944+
return lmost;
945+
}
906946

907-
for (i = 1; i < nr_types; i++) {
908-
const struct btf_type *t = btf__type_by_id(btf, i);
909-
const char *name = btf__name_by_offset(btf, t->name_off);
947+
/* Searches for a BTF type by name and optionally by kind. The function first
948+
* checks if the search should start from the base BTF (if @start_id is before
949+
* current BTF's start_id). If types are sorted, it uses binary search to find
950+
* the leftmost matching type and then verifies the kind. For unsorted types,
951+
* it falls back to linear search through all types.
952+
*
953+
* The function handles split BTF scenarios by recursively searching in base
954+
* BTFs when necessary. When @kind is -1, only the name matching is performed.
955+
*
956+
* Return: Type ID of matching type on success, -ENOENT if not found
957+
*/
958+
static __s32 btf_find_type_by_name_kind(const struct btf *btf, int start_id,
959+
const char *type_name, __u32 kind)
960+
{
961+
const struct btf_type *t;
962+
const char *tname;
963+
int err = -ENOENT;
964+
965+
if (start_id < btf->start_id) {
966+
err = btf_find_type_by_name_kind(btf->base_btf, start_id,
967+
type_name, kind);
968+
if (err > 0)
969+
goto out;
970+
start_id = btf->start_id;
971+
}
972+
973+
if (btf->nr_sorted_types != BTF_NEED_SORT_CHECK) {
974+
/* binary search */
975+
__s32 end_id;
976+
bool skip_first;
977+
int ret;
978+
979+
end_id = btf->start_id + btf->nr_sorted_types - 1;
980+
ret = btf_find_type_by_name_bsearch(btf, type_name, start_id, end_id);
981+
if (ret < 0)
982+
goto out;
983+
if (kind == -1)
984+
return ret;
985+
skip_first = true;
986+
do {
987+
t = btf_type_by_id(btf, ret);
988+
if (BTF_INFO_KIND(t->info) != kind) {
989+
if (skip_first) {
990+
skip_first = false;
991+
continue;
992+
}
993+
} else if (skip_first) {
994+
return ret;
995+
}
996+
tname = btf__str_by_offset(btf, t->name_off);
997+
if (!strcmp(tname, type_name))
998+
return ret;
999+
else
1000+
break;
1001+
} while (++ret <= end_id);
1002+
} else {
1003+
/* linear search */
1004+
__u32 i, total;
9101005

911-
if (name && !strcmp(type_name, name))
912-
return i;
1006+
total = btf__type_cnt(btf);
1007+
for (i = start_id; i < total; i++) {
1008+
t = btf_type_by_id(btf, i);
1009+
if (kind != -1 && btf_kind(t) != kind)
1010+
continue;
1011+
tname = btf__str_by_offset(btf, t->name_off);
1012+
if (tname && !strcmp(tname, type_name))
1013+
return i;
1014+
}
9131015
}
9141016

915-
return libbpf_err(-ENOENT);
1017+
out:
1018+
return err;
9161019
}
9171020

9181021
static __s32 btf_find_by_name_kind(const struct btf *btf, int start_id,
9191022
const char *type_name, __u32 kind)
9201023
{
921-
__u32 i, nr_types = btf__type_cnt(btf);
922-
9231024
if (kind == BTF_KIND_UNKN || !strcmp(type_name, "void"))
9241025
return 0;
9251026

926-
for (i = start_id; i < nr_types; i++) {
927-
const struct btf_type *t = btf__type_by_id(btf, i);
928-
const char *name;
929-
930-
if (btf_kind(t) != kind)
931-
continue;
932-
name = btf__name_by_offset(btf, t->name_off);
933-
if (name && !strcmp(type_name, name))
934-
return i;
935-
}
1027+
return libbpf_err(btf_find_type_by_name_kind(btf, start_id, type_name, kind));
1028+
}
9361029

937-
return libbpf_err(-ENOENT);
1030+
/* the kind value of -1 indicates that kind matching should be skipped */
1031+
__s32 btf__find_by_name(const struct btf *btf, const char *type_name)
1032+
{
1033+
return btf_find_by_name_kind(btf, btf->start_id, type_name, -1);
9381034
}
9391035

9401036
__s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name,
@@ -1006,6 +1102,7 @@ static struct btf *btf_new_empty(struct btf *base_btf)
10061102
btf->fd = -1;
10071103
btf->ptr_sz = sizeof(void *);
10081104
btf->swapped_endian = false;
1105+
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
10091106

10101107
if (base_btf) {
10111108
btf->base_btf = base_btf;
@@ -1057,6 +1154,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b
10571154
btf->start_id = 1;
10581155
btf->start_str_off = 0;
10591156
btf->fd = -1;
1157+
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
10601158

10611159
if (base_btf) {
10621160
btf->base_btf = base_btf;
@@ -1715,6 +1813,7 @@ static void btf_invalidate_raw_data(struct btf *btf)
17151813
free(btf->raw_data_swapped);
17161814
btf->raw_data_swapped = NULL;
17171815
}
1816+
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
17181817
}
17191818

17201819
/* Ensure BTF is ready to be modified (by splitting into a three memory

0 commit comments

Comments
 (0)