Re: [RFC PATCH 6/8] preempt/dynamic: Provide preempt_schedule[_notrace]() static calls

From: Peter Zijlstra
Date: Wed Jan 27 2021 - 06:39:06 EST


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)