Skip to content

Commit 859c8f2

Browse files
bobrikKernel Patches Daemon
authored andcommitted
kallsyms: cache bpf symbols to avoid quadratic iteration
The existing code iterates the whole list of bpf ksyms until the right one is found, which means quadratic complexity on top of linked list pointer chasing under rcu. This is't noticeable for most installations, but when one has 10000 bpf programs loaded, things start to add up and reading from `/proc/kallsyms` slows down a lot. Instead of doing that, we can cache the list of bpf symbols in linear time when `/proc/kallsyms` is opened, which makes the whole thing fast. Reading `/proc/kallsyms` on Apple M3 Pro in a VM and measuring system time: Before: * 15 bpf symbols: ~35ms * 10015 bpf symbols: ~1250ms After: * 15 bpf symbols: ~35ms * 10015 bpf symbols: ~50ms Testing in production on v6.12 series (with ~10000 bpf ksyms as well): * On AMD EPYC 9684X (Zen4): ~870ms -> ~100ms * On Ampere Altra Max M128-30: ~4650ms -> ~70ms Signed-off-by: Ivan Babrou <[email protected]>
1 parent 68193d8 commit 859c8f2

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

include/linux/filter.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,11 @@ static inline bool insn_is_cast_user(const struct bpf_insn *insn)
648648
offsetof(TYPE, MEMBER); \
649649
})
650650

651+
struct bpf_ksym_cache_entry {
652+
unsigned long value;
653+
char name[KSYM_NAME_LEN];
654+
};
655+
651656
/* A struct sock_filter is architecture independent. */
652657
struct compat_sock_fprog {
653658
u16 len;
@@ -1381,6 +1386,9 @@ int __bpf_address_lookup(unsigned long addr, unsigned long *size,
13811386
bool is_bpf_text_address(unsigned long addr);
13821387
int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
13831388
char *sym);
1389+
1390+
int bpf_get_all_kallsyms(struct bpf_ksym_cache_entry **cache_ptr,
1391+
unsigned int *count_ptr);
13841392
struct bpf_prog *bpf_prog_ksym_find(unsigned long addr);
13851393

13861394
static inline int
@@ -1449,6 +1457,14 @@ static inline int bpf_get_kallsym(unsigned int symnum, unsigned long *value,
14491457
return -ERANGE;
14501458
}
14511459

1460+
static inline int bpf_get_all_kallsyms(struct bpf_ksym_cache_entry **cache_ptr,
1461+
unsigned int *count_ptr)
1462+
{
1463+
*cache_ptr = NULL;
1464+
*count_ptr = 0;
1465+
return 0;
1466+
}
1467+
14521468
static inline struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
14531469
{
14541470
return NULL;

kernel/bpf/core.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,52 @@ int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
825825
return ret;
826826
}
827827

828+
int bpf_get_all_kallsyms(struct bpf_ksym_cache_entry **cache_ptr,
829+
unsigned int *count_ptr)
830+
{
831+
struct bpf_ksym_cache_entry *cache;
832+
struct bpf_ksym *ksym;
833+
unsigned int count = 0;
834+
unsigned int i = 0;
835+
836+
if (!bpf_jit_kallsyms_enabled()) {
837+
*cache_ptr = NULL;
838+
*count_ptr = 0;
839+
return 0;
840+
}
841+
842+
rcu_read_lock();
843+
list_for_each_entry_rcu(ksym, &bpf_kallsyms, lnode)
844+
count++;
845+
rcu_read_unlock();
846+
847+
if (count == 0) {
848+
*cache_ptr = NULL;
849+
*count_ptr = 0;
850+
return 0;
851+
}
852+
853+
cache = kvmalloc_array(count, sizeof(*cache), GFP_KERNEL);
854+
if (!cache)
855+
return -ENOMEM;
856+
857+
rcu_read_lock();
858+
list_for_each_entry_rcu(ksym, &bpf_kallsyms, lnode) {
859+
if (i >= count) {
860+
/* List grew since we counted, oh well */
861+
break;
862+
}
863+
strscpy(cache[i].name, ksym->name, KSYM_NAME_LEN);
864+
cache[i].value = ksym->start;
865+
i++;
866+
}
867+
rcu_read_unlock();
868+
869+
*cache_ptr = cache;
870+
*count_ptr = i;
871+
return 0;
872+
}
873+
828874
int bpf_jit_add_poke_descriptor(struct bpf_prog *prog,
829875
struct bpf_jit_poke_descriptor *poke)
830876
{

kernel/kallsyms.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ struct kallsym_iter {
566566
char module_name[MODULE_NAME_LEN];
567567
int exported;
568568
int show_value;
569+
struct bpf_ksym_cache_entry *bpf_cache;
570+
unsigned int bpf_cache_count;
569571
};
570572

571573
static int get_ksymbol_mod(struct kallsym_iter *iter)
@@ -603,11 +605,27 @@ static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)
603605

604606
static int get_ksymbol_bpf(struct kallsym_iter *iter)
605607
{
608+
unsigned int index = iter->pos - iter->pos_ftrace_mod_end;
606609
int ret;
607610

611+
if (iter->bpf_cache) {
612+
if (index >= iter->bpf_cache_count) {
613+
iter->pos_bpf_end = iter->pos;
614+
return 0;
615+
}
616+
617+
strscpy(iter->module_name, "bpf", MODULE_NAME_LEN);
618+
iter->exported = 0;
619+
strscpy(iter->name, iter->bpf_cache[index].name, KSYM_NAME_LEN);
620+
iter->value = iter->bpf_cache[index].value;
621+
iter->type = BPF_SYM_ELF_TYPE;
622+
623+
return 1;
624+
}
625+
608626
strscpy(iter->module_name, "bpf", MODULE_NAME_LEN);
609627
iter->exported = 0;
610-
ret = bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end,
628+
ret = bpf_get_kallsym(index,
611629
&iter->value, &iter->type,
612630
iter->name);
613631
if (ret < 0) {
@@ -862,9 +880,21 @@ static int kallsyms_open(struct inode *inode, struct file *file)
862880
* the result here at open time.
863881
*/
864882
iter->show_value = kallsyms_show_value(file->f_cred);
883+
884+
bpf_get_all_kallsyms(&iter->bpf_cache, &iter->bpf_cache_count);
885+
865886
return 0;
866887
}
867888

889+
static int kallsyms_release(struct inode *inode, struct file *file)
890+
{
891+
struct seq_file *m = file->private_data;
892+
struct kallsym_iter *iter = m->private;
893+
894+
kvfree(iter->bpf_cache);
895+
return seq_release_private(inode, file);
896+
}
897+
868898
#ifdef CONFIG_KGDB_KDB
869899
const char *kdb_walk_kallsyms(loff_t *pos)
870900
{
@@ -889,7 +919,7 @@ static const struct proc_ops kallsyms_proc_ops = {
889919
.proc_open = kallsyms_open,
890920
.proc_read = seq_read,
891921
.proc_lseek = seq_lseek,
892-
.proc_release = seq_release_private,
922+
.proc_release = kallsyms_release,
893923
};
894924

895925
static int __init kallsyms_init(void)

0 commit comments

Comments
 (0)