Skip to contentSkip to conversation

powerpc/ftrace: Move ftrace sequence out of line

RFC PATCH Superseded Naveen N Rao 3 participants 16 messages Jun 10, 2024 – Jun 12, 2024

Subsystems (3)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover powerpc/ftrace: Move ftrace sequence out of line

This is v2 of the series posted here:
http://lkml.kernel.org/r/[email protected]

Since v2, the primary change is that the entire ftrace sequence is moved out of line and this is now restricted to 64-bit powerpc by default. Patch 5 has the details.

I have dropped patches to enable DYNAMIC_FTRACE_WITH_CALL_OPS and ftrace direct support so that this approach can be finalized.

This series depends on Benjamin Gray's series adding support for patch_ulong():
http://lkml.kernel.org/r/[email protected]

Appreciate feedback on the approach.

Thanks,
Naveen

Naveen N Rao (5):
powerpc/kprobes: Use ftrace to determine if a probe is at function
entry
powerpc/ftrace: Remove pointer to struct module from dyn_arch_ftrace powerpc/ftrace: Unify 32-bit and 64-bit ftrace entry code
kbuild: Add generic hook for architectures to use before the final
vmlinux link
powerpc64/ftrace: Move ftrace sequence out of line

 arch/Kconfig                             |   3 +
 arch/powerpc/Kconfig                     |   4 +
 arch/powerpc/Makefile                    |   4 +
 arch/powerpc/include/asm/ftrace.h        |  11 +-
 arch/powerpc/include/asm/module.h        |   5 +
 arch/powerpc/kernel/asm-offsets.c        |   4 +
 arch/powerpc/kernel/kprobes.c            |  18 +--
 arch/powerpc/kernel/module_64.c          |  67 +++++++-
 arch/powerpc/kernel/trace/ftrace.c       | 196 ++++++++++++++++++++---
 arch/powerpc/kernel/trace/ftrace_64_pg.c |  73 ++++-----
 arch/powerpc/kernel/trace/ftrace_entry.S |  75 ++++++---
 arch/powerpc/kernel/vmlinux.lds.S        |   3 +-
 arch/powerpc/tools/vmlinux_o.sh          |  47 ++++++
 scripts/link-vmlinux.sh                  |  18 ++-
 14 files changed, 419 insertions(+), 109 deletions(-)
 create mode 100755 arch/powerpc/tools/vmlinux_o.sh

base-commit: 2c644f2847c188b4fa545e602e4a1d4db55e8c8d
prerequisite-patch-id: a1d50e589288239d5a8b1c1f354cd4737057c9d3
prerequisite-patch-id: da4142d56880861bd0ad7ad7087c9e2670a2ee54
prerequisite-patch-id: 609d292e054b2396b603890522a940fa0bdfb6d8
prerequisite-patch-id: 6f7213fb77b1260defbf43be0e47bff9c80054cc
prerequisite-patch-id: ad3b71bf071ae4ba1bee5b087e61a2055772a74f

Signature · 1 line
2.45.2
#1 lore raw

RFC PATCH v2 1/5 powerpc/kprobes: Use ftrace to determine if a probe is at function entry

Rather than hard-coding the offset into a function to be used to determine if a kprobe is at function entry, use ftrace_location() to determine the ftrace location within the function and categorize all instructions till that offset to be function entry.

For functions that cannot be traced, we fall back to using a fixed offset of 8 (two instructions) to categorize a probe as being at function entry for 64-bit elfv2, unless we are using pcrel.

---
 arch/powerpc/kernel/kprobes.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)
arch/powerpc/kernel/kprobes.c +8 -10
index 14c5ddec3056..ca204f4f21c1 100644
@@ -105,24 +105,22 @@ kprobe_opcode_t *kprobe_lookup_name(const char *name, unsigned int offset)
 	return addr;
 }
 
-static bool arch_kprobe_on_func_entry(unsigned long offset)
+static bool arch_kprobe_on_func_entry(unsigned long addr, unsigned long offset)
 {
-#ifdef CONFIG_PPC64_ELF_ABI_V2
-#ifdef CONFIG_KPROBES_ON_FTRACE
-	return offset <= 16;
-#else
-	return offset <= 8;
-#endif
-#else
+	unsigned long ip = ftrace_location(addr);
+
+	if (ip)
+		return offset <= (ip - addr);
+	if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2) && !IS_ENABLED(CONFIG_PPC_KERNEL_PCREL))
+		return offset <= 8;
 	return !offset;
-#endif
 }
 
 /* XXX try and fold the magic of kprobe_lookup_name() in this */
 kprobe_opcode_t *arch_adjust_kprobe_addr(unsigned long addr, unsigned long offset,
 					 bool *on_func_entry)
 {
-	*on_func_entry = arch_kprobe_on_func_entry(offset);
+	*on_func_entry = arch_kprobe_on_func_entry(addr, offset);
 	return (kprobe_opcode_t *)(addr + offset);
 }
 
Signature · 1 line
2.45.2
#2 lore raw

RFC PATCH v2 2/5 powerpc/ftrace: Remove pointer to struct module from dyn_arch_ftrace

In reply to Naveen N Rao

Pointer to struct module is only relevant for ftrace records belonging to kernel modules. Having this field in dyn_arch_ftrace wastes memory for all ftrace records belonging to the kernel. Remove the same in favour of looking up the module from the ftrace record address, similar to other architectures.

---
 arch/powerpc/include/asm/ftrace.h        |  1 -
 arch/powerpc/kernel/trace/ftrace.c       | 47 ++++++++++-----
 arch/powerpc/kernel/trace/ftrace_64_pg.c | 73 +++++++++++-------------
 3 files changed, 64 insertions(+), 57 deletions(-)
arch/powerpc/include/asm/ftrace.h +0 -1
index 107fc5a48456..201f9d15430a 100644
@@ -26,7 +26,6 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
 struct module;
 struct dyn_ftrace;
 struct dyn_arch_ftrace {
-	struct module *mod;
 };
 
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
arch/powerpc/kernel/trace/ftrace.c +32 -15

73 lines not on this page · raw message

arch/powerpc/kernel/trace/ftrace_64_pg.c +32 -41

115 lines not on this page · raw message

Signature · 1 line
2.45.2
maintainer
#3 lore raw

