Skip to content

Commit f07d5ac

Browse files
pengdonglinKernel Patches Daemon
authored andcommitted
btf: Sort BTF types by name and kind to optimize btf_find_by_name_kind lookup
Currently, when the funcgraph-args feature is in use, the btf_find_by_name_kind function is invoked quite frequently. However, this function only supports linear search. When the number of btf_type entries to search through is large, such as in the vmlinux BTF which contains over 80,000 named btf_types, it consumes a significant amount of time. This patch optimizes the btf_find_by_name_kind lookup by sorting BTF types according to their names and kinds. Additionally, it modifies the search direction. Now, it first searches the BTF and then its base. It should be noted that this change incurs some additional memory and boot-time overhead. Therefore, the option is disabled by default. Here is a test case: # echo 1 > options/funcgraph-args # echo function_graph > current_tracer Before: # time cat trace | wc -l 124176 real 0m16.154s user 0m0.000s sys 0m15.962s After: # time cat trace | wc -l 124176 real 0m0.948s user 0m0.000s sys 0m0.973s An improvement of more than 20 times can be observed. Cc: Eduard Zingerman <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Song Liu <[email protected]> Cc: Masami Hiramatsu (Google) <[email protected]> Cc: Steven Rostedt <[email protected]> Signed-off-by: pengdonglin <[email protected]> Signed-off-by: pengdonglin <[email protected]>
1 parent 06bf66d commit f07d5ac

File tree

3 files changed

+165
-9
lines changed

3 files changed

+165
-9
lines changed

include/linux/btf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ bool btf_is_module(const struct btf *btf);
220220
bool btf_is_vmlinux(const struct btf *btf);
221221
struct module *btf_try_get_module(const struct btf *btf);
222222
u32 btf_nr_types(const struct btf *btf);
223+
u32 btf_type_cnt(const struct btf *btf);
223224
struct btf *btf_base_btf(const struct btf *btf);
224225
bool btf_type_is_i32(const struct btf_type *t);
225226
bool btf_type_is_i64(const struct btf_type *t);

kernel/bpf/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,17 @@ config BPF_LSM
101101

102102
If you are unsure how to answer this question, answer N.
103103

104+
config BPF_SORT_BTF_BY_NAME_KIND
105+
bool "Sort BTF type by name and kind"
106+
depends on DEBUG_INFO_BTF
107+
default n
108+
help
109+
Sort BTF types by name and kind to enable binary search, improving
110+
the performance of btf_find_by_name_kind. Currently applies to
111+
vmlinux and kernel module BTFs. Note that this option introduces
112+
extra memory and boot-time overhead.
113+
114+
For instance, a BTF file with 80,000 named btf_types consumes
115+
approximately 312 KB of additional memory.
116+
104117
endmenu # "BPF subsystem"

kernel/bpf/btf.c

Lines changed: 151 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ struct btf_struct_ops_tab {
250250
struct bpf_struct_ops_desc ops[];
251251
};
252252

253+
struct btf_sorted_ids {
254+
u32 cnt;
255+
u32 ids[];
256+
};
257+
253258
struct btf {
254259
void *data;
255260
struct btf_type **types;
@@ -268,6 +273,9 @@ struct btf {
268273
struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
269274
struct btf_struct_metas *struct_meta_tab;
270275
struct btf_struct_ops_tab *struct_ops_tab;
276+
#ifdef CONFIG_BPF_SORT_BTF_BY_NAME_KIND
277+
struct btf_sorted_ids *sorted_ids;
278+
#endif
271279

272280
/* split BTF support */
273281
struct btf *base_btf;
@@ -470,6 +478,9 @@ static int btf_resolve(struct btf_verifier_env *env,
470478
static int btf_func_check(struct btf_verifier_env *env,
471479
const struct btf_type *t);
472480

481+
static int cmp_name_kind(const char *sa, u8 ka,
482+
const char *sb, u8 kb);
483+
473484
static bool btf_type_is_modifier(const struct btf_type *t)
474485
{
475486
/* Some of them is not strictly a C modifier
@@ -544,22 +555,59 @@ u32 btf_nr_types(const struct btf *btf)
544555
return total;
545556
}
546557

558+
u32 btf_type_cnt(const struct btf *btf)
559+
{
560+
return btf->start_id + btf->nr_types;
561+
}
562+
547563
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
548564
{
549565
const struct btf_type *t;
566+
struct btf_sorted_ids *sorted_ids = NULL;
550567
const char *tname;
551568
u32 i, total;
552569

553-
total = btf_nr_types(btf);
554-
for (i = 1; i < total; i++) {
555-
t = btf_type_by_id(btf, i);
556-
if (BTF_INFO_KIND(t->info) != kind)
557-
continue;
570+
do {
571+
#ifdef CONFIG_BPF_SORT_BTF_BY_NAME_KIND
572+
sorted_ids = btf->sorted_ids;
573+
#endif
574+
if (sorted_ids) {
575+
/* binary search */
576+
u32 start, end, mid;
577+
u32 *ids = sorted_ids->ids;
578+
int ret;
579+
580+
start = 0;
581+
end = sorted_ids->cnt - 1;
582+
while (start <= end) {
583+
mid = start + (end - start) / 2;
584+
t = btf_type_by_id(btf, ids[mid]);
585+
tname = btf_name_by_offset(btf, t->name_off);
586+
ret = cmp_name_kind(tname, BTF_INFO_KIND(t->info),
587+
name, kind);
588+
if (ret < 0)
589+
start = mid + 1;
590+
else if (ret > 0)
591+
end = mid - 1;
592+
else
593+
return ids[mid];
594+
}
595+
} else {
596+
/* linear search */
597+
total = btf_type_cnt(btf);
598+
for (i = btf->start_id; i < total; i++) {
599+
t = btf_type_by_id(btf, i);
600+
if (BTF_INFO_KIND(t->info) != kind)
601+
continue;
602+
603+
tname = btf_name_by_offset(btf, t->name_off);
604+
if (!strcmp(tname, name))
605+
return i;
606+
}
607+
}
558608

559-
tname = btf_name_by_offset(btf, t->name_off);
560-
if (!strcmp(tname, name))
561-
return i;
562-
}
609+
btf = btf->base_btf;
610+
} while (btf);
563611

564612
return -ENOENT;
565613
}
@@ -1737,12 +1785,29 @@ static void btf_free_struct_ops_tab(struct btf *btf)
17371785
btf->struct_ops_tab = NULL;
17381786
}
17391787

