diff options
| -rw-r--r-- | arch/sparc/kernel/perf_event.c | 120 |
1 files changed, 119 insertions, 1 deletions
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 198fb4e79ba2..2386ac6ec956 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* Performance event support for sparc64. | 1 | /* Performance event support for sparc64. |
| 2 | * | 2 | * |
| 3 | * Copyright (C) 2009 David S. Miller <davem@davemloft.net> | 3 | * Copyright (C) 2009, 2010 David S. Miller <davem@davemloft.net> |
| 4 | * | 4 | * |
| 5 | * This code is based almost entirely upon the x86 perf event | 5 | * This code is based almost entirely upon the x86 perf event |
| 6 | * code, which is: | 6 | * code, which is: |
| @@ -18,11 +18,15 @@ | |||
| 18 | #include <linux/kdebug.h> | 18 | #include <linux/kdebug.h> |
| 19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
| 20 | 20 | ||
| 21 | #include <asm/stacktrace.h> | ||
| 21 | #include <asm/cpudata.h> | 22 | #include <asm/cpudata.h> |
| 23 | #include <asm/uaccess.h> | ||
| 22 | #include <asm/atomic.h> | 24 | #include <asm/atomic.h> |
| 23 | #include <asm/nmi.h> | 25 | #include <asm/nmi.h> |
| 24 | #include <asm/pcr.h> | 26 | #include <asm/pcr.h> |
| 25 | 27 | ||
| 28 | #include "kstack.h" | ||
| 29 | |||
| 26 | /* Sparc64 chips have two performance counters, 32-bits each, with | 30 | /* Sparc64 chips have two performance counters, 32-bits each, with |
| 27 | * overflow interrupts generated on transition from 0xffffffff to 0. | 31 | * overflow interrupts generated on transition from 0xffffffff to 0. |
| 28 | * The counters are accessed in one go using a 64-bit register. | 32 | * The counters are accessed in one go using a 64-bit register. |
| @@ -1062,3 +1066,117 @@ void __init init_hw_perf_events(void) | |||
| 1062 | 1066 | ||
| 1063 | register_die_notifier(&perf_event_nmi_notifier); | 1067 | register_die_notifier(&perf_event_nmi_notifier); |
| 1064 | } | 1068 | } |
| 1069 | |||
| 1070 | static inline void callchain_store(struct perf_callchain_entry *entry, u64 ip) | ||
| 1071 | { | ||
| 1072 | if (entry->nr < PERF_MAX_STACK_DEPTH) | ||
| 1073 | entry->ip[entry->nr++] = ip; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static void perf_callchain_kernel(struct pt_regs *regs, | ||
| 1077 | struct perf_callchain_entry *entry) | ||
| 1078 | { | ||
| 1079 | unsigned long ksp, fp; | ||
| 1080 | |||
| 1081 | callchain_store(entry, PERF_CONTEXT_KERNEL); | ||
| 1082 | callchain_store(entry, regs->tpc); | ||
| 1083 | |||
| 1084 | ksp = regs->u_regs[UREG_I6]; | ||
| 1085 | fp = ksp + STACK_BIAS; | ||
| 1086 | do { | ||
| 1087 | struct sparc_stackf *sf; | ||
| 1088 | struct pt_regs *regs; | ||
| 1089 | unsigned long pc; | ||
| 1090 | |||
| 1091 | if (!kstack_valid(current_thread_info(), fp)) | ||
| 1092 | break; | ||
| 1093 | |||
| 1094 | sf = (struct sparc_stackf *) fp; | ||
| 1095 | regs = (struct pt_regs *) (sf + 1); | ||
| 1096 | |||
| 1097 | if (kstack_is_trap_frame(current_thread_info(), regs)) { | ||
| 1098 | if (user_mode(regs)) | ||
| 1099 | break; | ||
| 1100 | pc = regs->tpc; | ||
| 1101 | fp = regs->u_regs[UREG_I6] + STACK_BIAS; | ||
| 1102 | } else { | ||
| 1103 | pc = sf->callers_pc; | ||
| 1104 | fp = (unsigned long)sf->fp + STACK_BIAS; | ||
| 1105 | } | ||
| 1106 | callchain_store(entry, pc); | ||
| 1107 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | static void perf_callchain_user_64(struct pt_regs *regs, | ||
| 1111 | struct perf_callchain_entry *entry) | ||
| 1112 | { | ||
| 1113 | unsigned long ufp; | ||
| 1114 | |||
| 1115 | callchain_store(entry, PERF_CONTEXT_USER); | ||
| 1116 | callchain_store(entry, regs->tpc); | ||
| 1117 | |||
| 1118 | ufp = regs->u_regs[UREG_I6] + STACK_BIAS; | ||
| 1119 | do { | ||
| 1120 | struct sparc_stackf *usf, sf; | ||
| 1121 | unsigned long pc; | ||
| 1122 | |||
| 1123 | usf = (struct sparc_stackf *) ufp; | ||
| 1124 | if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) | ||
| 1125 | break; | ||
| 1126 | |||
| 1127 | pc = sf.callers_pc; | ||
| 1128 | ufp = (unsigned long)sf.fp + STACK_BIAS; | ||
| 1129 | callchain_store(entry, pc); | ||
| 1130 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | static void perf_callchain_user_32(struct pt_regs *regs, | ||
| 1134 | struct perf_callchain_entry *entry) | ||
| 1135 | { | ||
| 1136 | unsigned long ufp; | ||
| 1137 | |||
| 1138 | callchain_store(entry, PERF_CONTEXT_USER); | ||
| 1139 | callchain_store(entry, regs->tpc); | ||
| 1140 | |||
| 1141 | ufp = regs->u_regs[UREG_I6]; | ||
| 1142 | do { | ||
| 1143 | struct sparc_stackf32 *usf, sf; | ||
| 1144 | unsigned long pc; | ||
| 1145 | |||
| 1146 | usf = (struct sparc_stackf32 *) ufp; | ||
| 1147 | if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) | ||
| 1148 | break; | ||
| 1149 | |||
| 1150 | pc = sf.callers_pc; | ||
| 1151 | ufp = (unsigned long)sf.fp; | ||
| 1152 | callchain_store(entry, pc); | ||
| 1153 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | /* Like powerpc we can't get PMU interrupts within the PMU handler, | ||
| 1157 | * so no need for seperate NMI and IRQ chains as on x86. | ||
| 1158 | */ | ||
| 1159 | static DEFINE_PER_CPU(struct perf_callchain_entry, callchain); | ||
| 1160 | |||
| 1161 | struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | ||
| 1162 | { | ||
| 1163 | struct perf_callchain_entry *entry = &__get_cpu_var(callchain); | ||
| 1164 | |||
| 1165 | entry->nr = 0; | ||
| 1166 | if (!user_mode(regs)) { | ||
| 1167 | stack_trace_flush(); | ||
| 1168 | perf_callchain_kernel(regs, entry); | ||
| 1169 | if (current->mm) | ||
| 1170 | regs = task_pt_regs(current); | ||
| 1171 | else | ||
| 1172 | regs = NULL; | ||
| 1173 | } | ||
| 1174 | if (regs) { | ||
| 1175 | flushw_user(); | ||
| 1176 | if (test_thread_flag(TIF_32BIT)) | ||
| 1177 | perf_callchain_user_32(regs, entry); | ||
| 1178 | else | ||
| 1179 | perf_callchain_user_64(regs, entry); | ||
| 1180 | } | ||
| 1181 | return entry; | ||
| 1182 | } | ||