Re: [RFC PATCH v2 2/5] powerpc/ftrace: Remove pointer to struct module from dyn_arch_ftrace

Quoted 18 lines · On Mon, 10 Jun 2024 14:08:15 +0530 Naveen N Rao <[email protected]> wrote:

Pointer to struct module is only relevant for ftrace records belonging to kernel modules. Having this field in dyn_arch_ftrace wastes memory for all ftrace records belonging to the kernel. Remove the same in favour of looking up the module from the ftrace record address, similar to other architectures.

---
 arch/powerpc/include/asm/ftrace.h        |  1 -
 arch/powerpc/kernel/trace/ftrace.c       | 47 ++++++++++-----
 arch/powerpc/kernel/trace/ftrace_64_pg.c | 73 +++++++++++-------------
 3 files changed, 64 insertions(+), 57 deletions(-)
arch/powerpc/include/asm/ftrace.h +0 -0
index 107fc5a48456..201f9d15430a 100644

arch/powerpc/include/asm/ftrace.h unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip, · line 29

Quoted 5 lines
arch/powerpc/include/asm/ftrace.h +0 -1
@@ -26,7 +26,6 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
 struct module;
 struct dyn_ftrace;
 struct dyn_arch_ftrace {
-	struct module *mod;
 };

Nice. I always hated that extra field.

Quoted 20 lines
arch/powerpc/include/asm/ftrace.h +0 -0
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
arch/powerpc/kernel/trace/ftrace.c +10 -0
index d8d6b4fd9a14..041be965485e 100644
@@ -106,20 +106,36 @@ static unsigned long find_ftrace_tramp(unsigned long ip)
 	return 0;
 }
 
+static struct module *ftrace_lookup_module(struct dyn_ftrace *rec)
+{
+	struct module *mod = NULL;
+
+#ifdef CONFIG_MODULES
+	/*
+	 * NOTE: __module_text_address() must be called with preemption
+	 * disabled, but we can rely on ftrace_lock to ensure that 'mod'
+	 * retains its validity throughout the remainder of this code.
+	*/

arch/powerpc/kernel/trace/ftrace.c static unsigned long find_ftrace_tramp(unsigned long ip) · line 128

Quoted 10 lines
arch/powerpc/kernel/trace/ftrace.c +10 -0
+	preempt_disable();
+	mod = __module_text_address(rec->ip);
+	preempt_enable();
+
+	if (!mod)
+		pr_err("No module loaded at addr=%lx\n", rec->ip);
+#endif
+
+	return mod;
+}

It may look nicer to have:

#ifdef CONFIG_MODULES
static struct module *ftrace_lookup_module(struct dyn_ftrace *rec)

{
	struct module *mod = NULL;

	/*
	 * NOTE: __module_text_address() must be called with preemption
	 * disabled, but we can rely on ftrace_lock to ensure that 'mod'
	 * retains its validity throughout the remainder of this code.
	*/
	preempt_disable();
	mod = __module_text_address(rec->ip);
	preempt_enable();

	if (!mod)
		pr_err("No module loaded at addr=%lx\n", rec->ip);

	return mod;
}
#else
static inline struct module *ftrace_lookup_module(struct dyn_ftrace *rec)
{
	return NULL;
}

#endif

Quoted 21 lines
arch/powerpc/kernel/trace/ftrace.c +2 -6
+
 static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_inst_t *call_inst)
 {
 	unsigned long ip = rec->ip;
 	unsigned long stub;
+	struct module *mod;
 
 	if (is_offset_in_branch_range(addr - ip)) {
 		/* Within range */
 		stub = addr;
-#ifdef CONFIG_MODULES
-	} else if (rec->arch.mod) {
-		/* Module code would be going to one of the module stubs */
-		stub = (addr == (unsigned long)ftrace_caller ? rec->arch.mod->arch.tramp :
-							       rec->arch.mod->arch.tramp_regs);
-#endif
 	} else if (core_kernel_text(ip)) {
 		/* We would be branching to one of our ftrace stubs */
 		stub = find_ftrace_tramp(ip);
@@ -128,7 +144,16 @@ static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_
 			return -EINVAL;

arch/powerpc/kernel/trace/ftrace.c static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_ · line 153

Quoted 10 lines
arch/powerpc/kernel/trace/ftrace.c +7 -1
 		}
 	} else {
-		return -EINVAL;
+		mod = ftrace_lookup_module(rec);
+		if (mod) {
+#ifdef CONFIG_MODULES
+			/* Module code would be going to one of the module stubs */
+			stub = (addr == (unsigned long)ftrace_caller ? mod->arch.tramp :
+								       mod->arch.tramp_regs);
+#endif

You have CONFIG_MODULES here and in ftrace_lookup_module() above, which would always return NULL. Could you combine the above to be done in ftrace_lookup_module() so that there's no #ifdef CONFIG_MODULES here?

Quoted 11 lines
arch/powerpc/kernel/trace/ftrace.c +3 -1
+		} else {
+			return -EINVAL;
+		}
 	}
 
 	*call_inst = ftrace_create_branch_inst(ip, stub, 1);
@@ -256,14 +281,6 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
 	if (ret)
 		return ret;
 
-	if (!core_kernel_text(ip)) {

arch/powerpc/kernel/trace/ftrace.c int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) · line 286

Quoted 10 lines
arch/powerpc/kernel/trace/ftrace.c +0 -7
-		if (!mod) {
-			pr_err("0x%lx: No module provided for non-kernel address\n", ip);
-			return -EFAULT;
-		}
-		rec->arch.mod = mod;
-	}
-
 	/* Nop-out the ftrace location */
 	new = ppc_inst(PPC_RAW_NOP());
 	addr = MCOUNT_ADDR;

-- Steve

#4 lore raw

Re: [RFC PATCH v2 2/5] powerpc/ftrace: Remove pointer to struct module from dyn_arch_ftrace

Quoted 16 lines · Steven Rostedt
Quoted 13 lines · On Mon, 10 Jun 2024 14:08:15 +0530 Naveen N Rao <[email protected]> wrote:

Pointer to struct module is only relevant for ftrace records belonging to kernel modules. Having this field in dyn_arch_ftrace wastes memory for all ftrace records belonging to the kernel. Remove the same in favour of looking up the module from the ftrace record address, similar to other architectures.

