hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7W46D
--------------------------------
It was reported that if 'static_call' is used in a old function, then the livepatch module created by kpatch for that old function cannot be inserted normally.
Root cause is that relocation of static_call symbols in livepatch module has not been done while initing: load_module prepare_coming_module blocking_notifier_call_chain_robust notifier_call_chain_robust static_call_module_notify <-- 1. static_call symbols init here, but relocation is done at below MARK "2." do_init_module do_one_initcall klp_register_patch klp_init_patch klp_init_object klp_init_object_loaded <-- 2. relocate .klp.xxx here
To solve it, we move the static_call initialization after relocation.
Signed-off-by: Zheng Yejian zhengyejian1@huawei.com --- include/linux/static_call.h | 6 ++++++ kernel/livepatch/core.c | 13 +++++++++++++ kernel/static_call_inline.c | 20 ++++++++++++++++++++ 3 files changed, 39 insertions(+)
diff --git a/include/linux/static_call.h b/include/linux/static_call.h index 141e6b176a1b..a2c6241c09f2 100644 --- a/include/linux/static_call.h +++ b/include/linux/static_call.h @@ -343,4 +343,10 @@ static inline int static_call_text_reserved(void *start, void *end)
#endif /* CONFIG_HAVE_STATIC_CALL */
+#if defined(CONFIG_HAVE_STATIC_CALL_INLINE) && defined(CONFIG_LIVEPATCH_WO_FTRACE) +int klp_static_call_register(struct module *mod); +#else +static inline int klp_static_call_register(struct module *mod) { return 0; } +#endif + #endif /* _LINUX_STATIC_CALL_H */ diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 18df34068ba0..f7b3a083a284 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -34,6 +34,7 @@ #ifdef CONFIG_LIVEPATCH_RESTRICT_KPROBE #include <linux/kprobes.h> #endif /* CONFIG_LIVEPATCH_RESTRICT_KPROBE */ +#include <linux/static_call.h> #endif /* CONFIG_LIVEPATCH_FTRACE */
/* @@ -1068,6 +1069,18 @@ static int klp_init_patch(struct klp_patch *patch) pr_err("register jump label failed, ret=%d\n", ret); return ret; } + ret = klp_static_call_register(patch->mod); + if (ret) { + /* + * We no need to distinctly clean pre-registered jump_label + * here because it will be clean at path: + * load_module + * do_init_module + * fail_free_freeinit: <-- notify GOING here + */ + pr_err("register static call failed, ret=%d\n", ret); + return ret; + } klp_for_each_object(patch, obj) klp_load_hook(obj); #endif diff --git a/kernel/static_call_inline.c b/kernel/static_call_inline.c index 639397b5491c..6d0e1f185e02 100644 --- a/kernel/static_call_inline.c +++ b/kernel/static_call_inline.c @@ -367,6 +367,11 @@ static int static_call_add_module(struct module *mod) struct static_call_site *stop = start + mod->num_static_call_sites; struct static_call_site *site;
+#ifdef CONFIG_LIVEPATCH_WO_FTRACE + if (unlikely(!mod_klp_rel_completed(mod))) + return 0; +#endif + for (site = start; site != stop; site++) { unsigned long s_key = __static_call_key(site); unsigned long addr = s_key & ~STATIC_CALL_SITE_FLAGS; @@ -409,6 +414,11 @@ static void static_call_del_module(struct module *mod) struct static_call_mod *site_mod, **prev; struct static_call_site *site;
+#ifdef CONFIG_LIVEPATCH_WO_FTRACE + if (unlikely(!mod_klp_rel_completed(mod))) + return; +#endif + for (site = start; site < stop; site++) { key = static_call_key(site); if (key == prev_key) @@ -461,6 +471,16 @@ static struct notifier_block static_call_module_nb = { .notifier_call = static_call_module_notify, };
+#ifdef CONFIG_LIVEPATCH_WO_FTRACE +int klp_static_call_register(struct module *mod) +{ + int ret; + + ret = static_call_module_notify(&static_call_module_nb, MODULE_STATE_COMING, mod); + return notifier_to_errno(ret); +} +#endif /* CONFIG_LIVEPATCH_WO_FTRACE */ + #else
static inline int __static_call_mod_text_reserved(void *start, void *end)