Re: [PATCH v2 2/2] ftrace: Consolidate ftrace_regs accessor functions for archs using pt_regs

From: Steven Rostedt
Date: Wed Oct 09 2024 - 12:00:10 EST




Loongarch maintainers, please note the below comments!


On Tue, 08 Oct 2024 19:05:29 -0400
Steven Rostedt <rostedt@xxxxxxxxxxx> wrote:


> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index bbb69c7751b9..5ccff4de7f09 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -54,6 +54,7 @@ extern void return_to_handler(void);
> unsigned long ftrace_call_adjust(unsigned long addr);
>
> #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
> +#define HAVE_ARCH_FTRACE_REGS
> struct dyn_ftrace;
> struct ftrace_ops;
> struct ftrace_regs;
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 0e15d36ce251..8f13eaeaa325 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -43,43 +43,20 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent);
>
> #ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
> struct ftrace_ops;
> -struct ftrace_regs;
> -#define arch_ftrace_regs(fregs) ((struct __arch_ftrace_regs *)(fregs))
>
> -struct __arch_ftrace_regs {
> - struct pt_regs regs;
> -};
> +#include <linux/ftrace_regs.h>
>
> static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs)
> {
> return &arch_ftrace_regs(fregs)->regs;
> }

The above function is incorrect. I know I just added the comment about how
it is to work below, but if pt_regs is not fully filled, then
arch_ftrace_get_regs() must return NULL.

This is because if a callback is registered with ftrace, and forgets to add
the FTRACE_OPS_FL_SAVE_REGS flag, then when it does:

regs = ftrace_get_regs(fregs);

it should get NULL and not a partially filled pt_regs set. Because the API
is that ftrace_get_regs() will return either a full pt_regs (where the
caller can know that it has all the correct registers) or NULL where it
does not have any registers.

It's an all or nothing approach.

You can see x86 has:

static __always_inline struct pt_regs *
arch_ftrace_get_regs(struct ftrace_regs *fregs)
{
/* Only when FL_SAVE_REGS is set, cs will be non zero */
if (!arch_ftrace_regs(fregs)->regs.cs)
return NULL;
return &arch_ftrace_regs(fregs)->regs;
}

Where it checks if regs.cs is set to determine if it has all the regs or
not.

Please do something similar for your architecture.

>
> -static __always_inline unsigned long
> -ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs)
> -{
> - return instruction_pointer(&arch_ftrace_regs(fregs)->regs);
> -}
> -
> static __always_inline void
> ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
> {
> instruction_pointer_set(&arch_ftrace_regs(fregs)->regs, ip);
> }
>
>


> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f7d4f152f84d..c96f9b0eb86e 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -113,6 +113,8 @@ static inline int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *val
>
> #ifdef CONFIG_FUNCTION_TRACER
>
> +#include <linux/ftrace_regs.h>
> +
> extern int ftrace_enabled;
>
> /**
> @@ -150,14 +152,11 @@ struct ftrace_regs {
> #define ftrace_regs_size() sizeof(struct __arch_ftrace_regs)
>
> #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
> -
> -struct __arch_ftrace_regs {
> - struct pt_regs regs;
> -};
> -
> -struct ftrace_regs;
> -#define arch_ftrace_regs(fregs) ((struct __arch_ftrace_regs *)(fregs))
> -
> +/*
> + * Architectures that define HAVE_DYNAMIC_FTRACE_WITH_ARGS must define their own
> + * arch_ftrace_get_regs() where it only returns pt_regs *if* it is fully
> + * populated. It should return NULL otherwise.
> + */

I'm adding the above comment to help other architectures know of this requirement.

> static inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs)
> {
> return &arch_ftrace_regs(fregs)->regs;


-- Steve