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)

Reply via email to