Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

static_call: Allow early init

In order to use static_call() to wire up x86_pmu, we need to
initialize earlier, specifically before memory allocation works; copy
some of the tricks from jump_label to enable this.

Primarily we overload key->next to store a sites pointer when there
are no modules, this avoids having to use kmalloc() to initialize the
sites and allows us to run much earlier.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Link: https://lore.kernel.org/r/20200818135805.220737930@infradead.org

authored by

Peter Zijlstra and committed by
Ingo Molnar
a945c834 6c3fce79

+85 -7
+2
arch/x86/kernel/setup.c
··· 19 19 #include <linux/hugetlb.h> 20 20 #include <linux/tboot.h> 21 21 #include <linux/usb/xhci-dbgp.h> 22 + #include <linux/static_call.h> 22 23 23 24 #include <uapi/linux/mount.h> 24 25 ··· 850 849 early_cpu_init(); 851 850 arch_init_ideal_nops(); 852 851 jump_label_init(); 852 + static_call_init(); 853 853 early_ioremap_init(); 854 854 855 855 setup_olpc_ofw_pgd();
+4 -1
arch/x86/kernel/static_call.c
··· 11 11 RET = 3, /* tramp / site cond-tail-call */ 12 12 }; 13 13 14 - static void __static_call_transform(void *insn, enum insn_type type, void *func) 14 + static void __ref __static_call_transform(void *insn, enum insn_type type, void *func) 15 15 { 16 16 int size = CALL_INSN_SIZE; 17 17 const void *code; ··· 37 37 38 38 if (memcmp(insn, code, size) == 0) 39 39 return; 40 + 41 + if (unlikely(system_state == SYSTEM_BOOTING)) 42 + return text_poke_early(insn, code, size); 40 43 41 44 text_poke_bp(insn, code, size, NULL); 42 45 }
+13 -2
include/linux/static_call.h
··· 136 136 137 137 #ifdef CONFIG_HAVE_STATIC_CALL_INLINE 138 138 139 + extern void __init static_call_init(void); 140 + 139 141 struct static_call_mod { 140 142 struct static_call_mod *next; 141 143 struct module *mod; /* for vmlinux, mod == NULL */ ··· 146 144 147 145 struct static_call_key { 148 146 void *func; 149 - struct static_call_mod *mods; 147 + union { 148 + /* bit 0: 0 = mods, 1 = sites */ 149 + unsigned long type; 150 + struct static_call_mod *mods; 151 + struct static_call_site *sites; 152 + }; 150 153 }; 151 154 152 155 extern void __static_call_update(struct static_call_key *key, void *tramp, void *func); ··· 162 155 DECLARE_STATIC_CALL(name, _func); \ 163 156 struct static_call_key STATIC_CALL_KEY(name) = { \ 164 157 .func = _func, \ 165 - .mods = NULL, \ 158 + .type = 1, \ 166 159 }; \ 167 160 ARCH_DEFINE_STATIC_CALL_TRAMP(name, _func) 168 161 ··· 186 179 EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name)) 187 180 188 181 #elif defined(CONFIG_HAVE_STATIC_CALL) 182 + 183 + static inline void static_call_init(void) { } 189 184 190 185 struct static_call_key { 191 186 void *func; ··· 233 224 EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name)) 234 225 235 226 #else /* Generic implementation */ 227 + 228 + static inline void static_call_init(void) { } 236 229 237 230 struct static_call_key { 238 231 void *func;
+66 -4
kernel/static_call.c
··· 94 94 static_call_site_cmp, static_call_site_swap); 95 95 } 96 96 97 + static inline bool static_call_key_has_mods(struct static_call_key *key) 98 + { 99 + return !(key->type & 1); 100 + } 101 + 102 + static inline struct static_call_mod *static_call_key_next(struct static_call_key *key) 103 + { 104 + if (!static_call_key_has_mods(key)) 105 + return NULL; 106 + 107 + return key->mods; 108 + } 109 + 110 + static inline struct static_call_site *static_call_key_sites(struct static_call_key *key) 111 + { 112 + if (static_call_key_has_mods(key)) 113 + return NULL; 114 + 115 + return (struct static_call_site *)(key->type & ~1); 116 + } 117 + 97 118 void __static_call_update(struct static_call_key *key, void *tramp, void *func) 98 119 { 99 120 struct static_call_site *site, *stop; 100 - struct static_call_mod *site_mod; 121 + struct static_call_mod *site_mod, first; 101 122 102 123 cpus_read_lock(); 103 124 static_call_lock(); ··· 137 116 if (WARN_ON_ONCE(!static_call_initialized)) 138 117 goto done; 139 118 140 - for (site_mod = key->mods; site_mod; site_mod = site_mod->next) { 119 + first = (struct static_call_mod){ 120 + .next = static_call_key_next(key), 121 + .mod = NULL, 122 + .sites = static_call_key_sites(key), 123 + }; 124 + 125 + for (site_mod = &first; site_mod; site_mod = site_mod->next) { 141 126 struct module *mod = site_mod->mod; 142 127 143 128 if (!site_mod->sites) { 144 129 /* 145 130 * This can happen if the static call key is defined in 146 131 * a module which doesn't use it. 132 + * 133 + * It also happens in the has_mods case, where the 134 + * 'first' entry has no sites associated with it. 147 135 */ 148 136 continue; 149 137 } ··· 222 192 if (key != prev_key) { 223 193 prev_key = key; 224 194 195 + /* 196 + * For vmlinux (!mod) avoid the allocation by storing 197 + * the sites pointer in the key itself. Also see 198 + * __static_call_update()'s @first. 199 + * 200 + * This allows architectures (eg. x86) to call 201 + * static_call_init() before memory allocation works. 202 + */ 203 + if (!mod) { 204 + key->sites = site; 205 + key->type |= 1; 206 + goto do_transform; 207 + } 208 + 225 209 site_mod = kzalloc(sizeof(*site_mod), GFP_KERNEL); 226 210 if (!site_mod) 227 211 return -ENOMEM; 228 212 213 + /* 214 + * When the key has a direct sites pointer, extract 215 + * that into an explicit struct static_call_mod, so we 216 + * can have a list of modules. 217 + */ 218 + if (static_call_key_sites(key)) { 219 + site_mod->mod = NULL; 220 + site_mod->next = NULL; 221 + site_mod->sites = static_call_key_sites(key); 222 + 223 + key->mods = site_mod; 224 + 225 + site_mod = kzalloc(sizeof(*site_mod), GFP_KERNEL); 226 + if (!site_mod) 227 + return -ENOMEM; 228 + } 229 + 229 230 site_mod->mod = mod; 230 231 site_mod->sites = site; 231 - site_mod->next = key->mods; 232 + site_mod->next = static_call_key_next(key); 232 233 key->mods = site_mod; 233 234 } 234 235 236 + do_transform: 235 237 arch_static_call_transform(site_addr, NULL, key->func, 236 238 static_call_is_tail(site)); 237 239 } ··· 410 348 return __static_call_mod_text_reserved(start, end); 411 349 } 412 350 413 - static void __init static_call_init(void) 351 + void __init static_call_init(void) 414 352 { 415 353 int ret; 416 354