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