Re: [PATCH 2/5] livepatch: Allow architectures to specify an alternate ftrace location

From: Miroslav Benes
Date: Thu Apr 14 2016 - 08:01:31 EST


On Wed, 13 Apr 2016, Michael Ellerman wrote:

> When livepatch tries to patch a function it takes the function address
> and asks ftrace to install the livepatch handler at that location.
> ftrace will look for an mcount call site at that exact address.
>
> On powerpc the mcount location is not the first instruction of the
> function, and in fact it's not at a constant offset from the start of
> the function. To accommodate this add a hook which arch code can
> override to customise the behaviour.
>
> Signed-off-by: Torsten Duwe <duwe@xxxxxxx>
> Signed-off-by: Balbir Singh <bsingharora@xxxxxxxxx>
> Signed-off-by: Petr Mladek <pmladek@xxxxxxxx>
> Signed-off-by: Michael Ellerman <mpe@xxxxxxxxxxxxxx>
> ---
> kernel/livepatch/core.c | 34 +++++++++++++++++++++++++++++++---
> 1 file changed, 31 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index d68fbf63b083..b0476bb30f92 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -298,6 +298,19 @@ unlock:
> rcu_read_unlock();
> }
>
> +/*
> + * Convert a function address into the appropriate ftrace location.
> + *
> + * Usually this is just the address of the function, but on some architectures
> + * it's more complicated so allow them to provide a custom behaviour.
> + */
> +#ifndef klp_get_ftrace_location
> +static unsigned long klp_get_ftrace_location(unsigned long faddr)
> +{
> + return faddr;
> +}
> +#endif

Whoah, what an ugly hack :)

Note to my future self - This is what you want to do if you need a weak
static inline function.

static inline is probably unnecessary here so __weak function would be
enough. It would introduce powerpc-specific livepatch.c though because of
it and this is not worth it.

> static void klp_disable_func(struct klp_func *func)
> {
> struct klp_ops *ops;
> @@ -312,8 +325,14 @@ static void klp_disable_func(struct klp_func *func)
> return;
>
> if (list_is_singular(&ops->func_stack)) {
> + unsigned long ftrace_loc;

This is a nit, but could you move the definition up to have them all in
one place to be consistent with the rest of the code? The same applies to
klp_enable_func() below.

> +
> + ftrace_loc = klp_get_ftrace_location(func->old_addr);
> + if (WARN_ON(!ftrace_loc))
> + return;
> +
> WARN_ON(unregister_ftrace_function(&ops->fops));
> - WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0));
> + WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
>
> list_del_rcu(&func->stack_node);
> list_del(&ops->node);
> @@ -338,6 +357,15 @@ static int klp_enable_func(struct klp_func *func)
>
> ops = klp_find_ops(func->old_addr);
> if (!ops) {
> + unsigned long ftrace_loc;

Here.

> +
> + ftrace_loc = klp_get_ftrace_location(func->old_addr);
> + if (!ftrace_loc) {
> + pr_err("failed to find location for function '%s'\n",
> + func->old_name);
> + return -EINVAL;
> + }
> +
> ops = kzalloc(sizeof(*ops), GFP_KERNEL);
> if (!ops)
> return -ENOMEM;
> @@ -352,7 +380,7 @@ static int klp_enable_func(struct klp_func *func)
> INIT_LIST_HEAD(&ops->func_stack);
> list_add_rcu(&func->stack_node, &ops->func_stack);
>
> - ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 0, 0);
> + ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
> if (ret) {
> pr_err("failed to set ftrace filter for function '%s' (%d)\n",
> func->old_name, ret);
> @@ -363,7 +391,7 @@ static int klp_enable_func(struct klp_func *func)
> if (ret) {
> pr_err("failed to register ftrace handler for function '%s' (%d)\n",
> func->old_name, ret);
> - ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0);
> + ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
> goto err;
> }

Otherwise it is ok.

Miroslav