---
 arch/powerpc/include/asm/ftrace.h        |  1 -
 arch/powerpc/kernel/trace/ftrace.c       | 47 ++++++++++-----
 arch/powerpc/kernel/trace/ftrace_64_pg.c | 73 +++++++++++-------------
 3 files changed, 64 insertions(+), 57 deletions(-)
Quoted 12 lines

arch/powerpc/include/asm/ftrace.h unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip, · line 29

Quoted 10 lines
arch/powerpc/include/asm/ftrace.h +0 -1
index 107fc5a48456..201f9d15430a 100644
@@ -26,7 +26,6 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
 struct module;
 struct dyn_ftrace;
 struct dyn_arch_ftrace {
-	struct module *mod;
 };

Nice. I always hated that extra field.

It was your complaint a while back that prompted this change :)

Though I introduce a different pointer here in the next patch. /me ducks.

Quoted 59 lines
Quoted 22 lines
arch/powerpc/include/asm/ftrace.h +0 -0
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
arch/powerpc/kernel/trace/ftrace.c +10 -0
index d8d6b4fd9a14..041be965485e 100644
@@ -106,20 +106,36 @@ static unsigned long find_ftrace_tramp(unsigned long ip)
 	return 0;
 }
 
+static struct module *ftrace_lookup_module(struct dyn_ftrace *rec)
+{
+	struct module *mod = NULL;
+
+#ifdef CONFIG_MODULES
+	/*
+	 * NOTE: __module_text_address() must be called with preemption
+	 * disabled, but we can rely on ftrace_lock to ensure that 'mod'
+	 * retains its validity throughout the remainder of this code.
+	*/

arch/powerpc/kernel/trace/ftrace.c static unsigned long find_ftrace_tramp(unsigned long ip) · line 128

Quoted 10 lines
arch/powerpc/kernel/trace/ftrace.c +10 -0
+	preempt_disable();
+	mod = __module_text_address(rec->ip);
+	preempt_enable();
+
+	if (!mod)
+		pr_err("No module loaded at addr=%lx\n", rec->ip);
+#endif
+
+	return mod;
+}

It may look nicer to have:

#ifdef CONFIG_MODULES
static struct module *ftrace_lookup_module(struct dyn_ftrace *rec)

{
	struct module *mod = NULL;

	/*
	 * NOTE: __module_text_address() must be called with preemption
	 * disabled, but we can rely on ftrace_lock to ensure that 'mod'
	 * retains its validity throughout the remainder of this code.
	*/
	preempt_disable();
	mod = __module_text_address(rec->ip);
	preempt_enable();

	if (!mod)
		pr_err("No module loaded at addr=%lx\n", rec->ip);

	return mod;
}
#else
static inline struct module *ftrace_lookup_module(struct dyn_ftrace *rec)
{
	return NULL;
}
Quoted 1 line

#endif

I wrote this, and then I thought it will be simpler to do the version I posted. I will move back to this since it looks to be the preferred way.

