s390/perf: implement perf_callchain_user()
authorHeiko Carstens <hca@linux.ibm.com>
Mon, 30 Oct 2023 15:50:46 +0000 (16:50 +0100)
committerVasily Gorbik <gor@linux.ibm.com>
Sun, 5 Nov 2023 21:34:57 +0000 (22:34 +0100)
Daan De Meyer and Neal Gompa reported that s390 does not support perf user
stack unwinding.

This was never implemented since this requires user space to be compiled
with the -mbackchain compile option, which until now no distribution
did. However this is going to change with Fedora. Therefore provide a
perf_callchain_user() implementation.

Note that due to the way s390 sets up stack frames the provided call chains
can contain invalid values. This is especially true for the first stack
frame, where it is not possible to tell if the return address has been
written to the stack already or not.

Reported-by: Daan De Meyer <daan.j.demeyer@gmail.com>
Reported-by: Neal Gompa <ngompa@fedoraproject.org>
Closes: https://lore.kernel.org/all/CAO8sHcn3+_qrnvp0580aK7jN0Wion5F7KYeBAa4MnCY4mqABPA@mail.gmail.com/
Link: https://lore.kernel.org/all/20231030123558.10816-A-hca@linux.ibm.com
Reviewed-by: Neal Gompa <ngompa@fedoraproject.org>
Acked-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/include/asm/stacktrace.h
arch/s390/kernel/perf_event.c

index 78f7b729b65f105efbb6dd6dfeb56fd97552b05b..31ec4f545e036a26bfc0d8cc7fb8163fdd2c93a5 100644 (file)
@@ -6,6 +6,13 @@
 #include <linux/ptrace.h>
 #include <asm/switch_to.h>
 
+struct stack_frame_user {
+       unsigned long back_chain;
+       unsigned long empty1[5];
+       unsigned long gprs[10];
+       unsigned long empty2[4];
+};
+
 enum stack_type {
        STACK_TYPE_UNKNOWN,
        STACK_TYPE_TASK,
index c27321cb0969fbe6b3c6e9ec8a99db344000285e..dfa77da2fd2ec5413b6358c853f5a8532d250419 100644 (file)
 #include <linux/export.h>
 #include <linux/seq_file.h>
 #include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
 #include <linux/sysfs.h>
+#include <asm/stacktrace.h>
 #include <asm/irq.h>
 #include <asm/cpu_mf.h>
 #include <asm/lowcore.h>
@@ -212,6 +215,44 @@ void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
        }
 }
 
+void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
+                        struct pt_regs *regs)
+{
+       struct stack_frame_user __user *sf;
+       unsigned long ip, sp;
+       bool first = true;
+
+       if (is_compat_task())
+               return;
+       perf_callchain_store(entry, instruction_pointer(regs));
+       sf = (void __user *)user_stack_pointer(regs);
+       pagefault_disable();
+       while (entry->nr < entry->max_stack) {
+               if (__get_user(sp, &sf->back_chain))
+                       break;
+               if (__get_user(ip, &sf->gprs[8]))
+                       break;
+               if (ip & 0x1) {
+                       /*
+                        * If the instruction address is invalid, and this
+                        * is the first stack frame, assume r14 has not
+                        * been written to the stack yet. Otherwise exit.
+                        */
+                       if (first && !(regs->gprs[14] & 0x1))
+                               ip = regs->gprs[14];
+                       else
+                               break;
+               }
+               perf_callchain_store(entry, ip);
+               /* Sanity check: ABI requires SP to be aligned 8 bytes. */
+               if (!sp || sp & 0x7)
+                       break;
+               sf = (void __user *)sp;
+               first = false;
+       }
+       pagefault_enable();
+}
+
 /* Perf definitions for PMU event attributes in sysfs */
 ssize_t cpumf_events_sysfs_show(struct device *dev,
                                struct device_attribute *attr, char *page)