kmod/core: fail more gracefully in kpatch_unregister

In kpatch_unregister(), if kpatch_remove_patch succeeds but one of the
subsequent ftrace unregistering calls fails, it returns an error and
fails to module_put() the patch module, even though the patch has been
removed.  This causes the patch module to get stuck in a weird place
where its patch has been unregistered but the patch module can't ever be
removed.

These errors aren't serious and wouldn't cause any real problems if they
did somehow fail, so instead just WARN if they fail.
This commit is contained in:
Josh Poimboeuf 2014-04-28 10:29:33 -05:00
parent 065619ec68
commit 87d852afa2

View File

@ -321,8 +321,8 @@ static struct ftrace_ops kpatch_ftrace_ops __read_mostly = {
};
/* Remove kpatch_funcs from ftrace filter */
static int kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
int num_funcs)
static void kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
int num_funcs)
{
int i, ret = 0;
@ -339,14 +339,8 @@ static int kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
/* Remove the ftrace handler for this function. */
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, func->old_addr,
1, 0);
if (ret) {
pr_err("can't remove ftrace filter at address 0x%lx\n",
func->old_addr);
break;
}
WARN_ON(ret);
}
return ret;
}
int kpatch_register(struct kpatch_module *kpmod)
@ -480,12 +474,11 @@ int kpatch_unregister(struct kpatch_module *kpmod)
if (kpatch_num_registered == 1) {
ret = unregister_ftrace_function(&kpatch_ftrace_ops);
if (ret) {
pr_err("can't unregister ftrace handler\n");
goto out;
}
if (ret)
WARN_ON(1);
else
kpatch_num_registered--;
}
kpatch_num_registered--;
/*
* This synchronize_rcu is to ensure any other kpatch_get_func
@ -494,9 +487,7 @@ int kpatch_unregister(struct kpatch_module *kpmod)
*/
synchronize_rcu();
ret = kpatch_remove_funcs_from_filter(funcs, num_funcs);
if (ret)
goto out;
kpatch_remove_funcs_from_filter(funcs, num_funcs);
pr_notice("unloaded patch module \"%s\"\n", kpmod->mod->name);