Skip to content

Commit f54fb89

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 cb7b7a2 commit f54fb89

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;
@@ -1380,6 +1385,9 @@ int __bpf_address_lookup(unsigned long addr, unsigned long *size,
13801385
bool is_bpf_text_address(unsigned long addr);
13811386
int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
13821387
char *sym);
1388+
1389+
int bpf_get_all_kallsyms(struct bpf_ksym_cache_entry **cache_ptr,
1390+
unsigned int *count_ptr);
13831391
struct bpf_prog *bpf_prog_ksym_find(unsigned long addr);
13841392

13851393
static inline int
@@ -1448,6 +1456,14 @@ static inline int bpf_get_kallsym(unsigned int symnum, unsigned long *value,
14481456
return -ERANGE;
14491457
}
14501458

1459+
static inline int bpf_get_all_kallsyms(struct bpf_ksym_cache_entry **cache_ptr,
1460+
unsigned int *count_ptr)
1461+
{
1462+
*cache_ptr = NULL;
1463+
*count_ptr = 0;
1464+
return 0;
1465+
}
1466+
14511467
static inline struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
14521468
{
14531469
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)