Quoted 28 lines
Quoted 28 lines
arch/powerpc/kernel/trace/ftrace.c +5 -7
+
 static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_inst_t *call_inst)
 {
 	unsigned long ip = rec->ip;
 	unsigned long stub;
+	struct module *mod;
 
 	if (is_offset_in_branch_range(addr - ip)) {
 		/* Within range */
 		stub = addr;
-#ifdef CONFIG_MODULES
-	} else if (rec->arch.mod) {
-		/* Module code would be going to one of the module stubs */
-		stub = (addr == (unsigned long)ftrace_caller ? rec->arch.mod->arch.tramp :
-							       rec->arch.mod->arch.tramp_regs);
-#endif
 	} else if (core_kernel_text(ip)) {
 		/* We would be branching to one of our ftrace stubs */
 		stub = find_ftrace_tramp(ip);
@@ -128,7 +144,16 @@ static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_
 			return -EINVAL;
 		}
 	} else {
-		return -EINVAL;
+		mod = ftrace_lookup_module(rec);
+		if (mod) {
+#ifdef CONFIG_MODULES
Quoted 8 lines

arch/powerpc/kernel/trace/ftrace.c static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_ · line 153

Quoted 4 lines
arch/powerpc/kernel/trace/ftrace.c +4 -0
+			/* Module code would be going to one of the module stubs */
+			stub = (addr == (unsigned long)ftrace_caller ? mod->arch.tramp :
+								       mod->arch.tramp_regs);
+#endif

You have CONFIG_MODULES here and in ftrace_lookup_module() above, which would always return NULL. Could you combine the above to be done in ftrace_lookup_module() so that there's no #ifdef CONFIG_MODULES here?

Yes, indeed. That will look cleaner.

Thanks,
Naveen

#5 lore raw

RFC PATCH v2 3/5 powerpc/ftrace: Unify 32-bit and 64-bit ftrace entry code

In reply to Naveen N Rao

On 32-bit powerpc, gcc generates a three instruction sequence for function profiling:

	mflr	r0
	stw	r0, 4(r1)
	bl	_mcount

On kernel boot, the call to _mcount() is nop-ed out, to be patched back in when ftrace is actually enabled. The 'stw' instruction therefore is not necessary unless ftrace is enabled. Nop it out during ftrace init.

When ftrace is enabled, we want the 'stw' so that stack unwinding works properly. Perform the same within the ftrace handler, similar to 64-bit powerpc.

For 64-bit powerpc, early versions of gcc used to emit a three instruction sequence for function profiling (with -mprofile-kernel) with a 'std' instruction to mimic the 'stw' above. Address that scenario also by nop-ing out the 'std' instruction during ftrace init.

---
 arch/powerpc/kernel/trace/ftrace.c       | 6 ++++--
 arch/powerpc/kernel/trace/ftrace_entry.S | 4 ++--
 2 files changed, 6 insertions(+), 4 deletions(-)
arch/powerpc/kernel/trace/ftrace.c +4 -2
index 041be965485e..2e1667a578ff 100644
@@ -266,13 +266,15 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
 		/* Expected sequence: 'mflr r0', 'stw r0,4(r1)', 'bl _mcount' */
 		ret = ftrace_validate_inst(ip - 8, ppc_inst(PPC_RAW_MFLR(_R0)));
 		if (!ret)
-			ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_STW(_R0, _R1, 4)));
+			ret = ftrace_modify_code(ip - 4, ppc_inst(PPC_RAW_STW(_R0, _R1, 4)),
+						 ppc_inst(PPC_RAW_NOP()));
 	} else if (IS_ENABLED(CONFIG_MPROFILE_KERNEL)) {
 		/* Expected sequence: 'mflr r0', ['std r0,16(r1)'], 'bl _mcount' */
 		ret = ftrace_read_inst(ip - 4, &old);
 		if (!ret && !ppc_inst_equal(old, ppc_inst(PPC_RAW_MFLR(_R0)))) {
 			ret = ftrace_validate_inst(ip - 8, ppc_inst(PPC_RAW_MFLR(_R0)));
-			ret |= ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_STD(_R0, _R1, 16)));
+			ret |= ftrace_modify_code(ip - 4, ppc_inst(PPC_RAW_STD(_R0, _R1, 16)),
+						  ppc_inst(PPC_RAW_NOP()));
 		}
 	} else {
 		return -EINVAL;
arch/powerpc/kernel/trace/ftrace_entry.S +2 -2
index 76dbe9fd2c0f..244a1c7bb1e8 100644
@@ -33,6 +33,8 @@
  * and then arrange for the ftrace function to be called.
  */
 .macro	ftrace_regs_entry allregs
+	/* Save the original return address in A's stack frame */
+	PPC_STL		r0, LRSAVE(r1)
 	/* Create a minimal stack frame for representing B */
 	PPC_STLU	r1, -STACK_FRAME_MIN_SIZE(r1)
 
@@ -44,8 +46,6 @@
 	SAVE_GPRS(3, 10, r1)
 
 #ifdef CONFIG_PPC64
-	/* Save the original return address in A's stack frame */
-	std	r0, LRSAVE+SWITCH_FRAME_SIZE+STACK_FRAME_MIN_SIZE(r1)
 	/* Ok to continue? */
 	lbz	r3, PACA_FTRACE_ENABLED(r13)
 	cmpdi	r3, 0
Signature · 1 line
2.45.2
maintainer
#6 lore raw

Re: [RFC PATCH v2 3/5] powerpc/ftrace: Unify 32-bit and 64-bit ftrace entry code

Quoted 14 lines · On Mon, 10 Jun 2024 14:08:16 +0530 Naveen N Rao <[email protected]> wrote:

On 32-bit powerpc, gcc generates a three instruction sequence for function profiling:

	mflr	r0
	stw	r0, 4(r1)
	bl	_mcount

On kernel boot, the call to _mcount() is nop-ed out, to be patched back in when ftrace is actually enabled. The 'stw' instruction therefore is not necessary unless ftrace is enabled. Nop it out during ftrace init.

When ftrace is enabled, we want the 'stw' so that stack unwinding works properly. Perform the same within the ftrace handler, similar to 64-bit powerpc.

Quoted 6 lines

For 64-bit powerpc, early versions of gcc used to emit a three instruction sequence for function profiling (with -mprofile-kernel) with a 'std' instruction to mimic the 'stw' above. Address that scenario also by nop-ing out the 'std' instruction during ftrace init.

Isn't there still the race that there's a preemption between the:

	stw	r0, 4(r1)

and
bl _mcount

And if this breaks stack unwinding, couldn't this cause an issue for live kernel patching?

I know it's very unlikely, but in theory, I think the race exists.

-- Steve

#7 lore raw

Re: [RFC PATCH v2 3/5] powerpc/ftrace: Unify 32-bit and 64-bit ftrace entry code

Quoted 26 lines · Steven Rostedt
Quoted 14 lines · On Mon, 10 Jun 2024 14:08:16 +0530 Naveen N Rao <[email protected]> wrote:

On 32-bit powerpc, gcc generates a three instruction sequence for function profiling:

	mflr	r0
	stw	r0, 4(r1)
	bl	_mcount

On kernel boot, the call to _mcount() is nop-ed out, to be patched back in when ftrace is actually enabled. The 'stw' instruction therefore is not necessary unless ftrace is enabled. Nop it out during ftrace init.

When ftrace is enabled, we want the 'stw' so that stack unwinding works properly. Perform the same within the ftrace handler, similar to 64-bit powerpc.

Quoted 6 lines

For 64-bit powerpc, early versions of gcc used to emit a three instruction sequence for function profiling (with -mprofile-kernel) with a 'std' instruction to mimic the 'stw' above. Address that scenario also by nop-ing out the 'std' instruction during ftrace init.

Isn't there still the race that there's a preemption between the:

Quoted 8 lines
	stw	r0, 4(r1)

and
bl _mcount

And if this breaks stack unwinding, couldn't this cause an issue for live kernel patching?

I know it's very unlikely, but in theory, I think the race exists.

I *think* you are assuming that we will be patching back the 'stw' instruction here? So, there could be an issue if a cpu has executed the nop instead of 'stw' and then sees the call to _mcount().

But, we don't patch back the 'stw' instruction. That is instead done as part of ftrace_caller(), along with setting up an additional stack frame to ensure reliable stack unwinding. Commit 41a506ef71eb ("powerpc/ftrace: Create a dummy stackframe to fix stack unwind") has more details.

The primary motivation for this patch is to address differences in the function profile sequence with various toolchains. Since commit 0f71dcfb4aef ("powerpc/ftrace: Add support for -fpatchable-function-entry"), we use the same two-instruction profile sequence across 32-bit and 64-bit powerpc:

	mflr	r0
	bl	ftrace_caller

This has also been true on 64-bit powerpc with -mprofile-kernel, except the very early versions of gcc that supported that option (gcc v5).

On 32-bit powerpc, we used to use the three instruction sequence before support for -fpatchable-function-entry was introduced.

In this patch, we move all toolchain variants to use the two-instruction sequence for consistency.

Thanks,
Naveen

maintainer
#8 lore raw

Re: [RFC PATCH v2 3/5] powerpc/ftrace: Unify 32-bit and 64-bit ftrace entry code

Quoted 3 lines · On Tue, 11 Jun 2024 20:17:19 +0530 Naveen N Rao <[email protected]> wrote:
Quoted 1 line

I know it's very unlikely, but in theory, I think the race exists.

I *think* you are assuming that we will be patching back the 'stw'

Yes, that was what I was assuming :-p

Quoted 17 lines

instruction here? So, there could be an issue if a cpu has executed the nop instead of 'stw' and then sees the call to _mcount().

But, we don't patch back the 'stw' instruction. That is instead done as part of ftrace_caller(), along with setting up an additional stack frame to ensure reliable stack unwinding. Commit 41a506ef71eb ("powerpc/ftrace: Create a dummy stackframe to fix stack unwind") has more details.

The primary motivation for this patch is to address differences in the function profile sequence with various toolchains. Since commit 0f71dcfb4aef ("powerpc/ftrace: Add support for -fpatchable-function-entry"), we use the same two-instruction profile sequence across 32-bit and 64-bit powerpc:

	mflr	r0
	bl	ftrace_caller
Quoted 8 lines

This has also been true on 64-bit powerpc with -mprofile-kernel, except the very early versions of gcc that supported that option (gcc v5).

On 32-bit powerpc, we used to use the three instruction sequence before support for -fpatchable-function-entry was introduced.

In this patch, we move all toolchain variants to use the two-instruction sequence for consistency.

OK, if you are not patching that back, then all should be good.

-- Steve

#9 lore raw

RFC PATCH v2 4/5 kbuild: Add generic hook for architectures to use before the final vmlinux link

In reply to Naveen N Rao

On powerpc, we would like to be able to make a pass on vmlinux.o and generate a new object file to be linked into vmlinux. Add a generic pass in link-vmlinux.sh that architectures can use for this purpose. Architectures need to select CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX and must provide arch/<arch>/tools/vmlinux_o.sh, which will be invoked prior to the final vmlinux link step.

---
 arch/Kconfig            |  3 +++
 scripts/link-vmlinux.sh | 18 +++++++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)
arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
 	bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+	def_bool n
+
 endmenu
scripts/link-vmlinux.sh +15 -3
index 46ce5d04dbeb..07f70e105d82 100755
@@ -122,7 +122,7 @@ gen_btf()
 		return 1
 	fi
 
-	vmlinux_link ${1}
+	vmlinux_link ${1} ${arch_vmlinux_o}
 
 	info "BTF" ${2}
 	LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
@@ -178,7 +178,7 @@ kallsyms_step()
 	kallsymso=${kallsyms_vmlinux}.o
 	kallsyms_S=${kallsyms_vmlinux}.S
 
-	vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o}
+	vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
 	mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms
 	kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S}
 
@@ -203,6 +203,7 @@ sorttable()
 
 cleanup()
 {
+	rm -f .arch.vmlinux.*
 	rm -f .btf.*
 	rm -f System.map
 	rm -f vmlinux
@@ -223,6 +224,17 @@ fi
 
 ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
 
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+	arch_vmlinux_o=.arch.vmlinux.o
+	info "ARCH" ${arch_vmlinux_o}
+	if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+		echo >&2 "Failed to generate ${arch_vmlinux_o}"
+		echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+		exit 1
+	fi
+fi
+
 btf_vmlinux_bin_o=""
 if is_enabled CONFIG_DEBUG_INFO_BTF; then
 	btf_vmlinux_bin_o=.btf.vmlinux.bin.o
@@ -273,7 +285,7 @@ if is_enabled CONFIG_KALLSYMS; then
 	fi
 fi
 
-vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o}
+vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
 
 # fill in BTF IDs
 if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then
Signature · 1 line
2.45.2
#10 lore raw

Re: [RFC PATCH v2 4/5] kbuild: Add generic hook for architectures to use before the final vmlinux link

Quoted 60 lines · Naveen N Rao

On powerpc, we would like to be able to make a pass on vmlinux.o and generate a new object file to be linked into vmlinux. Add a generic pass in link-vmlinux.sh that architectures can use for this purpose. Architectures need to select CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX and must provide arch/<arch>/tools/vmlinux_o.sh, which will be invoked prior to the final vmlinux link step.

---
 arch/Kconfig            |  3 +++
 scripts/link-vmlinux.sh | 18 +++++++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)
arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
        bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+       def_bool n
+
 endmenu
scripts/link-vmlinux.sh +3 -2
index 46ce5d04dbeb..07f70e105d82 100755
@@ -122,7 +122,7 @@ gen_btf()
                return 1
        fi
 
-       vmlinux_link ${1}
+       vmlinux_link ${1} ${arch_vmlinux_o}
 
        info "BTF" ${2}
        LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
@@ -178,7 +178,7 @@ kallsyms_step()
        kallsymso=${kallsyms_vmlinux}.o
        kallsyms_S=${kallsyms_vmlinux}.S
 
-       vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o}
+       vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
        mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms
        kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S}
 
@@ -203,6 +203,7 @@ sorttable()
 
 cleanup()
 {
+       rm -f .arch.vmlinux.*
        rm -f .btf.*
        rm -f System.map
        rm -f vmlinux
@@ -223,6 +224,17 @@ fi
 
 ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
 

scripts/link-vmlinux.sh fi · line 236

Quoted 10 lines
scripts/link-vmlinux.sh +10 -0
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+       info "ARCH" ${arch_vmlinux_o}
+       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+               echo >&2 "Failed to generate ${arch_vmlinux_o}"
+               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+               exit 1
+       fi
+fi

This is wrong because scripts/link-vmlinux.sh is not triggered even when source files under arch/powerpc/tools/ are changed.

Presumably, scripts/Makefile.vmlinux will be the right place.

Quoted 16 lines
scripts/link-vmlinux.sh +2 -2
+
 btf_vmlinux_bin_o=""
 if is_enabled CONFIG_DEBUG_INFO_BTF; then
        btf_vmlinux_bin_o=.btf.vmlinux.bin.o
@@ -273,7 +285,7 @@ if is_enabled CONFIG_KALLSYMS; then
        fi
 fi
 
-vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o}
+vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
 
 # fill in BTF IDs
 if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then
--

2.45.2

Signature · 2 lines
Best Regards
Masahiro Yamada
#11 lore raw

Re: [RFC PATCH v2 4/5] kbuild: Add generic hook for architectures to use before the final vmlinux link

Quoted 31 lines · Masahiro Yamada
Quoted 30 lines · Naveen N Rao

On powerpc, we would like to be able to make a pass on vmlinux.o and generate a new object file to be linked into vmlinux. Add a generic pass in link-vmlinux.sh that architectures can use for this purpose. Architectures need to select CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX and must provide arch/<arch>/tools/vmlinux_o.sh, which will be invoked prior to the final vmlinux link step.

---
 arch/Kconfig            |  3 +++
 scripts/link-vmlinux.sh | 18 +++++++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)
arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
        bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+       def_bool n
+
 endmenu
scripts/link-vmlinux.sh +0 -0
index 46ce5d04dbeb..07f70e105d82 100755

...

Quoted 17 lines

scripts/link-vmlinux.sh

Quoted 11 lines
scripts/link-vmlinux.sh +10 -0
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+       info "ARCH" ${arch_vmlinux_o}
+       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+               echo >&2 "Failed to generate ${arch_vmlinux_o}"
+               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+               exit 1
+       fi
+fi

This is wrong because scripts/link-vmlinux.sh is not triggered even when source files under arch/powerpc/tools/ are changed.

Quoted 1 line

Presumably, scripts/Makefile.vmlinux will be the right place.

Ah, yes. Something like this?

scripts/Makefile.vmlinux +4 -0
index 49946cb96844..77d90b6ac53e 100644
@@ -22,6 +22,10 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: $(srctree)/arch/$(SRCARCH)/tools/vmlinux_o.sh
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link

Thanks,
Naveen

#12 lore raw

Re: [RFC PATCH v2 4/5] kbuild: Add generic hook for architectures to use before the final vmlinux link

Quoted 72 lines · Naveen N Rao
Quoted 31 lines · Masahiro Yamada
Quoted 30 lines · Naveen N Rao

On powerpc, we would like to be able to make a pass on vmlinux.o and generate a new object file to be linked into vmlinux. Add a generic pass in link-vmlinux.sh that architectures can use for this purpose. Architectures need to select CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX and must provide arch/<arch>/tools/vmlinux_o.sh, which will be invoked prior to the final vmlinux link step.

---
 arch/Kconfig            |  3 +++
 scripts/link-vmlinux.sh | 18 +++++++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)
arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
        bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+       def_bool n
+
 endmenu
scripts/link-vmlinux.sh +0 -0
index 46ce5d04dbeb..07f70e105d82 100755

...

Quoted 17 lines

scripts/link-vmlinux.sh

Quoted 11 lines
scripts/link-vmlinux.sh +10 -0
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+       info "ARCH" ${arch_vmlinux_o}
+       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+               echo >&2 "Failed to generate ${arch_vmlinux_o}"
+               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+               exit 1
+       fi
+fi

This is wrong because scripts/link-vmlinux.sh is not triggered even when source files under arch/powerpc/tools/ are changed.

Quoted 1 line

Presumably, scripts/Makefile.vmlinux will be the right place.

Ah, yes. Something like this?

scripts/Makefile.vmlinux +4 -0
index 49946cb96844..77d90b6ac53e 100644
@@ -22,6 +22,10 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: $(srctree)/arch/$(SRCARCH)/tools/vmlinux_o.sh
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
Quoted 3 lines

Thanks,
Naveen

No.

Something like below.

Then, you can do everything in Makefile, not a shell script.

ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
vmlinux: .arch.vmlinux.o

.arch.vmlinux.o: FORCE

        $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o

endif

I did not test it, though.

Signature · 2 lines
Best Regards
Masahiro Yamada
#13 lore raw

Re: [RFC PATCH v2 4/5] kbuild: Add generic hook for architectures to use before the final vmlinux link

Quoted 90 lines · Masahiro Yamada
Quoted 72 lines · Naveen N Rao
Quoted 31 lines · Masahiro Yamada
Quoted 30 lines · Naveen N Rao

On powerpc, we would like to be able to make a pass on vmlinux.o and generate a new object file to be linked into vmlinux. Add a generic pass in link-vmlinux.sh that architectures can use for this purpose. Architectures need to select CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX and must provide arch/<arch>/tools/vmlinux_o.sh, which will be invoked prior to the final vmlinux link step.

---
 arch/Kconfig            |  3 +++
 scripts/link-vmlinux.sh | 18 +++++++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)
arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
        bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+       def_bool n
+
 endmenu
scripts/link-vmlinux.sh +0 -0
index 46ce5d04dbeb..07f70e105d82 100755

...

Quoted 17 lines

scripts/link-vmlinux.sh

Quoted 11 lines
scripts/link-vmlinux.sh +10 -0
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+       info "ARCH" ${arch_vmlinux_o}
+       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+               echo >&2 "Failed to generate ${arch_vmlinux_o}"
+               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+               exit 1
+       fi
+fi

This is wrong because scripts/link-vmlinux.sh is not triggered even when source files under arch/powerpc/tools/ are changed.

Quoted 1 line

Presumably, scripts/Makefile.vmlinux will be the right place.

Ah, yes. Something like this?

scripts/Makefile.vmlinux +4 -0
index 49946cb96844..77d90b6ac53e 100644
@@ -22,6 +22,10 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: $(srctree)/arch/$(SRCARCH)/tools/vmlinux_o.sh
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
Quoted 3 lines

Thanks,
Naveen

No.

Something like below.

Then, you can do everything in Makefile, not a shell script.

ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
vmlinux: .arch.vmlinux.o

Quoted 8 lines

.arch.vmlinux.o: FORCE

        $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o

endif

I did not test it, though.

Thanks for the pointer. I will try and build on that.

Just to be completely sure, does the below incremetal diff on top of the existing patch capture your suggestion?

---

scripts/Makefile.vmlinux +7 -0
index 49946cb96844..04e088d7a1ca 100644
@@ -22,6 +22,13 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: .arch.vmlinux.o
+
+.arch.vmlinux.o: FORCE
+        $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
scripts/link-vmlinux.sh +0 -6
index 07f70e105d82..f1b705b8cdca 100755
@@ -227,12 +227,6 @@ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
 arch_vmlinux_o=""
 if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
        arch_vmlinux_o=.arch.vmlinux.o
-       info "ARCH" ${arch_vmlinux_o}
-       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
-               echo >&2 "Failed to generate ${arch_vmlinux_o}"
-               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
-               exit 1
-       fi
 fi
 
 btf_vmlinux_bin_o=""

Thanks,
Naveen

#14 lore raw

Re: [RFC PATCH v2 4/5] kbuild: Add generic hook for architectures to use before the final vmlinux link

Quoted 100 lines · Naveen N Rao
Quoted 59 lines · Masahiro Yamada
Quoted 41 lines · Naveen N Rao
Quoted 18 lines · Masahiro Yamada

Review

Quoted 11 lines · Naveen N Rao
(quoted diff) +10 -0
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+       info "ARCH" ${arch_vmlinux_o}
+       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
+               echo >&2 "Failed to generate ${arch_vmlinux_o}"
+               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
+               exit 1
+       fi
+fi

This is wrong because scripts/link-vmlinux.sh is not triggered even when source files under arch/powerpc/tools/ are changed.

Quoted 1 line

Presumably, scripts/Makefile.vmlinux will be the right place.

Ah, yes. Something like this?

scripts/Makefile.vmlinux +4 -0
index 49946cb96844..77d90b6ac53e 100644
@@ -22,6 +22,10 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: $(srctree)/arch/$(SRCARCH)/tools/vmlinux_o.sh
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
Quoted 3 lines

Thanks,
Naveen

No.

Something like below.

Then, you can do everything in Makefile, not a shell script.

ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
vmlinux: .arch.vmlinux.o

Quoted 8 lines

.arch.vmlinux.o: FORCE

        $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o

endif

I did not test it, though.

Thanks for the pointer. I will try and build on that.

Just to be completely sure, does the below incremetal diff on top of the existing patch capture your suggestion?

---

scripts/Makefile.vmlinux +7 -0
index 49946cb96844..04e088d7a1ca 100644
@@ -22,6 +22,13 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux: .arch.vmlinux.o
+
+.arch.vmlinux.o: FORCE
+        $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
scripts/link-vmlinux.sh +0 -0
index 07f70e105d82..f1b705b8cdca 100755
@@ -227,12 +227,6 @@ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
 arch_vmlinux_o=""
 if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then

scripts/link-vmlinux.sh ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o · line 232

Quoted 10 lines
scripts/link-vmlinux.sh +0 -6
        arch_vmlinux_o=.arch.vmlinux.o
-       info "ARCH" ${arch_vmlinux_o}
-       if ! ${srctree}/arch/${SRCARCH}/tools/vmlinux_o.sh ${arch_vmlinux_o} ; then
-               echo >&2 "Failed to generate ${arch_vmlinux_o}"
-               echo >&2 "Try to disable CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX"
-               exit 1
-       fi
 fi
 
 btf_vmlinux_bin_o=""

This is what I ended up with:

---

arch/Kconfig +3 -0
index 975dd22a2dbd..649f0903e7ef 100644
@@ -1643,4 +1643,7 @@ config CC_HAS_SANE_FUNCTION_ALIGNMENT
 config ARCH_NEED_CMPXCHG_1_EMU
        bool
 
+config ARCH_WANTS_PRE_LINK_VMLINUX
+       def_bool n
+
 endmenu
scripts/Makefile.vmlinux +8 -0
index 49946cb96844..6410e0be7f52 100644
@@ -22,6 +22,14 @@ targets += .vmlinux.export.o
 vmlinux: .vmlinux.export.o
 endif
 
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+targets += .arch.vmlinux.o
+.arch.vmlinux.o: vmlinux.o FORCE
+       $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools .arch.vmlinux.o
+
+vmlinux: .arch.vmlinux.o
+endif
+
 ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
 
 # Final link of vmlinux with optional arch pass after final link
scripts/link-vmlinux.sh +8 -3
index 46ce5d04dbeb..eb5fb71f5207 100755
@@ -122,7 +122,7 @@ gen_btf()
                return 1
        fi
 
-       vmlinux_link ${1}
+       vmlinux_link ${1} ${arch_vmlinux_o}
 
        info "BTF" ${2}
        LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
@@ -178,7 +178,7 @@ kallsyms_step()
        kallsymso=${kallsyms_vmlinux}.o
        kallsyms_S=${kallsyms_vmlinux}.S
 
-       vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o}
+       vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
        mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms
        kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S}
 
@@ -223,6 +223,11 @@ fi
 
 ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
 
+arch_vmlinux_o=""
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+       arch_vmlinux_o=.arch.vmlinux.o
+fi
+
 btf_vmlinux_bin_o=""
 if is_enabled CONFIG_DEBUG_INFO_BTF; then
        btf_vmlinux_bin_o=.btf.vmlinux.bin.o
@@ -273,7 +278,7 @@ if is_enabled CONFIG_KALLSYMS; then
        fi
 fi
 
-vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o}
+vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} ${arch_vmlinux_o}
 
 # fill in BTF IDs
 if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then

---
Along with the below Makefile, things seem to be working properly in my limited tests:

arch/powerpc/tools/Makefile new file +10 -0
new file mode 100644
index 000000000000..9e2ba9a85baa
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+quiet_cmd_gen_ftrace_pfe_stubs = FTRACE  $@
+      cmd_gen_ftrace_pfe_stubs = $< $(objtree)/vmlinux.o $@
+
+targets += .arch.vmlinux.o
+.arch.vmlinux.o: $(srctree)/arch/powerpc/tools/gen-ftrace-pfe-stubs.sh $(objtree)/vmlinux.o FORCE
+       $(call if_changed,gen_ftrace_pfe_stubs)
+
+clean-files += $(objtree)/.arch.vmlinux.S $(objtree)/.arch.vmlinux.o

- Naveen

#15 lore raw

RFC PATCH v2 5/5 powerpc64/ftrace: Move ftrace sequence out of line

In reply to Naveen N Rao

Function profile sequence on powerpc includes two instructions at the beginning of each function:

	mflr	r0
	bl	ftrace_caller

The call to ftrace_caller() gets nop'ed out during kernel boot and is patched in when ftrace is enabled.

Given the sequence, we cannot return from ftrace_caller with 'blr' as we need to keep LR and r0 intact. This results in link stack imbalance when ftrace is enabled. To address that, we would like to use a three instruction sequence:

	mflr	r0
	bl	ftrace_caller
	mtlr	r0

Further more, to support DYNAMIC_FTRACE_WITH_CALL_OPS, we need to reserve two instruction slots before the function. This results in a total of five instruction slots to be reserved for ftrace use on each function that is traced.

Move the function profile sequence out-of-line to minimize its impact. To do this, we reserve a single nop at function entry using -fpatchable-function-entry=1 and add a pass on vmlinux.o to determine the total number of functions that can be traced. This is then used to generate a .S file reserving the appropriate amount of space for use as ftrace stubs, which is built and linked into vmlinux.

On bootup, the stub space is split into separate stubs per function and populated with the proper instruction sequence. A pointer to the associated stub is maintained in dyn_arch_ftrace.

For modules, space for ftrace stubs is reserved from the generic module stub space.

This is restricted to and enabled by default only on 64-bit powerpc.

---
 arch/powerpc/Kconfig                     |   4 +
 arch/powerpc/Makefile                    |   4 +
 arch/powerpc/include/asm/ftrace.h        |  10 ++
 arch/powerpc/include/asm/module.h        |   5 +
 arch/powerpc/kernel/asm-offsets.c        |   4 +
 arch/powerpc/kernel/module_64.c          |  67 +++++++++--
 arch/powerpc/kernel/trace/ftrace.c       | 145 +++++++++++++++++++++--
 arch/powerpc/kernel/trace/ftrace_entry.S |  71 ++++++++---
 arch/powerpc/kernel/vmlinux.lds.S        |   3 +-
 arch/powerpc/tools/vmlinux_o.sh          |  47 ++++++++
 10 files changed, 324 insertions(+), 36 deletions(-)
 create mode 100755 arch/powerpc/tools/vmlinux_o.sh
arch/powerpc/Kconfig +4 -0
index c88c6d46a5bc..c393daeaf643 100644
@@ -568,6 +568,10 @@ config ARCH_USING_PATCHABLE_FUNCTION_ENTRY
 	def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mlittle-endian) if PPC64 && CPU_LITTLE_ENDIAN
 	def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mbig-endian) if PPC64 && CPU_BIG_ENDIAN
 
+config FTRACE_PFE_OUT_OF_LINE
+	def_bool PPC64 && ARCH_USING_PATCHABLE_FUNCTION_ENTRY
+	select ARCH_WANTS_PRE_LINK_VMLINUX
+
 config HOTPLUG_CPU
 	bool "Support for enabling/disabling CPUs"
 	depends on SMP && (PPC_PSERIES || \
arch/powerpc/Makefile +4 -0
index a8479c881cac..bb920d48ec6e 100644
@@ -155,7 +155,11 @@ CC_FLAGS_NO_FPU		:= $(call cc-option,-msoft-float)
 ifdef CONFIG_FUNCTION_TRACER
 ifdef CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY
 KBUILD_CPPFLAGS	+= -DCC_USING_PATCHABLE_FUNCTION_ENTRY
+ifdef CONFIG_FTRACE_PFE_OUT_OF_LINE
+CC_FLAGS_FTRACE := -fpatchable-function-entry=1
+else
 CC_FLAGS_FTRACE := -fpatchable-function-entry=2
+endif
 else
 CC_FLAGS_FTRACE := -pg
 ifdef CONFIG_MPROFILE_KERNEL
arch/powerpc/include/asm/ftrace.h +10 -0
index 201f9d15430a..9da1da0f87b4 100644
@@ -26,6 +26,9 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
 struct module;
 struct dyn_ftrace;
 struct dyn_arch_ftrace {
+#ifdef CONFIG_FTRACE_PFE_OUT_OF_LINE
+	unsigned long pfe_stub;
+#endif
 };
 
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
@@ -132,6 +135,13 @@ static inline u8 this_cpu_get_ftrace_enabled(void) { return 1; }
 
 #ifdef CONFIG_FUNCTION_TRACER
 extern unsigned int ftrace_tramp_text[], ftrace_tramp_init[];
+#ifdef CONFIG_FTRACE_PFE_OUT_OF_LINE
+struct ftrace_pfe_stub {
+	u32	insn[4];
+};
+extern struct ftrace_pfe_stub ftrace_pfe_stub_text[], ftrace_pfe_stub_inittext[];
+extern unsigned long ftrace_pfe_stub_text_count, ftrace_pfe_stub_inittext_count;
+#endif
 void ftrace_free_init_tramp(void);
 unsigned long ftrace_call_adjust(unsigned long addr);
 #else
arch/powerpc/include/asm/module.h +5 -0
index 300c777cc307..28dbd1ec5593 100644
@@ -47,6 +47,11 @@ struct mod_arch_specific {
 #ifdef CONFIG_DYNAMIC_FTRACE
 	unsigned long tramp;
 	unsigned long tramp_regs;
+#ifdef CONFIG_FTRACE_PFE_OUT_OF_LINE
+	struct ftrace_pfe_stub *pfe_stubs;
+	unsigned int pfe_stub_count;
+	unsigned int pfe_stub_index;
+#endif
 #endif
 };
 
arch/powerpc/kernel/asm-offsets.c +4 -0
index f029755f9e69..5f1a411d714c 100644
@@ -674,5 +674,9 @@ int main(void)
 	DEFINE(BPT_SIZE, BPT_SIZE);
 #endif
 
+#ifdef CONFIG_FTRACE_PFE_OUT_OF_LINE
+	DEFINE(FTRACE_PFE_STUB_SIZE, sizeof(struct ftrace_pfe_stub));
+#endif
+
 	return 0;
 }
arch/powerpc/kernel/module_64.c +60 -7

99 lines not on this page · raw message

arch/powerpc/kernel/trace/ftrace.c +135 -10

217 lines not on this page · raw message

arch/powerpc/kernel/trace/ftrace_entry.S +54 -17

137 lines not on this page · raw message

arch/powerpc/kernel/vmlinux.lds.S +1 -2
index f420df7888a7..0aef9959f2cd 100644
@@ -267,14 +267,13 @@ SECTIONS
 	.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {
 		_sinittext = .;
 		INIT_TEXT
-
+		*(.tramp.ftrace.init);
 		/*
 		 *.init.text might be RO so we must ensure this section ends on
 		 * a page boundary.
 		 */
 		. = ALIGN(PAGE_SIZE);
 		_einittext = .;
-		*(.tramp.ftrace.init);
 	} :text
 
 	/* .exit.text is discarded at runtime, not link time,
arch/powerpc/tools/vmlinux_o.sh new file +47 -0

47 lines not on this page · raw message

Signature · 1 line
2.45.2