aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.com>2019-01-09 07:43:26 -0500
committerJiri Kosina <jkosina@suse.cz>2019-01-11 14:51:24 -0500
commitd697bad588eb4e76311193e6eaacc7c7aaa5a4ba (patch)
tree53f96c1a0ddbec0307bc3c7f2c1bfda8c5368fc7
parente1452b607c48c642caf57299f4da83aa002f8533 (diff)
livepatch: Remove Nop structures when unused
Replaced patches are removed from the stack when the transition is finished. It means that Nop structures will never be needed again and can be removed. Why should we care? + Nop structures give the impression that the function is patched even though the ftrace handler has no effect. + Ftrace handlers do not come for free. They cause slowdown that might be visible in some workloads. The ftrace-related slowdown might actually be the reason why the function is no longer patched in the new cumulative patch. One would expect that cumulative patch would help solve these problems as well. + Cumulative patches are supposed to replace any earlier version of the patch. The amount of NOPs depends on which version was replaced. This multiplies the amount of scenarios that might happen. One might say that NOPs are innocent. But there are even optimized NOP instructions for different processors, for example, see arch/x86/kernel/alternative.c. And klp_ftrace_handler() is much more complicated. + It sounds natural to clean up a mess that is no longer needed. It could only be worse if we do not do it. This patch allows to unpatch and free the dynamic structures independently when the transition finishes. The free part is a bit tricky because kobject free callbacks are called asynchronously. We could not wait for them easily. Fortunately, we do not have to. Any further access can be avoided by removing them from the dynamic lists. Signed-off-by: Petr Mladek <pmladek@suse.com> Acked-by: Miroslav Benes <mbenes@suse.cz> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--kernel/livepatch/core.c48
-rw-r--r--kernel/livepatch/core.h1
-rw-r--r--kernel/livepatch/patch.c31
-rw-r--r--kernel/livepatch/patch.h1
-rw-r--r--kernel/livepatch/transition.c4
5 files changed, 76 insertions, 9 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index ecb7660f1d8b..113645ee86b6 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -611,11 +611,16 @@ static struct kobj_type klp_ktype_func = {
611 .sysfs_ops = &kobj_sysfs_ops, 611 .sysfs_ops = &kobj_sysfs_ops,
612}; 612};
613 613
614static void klp_free_funcs(struct klp_object *obj) 614static void __klp_free_funcs(struct klp_object *obj, bool nops_only)
615{ 615{
616 struct klp_func *func, *tmp_func; 616 struct klp_func *func, *tmp_func;
617 617
618 klp_for_each_func_safe(obj, func, tmp_func) { 618 klp_for_each_func_safe(obj, func, tmp_func) {
619 if (nops_only && !func->nop)
620 continue;
621
622 list_del(&func->node);
623
619 /* Might be called from klp_init_patch() error path. */ 624 /* Might be called from klp_init_patch() error path. */
620 if (func->kobj_added) { 625 if (func->kobj_added) {
621 kobject_put(&func->kobj); 626 kobject_put(&func->kobj);
@@ -640,12 +645,17 @@ static void klp_free_object_loaded(struct klp_object *obj)
640 } 645 }
641} 646}
642 647
643static void klp_free_objects(struct klp_patch *patch) 648static void __klp_free_objects(struct klp_patch *patch, bool nops_only)
644{ 649{
645 struct klp_object *obj, *tmp_obj; 650 struct klp_object *obj, *tmp_obj;
646 651
647 klp_for_each_object_safe(patch, obj, tmp_obj) { 652 klp_for_each_object_safe(patch, obj, tmp_obj) {
648 klp_free_funcs(obj); 653 __klp_free_funcs(obj, nops_only);
654
655 if (nops_only && !obj->dynamic)
656 continue;
657
658 list_del(&obj->node);
649 659
650 /* Might be called from klp_init_patch() error path. */ 660 /* Might be called from klp_init_patch() error path. */
651 if (obj->kobj_added) { 661 if (obj->kobj_added) {
@@ -656,6 +666,16 @@ static void klp_free_objects(struct klp_patch *patch)
656 } 666 }
657} 667}
658 668
669static void klp_free_objects(struct klp_patch *patch)
670{
671 __klp_free_objects(patch, false);
672}
673
674static void klp_free_objects_dynamic(struct klp_patch *patch)
675{
676 __klp_free_objects(patch, true);
677}
678
659/* 679/*
660 * This function implements the free operations that can be called safely 680 * This function implements the free operations that can be called safely
661 * under klp_mutex. 681 * under klp_mutex.
@@ -1085,6 +1105,28 @@ void klp_discard_replaced_patches(struct klp_patch *new_patch)
1085} 1105}
1086 1106
1087/* 1107/*
1108 * This function removes the dynamically allocated 'nop' functions.
1109 *
1110 * We could be pretty aggressive. NOPs do not change the existing
1111 * behavior except for adding unnecessary delay by the ftrace handler.
1112 *
1113 * It is safe even when the transition was forced. The ftrace handler
1114 * will see a valid ops->func_stack entry thanks to RCU.
1115 *
1116 * We could even free the NOPs structures. They must be the last entry
1117 * in ops->func_stack. Therefore unregister_ftrace_function() is called.
1118 * It does the same as klp_synchronize_transition() to make sure that
1119 * nobody is inside the ftrace handler once the operation finishes.
1120 *
1121 * IMPORTANT: It must be called right after removing the replaced patches!
1122 */
1123void klp_discard_nops(struct klp_patch *new_patch)
1124{
1125 klp_unpatch_objects_dynamic(klp_transition_patch);
1126 klp_free_objects_dynamic(klp_transition_patch);
1127}
1128
1129/*
1088 * Remove parts of patches that touch a given kernel module. The list of 1130 * Remove parts of patches that touch a given kernel module. The list of
1089 * patches processed might be limited. When limit is NULL, all patches 1131 * patches processed might be limited. When limit is NULL, all patches
1090 * will be handled. 1132 * will be handled.
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
index f6a853adcc00..e6200f38701f 100644
--- a/kernel/livepatch/core.h
+++ b/kernel/livepatch/core.h
@@ -9,6 +9,7 @@ extern struct list_head klp_patches;
9 9
10void klp_free_patch_start(struct klp_patch *patch); 10void klp_free_patch_start(struct klp_patch *patch);
11void klp_discard_replaced_patches(struct klp_patch *new_patch); 11void klp_discard_replaced_patches(struct klp_patch *new_patch);
12void klp_discard_nops(struct klp_patch *new_patch);
12 13
13static inline bool klp_is_object_loaded(struct klp_object *obj) 14static inline bool klp_is_object_loaded(struct klp_object *obj)
14{ 15{
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
index 0ff466ab4b5a..99cb3ad05eb4 100644
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -246,15 +246,26 @@ err:
246 return ret; 246 return ret;
247} 247}
248 248
249void klp_unpatch_object(struct klp_object *obj) 249static void __klp_unpatch_object(struct klp_object *obj, bool nops_only)
250{ 250{
251 struct klp_func *func; 251 struct klp_func *func;
252 252
253 klp_for_each_func(obj, func) 253 klp_for_each_func(obj, func) {
254 if (nops_only && !func->nop)
255 continue;
256
254 if (func->patched) 257 if (func->patched)
255 klp_unpatch_func(func); 258 klp_unpatch_func(func);
259 }
256 260
257 obj->patched = false; 261 if (obj->dynamic || !nops_only)
262 obj->patched = false;
263}
264
265
266void klp_unpatch_object(struct klp_object *obj)
267{
268 __klp_unpatch_object(obj, false);
258} 269}
259 270
260int klp_patch_object(struct klp_object *obj) 271int klp_patch_object(struct klp_object *obj)
@@ -277,11 +288,21 @@ int klp_patch_object(struct klp_object *obj)
277 return 0; 288 return 0;
278} 289}
279 290
280void klp_unpatch_objects(struct klp_patch *patch) 291static void __klp_unpatch_objects(struct klp_patch *patch, bool nops_only)
281{ 292{
282 struct klp_object *obj; 293 struct klp_object *obj;
283 294
284 klp_for_each_object(patch, obj) 295 klp_for_each_object(patch, obj)
285 if (obj->patched) 296 if (obj->patched)
286 klp_unpatch_object(obj); 297 __klp_unpatch_object(obj, nops_only);
298}
299
300void klp_unpatch_objects(struct klp_patch *patch)
301{
302 __klp_unpatch_objects(patch, false);
303}
304
305void klp_unpatch_objects_dynamic(struct klp_patch *patch)
306{
307 __klp_unpatch_objects(patch, true);
287} 308}
diff --git a/kernel/livepatch/patch.h b/kernel/livepatch/patch.h
index a9b16e513656..d5f2fbe373e0 100644
--- a/kernel/livepatch/patch.h
+++ b/kernel/livepatch/patch.h
@@ -30,5 +30,6 @@ struct klp_ops *klp_find_ops(void *old_func);
30int klp_patch_object(struct klp_object *obj); 30int klp_patch_object(struct klp_object *obj);
31void klp_unpatch_object(struct klp_object *obj); 31void klp_unpatch_object(struct klp_object *obj);
32void klp_unpatch_objects(struct klp_patch *patch); 32void klp_unpatch_objects(struct klp_patch *patch);
33void klp_unpatch_objects_dynamic(struct klp_patch *patch);
33 34
34#endif /* _LIVEPATCH_PATCH_H */ 35#endif /* _LIVEPATCH_PATCH_H */
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index f4c5908a9731..300273819674 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -85,8 +85,10 @@ static void klp_complete_transition(void)
85 klp_transition_patch->mod->name, 85 klp_transition_patch->mod->name,
86 klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); 86 klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
87 87
88 if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) 88 if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
89 klp_discard_replaced_patches(klp_transition_patch); 89 klp_discard_replaced_patches(klp_transition_patch);
90 klp_discard_nops(klp_transition_patch);
91 }
90 92
91 if (klp_target_state == KLP_UNPATCHED) { 93 if (klp_target_state == KLP_UNPATCHED) {
92 /* 94 /*