unwind x86: Add dump_trace switch for legacy and dwarf backtrace

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/arch/x86/kernel/dumptrace.c b/arch/x86/kernel/dumptrace.c
index 3cb943f..2e2cc83 100644
--- a/arch/x86/kernel/dumptrace.c
+++ b/arch/x86/kernel/dumptrace.c
@@ -1,7 +1,75 @@
 
 #include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/kobject.h>
 #include <asm/stacktrace.h>
+#include <asm/ptrace.h>
 
 dump_trace_t dump_trace = dump_trace_legacy;
 
 EXPORT_SYMBOL(dump_trace);
+
+#ifdef CONFIG_DWARF_UNWIND
+struct backtrace {
+	const char	*name;
+	dump_trace_t	fn;
+};
+
+enum {
+	BACKTRACE_LEGACY = 0,
+	BACKTRACE_DWARF  = 1,
+};
+
+static struct backtrace bt[] = {
+	[BACKTRACE_LEGACY] = {
+		.name	= "legacy",
+		.fn	= dump_trace_legacy,
+	},
+	[BACKTRACE_DWARF] = {
+		.name	= "dwarf",
+		.fn	= dump_trace_dwarf,
+	},
+};
+
+static int bt_idx;
+static DEFINE_SPINLOCK(bt_lock);
+
+int backtrace_switch(int i)
+{
+	struct backtrace *b = &bt[i];
+	unsigned long flags;
+
+	spin_lock_irqsave(&bt_lock, flags);
+	dump_trace = b->fn;
+	bt_idx = i;
+	spin_unlock_irqrestore(&bt_lock, flags);
+	return 0;
+}
+
+ssize_t backtrace_show(struct kobject *kobj,
+		       struct kobj_attribute *attr,
+		       char *buf)
+{
+	struct backtrace *b = &bt[bt_idx];
+	return sprintf(buf, "%s\n", b->name);
+}
+
+ssize_t backtrace_store(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(bt); i++) {
+		struct backtrace *b = &bt[i];
+
+		if (!strncmp(buf, b->name, strlen(b->name))) {
+			ret = backtrace_switch(i);
+			break;
+		}
+	}
+
+	return ret < 0 ? ret : count;
+}
+#endif /* CONFIG_DWARF_UNWIND */
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index 9659d38..ec92a6d 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -132,6 +132,25 @@
 
 #endif /* CONFIG_KEXEC */
 
+#ifdef CONFIG_DWARF_UNWIND
+__weak ssize_t backtrace_show(struct kobject *kobj,
+			      struct kobj_attribute *attr,
+			      char *buf)
+{
+	WARN(1, "backtrace switch not implemented\n");
+	return -ENODEV;
+}
+
+__weak ssize_t backtrace_store(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       const char *buf, size_t count)
+{
+	WARN(1, "backtrace switch not implemented\n");
+	return -ENODEV;
+}
+KERNEL_ATTR_RW(backtrace);
+#endif
+
 /* whether file capabilities are enabled */
 static ssize_t fscaps_show(struct kobject *kobj,
 				  struct kobj_attribute *attr, char *buf)
@@ -197,6 +216,9 @@
 	&vmcoreinfo_attr.attr,
 #endif
 	&rcu_expedited_attr.attr,
+#ifdef CONFIG_DWARF_UNWIND
+	&backtrace_attr.attr,
+#endif
 	NULL
 };