On Wed, Jan 27, 2021 at 10:13:47AM +0100, Peter Zijlstra wrote: > On Tue, Jan 26, 2021 at 05:57:30PM -0600, Josh Poimboeuf wrote:
> > Well, I hate it, but I'm not sure I have any better ideas. It should be > > possible to use kallsyms, instead of the rb-tree/register nonsense. Not > > sure about the performance impact though. Might be a good reason to > > speed up kallsyms! > > Oh right, let me see if I can make that work. Something like so compiles.. but it does make the whole thing depend on KALLSYMS_ALL, which is somewhat yuck. Also, kallsyms_lookup_name() is horrible, but not trivial to fix because of that compression scheme used. --- --- a/kernel/static_call.c +++ b/kernel/static_call.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/cpu.h> #include <linux/processor.h> +#include <linux/kallsyms.h> #include <asm/sections.h> extern struct static_call_site __start_static_call_sites[], @@ -325,8 +326,66 @@ static int __static_call_mod_text_reserv static int static_call_add_module(struct module *mod) { - return __static_call_init(mod, mod->static_call_sites, - mod->static_call_sites + mod->num_static_call_sites); + struct static_call_site *start = mod->static_call_sites; + struct static_call_site *stop = start + mod->num_static_call_sites; + struct static_call_site *site; + + struct { + unsigned long tramp; + unsigned long key; + } cache[8] = { { 0, 0}, }; + int idx = 0; + + for (site = start; site != stop; site++) { + unsigned long key, addr = (unsigned long)static_call_key(site); + unsigned long sym_size, sym_offset; + char sym_name[KSYM_NAME_LEN]; + const char *name; + int i; + + if (!kernel_text_address(addr)) + continue; + + /* + * Gotta fix up the keys that point to the trampoline. + */ + + /* Simple cache to avoid kallsyms */ + for (i = 0; i < ARRAY_SIZE(cache); i++) { + if (cache[i].tramp == addr) { + key = cache[i].key; + goto got_key; + } + } + + name = kallsyms_lookup(addr, &sym_size, &sym_offset, NULL, sym_name); + if (!name) + goto fail; + + if (name != sym_name) + strcpy(sym_name, name); + memcpy(sym_name, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN); + key = kallsyms_lookup_name(sym_name); + if (!key) + goto fail; + + /* Remember for next time.. */ + cache[idx].tramp = addr; + cache[idx].key = key; + idx++; + idx %= ARRAY_SIZE(cache); + +got_key: + site->key = (key - (unsigned long)&site->key) | + (site->key & STATIC_CALL_SITE_FLAGS); + } + + return __static_call_init(mod, start, stop); + +fail: + pr_warn("Failed to fixup __raw_static_call() usage at: %ps\n", + static_call_addr(site)); + return -EINVAL; } static void static_call_del_module(struct module *mod)