1788+
#ifdef CONFIG_BPF_SORT_BTF_BY_NAME_KIND
1789+
static void btf_free_sorted_ids(struct btf *btf)
1790+
{
1791+
struct btf_sorted_ids *sorted_ids = btf->sorted_ids;
1792+
1793+
if (!sorted_ids)
1794+
return;
1795+
1796+
kvfree(sorted_ids);
1797+
btf->sorted_ids = NULL;
1798+
}
1799+
#else
1800+
static void btf_free_sorted_ids(struct btf *btf)
1801+
{}
1802+
#endif
1803+
17401804
static void btf_free(struct btf *btf)
17411805
{
17421806
btf_free_struct_meta_tab(btf);
17431807
btf_free_dtor_kfunc_tab(btf);
17441808
btf_free_kfunc_set_tab(btf);
17451809
btf_free_struct_ops_tab(btf);
1810+
btf_free_sorted_ids(btf);
17461811
kvfree(btf->types);
17471812
kvfree(btf->resolved_sizes);
17481813
kvfree(btf->resolved_ids);
@@ -6189,6 +6254,81 @@ int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_ty
61896254
return kctx_type_id;
61906255
}
61916256

6257+
#ifdef CONFIG_BPF_SORT_BTF_BY_NAME_KIND
6258+
static int cmp_name_kind(const char *sa, u8 ka, const char *sb, u8 kb)
6259+
{
6260+
return strcmp(sa, sb) ?: (ka - kb);
6261+
}
6262+
6263+
static int btf_compare_name_kind(const void *a, const void *b, const void *priv)
6264+
{
6265+
const struct btf *btf = priv;
6266+
const struct btf_type *ba, *bb;
6267+
u32 ia = *(const u32 *)a;
6268+
u32 ib = *(const u32 *)b;
6269+
6270+
ba = btf_type_by_id(btf, ia);
6271+
bb = btf_type_by_id(btf, ib);
6272+
6273+
return cmp_name_kind(btf_name_by_offset(btf, ba->name_off),
6274+
BTF_INFO_KIND(ba->info),
6275+
btf_name_by_offset(btf, bb->name_off),
6276+
BTF_INFO_KIND(bb->info));
6277+
}
6278+
6279+
static void btf_sort_by_name_kind(struct btf *btf)
6280+
{
6281+
const struct btf_type *t;
6282+
struct btf_sorted_ids *sorted_ids;
6283+
const char *name;
6284+
u32 *ids;
6285+
u32 total, cnt = 0;
6286+
u32 i, j = 0;
6287+
6288+
total = btf_type_cnt(btf);
6289+
for (i = btf->start_id; i < total; i++) {
6290+
t = btf_type_by_id(btf, i);
6291+
name = btf_name_by_offset(btf, t->name_off);
6292+
if (str_is_empty(name))
6293+
continue;
6294+
cnt++;
6295+
}
6296+
6297+
/* Use linear search when the number is below the threshold */
6298+
if (cnt < 8)
6299+
return;
6300+
6301+
sorted_ids = kvmalloc(struct_size(sorted_ids, ids, cnt), GFP_KERNEL);
6302+
if (!sorted_ids) {
6303+
pr_warn("Failed to allocate memory for sorted_ids\n");
6304+
return;
6305+
}
6306+
6307+
ids = sorted_ids->ids;
6308+
for (i = btf->start_id; i < total; i++) {
6309+
t = btf_type_by_id(btf, i);
6310+
name = btf_name_by_offset(btf, t->name_off);
6311+
if (str_is_empty(name))
6312+
continue;
6313+
ids[j++] = i;
6314+
}
6315+
6316+
sort_r(ids, cnt, sizeof(ids[0]), btf_compare_name_kind, NULL, btf);
6317+
6318+
sorted_ids->cnt = cnt;
6319+
btf->sorted_ids = sorted_ids;
6320+
}
6321+
#else
6322+
static int cmp_name_kind(const char *sa, u8 ka, const char *sb, u8 kb)
6323+
{
6324+
return 0;
6325+
}
6326+
6327+
static void btf_sort_by_name_kind(struct btf *btf)
6328+
{
6329+
}
6330+
#endif
6331+
61926332
BTF_ID_LIST_SINGLE(bpf_ctx_convert_btf_id, struct, bpf_ctx_convert)
61936333

61946334
static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name,
@@ -6230,6 +6370,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name
62306370
if (err)
62316371
goto errout;
62326372

6373+
btf_sort_by_name_kind(btf);
62336374
refcount_set(&btf->refcnt, 1);
62346375

62356376
return btf;
@@ -6362,6 +6503,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
63626503
base_btf = vmlinux_btf;
63636504
}
63646505

6506+
btf_sort_by_name_kind(btf);
63656507
btf_verifier_env_free(env);
63666508
refcount_set(&btf->refcnt, 1);
63676509
return btf;

0 commit comments

Comments
 (0)