Commit 00b8f39f authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'kallsyms: Optimize the search for module symbols by livepatch and bpf'

Jiri Olsa says:

====================

hi,
sending new version of [1] patchset posted originally by Zhen Lei.
It contains 2 changes that improove search performance for livepatch
and bpf.

v3 changes:
  - fixed off by 1 issue, simplified condition, added acks [Song]
  - added module attach as subtest [Andrii]

v2 changes:
  - reworked the bpf change and meassured the performance
  - adding new selftest to benchmark kprobe multi module attachment
  - skipping patch 3 as requested by Zhen Lei
  - added Reviewed-by for patch 1 [Petr Mladek]

thanks,
jirka

[1] https://lore.kernel.org/bpf/20221230112729.351-1-thunder.leizhen@huawei.com/
---
Jiri Olsa (2):
      selftests/bpf: Add serial_test_kprobe_multi_bench_attach_kernel/module tests
      bpf: Change modules resolving for kprobe multi link
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 92afc532 6a5f2d6e
...@@ -879,11 +879,13 @@ static inline bool module_sig_ok(struct module *module) ...@@ -879,11 +879,13 @@ static inline bool module_sig_ok(struct module *module)
#endif /* CONFIG_MODULE_SIG */ #endif /* CONFIG_MODULE_SIG */
#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS) #if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS)
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, int module_kallsyms_on_each_symbol(const char *modname,
int (*fn)(void *, const char *,
struct module *, unsigned long), struct module *, unsigned long),
void *data); void *data);
#else #else
static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, static inline int module_kallsyms_on_each_symbol(const char *modname,
int (*fn)(void *, const char *,
struct module *, unsigned long), struct module *, unsigned long),
void *data) void *data)
{ {
......
...@@ -118,7 +118,6 @@ static struct klp_object *klp_find_object(struct klp_patch *patch, ...@@ -118,7 +118,6 @@ static struct klp_object *klp_find_object(struct klp_patch *patch,
} }
struct klp_find_arg { struct klp_find_arg {
const char *objname;
const char *name; const char *name;
unsigned long addr; unsigned long addr;
unsigned long count; unsigned long count;
...@@ -148,15 +147,9 @@ static int klp_find_callback(void *data, const char *name, ...@@ -148,15 +147,9 @@ static int klp_find_callback(void *data, const char *name,
{ {
struct klp_find_arg *args = data; struct klp_find_arg *args = data;
if ((mod && !args->objname) || (!mod && args->objname))
return 0;
if (strcmp(args->name, name)) if (strcmp(args->name, name))
return 0; return 0;
if (args->objname && strcmp(args->objname, mod->name))
return 0;
return klp_match_callback(data, addr); return klp_match_callback(data, addr);
} }
...@@ -164,7 +157,6 @@ static int klp_find_object_symbol(const char *objname, const char *name, ...@@ -164,7 +157,6 @@ static int klp_find_object_symbol(const char *objname, const char *name,
unsigned long sympos, unsigned long *addr) unsigned long sympos, unsigned long *addr)
{ {
struct klp_find_arg args = { struct klp_find_arg args = {
.objname = objname,
.name = name, .name = name,
.addr = 0, .addr = 0,
.count = 0, .count = 0,
...@@ -172,7 +164,7 @@ static int klp_find_object_symbol(const char *objname, const char *name, ...@@ -172,7 +164,7 @@ static int klp_find_object_symbol(const char *objname, const char *name,
}; };
if (objname) if (objname)
module_kallsyms_on_each_symbol(klp_find_callback, &args); module_kallsyms_on_each_symbol(objname, klp_find_callback, &args);
else else
kallsyms_on_each_match_symbol(klp_match_callback, name, &args); kallsyms_on_each_match_symbol(klp_match_callback, name, &args);
......
...@@ -494,7 +494,8 @@ unsigned long module_kallsyms_lookup_name(const char *name) ...@@ -494,7 +494,8 @@ unsigned long module_kallsyms_lookup_name(const char *name)
return ret; return ret;
} }
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, int module_kallsyms_on_each_symbol(const char *modname,
int (*fn)(void *, const char *,
struct module *, unsigned long), struct module *, unsigned long),
void *data) void *data)
{ {
...@@ -509,6 +510,9 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, ...@@ -509,6 +510,9 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
if (mod->state == MODULE_STATE_UNFORMED) if (mod->state == MODULE_STATE_UNFORMED)
continue; continue;
if (modname && strcmp(modname, mod->name))
continue;
/* Use rcu_dereference_sched() to remain compliant with the sparse tool */ /* Use rcu_dereference_sched() to remain compliant with the sparse tool */
preempt_disable(); preempt_disable();
kallsyms = rcu_dereference_sched(mod->kallsyms); kallsyms = rcu_dereference_sched(mod->kallsyms);
...@@ -525,6 +529,13 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, ...@@ -525,6 +529,13 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
if (ret != 0) if (ret != 0)
goto out; goto out;
} }
/*
* The given module is found, the subsequent modules do not
* need to be compared.
*/
if (modname)
break;
} }
out: out:
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
......
...@@ -2682,69 +2682,77 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv) ...@@ -2682,69 +2682,77 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv)
} }
} }
struct module_addr_args { struct modules_array {
unsigned long *addrs;
u32 addrs_cnt;
struct module **mods; struct module **mods;
int mods_cnt; int mods_cnt;
int mods_cap; int mods_cap;
}; };
static int module_callback(void *data, const char *name, static int add_module(struct modules_array *arr, struct module *mod)
struct module *mod, unsigned long addr)
{ {
struct module_addr_args *args = data;
struct module **mods; struct module **mods;
/* We iterate all modules symbols and for each we: if (arr->mods_cnt == arr->mods_cap) {
* - search for it in provided addresses array arr->mods_cap = max(16, arr->mods_cap * 3 / 2);
* - if found we check if we already have the module pointer stored mods = krealloc_array(arr->mods, arr->mods_cap, sizeof(*mods), GFP_KERNEL);
* (we iterate modules sequentially, so we can check just the last
* module pointer)
* - take module reference and store it
*/
if (!bsearch(&addr, args->addrs, args->addrs_cnt, sizeof(addr),
bpf_kprobe_multi_addrs_cmp))
return 0;
if (args->mods && args->mods[args->mods_cnt - 1] == mod)
return 0;
if (args->mods_cnt == args->mods_cap) {
args->mods_cap = max(16, args->mods_cap * 3 / 2);
mods = krealloc_array(args->mods, args->mods_cap, sizeof(*mods), GFP_KERNEL);
if (!mods) if (!mods)
return -ENOMEM; return -ENOMEM;
args->mods = mods; arr->mods = mods;
} }
if (!try_module_get(mod)) arr->mods[arr->mods_cnt] = mod;
return -EINVAL; arr->mods_cnt++;
args->mods[args->mods_cnt] = mod;
args->mods_cnt++;
return 0; return 0;
} }
static bool has_module(struct modules_array *arr, struct module *mod)
{
int i;
for (i = arr->mods_cnt - 1; i >= 0; i--) {
if (arr->mods[i] == mod)
return true;
}
return false;
}
static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt) static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt)
{ {
struct module_addr_args args = { struct modules_array arr = {};
.addrs = addrs, u32 i, err = 0;
.addrs_cnt = addrs_cnt,
}; for (i = 0; i < addrs_cnt; i++) {
int err; struct module *mod;
preempt_disable();
mod = __module_address(addrs[i]);
/* Either no module or we it's already stored */
if (!mod || has_module(&arr, mod)) {
preempt_enable();
continue;
}
if (!try_module_get(mod))
err = -EINVAL;
preempt_enable();
if (err)
break;
err = add_module(&arr, mod);
if (err) {
module_put(mod);
break;
}
}
/* We return either err < 0 in case of error, ... */ /* We return either err < 0 in case of error, ... */
err = module_kallsyms_on_each_symbol(module_callback, &args);
if (err) { if (err) {
kprobe_multi_put_modules(args.mods, args.mods_cnt); kprobe_multi_put_modules(arr.mods, arr.mods_cnt);
kfree(args.mods); kfree(arr.mods);
return err; return err;
} }
/* or number of modules found if everything is ok. */ /* or number of modules found if everything is ok. */
*mods = args.mods; *mods = arr.mods;
return args.mods_cnt; return arr.mods_cnt;
} }
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
...@@ -2857,13 +2865,6 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr ...@@ -2857,13 +2865,6 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
bpf_kprobe_multi_cookie_cmp, bpf_kprobe_multi_cookie_cmp,
bpf_kprobe_multi_cookie_swap, bpf_kprobe_multi_cookie_swap,
link); link);
} else {
/*
* We need to sort addrs array even if there are no cookies
* provided, to allow bsearch in get_modules_for_addrs.
*/
sort(addrs, cnt, sizeof(*addrs),
bpf_kprobe_multi_addrs_cmp, NULL);
} }
err = get_modules_for_addrs(&link->mods, addrs, cnt); err = get_modules_for_addrs(&link->mods, addrs, cnt);
......
...@@ -8324,7 +8324,7 @@ int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *a ...@@ -8324,7 +8324,7 @@ int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *a
found_all = kallsyms_on_each_symbol(kallsyms_callback, &args); found_all = kallsyms_on_each_symbol(kallsyms_callback, &args);
if (found_all) if (found_all)
return 0; return 0;
found_all = module_kallsyms_on_each_symbol(kallsyms_callback, &args); found_all = module_kallsyms_on_each_symbol(NULL, kallsyms_callback, &args);
return found_all ? 0 : -ESRCH; return found_all ? 0 : -ESRCH;
} }
......
...@@ -322,7 +322,7 @@ static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused) ...@@ -322,7 +322,7 @@ static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused)
return strcmp((const char *) key1, (const char *) key2) == 0; return strcmp((const char *) key1, (const char *) key2) == 0;
} }
static int get_syms(char ***symsp, size_t *cntp) static int get_syms(char ***symsp, size_t *cntp, bool kernel)
{ {
size_t cap = 0, cnt = 0, i; size_t cap = 0, cnt = 0, i;
char *name = NULL, **syms = NULL; char *name = NULL, **syms = NULL;
...@@ -349,8 +349,9 @@ static int get_syms(char ***symsp, size_t *cntp) ...@@ -349,8 +349,9 @@ static int get_syms(char ***symsp, size_t *cntp)
} }
while (fgets(buf, sizeof(buf), f)) { while (fgets(buf, sizeof(buf), f)) {
/* skip modules */ if (kernel && strchr(buf, '['))
if (strchr(buf, '[')) continue;
if (!kernel && !strchr(buf, '['))
continue; continue;
free(name); free(name);
...@@ -404,7 +405,7 @@ static int get_syms(char ***symsp, size_t *cntp) ...@@ -404,7 +405,7 @@ static int get_syms(char ***symsp, size_t *cntp)
return err; return err;
} }
void serial_test_kprobe_multi_bench_attach(void) static void test_kprobe_multi_bench_attach(bool kernel)
{ {
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
struct kprobe_multi_empty *skel = NULL; struct kprobe_multi_empty *skel = NULL;
...@@ -415,7 +416,7 @@ void serial_test_kprobe_multi_bench_attach(void) ...@@ -415,7 +416,7 @@ void serial_test_kprobe_multi_bench_attach(void)
char **syms = NULL; char **syms = NULL;
size_t cnt = 0, i; size_t cnt = 0, i;
if (!ASSERT_OK(get_syms(&syms, &cnt), "get_syms")) if (!ASSERT_OK(get_syms(&syms, &cnt, kernel), "get_syms"))
return; return;
skel = kprobe_multi_empty__open_and_load(); skel = kprobe_multi_empty__open_and_load();
...@@ -453,6 +454,14 @@ void serial_test_kprobe_multi_bench_attach(void) ...@@ -453,6 +454,14 @@ void serial_test_kprobe_multi_bench_attach(void)
} }
} }
void serial_test_kprobe_multi_bench_attach(void)
{
if (test__start_subtest("kernel"))
test_kprobe_multi_bench_attach(true);
if (test__start_subtest("modules"))
test_kprobe_multi_bench_attach(false);
}
void test_kprobe_multi_test(void) void test_kprobe_multi_test(void)
{ {
if (!ASSERT_OK(load_kallsyms(), "load_kallsyms")) if (!ASSERT_OK(load_kallsyms(), "load_kallsyms"))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment