[RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding

Jens Remus posted 16 patches 2 months, 4 weeks ago
[RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Jens Remus 2 months, 4 weeks ago
Add support for unwinding of user space using back chain to the
unwind user interface.  Use it as secondary fallback for unwinding
using SFrame, if that fails and the primary fallback using frame
pointer is not available.

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
 arch/Kconfig                          |  6 ++++++
 include/linux/unwind_user_backchain.h | 17 +++++++++++++++++
 include/linux/unwind_user_types.h     |  1 +
 kernel/unwind/Makefile                |  1 +
 kernel/unwind/user.c                  | 24 +++++++++++++++++++++---
 kernel/unwind/user_backchain.c        | 13 +++++++++++++
 6 files changed, 59 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/unwind_user_backchain.h
 create mode 100644 kernel/unwind/user_backchain.c

diff --git a/arch/Kconfig b/arch/Kconfig
index 9e28dffe42cb..4fe16ad6f053 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -438,6 +438,12 @@ config HAVE_HARDLOCKUP_DETECTOR_ARCH
 config UNWIND_USER
 	bool
 
+config HAVE_UNWIND_USER_BACKCHAIN
+	bool
+	select UNWIND_USER
+	help
+	  The arch supports unwinding of user space using back chain.
+
 config HAVE_UNWIND_USER_FP
 	bool
 	select UNWIND_USER
diff --git a/include/linux/unwind_user_backchain.h b/include/linux/unwind_user_backchain.h
new file mode 100644
index 000000000000..daae74c97c54
--- /dev/null
+++ b/include/linux/unwind_user_backchain.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_UNWIND_USER_BACKCHAIN_H
+#define _LINUX_UNWIND_USER_BACKCHAIN_H
+
+struct unwind_user_state;
+
+#ifdef CONFIG_HAVE_UNWIND_USER_BACKCHAIN
+
+extern int unwind_user_backchain_next(struct unwind_user_state *state);
+
+#else /* !CONFIG_HAVE_UNWIND_USER_BACKCHAIN */
+
+static inline int unwind_user_backchain_next(struct unwind_user_state *state) { return -EINVAL; }
+
+#endif /* !CONFIG_HAVE_UNWIND_USER_BACKCHAIN */
+
+#endif /* _LINUX_UNWIND_USER_BACKCHAIN_H */
diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h
index 57fd16e314cf..41b1bc082cb1 100644
--- a/include/linux/unwind_user_types.h
+++ b/include/linux/unwind_user_types.h
@@ -14,6 +14,7 @@ enum unwind_user_type {
 	UNWIND_USER_TYPE_FP,
 	UNWIND_USER_TYPE_COMPAT_FP,
 	UNWIND_USER_TYPE_SFRAME,
+	UNWIND_USER_TYPE_BACKCHAIN,
 };
 
 struct unwind_stacktrace {
diff --git a/kernel/unwind/Makefile b/kernel/unwind/Makefile
index 146038165865..38cef261abcb 100644
--- a/kernel/unwind/Makefile
+++ b/kernel/unwind/Makefile
@@ -1,2 +1,3 @@
  obj-$(CONFIG_UNWIND_USER)		+= user.o deferred.o
+ obj-$(CONFIG_HAVE_UNWIND_USER_BACKCHAIN)	+= user_backchain.o
  obj-$(CONFIG_HAVE_UNWIND_USER_SFRAME)	+= sframe.o
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index ee00d39d2a8e..3c3f75bc146b 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -7,6 +7,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 #include <linux/unwind_user.h>
+#include <linux/unwind_user_backchain.h>
 #include <linux/uaccess.h>
 #include <linux/sframe.h>
 
@@ -39,6 +40,12 @@ static inline bool sframe_state(struct unwind_user_state *state)
 	       state->type == UNWIND_USER_TYPE_SFRAME;
 }
 
+static inline bool backchain_state(struct unwind_user_state *state)
+{
+	return IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN) &&
+	       state->type == UNWIND_USER_TYPE_BACKCHAIN;
+}
+
 #define unwind_get_user_long(to, from, state)				\
 ({									\
 	int __ret;							\
@@ -66,12 +73,20 @@ static int unwind_user_next(struct unwind_user_state *state)
 		/* sframe expects the frame to be local storage */
 		frame = &_frame;
 		if (sframe_find(state->ip, frame, topmost)) {
-			if (!IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
-				goto done;
-			frame = &fp_frame;
+			if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP)) {
+				frame = &fp_frame;
+			} else if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN)) {
+				if (unwind_user_backchain_next(state))
+					goto done;
+				goto done_backchain;
+			}
 		}
 	} else if (fp_state(state)) {
 		frame = &fp_frame;
+	} else if (backchain_state(state)) {
+		if (unwind_user_backchain_next(state))
+			goto done;
+		goto done_backchain;
 	} else {
 		goto done;
 	}
@@ -153,6 +168,7 @@ static int unwind_user_next(struct unwind_user_state *state)
 
 	arch_unwind_user_next(state);
 
+done_backchain:
 	state->topmost = false;
 	return 0;
 
@@ -178,6 +194,8 @@ static int unwind_user_start(struct unwind_user_state *state)
 		state->type = UNWIND_USER_TYPE_SFRAME;
 	else if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
 		state->type = UNWIND_USER_TYPE_FP;
+	else if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN))
+		state->type = UNWIND_USER_TYPE_BACKCHAIN;
 	else
 		state->type = UNWIND_USER_TYPE_NONE;
 
diff --git a/kernel/unwind/user_backchain.c b/kernel/unwind/user_backchain.c
new file mode 100644
index 000000000000..5b60a3d4f34f
--- /dev/null
+++ b/kernel/unwind/user_backchain.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt)	"backchain: " fmt
+
+#include <linux/sched.h>
+#include <linux/unwind_user.h>
+#include <linux/unwind_user_backchain.h>
+#include <asm/unwind_user_backchain.h>
+
+int unwind_user_backchain_next(struct unwind_user_state *state)
+{
+	return arch_unwind_user_backchain_next(state);
+}
-- 
2.48.1
Re: [RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Josh Poimboeuf 2 months, 3 weeks ago
On Thu, Jul 10, 2025 at 06:35:18PM +0200, Jens Remus wrote:
> @@ -66,12 +73,20 @@ static int unwind_user_next(struct unwind_user_state *state)
>  		/* sframe expects the frame to be local storage */
>  		frame = &_frame;
>  		if (sframe_find(state->ip, frame, topmost)) {
> -			if (!IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
> -				goto done;
> -			frame = &fp_frame;
> +			if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP)) {
> +				frame = &fp_frame;
> +			} else if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN)) {
> +				if (unwind_user_backchain_next(state))
> +					goto done;
> +				goto done_backchain;
> +			}
>  		}
>  	} else if (fp_state(state)) {
>  		frame = &fp_frame;
> +	} else if (backchain_state(state)) {
> +		if (unwind_user_backchain_next(state))
> +			goto done;
> +		goto done_backchain;
>  	} else {
>  		goto done;
>  	}
> @@ -153,6 +168,7 @@ static int unwind_user_next(struct unwind_user_state *state)
>  
>  	arch_unwind_user_next(state);
>  
> +done_backchain:
>  	state->topmost = false;
>  	return 0;

This feels very grafted on, is there not some way to make it more
generic, i.e., to just work with CONFIG_HAVE_UNWIND_USER_FP?

Also, if distros aren't even compiling with -mbackchain, I wonder if we
can just not do this altogether :-)

-- 
Josh
Re: [RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Jens Remus 2 months, 3 weeks ago
On 17.07.2025 04:06, Josh Poimboeuf wrote:
> On Thu, Jul 10, 2025 at 06:35:18PM +0200, Jens Remus wrote:
>> @@ -66,12 +73,20 @@ static int unwind_user_next(struct unwind_user_state *state)
>>  		/* sframe expects the frame to be local storage */
>>  		frame = &_frame;
>>  		if (sframe_find(state->ip, frame, topmost)) {
>> -			if (!IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
>> -				goto done;
>> -			frame = &fp_frame;
>> +			if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP)) {
>> +				frame = &fp_frame;
>> +			} else if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN)) {
>> +				if (unwind_user_backchain_next(state))
>> +					goto done;
>> +				goto done_backchain;
>> +			}
>>  		}
>>  	} else if (fp_state(state)) {
>>  		frame = &fp_frame;
>> +	} else if (backchain_state(state)) {
>> +		if (unwind_user_backchain_next(state))
>> +			goto done;
>> +		goto done_backchain;
>>  	} else {
>>  		goto done;
>>  	}
>> @@ -153,6 +168,7 @@ static int unwind_user_next(struct unwind_user_state *state)
>>  
>>  	arch_unwind_user_next(state);
>>  
>> +done_backchain:
>>  	state->topmost = false;
>>  	return 0;
> 
> This feels very grafted on, is there not some way to make it more
> generic, i.e., to just work with CONFIG_HAVE_UNWIND_USER_FP?

I agree.  It could probably be made to compute the cfa_off and ra.offset
or ra.regnum.  Let me explore that, provided there would be any acceptance
for unwind user backchain at all. Note that Power is using backchain as
well, so they may want to build on that as well.

> Also, if distros aren't even compiling with -mbackchain, I wonder if we
> can just not do this altogether :-)

My original intent was to use unwind user's for_each_user_frame() to
replace the exiting stack tracing logic in arch_stack_walk_user_common()
in arch/s390/kernel/stacktrace.c, which currently supports backchain.
Given that for_each_user_frame() was made private in the latest unwind
user series version hinders me.  The use was also low, because the
currentl arch_stack_walk_user_common() implementation does not support
page faults, so that the attempt to use unwind user sframe would always
fail and fallback to unwind user backchain.  My hope was that somebody
with more Kernel skills could give me a few hints at how it could be
made to support deferred unwind. :-)

