diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2015-02-09 12:31:13 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2015-02-18 15:06:38 -0500 |
commit | 0937e3b025f70e33f018aa55ee8d32b8731730a7 (patch) | |
tree | f8bed0ffeae83f15e02f6f60bd1d910d9791836b /kernel/livepatch | |
parent | 4421f8f0fa02bc982b410cd773223cc280791c54 (diff) |
livepatch: simplify disable error path
If registering the function with ftrace has previously succeeded,
unregistering will almost never fail. Even if it does, it's not a fatal
error. We can still carry on and disable the klp_func from being used
by removing it from the klp_ops func stack.
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Reviewed-by: Petr Mladek <pmladek@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel/livepatch')
-rw-r--r-- | kernel/livepatch/core.c | 67 |
1 files changed, 17 insertions, 50 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index ff7f47d026ac..26df09d56f7c 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
@@ -322,32 +322,20 @@ static void notrace klp_ftrace_handler(unsigned long ip, | |||
322 | klp_arch_set_pc(regs, (unsigned long)func->new_func); | 322 | klp_arch_set_pc(regs, (unsigned long)func->new_func); |
323 | } | 323 | } |
324 | 324 | ||
325 | static int klp_disable_func(struct klp_func *func) | 325 | static void klp_disable_func(struct klp_func *func) |
326 | { | 326 | { |
327 | struct klp_ops *ops; | 327 | struct klp_ops *ops; |
328 | int ret; | ||
329 | |||
330 | if (WARN_ON(func->state != KLP_ENABLED)) | ||
331 | return -EINVAL; | ||
332 | 328 | ||
333 | if (WARN_ON(!func->old_addr)) | 329 | WARN_ON(func->state != KLP_ENABLED); |
334 | return -EINVAL; | 330 | WARN_ON(!func->old_addr); |
335 | 331 | ||
336 | ops = klp_find_ops(func->old_addr); | 332 | ops = klp_find_ops(func->old_addr); |
337 | if (WARN_ON(!ops)) | 333 | if (WARN_ON(!ops)) |
338 | return -EINVAL; | 334 | return; |
339 | 335 | ||
340 | if (list_is_singular(&ops->func_stack)) { | 336 | if (list_is_singular(&ops->func_stack)) { |
341 | ret = unregister_ftrace_function(&ops->fops); | 337 | WARN_ON(unregister_ftrace_function(&ops->fops)); |
342 | if (ret) { | 338 | WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0)); |
343 | pr_err("failed to unregister ftrace handler for function '%s' (%d)\n", | ||
344 | func->old_name, ret); | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0); | ||
349 | if (ret) | ||
350 | pr_warn("function unregister succeeded but failed to clear the filter\n"); | ||
351 | 339 | ||
352 | list_del_rcu(&func->stack_node); | 340 | list_del_rcu(&func->stack_node); |
353 | list_del(&ops->node); | 341 | list_del(&ops->node); |
@@ -357,8 +345,6 @@ static int klp_disable_func(struct klp_func *func) | |||
357 | } | 345 | } |
358 | 346 | ||
359 | func->state = KLP_DISABLED; | 347 | func->state = KLP_DISABLED; |
360 | |||
361 | return 0; | ||
362 | } | 348 | } |
363 | 349 | ||
364 | static int klp_enable_func(struct klp_func *func) | 350 | static int klp_enable_func(struct klp_func *func) |
@@ -419,23 +405,15 @@ err: | |||
419 | return ret; | 405 | return ret; |
420 | } | 406 | } |
421 | 407 | ||
422 | static int klp_disable_object(struct klp_object *obj) | 408 | static void klp_disable_object(struct klp_object *obj) |
423 | { | 409 | { |
424 | struct klp_func *func; | 410 | struct klp_func *func; |
425 | int ret; | ||
426 | 411 | ||
427 | for (func = obj->funcs; func->old_name; func++) { | 412 | for (func = obj->funcs; func->old_name; func++) |
428 | if (func->state != KLP_ENABLED) | 413 | if (func->state == KLP_ENABLED) |
429 | continue; | 414 | klp_disable_func(func); |
430 | |||
431 | ret = klp_disable_func(func); | ||
432 | if (ret) | ||
433 | return ret; | ||
434 | } | ||
435 | 415 | ||
436 | obj->state = KLP_DISABLED; | 416 | obj->state = KLP_DISABLED; |
437 | |||
438 | return 0; | ||
439 | } | 417 | } |
440 | 418 | ||
441 | static int klp_enable_object(struct klp_object *obj) | 419 | static int klp_enable_object(struct klp_object *obj) |
@@ -451,22 +429,19 @@ static int klp_enable_object(struct klp_object *obj) | |||
451 | 429 | ||
452 | for (func = obj->funcs; func->old_name; func++) { | 430 | for (func = obj->funcs; func->old_name; func++) { |
453 | ret = klp_enable_func(func); | 431 | ret = klp_enable_func(func); |
454 | if (ret) | 432 | if (ret) { |
455 | goto unregister; | 433 | klp_disable_object(obj); |
434 | return ret; | ||
435 | } | ||
456 | } | 436 | } |
457 | obj->state = KLP_ENABLED; | 437 | obj->state = KLP_ENABLED; |
458 | 438 | ||
459 | return 0; | 439 | return 0; |
460 | |||
461 | unregister: | ||
462 | WARN_ON(klp_disable_object(obj)); | ||
463 | return ret; | ||
464 | } | 440 | } |
465 | 441 | ||
466 | static int __klp_disable_patch(struct klp_patch *patch) | 442 | static int __klp_disable_patch(struct klp_patch *patch) |
467 | { | 443 | { |
468 | struct klp_object *obj; | 444 | struct klp_object *obj; |
469 | int ret; | ||
470 | 445 | ||
471 | /* enforce stacking: only the last enabled patch can be disabled */ | 446 | /* enforce stacking: only the last enabled patch can be disabled */ |
472 | if (!list_is_last(&patch->list, &klp_patches) && | 447 | if (!list_is_last(&patch->list, &klp_patches) && |
@@ -476,12 +451,8 @@ static int __klp_disable_patch(struct klp_patch *patch) | |||
476 | pr_notice("disabling patch '%s'\n", patch->mod->name); | 451 | pr_notice("disabling patch '%s'\n", patch->mod->name); |
477 | 452 | ||
478 | for (obj = patch->objs; obj->funcs; obj++) { | 453 | for (obj = patch->objs; obj->funcs; obj++) { |
479 | if (obj->state != KLP_ENABLED) | 454 | if (obj->state == KLP_ENABLED) |
480 | continue; | 455 | klp_disable_object(obj); |
481 | |||
482 | ret = klp_disable_object(obj); | ||
483 | if (ret) | ||
484 | return ret; | ||
485 | } | 456 | } |
486 | 457 | ||
487 | patch->state = KLP_DISABLED; | 458 | patch->state = KLP_DISABLED; |
@@ -931,7 +902,6 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
931 | { | 902 | { |
932 | struct module *pmod = patch->mod; | 903 | struct module *pmod = patch->mod; |
933 | struct module *mod = obj->mod; | 904 | struct module *mod = obj->mod; |
934 | int ret; | ||
935 | 905 | ||
936 | if (patch->state == KLP_DISABLED) | 906 | if (patch->state == KLP_DISABLED) |
937 | goto disabled; | 907 | goto disabled; |
@@ -939,10 +909,7 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
939 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | 909 | pr_notice("reverting patch '%s' on unloading module '%s'\n", |
940 | pmod->name, mod->name); | 910 | pmod->name, mod->name); |
941 | 911 | ||
942 | ret = klp_disable_object(obj); | 912 | klp_disable_object(obj); |
943 | if (ret) | ||
944 | pr_warn("failed to revert patch '%s' on module '%s' (%d)\n", | ||
945 | pmod->name, mod->name, ret); | ||
946 | 913 | ||
947 | disabled: | 914 | disabled: |
948 | klp_free_object_loaded(obj); | 915 | klp_free_object_loaded(obj); |