Regards,
Jens
-- 
Jens Remus
Linux on Z Development (D3303)
+49-7031-16-1128 Office
jremus@de.ibm.com

IBM

IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/

Re: [RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Josh Poimboeuf 2 months, 3 weeks ago
On Thu, Jul 17, 2025 at 02:20:12PM +0200, Jens Remus wrote:
> >> +done_backchain:
> >>  	state->topmost = false;
> >>  	return 0;
> > 
> > This feels very grafted on, is there not some way to make it more
> > generic, i.e., to just work with CONFIG_HAVE_UNWIND_USER_FP?
> 
> I agree.  It could probably be made to compute the cfa_off and ra.offset
> or ra.regnum.  Let me explore that, provided there would be any acceptance
> for unwind user backchain at all. Note that Power is using backchain as
> well, so they may want to build on that as well.
> 
> > Also, if distros aren't even compiling with -mbackchain, I wonder if we
> > can just not do this altogether :-)
> 
> My original intent was to use unwind user's for_each_user_frame() to
> replace the exiting stack tracing logic in arch_stack_walk_user_common()
> in arch/s390/kernel/stacktrace.c, which currently supports backchain.
> Given that for_each_user_frame() was made private in the latest unwind
> user series version hinders me.  The use was also low, because the
> currentl arch_stack_walk_user_common() implementation does not support
> page faults, so that the attempt to use unwind user sframe would always
> fail and fallback to unwind user backchain.  My hope was that somebody
> with more Kernel skills could give me a few hints at how it could be
> made to support deferred unwind. :-)

I believe stack_trace_save_user() is only used by ftrace, and that will
no longer be needed once ftrace starts using unwind_user.

Maybe Heiko knows if that backchain user stacktrace code has any users?

If distros aren't building with -mbackchain, maybe backchain support can
be considered obsoleted by sframe, and we can get away with not
implementing it.

-- 
Josh
Re: [RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Heiko Carstens 2 months ago
On Thu, Jul 17, 2025 at 10:19:54PM -0700, Josh Poimboeuf wrote:
> On Thu, Jul 17, 2025 at 02:20:12PM +0200, Jens Remus wrote:
> > > Also, if distros aren't even compiling with -mbackchain, I wonder if we
> > > can just not do this altogether :-)
> > 
> > My original intent was to use unwind user's for_each_user_frame() to
> > replace the exiting stack tracing logic in arch_stack_walk_user_common()
> > in arch/s390/kernel/stacktrace.c, which currently supports backchain.
> > Given that for_each_user_frame() was made private in the latest unwind
> > user series version hinders me.  The use was also low, because the
> > currentl arch_stack_walk_user_common() implementation does not support
> > page faults, so that the attempt to use unwind user sframe would always
> > fail and fallback to unwind user backchain.  My hope was that somebody
> > with more Kernel skills could give me a few hints at how it could be
> > made to support deferred unwind. :-)
> 
> I believe stack_trace_save_user() is only used by ftrace, and that will
> no longer be needed once ftrace starts using unwind_user.
> 
> Maybe Heiko knows if that backchain user stacktrace code has any users?
> 
> If distros aren't building with -mbackchain, maybe backchain support can
> be considered obsoleted by sframe, and we can get away with not
> implementing it.

I guess that's a valid option. I know only of some special cases where
users compile everything on their own with -mbackchain to make this
work on a per-case basis. It shouldn't cause to much pain for them to
switch to sframe, as soon as that is available.
Re: [RFC PATCH v1 12/16] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Jens Remus 2 months ago
On 8/1/2025 2:36 PM, Heiko Carstens wrote:
> On Thu, Jul 17, 2025 at 10:19:54PM -0700, Josh Poimboeuf wrote:
>> On Thu, Jul 17, 2025 at 02:20:12PM +0200, Jens Remus wrote:

>> I believe stack_trace_save_user() is only used by ftrace, and that will
>> no longer be needed once ftrace starts using unwind_user.
>>
>> Maybe Heiko knows if that backchain user stacktrace code has any users?
>>
>> If distros aren't building with -mbackchain, maybe backchain support can
>> be considered obsoleted by sframe, and we can get away with not
>> implementing it.
> 
> I guess that's a valid option. I know only of some special cases where
> users compile everything on their own with -mbackchain to make this
> work on a per-case basis. It shouldn't cause to much pain for them to
> switch to sframe, as soon as that is available.

Let me have another stab at a cleaner implementation.  Shouldn't be too
much effort.  We can then decide whether to leave it out or not.

Will be away from keyboard for a few weeks.

Regards,
Jens
-- 
Jens Remus
Linux on Z Development (D3303)
+49-7031-16-1128 Office
jremus@de.ibm.com

IBM

IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/