Commit 9f8b6a6cf0ee78de87ebe1e87f54bec1c1741ef7
Merge branch 'core' of git://git.kernel.org/pub/scm/linux/kernel/git/rric/oprofile into perf/core
Showing 8 changed files Inline Diff
arch/mips/include/asm/stacktrace.h
1 | #ifndef _ASM_STACKTRACE_H | 1 | #ifndef _ASM_STACKTRACE_H |
2 | #define _ASM_STACKTRACE_H | 2 | #define _ASM_STACKTRACE_H |
3 | 3 | ||
4 | #include <asm/ptrace.h> | 4 | #include <asm/ptrace.h> |
5 | 5 | ||
6 | #ifdef CONFIG_KALLSYMS | 6 | #ifdef CONFIG_KALLSYMS |
7 | extern int raw_show_trace; | 7 | extern int raw_show_trace; |
8 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | 8 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, |
9 | unsigned long pc, unsigned long *ra); | 9 | unsigned long pc, unsigned long *ra); |
10 | extern unsigned long unwind_stack_by_address(unsigned long stack_page, | ||
11 | unsigned long *sp, | ||
12 | unsigned long pc, | ||
13 | unsigned long *ra); | ||
10 | #else | 14 | #else |
11 | #define raw_show_trace 1 | 15 | #define raw_show_trace 1 |
12 | static inline unsigned long unwind_stack(struct task_struct *task, | 16 | static inline unsigned long unwind_stack(struct task_struct *task, |
13 | unsigned long *sp, unsigned long pc, unsigned long *ra) | 17 | unsigned long *sp, unsigned long pc, unsigned long *ra) |
14 | { | 18 | { |
15 | return 0; | 19 | return 0; |
16 | } | 20 | } |
17 | #endif | 21 | #endif |
18 | 22 | ||
19 | static __always_inline void prepare_frametrace(struct pt_regs *regs) | 23 | static __always_inline void prepare_frametrace(struct pt_regs *regs) |
20 | { | 24 | { |
21 | #ifndef CONFIG_KALLSYMS | 25 | #ifndef CONFIG_KALLSYMS |
22 | /* | 26 | /* |
23 | * Remove any garbage that may be in regs (specially func | 27 | * Remove any garbage that may be in regs (specially func |
24 | * addresses) to avoid show_raw_backtrace() to report them | 28 | * addresses) to avoid show_raw_backtrace() to report them |
25 | */ | 29 | */ |
26 | memset(regs, 0, sizeof(*regs)); | 30 | memset(regs, 0, sizeof(*regs)); |
27 | #endif | 31 | #endif |
28 | __asm__ __volatile__( | 32 | __asm__ __volatile__( |
29 | ".set push\n\t" | 33 | ".set push\n\t" |
30 | ".set noat\n\t" | 34 | ".set noat\n\t" |
31 | #ifdef CONFIG_64BIT | 35 | #ifdef CONFIG_64BIT |
32 | "1: dla $1, 1b\n\t" | 36 | "1: dla $1, 1b\n\t" |
33 | "sd $1, %0\n\t" | 37 | "sd $1, %0\n\t" |
34 | "sd $29, %1\n\t" | 38 | "sd $29, %1\n\t" |
35 | "sd $31, %2\n\t" | 39 | "sd $31, %2\n\t" |
36 | #else | 40 | #else |
37 | "1: la $1, 1b\n\t" | 41 | "1: la $1, 1b\n\t" |
38 | "sw $1, %0\n\t" | 42 | "sw $1, %0\n\t" |
39 | "sw $29, %1\n\t" | 43 | "sw $29, %1\n\t" |
40 | "sw $31, %2\n\t" | 44 | "sw $31, %2\n\t" |
41 | #endif | 45 | #endif |
42 | ".set pop\n\t" | 46 | ".set pop\n\t" |
43 | : "=m" (regs->cp0_epc), | 47 | : "=m" (regs->cp0_epc), |
44 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) | 48 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) |
45 | : : "memory"); | 49 | : : "memory"); |
46 | } | 50 | } |
47 | 51 | ||
48 | #endif /* _ASM_STACKTRACE_H */ | 52 | #endif /* _ASM_STACKTRACE_H */ |
49 | 53 |
arch/mips/kernel/process.c
1 | /* | 1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | * This file is subject to the terms and conditions of the GNU General Public |
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. | 4 | * for more details. |
5 | * | 5 | * |
6 | * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. | 6 | * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. |
7 | * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) | 7 | * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) |
8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. |
9 | * Copyright (C) 2004 Thiemo Seufer | 9 | * Copyright (C) 2004 Thiemo Seufer |
10 | */ | 10 | */ |
11 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <linux/tick.h> | 14 | #include <linux/tick.h> |
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
17 | #include <linux/stddef.h> | 17 | #include <linux/stddef.h> |
18 | #include <linux/unistd.h> | 18 | #include <linux/unistd.h> |
19 | #include <linux/ptrace.h> | 19 | #include <linux/ptrace.h> |
20 | #include <linux/mman.h> | 20 | #include <linux/mman.h> |
21 | #include <linux/personality.h> | 21 | #include <linux/personality.h> |
22 | #include <linux/sys.h> | 22 | #include <linux/sys.h> |
23 | #include <linux/user.h> | 23 | #include <linux/user.h> |
24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
25 | #include <linux/completion.h> | 25 | #include <linux/completion.h> |
26 | #include <linux/kallsyms.h> | 26 | #include <linux/kallsyms.h> |
27 | #include <linux/random.h> | 27 | #include <linux/random.h> |
28 | 28 | ||
29 | #include <asm/asm.h> | 29 | #include <asm/asm.h> |
30 | #include <asm/bootinfo.h> | 30 | #include <asm/bootinfo.h> |
31 | #include <asm/cpu.h> | 31 | #include <asm/cpu.h> |
32 | #include <asm/dsp.h> | 32 | #include <asm/dsp.h> |
33 | #include <asm/fpu.h> | 33 | #include <asm/fpu.h> |
34 | #include <asm/pgtable.h> | 34 | #include <asm/pgtable.h> |
35 | #include <asm/system.h> | 35 | #include <asm/system.h> |
36 | #include <asm/mipsregs.h> | 36 | #include <asm/mipsregs.h> |
37 | #include <asm/processor.h> | 37 | #include <asm/processor.h> |
38 | #include <asm/uaccess.h> | 38 | #include <asm/uaccess.h> |
39 | #include <asm/io.h> | 39 | #include <asm/io.h> |
40 | #include <asm/elf.h> | 40 | #include <asm/elf.h> |
41 | #include <asm/isadep.h> | 41 | #include <asm/isadep.h> |
42 | #include <asm/inst.h> | 42 | #include <asm/inst.h> |
43 | #include <asm/stacktrace.h> | 43 | #include <asm/stacktrace.h> |
44 | 44 | ||
45 | /* | 45 | /* |
46 | * The idle thread. There's no useful work to be done, so just try to conserve | 46 | * The idle thread. There's no useful work to be done, so just try to conserve |
47 | * power and have a low exit latency (ie sit in a loop waiting for somebody to | 47 | * power and have a low exit latency (ie sit in a loop waiting for somebody to |
48 | * say that they'd like to reschedule) | 48 | * say that they'd like to reschedule) |
49 | */ | 49 | */ |
50 | void __noreturn cpu_idle(void) | 50 | void __noreturn cpu_idle(void) |
51 | { | 51 | { |
52 | int cpu; | 52 | int cpu; |
53 | 53 | ||
54 | /* CPU is going idle. */ | 54 | /* CPU is going idle. */ |
55 | cpu = smp_processor_id(); | 55 | cpu = smp_processor_id(); |
56 | 56 | ||
57 | /* endless idle loop with no priority at all */ | 57 | /* endless idle loop with no priority at all */ |
58 | while (1) { | 58 | while (1) { |
59 | tick_nohz_stop_sched_tick(1); | 59 | tick_nohz_stop_sched_tick(1); |
60 | while (!need_resched() && cpu_online(cpu)) { | 60 | while (!need_resched() && cpu_online(cpu)) { |
61 | #ifdef CONFIG_MIPS_MT_SMTC | 61 | #ifdef CONFIG_MIPS_MT_SMTC |
62 | extern void smtc_idle_loop_hook(void); | 62 | extern void smtc_idle_loop_hook(void); |
63 | 63 | ||
64 | smtc_idle_loop_hook(); | 64 | smtc_idle_loop_hook(); |
65 | #endif | 65 | #endif |
66 | 66 | ||
67 | if (cpu_wait) { | 67 | if (cpu_wait) { |
68 | /* Don't trace irqs off for idle */ | 68 | /* Don't trace irqs off for idle */ |
69 | stop_critical_timings(); | 69 | stop_critical_timings(); |
70 | (*cpu_wait)(); | 70 | (*cpu_wait)(); |
71 | start_critical_timings(); | 71 | start_critical_timings(); |
72 | } | 72 | } |
73 | } | 73 | } |
74 | #ifdef CONFIG_HOTPLUG_CPU | 74 | #ifdef CONFIG_HOTPLUG_CPU |
75 | if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) && | 75 | if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) && |
76 | (system_state == SYSTEM_RUNNING || | 76 | (system_state == SYSTEM_RUNNING || |
77 | system_state == SYSTEM_BOOTING)) | 77 | system_state == SYSTEM_BOOTING)) |
78 | play_dead(); | 78 | play_dead(); |
79 | #endif | 79 | #endif |
80 | tick_nohz_restart_sched_tick(); | 80 | tick_nohz_restart_sched_tick(); |
81 | preempt_enable_no_resched(); | 81 | preempt_enable_no_resched(); |
82 | schedule(); | 82 | schedule(); |
83 | preempt_disable(); | 83 | preempt_disable(); |
84 | } | 84 | } |
85 | } | 85 | } |
86 | 86 | ||
87 | asmlinkage void ret_from_fork(void); | 87 | asmlinkage void ret_from_fork(void); |
88 | 88 | ||
89 | void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) | 89 | void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) |
90 | { | 90 | { |
91 | unsigned long status; | 91 | unsigned long status; |
92 | 92 | ||
93 | /* New thread loses kernel privileges. */ | 93 | /* New thread loses kernel privileges. */ |
94 | status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|ST0_FR|KU_MASK); | 94 | status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|ST0_FR|KU_MASK); |
95 | #ifdef CONFIG_64BIT | 95 | #ifdef CONFIG_64BIT |
96 | status |= test_thread_flag(TIF_32BIT_REGS) ? 0 : ST0_FR; | 96 | status |= test_thread_flag(TIF_32BIT_REGS) ? 0 : ST0_FR; |
97 | #endif | 97 | #endif |
98 | status |= KU_USER; | 98 | status |= KU_USER; |
99 | regs->cp0_status = status; | 99 | regs->cp0_status = status; |
100 | clear_used_math(); | 100 | clear_used_math(); |
101 | clear_fpu_owner(); | 101 | clear_fpu_owner(); |
102 | if (cpu_has_dsp) | 102 | if (cpu_has_dsp) |
103 | __init_dsp(); | 103 | __init_dsp(); |
104 | regs->cp0_epc = pc; | 104 | regs->cp0_epc = pc; |
105 | regs->regs[29] = sp; | 105 | regs->regs[29] = sp; |
106 | current_thread_info()->addr_limit = USER_DS; | 106 | current_thread_info()->addr_limit = USER_DS; |
107 | } | 107 | } |
108 | 108 | ||
109 | void exit_thread(void) | 109 | void exit_thread(void) |
110 | { | 110 | { |
111 | } | 111 | } |
112 | 112 | ||
113 | void flush_thread(void) | 113 | void flush_thread(void) |
114 | { | 114 | { |
115 | } | 115 | } |
116 | 116 | ||
117 | int copy_thread(unsigned long clone_flags, unsigned long usp, | 117 | int copy_thread(unsigned long clone_flags, unsigned long usp, |
118 | unsigned long unused, struct task_struct *p, struct pt_regs *regs) | 118 | unsigned long unused, struct task_struct *p, struct pt_regs *regs) |
119 | { | 119 | { |
120 | struct thread_info *ti = task_thread_info(p); | 120 | struct thread_info *ti = task_thread_info(p); |
121 | struct pt_regs *childregs; | 121 | struct pt_regs *childregs; |
122 | unsigned long childksp; | 122 | unsigned long childksp; |
123 | p->set_child_tid = p->clear_child_tid = NULL; | 123 | p->set_child_tid = p->clear_child_tid = NULL; |
124 | 124 | ||
125 | childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32; | 125 | childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32; |
126 | 126 | ||
127 | preempt_disable(); | 127 | preempt_disable(); |
128 | 128 | ||
129 | if (is_fpu_owner()) | 129 | if (is_fpu_owner()) |
130 | save_fp(p); | 130 | save_fp(p); |
131 | 131 | ||
132 | if (cpu_has_dsp) | 132 | if (cpu_has_dsp) |
133 | save_dsp(p); | 133 | save_dsp(p); |
134 | 134 | ||
135 | preempt_enable(); | 135 | preempt_enable(); |
136 | 136 | ||
137 | /* set up new TSS. */ | 137 | /* set up new TSS. */ |
138 | childregs = (struct pt_regs *) childksp - 1; | 138 | childregs = (struct pt_regs *) childksp - 1; |
139 | /* Put the stack after the struct pt_regs. */ | 139 | /* Put the stack after the struct pt_regs. */ |
140 | childksp = (unsigned long) childregs; | 140 | childksp = (unsigned long) childregs; |
141 | *childregs = *regs; | 141 | *childregs = *regs; |
142 | childregs->regs[7] = 0; /* Clear error flag */ | 142 | childregs->regs[7] = 0; /* Clear error flag */ |
143 | 143 | ||
144 | childregs->regs[2] = 0; /* Child gets zero as return value */ | 144 | childregs->regs[2] = 0; /* Child gets zero as return value */ |
145 | 145 | ||
146 | if (childregs->cp0_status & ST0_CU0) { | 146 | if (childregs->cp0_status & ST0_CU0) { |
147 | childregs->regs[28] = (unsigned long) ti; | 147 | childregs->regs[28] = (unsigned long) ti; |
148 | childregs->regs[29] = childksp; | 148 | childregs->regs[29] = childksp; |
149 | ti->addr_limit = KERNEL_DS; | 149 | ti->addr_limit = KERNEL_DS; |
150 | } else { | 150 | } else { |
151 | childregs->regs[29] = usp; | 151 | childregs->regs[29] = usp; |
152 | ti->addr_limit = USER_DS; | 152 | ti->addr_limit = USER_DS; |
153 | } | 153 | } |
154 | p->thread.reg29 = (unsigned long) childregs; | 154 | p->thread.reg29 = (unsigned long) childregs; |
155 | p->thread.reg31 = (unsigned long) ret_from_fork; | 155 | p->thread.reg31 = (unsigned long) ret_from_fork; |
156 | 156 | ||
157 | /* | 157 | /* |
158 | * New tasks lose permission to use the fpu. This accelerates context | 158 | * New tasks lose permission to use the fpu. This accelerates context |
159 | * switching for most programs since they don't use the fpu. | 159 | * switching for most programs since they don't use the fpu. |
160 | */ | 160 | */ |
161 | p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1); | 161 | p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1); |
162 | childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); | 162 | childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); |
163 | 163 | ||
164 | #ifdef CONFIG_MIPS_MT_SMTC | 164 | #ifdef CONFIG_MIPS_MT_SMTC |
165 | /* | 165 | /* |
166 | * SMTC restores TCStatus after Status, and the CU bits | 166 | * SMTC restores TCStatus after Status, and the CU bits |
167 | * are aliased there. | 167 | * are aliased there. |
168 | */ | 168 | */ |
169 | childregs->cp0_tcstatus &= ~(ST0_CU2|ST0_CU1); | 169 | childregs->cp0_tcstatus &= ~(ST0_CU2|ST0_CU1); |
170 | #endif | 170 | #endif |
171 | clear_tsk_thread_flag(p, TIF_USEDFPU); | 171 | clear_tsk_thread_flag(p, TIF_USEDFPU); |
172 | 172 | ||
173 | #ifdef CONFIG_MIPS_MT_FPAFF | 173 | #ifdef CONFIG_MIPS_MT_FPAFF |
174 | clear_tsk_thread_flag(p, TIF_FPUBOUND); | 174 | clear_tsk_thread_flag(p, TIF_FPUBOUND); |
175 | #endif /* CONFIG_MIPS_MT_FPAFF */ | 175 | #endif /* CONFIG_MIPS_MT_FPAFF */ |
176 | 176 | ||
177 | if (clone_flags & CLONE_SETTLS) | 177 | if (clone_flags & CLONE_SETTLS) |
178 | ti->tp_value = regs->regs[7]; | 178 | ti->tp_value = regs->regs[7]; |
179 | 179 | ||
180 | return 0; | 180 | return 0; |
181 | } | 181 | } |
182 | 182 | ||
183 | /* Fill in the fpu structure for a core dump.. */ | 183 | /* Fill in the fpu structure for a core dump.. */ |
184 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) | 184 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) |
185 | { | 185 | { |
186 | memcpy(r, ¤t->thread.fpu, sizeof(current->thread.fpu)); | 186 | memcpy(r, ¤t->thread.fpu, sizeof(current->thread.fpu)); |
187 | 187 | ||
188 | return 1; | 188 | return 1; |
189 | } | 189 | } |
190 | 190 | ||
191 | void elf_dump_regs(elf_greg_t *gp, struct pt_regs *regs) | 191 | void elf_dump_regs(elf_greg_t *gp, struct pt_regs *regs) |
192 | { | 192 | { |
193 | int i; | 193 | int i; |
194 | 194 | ||
195 | for (i = 0; i < EF_R0; i++) | 195 | for (i = 0; i < EF_R0; i++) |
196 | gp[i] = 0; | 196 | gp[i] = 0; |
197 | gp[EF_R0] = 0; | 197 | gp[EF_R0] = 0; |
198 | for (i = 1; i <= 31; i++) | 198 | for (i = 1; i <= 31; i++) |
199 | gp[EF_R0 + i] = regs->regs[i]; | 199 | gp[EF_R0 + i] = regs->regs[i]; |
200 | gp[EF_R26] = 0; | 200 | gp[EF_R26] = 0; |
201 | gp[EF_R27] = 0; | 201 | gp[EF_R27] = 0; |
202 | gp[EF_LO] = regs->lo; | 202 | gp[EF_LO] = regs->lo; |
203 | gp[EF_HI] = regs->hi; | 203 | gp[EF_HI] = regs->hi; |
204 | gp[EF_CP0_EPC] = regs->cp0_epc; | 204 | gp[EF_CP0_EPC] = regs->cp0_epc; |
205 | gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr; | 205 | gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr; |
206 | gp[EF_CP0_STATUS] = regs->cp0_status; | 206 | gp[EF_CP0_STATUS] = regs->cp0_status; |
207 | gp[EF_CP0_CAUSE] = regs->cp0_cause; | 207 | gp[EF_CP0_CAUSE] = regs->cp0_cause; |
208 | #ifdef EF_UNUSED0 | 208 | #ifdef EF_UNUSED0 |
209 | gp[EF_UNUSED0] = 0; | 209 | gp[EF_UNUSED0] = 0; |
210 | #endif | 210 | #endif |
211 | } | 211 | } |
212 | 212 | ||
213 | int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) | 213 | int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) |
214 | { | 214 | { |
215 | elf_dump_regs(*regs, task_pt_regs(tsk)); | 215 | elf_dump_regs(*regs, task_pt_regs(tsk)); |
216 | return 1; | 216 | return 1; |
217 | } | 217 | } |
218 | 218 | ||
219 | int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr) | 219 | int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr) |
220 | { | 220 | { |
221 | memcpy(fpr, &t->thread.fpu, sizeof(current->thread.fpu)); | 221 | memcpy(fpr, &t->thread.fpu, sizeof(current->thread.fpu)); |
222 | 222 | ||
223 | return 1; | 223 | return 1; |
224 | } | 224 | } |
225 | 225 | ||
226 | /* | 226 | /* |
227 | * Create a kernel thread | 227 | * Create a kernel thread |
228 | */ | 228 | */ |
229 | static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) | 229 | static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) |
230 | { | 230 | { |
231 | do_exit(fn(arg)); | 231 | do_exit(fn(arg)); |
232 | } | 232 | } |
233 | 233 | ||
234 | long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | 234 | long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) |
235 | { | 235 | { |
236 | struct pt_regs regs; | 236 | struct pt_regs regs; |
237 | 237 | ||
238 | memset(®s, 0, sizeof(regs)); | 238 | memset(®s, 0, sizeof(regs)); |
239 | 239 | ||
240 | regs.regs[4] = (unsigned long) arg; | 240 | regs.regs[4] = (unsigned long) arg; |
241 | regs.regs[5] = (unsigned long) fn; | 241 | regs.regs[5] = (unsigned long) fn; |
242 | regs.cp0_epc = (unsigned long) kernel_thread_helper; | 242 | regs.cp0_epc = (unsigned long) kernel_thread_helper; |
243 | regs.cp0_status = read_c0_status(); | 243 | regs.cp0_status = read_c0_status(); |
244 | #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) | 244 | #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) |
245 | regs.cp0_status = (regs.cp0_status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) | | 245 | regs.cp0_status = (regs.cp0_status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) | |
246 | ((regs.cp0_status & (ST0_KUC | ST0_IEC)) << 2); | 246 | ((regs.cp0_status & (ST0_KUC | ST0_IEC)) << 2); |
247 | #else | 247 | #else |
248 | regs.cp0_status |= ST0_EXL; | 248 | regs.cp0_status |= ST0_EXL; |
249 | #endif | 249 | #endif |
250 | 250 | ||
251 | /* Ok, create the new process.. */ | 251 | /* Ok, create the new process.. */ |
252 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | 252 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); |
253 | } | 253 | } |
254 | 254 | ||
255 | /* | 255 | /* |
256 | * | 256 | * |
257 | */ | 257 | */ |
258 | struct mips_frame_info { | 258 | struct mips_frame_info { |
259 | void *func; | 259 | void *func; |
260 | unsigned long func_size; | 260 | unsigned long func_size; |
261 | int frame_size; | 261 | int frame_size; |
262 | int pc_offset; | 262 | int pc_offset; |
263 | }; | 263 | }; |
264 | 264 | ||
265 | static inline int is_ra_save_ins(union mips_instruction *ip) | 265 | static inline int is_ra_save_ins(union mips_instruction *ip) |
266 | { | 266 | { |
267 | /* sw / sd $ra, offset($sp) */ | 267 | /* sw / sd $ra, offset($sp) */ |
268 | return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && | 268 | return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && |
269 | ip->i_format.rs == 29 && | 269 | ip->i_format.rs == 29 && |
270 | ip->i_format.rt == 31; | 270 | ip->i_format.rt == 31; |
271 | } | 271 | } |
272 | 272 | ||
273 | static inline int is_jal_jalr_jr_ins(union mips_instruction *ip) | 273 | static inline int is_jal_jalr_jr_ins(union mips_instruction *ip) |
274 | { | 274 | { |
275 | if (ip->j_format.opcode == jal_op) | 275 | if (ip->j_format.opcode == jal_op) |
276 | return 1; | 276 | return 1; |
277 | if (ip->r_format.opcode != spec_op) | 277 | if (ip->r_format.opcode != spec_op) |
278 | return 0; | 278 | return 0; |
279 | return ip->r_format.func == jalr_op || ip->r_format.func == jr_op; | 279 | return ip->r_format.func == jalr_op || ip->r_format.func == jr_op; |
280 | } | 280 | } |
281 | 281 | ||
282 | static inline int is_sp_move_ins(union mips_instruction *ip) | 282 | static inline int is_sp_move_ins(union mips_instruction *ip) |
283 | { | 283 | { |
284 | /* addiu/daddiu sp,sp,-imm */ | 284 | /* addiu/daddiu sp,sp,-imm */ |
285 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) | 285 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) |
286 | return 0; | 286 | return 0; |
287 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) | 287 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) |
288 | return 1; | 288 | return 1; |
289 | return 0; | 289 | return 0; |
290 | } | 290 | } |
291 | 291 | ||
292 | static int get_frame_info(struct mips_frame_info *info) | 292 | static int get_frame_info(struct mips_frame_info *info) |
293 | { | 293 | { |
294 | union mips_instruction *ip = info->func; | 294 | union mips_instruction *ip = info->func; |
295 | unsigned max_insns = info->func_size / sizeof(union mips_instruction); | 295 | unsigned max_insns = info->func_size / sizeof(union mips_instruction); |
296 | unsigned i; | 296 | unsigned i; |
297 | 297 | ||
298 | info->pc_offset = -1; | 298 | info->pc_offset = -1; |
299 | info->frame_size = 0; | 299 | info->frame_size = 0; |
300 | 300 | ||
301 | if (!ip) | 301 | if (!ip) |
302 | goto err; | 302 | goto err; |
303 | 303 | ||
304 | if (max_insns == 0) | 304 | if (max_insns == 0) |
305 | max_insns = 128U; /* unknown function size */ | 305 | max_insns = 128U; /* unknown function size */ |
306 | max_insns = min(128U, max_insns); | 306 | max_insns = min(128U, max_insns); |
307 | 307 | ||
308 | for (i = 0; i < max_insns; i++, ip++) { | 308 | for (i = 0; i < max_insns; i++, ip++) { |
309 | 309 | ||
310 | if (is_jal_jalr_jr_ins(ip)) | 310 | if (is_jal_jalr_jr_ins(ip)) |
311 | break; | 311 | break; |
312 | if (!info->frame_size) { | 312 | if (!info->frame_size) { |
313 | if (is_sp_move_ins(ip)) | 313 | if (is_sp_move_ins(ip)) |
314 | info->frame_size = - ip->i_format.simmediate; | 314 | info->frame_size = - ip->i_format.simmediate; |
315 | continue; | 315 | continue; |
316 | } | 316 | } |
317 | if (info->pc_offset == -1 && is_ra_save_ins(ip)) { | 317 | if (info->pc_offset == -1 && is_ra_save_ins(ip)) { |
318 | info->pc_offset = | 318 | info->pc_offset = |
319 | ip->i_format.simmediate / sizeof(long); | 319 | ip->i_format.simmediate / sizeof(long); |
320 | break; | 320 | break; |
321 | } | 321 | } |
322 | } | 322 | } |
323 | if (info->frame_size && info->pc_offset >= 0) /* nested */ | 323 | if (info->frame_size && info->pc_offset >= 0) /* nested */ |
324 | return 0; | 324 | return 0; |
325 | if (info->pc_offset < 0) /* leaf */ | 325 | if (info->pc_offset < 0) /* leaf */ |
326 | return 1; | 326 | return 1; |
327 | /* prologue seems boggus... */ | 327 | /* prologue seems boggus... */ |
328 | err: | 328 | err: |
329 | return -1; | 329 | return -1; |
330 | } | 330 | } |
331 | 331 | ||
332 | static struct mips_frame_info schedule_mfi __read_mostly; | 332 | static struct mips_frame_info schedule_mfi __read_mostly; |
333 | 333 | ||
334 | static int __init frame_info_init(void) | 334 | static int __init frame_info_init(void) |
335 | { | 335 | { |
336 | unsigned long size = 0; | 336 | unsigned long size = 0; |
337 | #ifdef CONFIG_KALLSYMS | 337 | #ifdef CONFIG_KALLSYMS |
338 | unsigned long ofs; | 338 | unsigned long ofs; |
339 | 339 | ||
340 | kallsyms_lookup_size_offset((unsigned long)schedule, &size, &ofs); | 340 | kallsyms_lookup_size_offset((unsigned long)schedule, &size, &ofs); |
341 | #endif | 341 | #endif |
342 | schedule_mfi.func = schedule; | 342 | schedule_mfi.func = schedule; |
343 | schedule_mfi.func_size = size; | 343 | schedule_mfi.func_size = size; |
344 | 344 | ||
345 | get_frame_info(&schedule_mfi); | 345 | get_frame_info(&schedule_mfi); |
346 | 346 | ||
347 | /* | 347 | /* |
348 | * Without schedule() frame info, result given by | 348 | * Without schedule() frame info, result given by |
349 | * thread_saved_pc() and get_wchan() are not reliable. | 349 | * thread_saved_pc() and get_wchan() are not reliable. |
350 | */ | 350 | */ |
351 | if (schedule_mfi.pc_offset < 0) | 351 | if (schedule_mfi.pc_offset < 0) |
352 | printk("Can't analyze schedule() prologue at %p\n", schedule); | 352 | printk("Can't analyze schedule() prologue at %p\n", schedule); |
353 | 353 | ||
354 | return 0; | 354 | return 0; |
355 | } | 355 | } |
356 | 356 | ||
357 | arch_initcall(frame_info_init); | 357 | arch_initcall(frame_info_init); |
358 | 358 | ||
359 | /* | 359 | /* |
360 | * Return saved PC of a blocked thread. | 360 | * Return saved PC of a blocked thread. |
361 | */ | 361 | */ |
362 | unsigned long thread_saved_pc(struct task_struct *tsk) | 362 | unsigned long thread_saved_pc(struct task_struct *tsk) |
363 | { | 363 | { |
364 | struct thread_struct *t = &tsk->thread; | 364 | struct thread_struct *t = &tsk->thread; |
365 | 365 | ||
366 | /* New born processes are a special case */ | 366 | /* New born processes are a special case */ |
367 | if (t->reg31 == (unsigned long) ret_from_fork) | 367 | if (t->reg31 == (unsigned long) ret_from_fork) |
368 | return t->reg31; | 368 | return t->reg31; |
369 | if (schedule_mfi.pc_offset < 0) | 369 | if (schedule_mfi.pc_offset < 0) |
370 | return 0; | 370 | return 0; |
371 | return ((unsigned long *)t->reg29)[schedule_mfi.pc_offset]; | 371 | return ((unsigned long *)t->reg29)[schedule_mfi.pc_offset]; |
372 | } | 372 | } |
373 | 373 | ||
374 | 374 | ||
375 | #ifdef CONFIG_KALLSYMS | 375 | #ifdef CONFIG_KALLSYMS |
376 | /* used by show_backtrace() */ | 376 | /* generic stack unwinding function */ |
377 | unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | 377 | unsigned long notrace unwind_stack_by_address(unsigned long stack_page, |
378 | unsigned long pc, unsigned long *ra) | 378 | unsigned long *sp, |
379 | unsigned long pc, | ||
380 | unsigned long *ra) | ||
379 | { | 381 | { |
380 | unsigned long stack_page; | ||
381 | struct mips_frame_info info; | 382 | struct mips_frame_info info; |
382 | unsigned long size, ofs; | 383 | unsigned long size, ofs; |
383 | int leaf; | 384 | int leaf; |
384 | extern void ret_from_irq(void); | 385 | extern void ret_from_irq(void); |
385 | extern void ret_from_exception(void); | 386 | extern void ret_from_exception(void); |
386 | 387 | ||
387 | stack_page = (unsigned long)task_stack_page(task); | ||
388 | if (!stack_page) | 388 | if (!stack_page) |
389 | return 0; | 389 | return 0; |
390 | 390 | ||
391 | /* | 391 | /* |
392 | * If we reached the bottom of interrupt context, | 392 | * If we reached the bottom of interrupt context, |
393 | * return saved pc in pt_regs. | 393 | * return saved pc in pt_regs. |
394 | */ | 394 | */ |
395 | if (pc == (unsigned long)ret_from_irq || | 395 | if (pc == (unsigned long)ret_from_irq || |
396 | pc == (unsigned long)ret_from_exception) { | 396 | pc == (unsigned long)ret_from_exception) { |
397 | struct pt_regs *regs; | 397 | struct pt_regs *regs; |
398 | if (*sp >= stack_page && | 398 | if (*sp >= stack_page && |
399 | *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { | 399 | *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { |
400 | regs = (struct pt_regs *)*sp; | 400 | regs = (struct pt_regs *)*sp; |
401 | pc = regs->cp0_epc; | 401 | pc = regs->cp0_epc; |
402 | if (__kernel_text_address(pc)) { | 402 | if (__kernel_text_address(pc)) { |
403 | *sp = regs->regs[29]; | 403 | *sp = regs->regs[29]; |
404 | *ra = regs->regs[31]; | 404 | *ra = regs->regs[31]; |
405 | return pc; | 405 | return pc; |
406 | } | 406 | } |
407 | } | 407 | } |
408 | return 0; | 408 | return 0; |
409 | } | 409 | } |
410 | if (!kallsyms_lookup_size_offset(pc, &size, &ofs)) | 410 | if (!kallsyms_lookup_size_offset(pc, &size, &ofs)) |
411 | return 0; | 411 | return 0; |
412 | /* | 412 | /* |
413 | * Return ra if an exception occurred at the first instruction | 413 | * Return ra if an exception occurred at the first instruction |
414 | */ | 414 | */ |
415 | if (unlikely(ofs == 0)) { | 415 | if (unlikely(ofs == 0)) { |
416 | pc = *ra; | 416 | pc = *ra; |
417 | *ra = 0; | 417 | *ra = 0; |
418 | return pc; | 418 | return pc; |
419 | } | 419 | } |
420 | 420 | ||
421 | info.func = (void *)(pc - ofs); | 421 | info.func = (void *)(pc - ofs); |
422 | info.func_size = ofs; /* analyze from start to ofs */ | 422 | info.func_size = ofs; /* analyze from start to ofs */ |
423 | leaf = get_frame_info(&info); | 423 | leaf = get_frame_info(&info); |
424 | if (leaf < 0) | 424 | if (leaf < 0) |
425 | return 0; | 425 | return 0; |
426 | 426 | ||
427 | if (*sp < stack_page || | 427 | if (*sp < stack_page || |
428 | *sp + info.frame_size > stack_page + THREAD_SIZE - 32) | 428 | *sp + info.frame_size > stack_page + THREAD_SIZE - 32) |
429 | return 0; | 429 | return 0; |
430 | 430 | ||
431 | if (leaf) | 431 | if (leaf) |
432 | /* | 432 | /* |
433 | * For some extreme cases, get_frame_info() can | 433 | * For some extreme cases, get_frame_info() can |
434 | * consider wrongly a nested function as a leaf | 434 | * consider wrongly a nested function as a leaf |
435 | * one. In that cases avoid to return always the | 435 | * one. In that cases avoid to return always the |
436 | * same value. | 436 | * same value. |
437 | */ | 437 | */ |
438 | pc = pc != *ra ? *ra : 0; | 438 | pc = pc != *ra ? *ra : 0; |
439 | else | 439 | else |
440 | pc = ((unsigned long *)(*sp))[info.pc_offset]; | 440 | pc = ((unsigned long *)(*sp))[info.pc_offset]; |
441 | 441 | ||
442 | *sp += info.frame_size; | 442 | *sp += info.frame_size; |
443 | *ra = 0; | 443 | *ra = 0; |
444 | return __kernel_text_address(pc) ? pc : 0; | 444 | return __kernel_text_address(pc) ? pc : 0; |
445 | } | ||
446 | EXPORT_SYMBOL(unwind_stack_by_address); | ||
447 | |||
448 | /* used by show_backtrace() */ | ||
449 | unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | ||
450 | unsigned long pc, unsigned long *ra) | ||
451 | { | ||
452 | unsigned long stack_page = (unsigned long)task_stack_page(task); | ||
453 | return unwind_stack_by_address(stack_page, sp, pc, ra); | ||
445 | } | 454 | } |
446 | #endif | 455 | #endif |
447 | 456 | ||
448 | /* | 457 | /* |
449 | * get_wchan - a maintenance nightmare^W^Wpain in the ass ... | 458 | * get_wchan - a maintenance nightmare^W^Wpain in the ass ... |
450 | */ | 459 | */ |
451 | unsigned long get_wchan(struct task_struct *task) | 460 | unsigned long get_wchan(struct task_struct *task) |
452 | { | 461 | { |
453 | unsigned long pc = 0; | 462 | unsigned long pc = 0; |
454 | #ifdef CONFIG_KALLSYMS | 463 | #ifdef CONFIG_KALLSYMS |
455 | unsigned long sp; | 464 | unsigned long sp; |
456 | unsigned long ra = 0; | 465 | unsigned long ra = 0; |
457 | #endif | 466 | #endif |
458 | 467 | ||
459 | if (!task || task == current || task->state == TASK_RUNNING) | 468 | if (!task || task == current || task->state == TASK_RUNNING) |
460 | goto out; | 469 | goto out; |
461 | if (!task_stack_page(task)) | 470 | if (!task_stack_page(task)) |
462 | goto out; | 471 | goto out; |
463 | 472 | ||
464 | pc = thread_saved_pc(task); | 473 | pc = thread_saved_pc(task); |
465 | 474 | ||
466 | #ifdef CONFIG_KALLSYMS | 475 | #ifdef CONFIG_KALLSYMS |
467 | sp = task->thread.reg29 + schedule_mfi.frame_size; | 476 | sp = task->thread.reg29 + schedule_mfi.frame_size; |
468 | 477 | ||
469 | while (in_sched_functions(pc)) | 478 | while (in_sched_functions(pc)) |
470 | pc = unwind_stack(task, &sp, pc, &ra); | 479 | pc = unwind_stack(task, &sp, pc, &ra); |
471 | #endif | 480 | #endif |
472 | 481 | ||
473 | out: | 482 | out: |
474 | return pc; | 483 | return pc; |
475 | } | 484 | } |
476 | 485 | ||
477 | /* | 486 | /* |
478 | * Don't forget that the stack pointer must be aligned on a 8 bytes | 487 | * Don't forget that the stack pointer must be aligned on a 8 bytes |
479 | * boundary for 32-bits ABI and 16 bytes for 64-bits ABI. | 488 | * boundary for 32-bits ABI and 16 bytes for 64-bits ABI. |
480 | */ | 489 | */ |
481 | unsigned long arch_align_stack(unsigned long sp) | 490 | unsigned long arch_align_stack(unsigned long sp) |
482 | { | 491 | { |
483 | if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) | 492 | if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) |
484 | sp -= get_random_int() & ~PAGE_MASK; | 493 | sp -= get_random_int() & ~PAGE_MASK; |
485 | 494 | ||
486 | return sp & ALMASK; | 495 | return sp & ALMASK; |
arch/mips/oprofile/Makefile
1 | ccflags-y := -Werror | 1 | ccflags-y := -Werror |
2 | 2 | ||
3 | obj-$(CONFIG_OPROFILE) += oprofile.o | 3 | obj-$(CONFIG_OPROFILE) += oprofile.o |
4 | 4 | ||
5 | DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ | 5 | DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ |
6 | oprof.o cpu_buffer.o buffer_sync.o \ | 6 | oprof.o cpu_buffer.o buffer_sync.o \ |
7 | event_buffer.o oprofile_files.o \ | 7 | event_buffer.o oprofile_files.o \ |
8 | oprofilefs.o oprofile_stats.o \ | 8 | oprofilefs.o oprofile_stats.o \ |
9 | timer_int.o ) | 9 | timer_int.o ) |
10 | 10 | ||
11 | oprofile-y := $(DRIVER_OBJS) common.o | 11 | oprofile-y := $(DRIVER_OBJS) common.o backtrace.o |
12 | 12 | ||
13 | oprofile-$(CONFIG_CPU_MIPS32) += op_model_mipsxx.o | 13 | oprofile-$(CONFIG_CPU_MIPS32) += op_model_mipsxx.o |
14 | oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o | 14 | oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o |
15 | oprofile-$(CONFIG_CPU_R10000) += op_model_mipsxx.o | 15 | oprofile-$(CONFIG_CPU_R10000) += op_model_mipsxx.o |
16 | oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o | 16 | oprofile-$(CONFIG_CPU_SB1) += op_model_mipsxx.o |
17 | oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o | 17 | oprofile-$(CONFIG_CPU_RM9000) += op_model_rm9000.o |
18 | oprofile-$(CONFIG_CPU_LOONGSON2) += op_model_loongson2.o | 18 | oprofile-$(CONFIG_CPU_LOONGSON2) += op_model_loongson2.o |
19 | 19 |
arch/mips/oprofile/backtrace.c
File was created | 1 | #include <linux/oprofile.h> | |
2 | #include <linux/sched.h> | ||
3 | #include <linux/mm.h> | ||
4 | #include <linux/uaccess.h> | ||
5 | #include <asm/ptrace.h> | ||
6 | #include <asm/stacktrace.h> | ||
7 | #include <linux/stacktrace.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <asm/sections.h> | ||
10 | #include <asm/inst.h> | ||
11 | |||
12 | struct stackframe { | ||
13 | unsigned long sp; | ||
14 | unsigned long pc; | ||
15 | unsigned long ra; | ||
16 | }; | ||
17 | |||
18 | static inline int get_mem(unsigned long addr, unsigned long *result) | ||
19 | { | ||
20 | unsigned long *address = (unsigned long *) addr; | ||
21 | if (!access_ok(VERIFY_READ, addr, sizeof(unsigned long))) | ||
22 | return -1; | ||
23 | if (__copy_from_user_inatomic(result, address, sizeof(unsigned long))) | ||
24 | return -3; | ||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | /* | ||
29 | * These two instruction helpers were taken from process.c | ||
30 | */ | ||
31 | static inline int is_ra_save_ins(union mips_instruction *ip) | ||
32 | { | ||
33 | /* sw / sd $ra, offset($sp) */ | ||
34 | return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) | ||
35 | && ip->i_format.rs == 29 && ip->i_format.rt == 31; | ||
36 | } | ||
37 | |||
38 | static inline int is_sp_move_ins(union mips_instruction *ip) | ||
39 | { | ||
40 | /* addiu/daddiu sp,sp,-imm */ | ||
41 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) | ||
42 | return 0; | ||
43 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) | ||
44 | return 1; | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * Looks for specific instructions that mark the end of a function. | ||
50 | * This usually means we ran into the code area of the previous function. | ||
51 | */ | ||
52 | static inline int is_end_of_function_marker(union mips_instruction *ip) | ||
53 | { | ||
54 | /* jr ra */ | ||
55 | if (ip->r_format.func == jr_op && ip->r_format.rs == 31) | ||
56 | return 1; | ||
57 | /* lui gp */ | ||
58 | if (ip->i_format.opcode == lui_op && ip->i_format.rt == 28) | ||
59 | return 1; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * TODO for userspace stack unwinding: | ||
65 | * - handle cases where the stack is adjusted inside a function | ||
66 | * (generally doesn't happen) | ||
67 | * - find optimal value for max_instr_check | ||
68 | * - try to find a way to handle leaf functions | ||
69 | */ | ||
70 | |||
71 | static inline int unwind_user_frame(struct stackframe *old_frame, | ||
72 | const unsigned int max_instr_check) | ||
73 | { | ||
74 | struct stackframe new_frame = *old_frame; | ||
75 | off_t ra_offset = 0; | ||
76 | size_t stack_size = 0; | ||
77 | unsigned long addr; | ||
78 | |||
79 | if (old_frame->pc == 0 || old_frame->sp == 0 || old_frame->ra == 0) | ||
80 | return -9; | ||
81 | |||
82 | for (addr = new_frame.pc; (addr + max_instr_check > new_frame.pc) | ||
83 | && (!ra_offset || !stack_size); --addr) { | ||
84 | union mips_instruction ip; | ||
85 | |||
86 | if (get_mem(addr, (unsigned long *) &ip)) | ||
87 | return -11; | ||
88 | |||
89 | if (is_sp_move_ins(&ip)) { | ||
90 | int stack_adjustment = ip.i_format.simmediate; | ||
91 | if (stack_adjustment > 0) | ||
92 | /* This marks the end of the previous function, | ||
93 | which means we overran. */ | ||
94 | break; | ||
95 | stack_size = (unsigned) stack_adjustment; | ||
96 | } else if (is_ra_save_ins(&ip)) { | ||
97 | int ra_slot = ip.i_format.simmediate; | ||
98 | if (ra_slot < 0) | ||
99 | /* This shouldn't happen. */ | ||
100 | break; | ||
101 | ra_offset = ra_slot; | ||
102 | } else if (is_end_of_function_marker(&ip)) | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | if (!ra_offset || !stack_size) | ||
107 | return -1; | ||
108 | |||
109 | if (ra_offset) { | ||
110 | new_frame.ra = old_frame->sp + ra_offset; | ||
111 | if (get_mem(new_frame.ra, &(new_frame.ra))) | ||
112 | return -13; | ||
113 | } | ||
114 | |||
115 | if (stack_size) { | ||
116 | new_frame.sp = old_frame->sp + stack_size; | ||
117 | if (get_mem(new_frame.sp, &(new_frame.sp))) | ||
118 | return -14; | ||
119 | } | ||
120 | |||
121 | if (new_frame.sp > old_frame->sp) | ||
122 | return -2; | ||
123 | |||
124 | new_frame.pc = old_frame->ra; | ||
125 | *old_frame = new_frame; | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static inline void do_user_backtrace(unsigned long low_addr, | ||
131 | struct stackframe *frame, | ||
132 | unsigned int depth) | ||
133 | { | ||
134 | const unsigned int max_instr_check = 512; | ||
135 | const unsigned long high_addr = low_addr + THREAD_SIZE; | ||
136 | |||
137 | while (depth-- && !unwind_user_frame(frame, max_instr_check)) { | ||
138 | oprofile_add_trace(frame->ra); | ||
139 | if (frame->sp < low_addr || frame->sp > high_addr) | ||
140 | break; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | #ifndef CONFIG_KALLSYMS | ||
145 | static inline void do_kernel_backtrace(unsigned long low_addr, | ||
146 | struct stackframe *frame, | ||
147 | unsigned int depth) { } | ||
148 | #else | ||
149 | static inline void do_kernel_backtrace(unsigned long low_addr, | ||
150 | struct stackframe *frame, | ||
151 | unsigned int depth) | ||
152 | { | ||
153 | while (depth-- && frame->pc) { | ||
154 | frame->pc = unwind_stack_by_address(low_addr, | ||
155 | &(frame->sp), | ||
156 | frame->pc, | ||
157 | &(frame->ra)); | ||
158 | oprofile_add_trace(frame->ra); | ||
159 | } | ||
160 | } | ||
161 | #endif | ||
162 | |||
163 | void notrace op_mips_backtrace(struct pt_regs *const regs, unsigned int depth) | ||
164 | { | ||
165 | struct stackframe frame = { .sp = regs->regs[29], | ||
166 | .pc = regs->cp0_epc, | ||
167 | .ra = regs->regs[31] }; | ||
168 | const int userspace = user_mode(regs); | ||
169 | const unsigned long low_addr = ALIGN(frame.sp, THREAD_SIZE); | ||
170 | |||
171 | if (userspace) | ||
172 | do_user_backtrace(low_addr, &frame, depth); | ||
173 | else | ||
174 | do_kernel_backtrace(low_addr, &frame, depth); | ||
175 | } | ||
176 |
arch/mips/oprofile/common.c
1 | /* | 1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | * This file is subject to the terms and conditions of the GNU General Public |
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. | 4 | * for more details. |
5 | * | 5 | * |
6 | * Copyright (C) 2004, 2005 Ralf Baechle | 6 | * Copyright (C) 2004, 2005 Ralf Baechle |
7 | * Copyright (C) 2005 MIPS Technologies, Inc. | 7 | * Copyright (C) 2005 MIPS Technologies, Inc. |
8 | */ | 8 | */ |
9 | #include <linux/compiler.h> | 9 | #include <linux/compiler.h> |
10 | #include <linux/errno.h> | 10 | #include <linux/errno.h> |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/oprofile.h> | 12 | #include <linux/oprofile.h> |
13 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
14 | #include <asm/cpu-info.h> | 14 | #include <asm/cpu-info.h> |
15 | 15 | ||
16 | #include "op_impl.h" | 16 | #include "op_impl.h" |
17 | 17 | ||
18 | extern struct op_mips_model op_model_mipsxx_ops __weak; | 18 | extern struct op_mips_model op_model_mipsxx_ops __weak; |
19 | extern struct op_mips_model op_model_rm9000_ops __weak; | 19 | extern struct op_mips_model op_model_rm9000_ops __weak; |
20 | extern struct op_mips_model op_model_loongson2_ops __weak; | 20 | extern struct op_mips_model op_model_loongson2_ops __weak; |
21 | 21 | ||
22 | static struct op_mips_model *model; | 22 | static struct op_mips_model *model; |
23 | 23 | ||
24 | static struct op_counter_config ctr[20]; | 24 | static struct op_counter_config ctr[20]; |
25 | 25 | ||
26 | static int op_mips_setup(void) | 26 | static int op_mips_setup(void) |
27 | { | 27 | { |
28 | /* Pre-compute the values to stuff in the hardware registers. */ | 28 | /* Pre-compute the values to stuff in the hardware registers. */ |
29 | model->reg_setup(ctr); | 29 | model->reg_setup(ctr); |
30 | 30 | ||
31 | /* Configure the registers on all cpus. */ | 31 | /* Configure the registers on all cpus. */ |
32 | on_each_cpu(model->cpu_setup, NULL, 1); | 32 | on_each_cpu(model->cpu_setup, NULL, 1); |
33 | 33 | ||
34 | return 0; | 34 | return 0; |
35 | } | 35 | } |
36 | 36 | ||
37 | static int op_mips_create_files(struct super_block *sb, struct dentry *root) | 37 | static int op_mips_create_files(struct super_block *sb, struct dentry *root) |
38 | { | 38 | { |
39 | int i; | 39 | int i; |
40 | 40 | ||
41 | for (i = 0; i < model->num_counters; ++i) { | 41 | for (i = 0; i < model->num_counters; ++i) { |
42 | struct dentry *dir; | 42 | struct dentry *dir; |
43 | char buf[4]; | 43 | char buf[4]; |
44 | 44 | ||
45 | snprintf(buf, sizeof buf, "%d", i); | 45 | snprintf(buf, sizeof buf, "%d", i); |
46 | dir = oprofilefs_mkdir(sb, root, buf); | 46 | dir = oprofilefs_mkdir(sb, root, buf); |
47 | 47 | ||
48 | oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); | 48 | oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); |
49 | oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); | 49 | oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); |
50 | oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); | 50 | oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); |
51 | oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); | 51 | oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); |
52 | oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); | 52 | oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); |
53 | oprofilefs_create_ulong(sb, dir, "exl", &ctr[i].exl); | 53 | oprofilefs_create_ulong(sb, dir, "exl", &ctr[i].exl); |
54 | /* Dummy. */ | 54 | /* Dummy. */ |
55 | oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); | 55 | oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); |
56 | } | 56 | } |
57 | 57 | ||
58 | return 0; | 58 | return 0; |
59 | } | 59 | } |
60 | 60 | ||
61 | static int op_mips_start(void) | 61 | static int op_mips_start(void) |
62 | { | 62 | { |
63 | on_each_cpu(model->cpu_start, NULL, 1); | 63 | on_each_cpu(model->cpu_start, NULL, 1); |
64 | 64 | ||
65 | return 0; | 65 | return 0; |
66 | } | 66 | } |
67 | 67 | ||
68 | static void op_mips_stop(void) | 68 | static void op_mips_stop(void) |
69 | { | 69 | { |
70 | /* Disable performance monitoring for all counters. */ | 70 | /* Disable performance monitoring for all counters. */ |
71 | on_each_cpu(model->cpu_stop, NULL, 1); | 71 | on_each_cpu(model->cpu_stop, NULL, 1); |
72 | } | 72 | } |
73 | 73 | ||
74 | int __init oprofile_arch_init(struct oprofile_operations *ops) | 74 | int __init oprofile_arch_init(struct oprofile_operations *ops) |
75 | { | 75 | { |
76 | struct op_mips_model *lmodel = NULL; | 76 | struct op_mips_model *lmodel = NULL; |
77 | int res; | 77 | int res; |
78 | 78 | ||
79 | switch (current_cpu_type()) { | 79 | switch (current_cpu_type()) { |
80 | case CPU_5KC: | 80 | case CPU_5KC: |
81 | case CPU_20KC: | 81 | case CPU_20KC: |
82 | case CPU_24K: | 82 | case CPU_24K: |
83 | case CPU_25KF: | 83 | case CPU_25KF: |
84 | case CPU_34K: | 84 | case CPU_34K: |
85 | case CPU_1004K: | 85 | case CPU_1004K: |
86 | case CPU_74K: | 86 | case CPU_74K: |
87 | case CPU_SB1: | 87 | case CPU_SB1: |
88 | case CPU_SB1A: | 88 | case CPU_SB1A: |
89 | case CPU_R10000: | 89 | case CPU_R10000: |
90 | case CPU_R12000: | 90 | case CPU_R12000: |
91 | case CPU_R14000: | 91 | case CPU_R14000: |
92 | lmodel = &op_model_mipsxx_ops; | 92 | lmodel = &op_model_mipsxx_ops; |
93 | break; | 93 | break; |
94 | 94 | ||
95 | case CPU_RM9000: | 95 | case CPU_RM9000: |
96 | lmodel = &op_model_rm9000_ops; | 96 | lmodel = &op_model_rm9000_ops; |
97 | break; | 97 | break; |
98 | case CPU_LOONGSON2: | 98 | case CPU_LOONGSON2: |
99 | lmodel = &op_model_loongson2_ops; | 99 | lmodel = &op_model_loongson2_ops; |
100 | break; | 100 | break; |
101 | }; | 101 | }; |
102 | 102 | ||
103 | if (!lmodel) | 103 | if (!lmodel) |
104 | return -ENODEV; | 104 | return -ENODEV; |
105 | 105 | ||
106 | res = lmodel->init(); | 106 | res = lmodel->init(); |
107 | if (res) | 107 | if (res) |
108 | return res; | 108 | return res; |
109 | 109 | ||
110 | model = lmodel; | 110 | model = lmodel; |
111 | 111 | ||
112 | ops->create_files = op_mips_create_files; | 112 | ops->create_files = op_mips_create_files; |
113 | ops->setup = op_mips_setup; | 113 | ops->setup = op_mips_setup; |
114 | //ops->shutdown = op_mips_shutdown; | 114 | //ops->shutdown = op_mips_shutdown; |
115 | ops->start = op_mips_start; | 115 | ops->start = op_mips_start; |
116 | ops->stop = op_mips_stop; | 116 | ops->stop = op_mips_stop; |
117 | ops->cpu_type = lmodel->cpu_type; | 117 | ops->cpu_type = lmodel->cpu_type; |
118 | ops->backtrace = op_mips_backtrace; | ||
118 | 119 | ||
119 | printk(KERN_INFO "oprofile: using %s performance monitoring.\n", | 120 | printk(KERN_INFO "oprofile: using %s performance monitoring.\n", |
120 | lmodel->cpu_type); | 121 | lmodel->cpu_type); |
121 | 122 | ||
122 | return 0; | 123 | return 0; |
123 | } | 124 | } |
124 | 125 | ||
125 | void oprofile_arch_exit(void) | 126 | void oprofile_arch_exit(void) |
126 | { | 127 | { |
127 | if (model) | 128 | if (model) |
128 | model->exit(); | 129 | model->exit(); |
129 | } | 130 | } |
130 | 131 |
arch/mips/oprofile/op_impl.h
1 | /** | 1 | /** |
2 | * @file arch/alpha/oprofile/op_impl.h | 2 | * @file arch/alpha/oprofile/op_impl.h |
3 | * | 3 | * |
4 | * @remark Copyright 2002 OProfile authors | 4 | * @remark Copyright 2002 OProfile authors |
5 | * @remark Read the file COPYING | 5 | * @remark Read the file COPYING |
6 | * | 6 | * |
7 | * @author Richard Henderson <rth@twiddle.net> | 7 | * @author Richard Henderson <rth@twiddle.net> |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #ifndef OP_IMPL_H | 10 | #ifndef OP_IMPL_H |
11 | #define OP_IMPL_H 1 | 11 | #define OP_IMPL_H 1 |
12 | 12 | ||
13 | extern int (*perf_irq)(void); | 13 | extern int (*perf_irq)(void); |
14 | 14 | ||
15 | /* Per-counter configuration as set via oprofilefs. */ | 15 | /* Per-counter configuration as set via oprofilefs. */ |
16 | struct op_counter_config { | 16 | struct op_counter_config { |
17 | unsigned long enabled; | 17 | unsigned long enabled; |
18 | unsigned long event; | 18 | unsigned long event; |
19 | unsigned long count; | 19 | unsigned long count; |
20 | /* Dummies because I am too lazy to hack the userspace tools. */ | 20 | /* Dummies because I am too lazy to hack the userspace tools. */ |
21 | unsigned long kernel; | 21 | unsigned long kernel; |
22 | unsigned long user; | 22 | unsigned long user; |
23 | unsigned long exl; | 23 | unsigned long exl; |
24 | unsigned long unit_mask; | 24 | unsigned long unit_mask; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | /* Per-architecture configury and hooks. */ | 27 | /* Per-architecture configury and hooks. */ |
28 | struct op_mips_model { | 28 | struct op_mips_model { |
29 | void (*reg_setup) (struct op_counter_config *); | 29 | void (*reg_setup) (struct op_counter_config *); |
30 | void (*cpu_setup) (void *dummy); | 30 | void (*cpu_setup) (void *dummy); |
31 | int (*init)(void); | 31 | int (*init)(void); |
32 | void (*exit)(void); | 32 | void (*exit)(void); |
33 | void (*cpu_start)(void *args); | 33 | void (*cpu_start)(void *args); |
34 | void (*cpu_stop)(void *args); | 34 | void (*cpu_stop)(void *args); |
35 | char *cpu_type; | 35 | char *cpu_type; |
36 | unsigned char num_counters; | 36 | unsigned char num_counters; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | void op_mips_backtrace(struct pt_regs * const regs, unsigned int depth); | ||
40 | |||
39 | #endif | 41 | #endif |
40 | 42 |
arch/x86/oprofile/backtrace.c
1 | /** | 1 | /** |
2 | * @file backtrace.c | 2 | * @file backtrace.c |
3 | * | 3 | * |
4 | * @remark Copyright 2002 OProfile authors | 4 | * @remark Copyright 2002 OProfile authors |
5 | * @remark Read the file COPYING | 5 | * @remark Read the file COPYING |
6 | * | 6 | * |
7 | * @author John Levon | 7 | * @author John Levon |
8 | * @author David Smith | 8 | * @author David Smith |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/oprofile.h> | 11 | #include <linux/oprofile.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
14 | #include <linux/compat.h> | ||
15 | #include <linux/highmem.h> | ||
16 | |||
14 | #include <asm/ptrace.h> | 17 | #include <asm/ptrace.h> |
15 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
16 | #include <asm/stacktrace.h> | 19 | #include <asm/stacktrace.h> |
17 | #include <linux/compat.h> | ||
18 | 20 | ||
19 | static int backtrace_stack(void *data, char *name) | 21 | static int backtrace_stack(void *data, char *name) |
20 | { | 22 | { |
21 | /* Yes, we want all stacks */ | 23 | /* Yes, we want all stacks */ |
22 | return 0; | 24 | return 0; |
23 | } | 25 | } |
24 | 26 | ||
25 | static void backtrace_address(void *data, unsigned long addr, int reliable) | 27 | static void backtrace_address(void *data, unsigned long addr, int reliable) |
26 | { | 28 | { |
27 | unsigned int *depth = data; | 29 | unsigned int *depth = data; |
28 | 30 | ||
29 | if ((*depth)--) | 31 | if ((*depth)--) |
30 | oprofile_add_trace(addr); | 32 | oprofile_add_trace(addr); |
31 | } | 33 | } |
32 | 34 | ||
33 | static struct stacktrace_ops backtrace_ops = { | 35 | static struct stacktrace_ops backtrace_ops = { |
34 | .stack = backtrace_stack, | 36 | .stack = backtrace_stack, |
35 | .address = backtrace_address, | 37 | .address = backtrace_address, |
36 | .walk_stack = print_context_stack, | 38 | .walk_stack = print_context_stack, |
37 | }; | 39 | }; |
38 | 40 | ||
41 | /* from arch/x86/kernel/cpu/perf_event.c: */ | ||
42 | |||
43 | /* | ||
44 | * best effort, GUP based copy_from_user() that assumes IRQ or NMI context | ||
45 | */ | ||
46 | static unsigned long | ||
47 | copy_from_user_nmi(void *to, const void __user *from, unsigned long n) | ||
48 | { | ||
49 | unsigned long offset, addr = (unsigned long)from; | ||
50 | unsigned long size, len = 0; | ||
51 | struct page *page; | ||
52 | void *map; | ||
53 | int ret; | ||
54 | |||
55 | do { | ||
56 | ret = __get_user_pages_fast(addr, 1, 0, &page); | ||
57 | if (!ret) | ||
58 | break; | ||
59 | |||
60 | offset = addr & (PAGE_SIZE - 1); | ||
61 | size = min(PAGE_SIZE - offset, n - len); | ||
62 | |||
63 | map = kmap_atomic(page); | ||
64 | memcpy(to, map+offset, size); | ||
65 | kunmap_atomic(map); | ||
66 | put_page(page); | ||
67 | |||
68 | len += size; | ||
69 | to += size; | ||
70 | addr += size; | ||
71 | |||
72 | } while (len < n); | ||
73 | |||
74 | return len; | ||
75 | } | ||
76 | |||
39 | #ifdef CONFIG_COMPAT | 77 | #ifdef CONFIG_COMPAT |
40 | static struct stack_frame_ia32 * | 78 | static struct stack_frame_ia32 * |
41 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | 79 | dump_user_backtrace_32(struct stack_frame_ia32 *head) |
42 | { | 80 | { |
81 | /* Also check accessibility of one struct frame_head beyond: */ | ||
43 | struct stack_frame_ia32 bufhead[2]; | 82 | struct stack_frame_ia32 bufhead[2]; |
44 | struct stack_frame_ia32 *fp; | 83 | struct stack_frame_ia32 *fp; |
84 | unsigned long bytes; | ||
45 | 85 | ||
46 | /* Also check accessibility of one struct frame_head beyond */ | 86 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
47 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 87 | if (bytes != sizeof(bufhead)) |
48 | return NULL; | 88 | return NULL; |
49 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
50 | return NULL; | ||
51 | 89 | ||
52 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | 90 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); |
53 | 91 | ||
54 | oprofile_add_trace(bufhead[0].return_address); | 92 | oprofile_add_trace(bufhead[0].return_address); |
55 | 93 | ||
56 | /* frame pointers should strictly progress back up the stack | 94 | /* frame pointers should strictly progress back up the stack |
57 | * (towards higher addresses) */ | 95 | * (towards higher addresses) */ |
58 | if (head >= fp) | 96 | if (head >= fp) |
59 | return NULL; | 97 | return NULL; |
60 | 98 | ||
61 | return fp; | 99 | return fp; |
62 | } | 100 | } |
63 | 101 | ||
64 | static inline int | 102 | static inline int |
65 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | 103 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) |
66 | { | 104 | { |
67 | struct stack_frame_ia32 *head; | 105 | struct stack_frame_ia32 *head; |
68 | 106 | ||
69 | /* User process is 32-bit */ | 107 | /* User process is 32-bit */ |
70 | if (!current || !test_thread_flag(TIF_IA32)) | 108 | if (!current || !test_thread_flag(TIF_IA32)) |
71 | return 0; | 109 | return 0; |
72 | 110 | ||
73 | head = (struct stack_frame_ia32 *) regs->bp; | 111 | head = (struct stack_frame_ia32 *) regs->bp; |
74 | while (depth-- && head) | 112 | while (depth-- && head) |
75 | head = dump_user_backtrace_32(head); | 113 | head = dump_user_backtrace_32(head); |
76 | 114 | ||
77 | return 1; | 115 | return 1; |
78 | } | 116 | } |
79 | 117 | ||
80 | #else | 118 | #else |
81 | static inline int | 119 | static inline int |
82 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | 120 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) |
83 | { | 121 | { |
84 | return 0; | 122 | return 0; |
85 | } | 123 | } |
86 | #endif /* CONFIG_COMPAT */ | 124 | #endif /* CONFIG_COMPAT */ |
87 | 125 | ||
88 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | 126 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) |
89 | { | 127 | { |
128 | /* Also check accessibility of one struct frame_head beyond: */ | ||
90 | struct stack_frame bufhead[2]; | 129 | struct stack_frame bufhead[2]; |
130 | unsigned long bytes; | ||
91 | 131 | ||
92 | /* Also check accessibility of one struct stack_frame beyond */ | 132 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
93 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 133 | if (bytes != sizeof(bufhead)) |
94 | return NULL; | ||
95 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
96 | return NULL; | 134 | return NULL; |
97 | 135 | ||
98 | oprofile_add_trace(bufhead[0].return_address); | 136 | oprofile_add_trace(bufhead[0].return_address); |
99 | 137 | ||
100 | /* frame pointers should strictly progress back up the stack | 138 | /* frame pointers should strictly progress back up the stack |
101 | * (towards higher addresses) */ | 139 | * (towards higher addresses) */ |
102 | if (head >= bufhead[0].next_frame) | 140 | if (head >= bufhead[0].next_frame) |
103 | return NULL; | 141 | return NULL; |
104 | 142 | ||
105 | return bufhead[0].next_frame; | 143 | return bufhead[0].next_frame; |
106 | } | 144 | } |
107 | 145 | ||
108 | void | 146 | void |
109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | 147 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) |
110 | { | 148 | { |
111 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); | 149 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); |
112 | 150 | ||
113 | if (!user_mode_vm(regs)) { | 151 | if (!user_mode_vm(regs)) { |
114 | unsigned long stack = kernel_stack_pointer(regs); | 152 | unsigned long stack = kernel_stack_pointer(regs); |
115 | if (depth) | 153 | if (depth) |
116 | dump_trace(NULL, regs, (unsigned long *)stack, 0, | 154 | dump_trace(NULL, regs, (unsigned long *)stack, 0, |
117 | &backtrace_ops, &depth); | 155 | &backtrace_ops, &depth); |
118 | return; | 156 | return; |
119 | } | 157 | } |
120 | 158 | ||
121 | if (x86_backtrace_32(regs, depth)) | 159 | if (x86_backtrace_32(regs, depth)) |
122 | return; | 160 | return; |
arch/x86/oprofile/nmi_int.c
1 | /** | 1 | /** |
2 | * @file nmi_int.c | 2 | * @file nmi_int.c |
3 | * | 3 | * |
4 | * @remark Copyright 2002-2009 OProfile authors | 4 | * @remark Copyright 2002-2009 OProfile authors |
5 | * @remark Read the file COPYING | 5 | * @remark Read the file COPYING |
6 | * | 6 | * |
7 | * @author John Levon <levon@movementarian.org> | 7 | * @author John Levon <levon@movementarian.org> |
8 | * @author Robert Richter <robert.richter@amd.com> | 8 | * @author Robert Richter <robert.richter@amd.com> |
9 | * @author Barry Kasindorf <barry.kasindorf@amd.com> | 9 | * @author Barry Kasindorf <barry.kasindorf@amd.com> |
10 | * @author Jason Yeh <jason.yeh@amd.com> | 10 | * @author Jason Yeh <jason.yeh@amd.com> |
11 | * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> | 11 | * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/notifier.h> | 15 | #include <linux/notifier.h> |
16 | #include <linux/smp.h> | 16 | #include <linux/smp.h> |
17 | #include <linux/oprofile.h> | 17 | #include <linux/oprofile.h> |
18 | #include <linux/syscore_ops.h> | 18 | #include <linux/syscore_ops.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/moduleparam.h> | 20 | #include <linux/moduleparam.h> |
21 | #include <linux/kdebug.h> | 21 | #include <linux/kdebug.h> |
22 | #include <linux/cpu.h> | 22 | #include <linux/cpu.h> |
23 | #include <asm/nmi.h> | 23 | #include <asm/nmi.h> |
24 | #include <asm/msr.h> | 24 | #include <asm/msr.h> |
25 | #include <asm/apic.h> | 25 | #include <asm/apic.h> |
26 | 26 | ||
27 | #include "op_counter.h" | 27 | #include "op_counter.h" |
28 | #include "op_x86_model.h" | 28 | #include "op_x86_model.h" |
29 | 29 | ||
30 | static struct op_x86_model_spec *model; | 30 | static struct op_x86_model_spec *model; |
31 | static DEFINE_PER_CPU(struct op_msrs, cpu_msrs); | 31 | static DEFINE_PER_CPU(struct op_msrs, cpu_msrs); |
32 | static DEFINE_PER_CPU(unsigned long, saved_lvtpc); | 32 | static DEFINE_PER_CPU(unsigned long, saved_lvtpc); |
33 | 33 | ||
34 | /* must be protected with get_online_cpus()/put_online_cpus(): */ | 34 | /* must be protected with get_online_cpus()/put_online_cpus(): */ |
35 | static int nmi_enabled; | 35 | static int nmi_enabled; |
36 | static int ctr_running; | 36 | static int ctr_running; |
37 | 37 | ||
38 | struct op_counter_config counter_config[OP_MAX_COUNTER]; | 38 | struct op_counter_config counter_config[OP_MAX_COUNTER]; |
39 | 39 | ||
40 | /* common functions */ | 40 | /* common functions */ |
41 | 41 | ||
42 | u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, | 42 | u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, |
43 | struct op_counter_config *counter_config) | 43 | struct op_counter_config *counter_config) |
44 | { | 44 | { |
45 | u64 val = 0; | 45 | u64 val = 0; |
46 | u16 event = (u16)counter_config->event; | 46 | u16 event = (u16)counter_config->event; |
47 | 47 | ||
48 | val |= ARCH_PERFMON_EVENTSEL_INT; | 48 | val |= ARCH_PERFMON_EVENTSEL_INT; |
49 | val |= counter_config->user ? ARCH_PERFMON_EVENTSEL_USR : 0; | 49 | val |= counter_config->user ? ARCH_PERFMON_EVENTSEL_USR : 0; |
50 | val |= counter_config->kernel ? ARCH_PERFMON_EVENTSEL_OS : 0; | 50 | val |= counter_config->kernel ? ARCH_PERFMON_EVENTSEL_OS : 0; |
51 | val |= (counter_config->unit_mask & 0xFF) << 8; | 51 | val |= (counter_config->unit_mask & 0xFF) << 8; |
52 | counter_config->extra &= (ARCH_PERFMON_EVENTSEL_INV | | 52 | counter_config->extra &= (ARCH_PERFMON_EVENTSEL_INV | |
53 | ARCH_PERFMON_EVENTSEL_EDGE | | 53 | ARCH_PERFMON_EVENTSEL_EDGE | |
54 | ARCH_PERFMON_EVENTSEL_CMASK); | 54 | ARCH_PERFMON_EVENTSEL_CMASK); |
55 | val |= counter_config->extra; | 55 | val |= counter_config->extra; |
56 | event &= model->event_mask ? model->event_mask : 0xFF; | 56 | event &= model->event_mask ? model->event_mask : 0xFF; |
57 | val |= event & 0xFF; | 57 | val |= event & 0xFF; |
58 | val |= (event & 0x0F00) << 24; | 58 | val |= (event & 0x0F00) << 24; |
59 | 59 | ||
60 | return val; | 60 | return val; |
61 | } | 61 | } |
62 | 62 | ||
63 | 63 | ||
64 | static int profile_exceptions_notify(struct notifier_block *self, | 64 | static int profile_exceptions_notify(struct notifier_block *self, |
65 | unsigned long val, void *data) | 65 | unsigned long val, void *data) |
66 | { | 66 | { |
67 | struct die_args *args = (struct die_args *)data; | 67 | struct die_args *args = (struct die_args *)data; |
68 | int ret = NOTIFY_DONE; | 68 | int ret = NOTIFY_DONE; |
69 | 69 | ||
70 | switch (val) { | 70 | switch (val) { |
71 | case DIE_NMI: | 71 | case DIE_NMI: |
72 | if (ctr_running) | 72 | if (ctr_running) |
73 | model->check_ctrs(args->regs, &__get_cpu_var(cpu_msrs)); | 73 | model->check_ctrs(args->regs, &__get_cpu_var(cpu_msrs)); |
74 | else if (!nmi_enabled) | 74 | else if (!nmi_enabled) |
75 | break; | 75 | break; |
76 | else | 76 | else |
77 | model->stop(&__get_cpu_var(cpu_msrs)); | 77 | model->stop(&__get_cpu_var(cpu_msrs)); |
78 | ret = NOTIFY_STOP; | 78 | ret = NOTIFY_STOP; |
79 | break; | 79 | break; |
80 | default: | 80 | default: |
81 | break; | 81 | break; |
82 | } | 82 | } |
83 | return ret; | 83 | return ret; |
84 | } | 84 | } |
85 | 85 | ||
86 | static void nmi_cpu_save_registers(struct op_msrs *msrs) | 86 | static void nmi_cpu_save_registers(struct op_msrs *msrs) |
87 | { | 87 | { |
88 | struct op_msr *counters = msrs->counters; | 88 | struct op_msr *counters = msrs->counters; |
89 | struct op_msr *controls = msrs->controls; | 89 | struct op_msr *controls = msrs->controls; |
90 | unsigned int i; | 90 | unsigned int i; |
91 | 91 | ||
92 | for (i = 0; i < model->num_counters; ++i) { | 92 | for (i = 0; i < model->num_counters; ++i) { |
93 | if (counters[i].addr) | 93 | if (counters[i].addr) |
94 | rdmsrl(counters[i].addr, counters[i].saved); | 94 | rdmsrl(counters[i].addr, counters[i].saved); |
95 | } | 95 | } |
96 | 96 | ||
97 | for (i = 0; i < model->num_controls; ++i) { | 97 | for (i = 0; i < model->num_controls; ++i) { |
98 | if (controls[i].addr) | 98 | if (controls[i].addr) |
99 | rdmsrl(controls[i].addr, controls[i].saved); | 99 | rdmsrl(controls[i].addr, controls[i].saved); |
100 | } | 100 | } |
101 | } | 101 | } |
102 | 102 | ||
103 | static void nmi_cpu_start(void *dummy) | 103 | static void nmi_cpu_start(void *dummy) |
104 | { | 104 | { |
105 | struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); | 105 | struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); |
106 | if (!msrs->controls) | 106 | if (!msrs->controls) |
107 | WARN_ON_ONCE(1); | 107 | WARN_ON_ONCE(1); |
108 | else | 108 | else |
109 | model->start(msrs); | 109 | model->start(msrs); |
110 | } | 110 | } |
111 | 111 | ||
112 | static int nmi_start(void) | 112 | static int nmi_start(void) |
113 | { | 113 | { |
114 | get_online_cpus(); | 114 | get_online_cpus(); |
115 | on_each_cpu(nmi_cpu_start, NULL, 1); | ||
116 | ctr_running = 1; | 115 | ctr_running = 1; |
116 | /* make ctr_running visible to the nmi handler: */ | ||
117 | smp_mb(); | ||
118 | on_each_cpu(nmi_cpu_start, NULL, 1); | ||
117 | put_online_cpus(); | 119 | put_online_cpus(); |
118 | return 0; | 120 | return 0; |
119 | } | 121 | } |
120 | 122 | ||
121 | static void nmi_cpu_stop(void *dummy) | 123 | static void nmi_cpu_stop(void *dummy) |
122 | { | 124 | { |
123 | struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); | 125 | struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); |
124 | if (!msrs->controls) | 126 | if (!msrs->controls) |
125 | WARN_ON_ONCE(1); | 127 | WARN_ON_ONCE(1); |
126 | else | 128 | else |
127 | model->stop(msrs); | 129 | model->stop(msrs); |
128 | } | 130 | } |
129 | 131 | ||
130 | static void nmi_stop(void) | 132 | static void nmi_stop(void) |
131 | { | 133 | { |
132 | get_online_cpus(); | 134 | get_online_cpus(); |
133 | on_each_cpu(nmi_cpu_stop, NULL, 1); | 135 | on_each_cpu(nmi_cpu_stop, NULL, 1); |
134 | ctr_running = 0; | 136 | ctr_running = 0; |
135 | put_online_cpus(); | 137 | put_online_cpus(); |
136 | } | 138 | } |
137 | 139 | ||
138 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 140 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
139 | 141 | ||
140 | static DEFINE_PER_CPU(int, switch_index); | 142 | static DEFINE_PER_CPU(int, switch_index); |
141 | 143 | ||
142 | static inline int has_mux(void) | 144 | static inline int has_mux(void) |
143 | { | 145 | { |
144 | return !!model->switch_ctrl; | 146 | return !!model->switch_ctrl; |
145 | } | 147 | } |
146 | 148 | ||
147 | inline int op_x86_phys_to_virt(int phys) | 149 | inline int op_x86_phys_to_virt(int phys) |
148 | { | 150 | { |
149 | return __this_cpu_read(switch_index) + phys; | 151 | return __this_cpu_read(switch_index) + phys; |
150 | } | 152 | } |
151 | 153 | ||
152 | inline int op_x86_virt_to_phys(int virt) | 154 | inline int op_x86_virt_to_phys(int virt) |
153 | { | 155 | { |
154 | return virt % model->num_counters; | 156 | return virt % model->num_counters; |
155 | } | 157 | } |
156 | 158 | ||
157 | static void nmi_shutdown_mux(void) | 159 | static void nmi_shutdown_mux(void) |
158 | { | 160 | { |
159 | int i; | 161 | int i; |
160 | 162 | ||
161 | if (!has_mux()) | 163 | if (!has_mux()) |
162 | return; | 164 | return; |
163 | 165 | ||
164 | for_each_possible_cpu(i) { | 166 | for_each_possible_cpu(i) { |
165 | kfree(per_cpu(cpu_msrs, i).multiplex); | 167 | kfree(per_cpu(cpu_msrs, i).multiplex); |
166 | per_cpu(cpu_msrs, i).multiplex = NULL; | 168 | per_cpu(cpu_msrs, i).multiplex = NULL; |
167 | per_cpu(switch_index, i) = 0; | 169 | per_cpu(switch_index, i) = 0; |
168 | } | 170 | } |
169 | } | 171 | } |
170 | 172 | ||
171 | static int nmi_setup_mux(void) | 173 | static int nmi_setup_mux(void) |
172 | { | 174 | { |
173 | size_t multiplex_size = | 175 | size_t multiplex_size = |
174 | sizeof(struct op_msr) * model->num_virt_counters; | 176 | sizeof(struct op_msr) * model->num_virt_counters; |
175 | int i; | 177 | int i; |
176 | 178 | ||
177 | if (!has_mux()) | 179 | if (!has_mux()) |
178 | return 1; | 180 | return 1; |
179 | 181 | ||
180 | for_each_possible_cpu(i) { | 182 | for_each_possible_cpu(i) { |
181 | per_cpu(cpu_msrs, i).multiplex = | 183 | per_cpu(cpu_msrs, i).multiplex = |
182 | kzalloc(multiplex_size, GFP_KERNEL); | 184 | kzalloc(multiplex_size, GFP_KERNEL); |
183 | if (!per_cpu(cpu_msrs, i).multiplex) | 185 | if (!per_cpu(cpu_msrs, i).multiplex) |
184 | return 0; | 186 | return 0; |
185 | } | 187 | } |
186 | 188 | ||
187 | return 1; | 189 | return 1; |
188 | } | 190 | } |
189 | 191 | ||
190 | static void nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) | 192 | static void nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) |
191 | { | 193 | { |
192 | int i; | 194 | int i; |
193 | struct op_msr *multiplex = msrs->multiplex; | 195 | struct op_msr *multiplex = msrs->multiplex; |
194 | 196 | ||
195 | if (!has_mux()) | 197 | if (!has_mux()) |
196 | return; | 198 | return; |
197 | 199 | ||
198 | for (i = 0; i < model->num_virt_counters; ++i) { | 200 | for (i = 0; i < model->num_virt_counters; ++i) { |
199 | if (counter_config[i].enabled) { | 201 | if (counter_config[i].enabled) { |
200 | multiplex[i].saved = -(u64)counter_config[i].count; | 202 | multiplex[i].saved = -(u64)counter_config[i].count; |
201 | } else { | 203 | } else { |
202 | multiplex[i].saved = 0; | 204 | multiplex[i].saved = 0; |
203 | } | 205 | } |
204 | } | 206 | } |
205 | 207 | ||
206 | per_cpu(switch_index, cpu) = 0; | 208 | per_cpu(switch_index, cpu) = 0; |
207 | } | 209 | } |
208 | 210 | ||
209 | static void nmi_cpu_save_mpx_registers(struct op_msrs *msrs) | 211 | static void nmi_cpu_save_mpx_registers(struct op_msrs *msrs) |
210 | { | 212 | { |
211 | struct op_msr *counters = msrs->counters; | 213 | struct op_msr *counters = msrs->counters; |
212 | struct op_msr *multiplex = msrs->multiplex; | 214 | struct op_msr *multiplex = msrs->multiplex; |
213 | int i; | 215 | int i; |
214 | 216 | ||
215 | for (i = 0; i < model->num_counters; ++i) { | 217 | for (i = 0; i < model->num_counters; ++i) { |
216 | int virt = op_x86_phys_to_virt(i); | 218 | int virt = op_x86_phys_to_virt(i); |
217 | if (counters[i].addr) | 219 | if (counters[i].addr) |
218 | rdmsrl(counters[i].addr, multiplex[virt].saved); | 220 | rdmsrl(counters[i].addr, multiplex[virt].saved); |
219 | } | 221 | } |
220 | } | 222 | } |
221 | 223 | ||
222 | static void nmi_cpu_restore_mpx_registers(struct op_msrs *msrs) | 224 | static void nmi_cpu_restore_mpx_registers(struct op_msrs *msrs) |
223 | { | 225 | { |
224 | struct op_msr *counters = msrs->counters; | 226 | struct op_msr *counters = msrs->counters; |
225 | struct op_msr *multiplex = msrs->multiplex; | 227 | struct op_msr *multiplex = msrs->multiplex; |
226 | int i; | 228 | int i; |
227 | 229 | ||
228 | for (i = 0; i < model->num_counters; ++i) { | 230 | for (i = 0; i < model->num_counters; ++i) { |
229 | int virt = op_x86_phys_to_virt(i); | 231 | int virt = op_x86_phys_to_virt(i); |
230 | if (counters[i].addr) | 232 | if (counters[i].addr) |
231 | wrmsrl(counters[i].addr, multiplex[virt].saved); | 233 | wrmsrl(counters[i].addr, multiplex[virt].saved); |
232 | } | 234 | } |
233 | } | 235 | } |
234 | 236 | ||
235 | static void nmi_cpu_switch(void *dummy) | 237 | static void nmi_cpu_switch(void *dummy) |
236 | { | 238 | { |
237 | int cpu = smp_processor_id(); | 239 | int cpu = smp_processor_id(); |
238 | int si = per_cpu(switch_index, cpu); | 240 | int si = per_cpu(switch_index, cpu); |
239 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); | 241 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); |
240 | 242 | ||
241 | nmi_cpu_stop(NULL); | 243 | nmi_cpu_stop(NULL); |
242 | nmi_cpu_save_mpx_registers(msrs); | 244 | nmi_cpu_save_mpx_registers(msrs); |
243 | 245 | ||
244 | /* move to next set */ | 246 | /* move to next set */ |
245 | si += model->num_counters; | 247 | si += model->num_counters; |
246 | if ((si >= model->num_virt_counters) || (counter_config[si].count == 0)) | 248 | if ((si >= model->num_virt_counters) || (counter_config[si].count == 0)) |
247 | per_cpu(switch_index, cpu) = 0; | 249 | per_cpu(switch_index, cpu) = 0; |
248 | else | 250 | else |
249 | per_cpu(switch_index, cpu) = si; | 251 | per_cpu(switch_index, cpu) = si; |
250 | 252 | ||
251 | model->switch_ctrl(model, msrs); | 253 | model->switch_ctrl(model, msrs); |
252 | nmi_cpu_restore_mpx_registers(msrs); | 254 | nmi_cpu_restore_mpx_registers(msrs); |
253 | 255 | ||
254 | nmi_cpu_start(NULL); | 256 | nmi_cpu_start(NULL); |
255 | } | 257 | } |
256 | 258 | ||
257 | 259 | ||
258 | /* | 260 | /* |
259 | * Quick check to see if multiplexing is necessary. | 261 | * Quick check to see if multiplexing is necessary. |
260 | * The check should be sufficient since counters are used | 262 | * The check should be sufficient since counters are used |
261 | * in ordre. | 263 | * in ordre. |
262 | */ | 264 | */ |
263 | static int nmi_multiplex_on(void) | 265 | static int nmi_multiplex_on(void) |
264 | { | 266 | { |
265 | return counter_config[model->num_counters].count ? 0 : -EINVAL; | 267 | return counter_config[model->num_counters].count ? 0 : -EINVAL; |
266 | } | 268 | } |
267 | 269 | ||
268 | static int nmi_switch_event(void) | 270 | static int nmi_switch_event(void) |
269 | { | 271 | { |
270 | if (!has_mux()) | 272 | if (!has_mux()) |
271 | return -ENOSYS; /* not implemented */ | 273 | return -ENOSYS; /* not implemented */ |
272 | if (nmi_multiplex_on() < 0) | 274 | if (nmi_multiplex_on() < 0) |
273 | return -EINVAL; /* not necessary */ | 275 | return -EINVAL; /* not necessary */ |
274 | 276 | ||
275 | get_online_cpus(); | 277 | get_online_cpus(); |
276 | if (ctr_running) | 278 | if (ctr_running) |
277 | on_each_cpu(nmi_cpu_switch, NULL, 1); | 279 | on_each_cpu(nmi_cpu_switch, NULL, 1); |
278 | put_online_cpus(); | 280 | put_online_cpus(); |
279 | 281 | ||
280 | return 0; | 282 | return 0; |
281 | } | 283 | } |
282 | 284 | ||
283 | static inline void mux_init(struct oprofile_operations *ops) | 285 | static inline void mux_init(struct oprofile_operations *ops) |
284 | { | 286 | { |
285 | if (has_mux()) | 287 | if (has_mux()) |
286 | ops->switch_events = nmi_switch_event; | 288 | ops->switch_events = nmi_switch_event; |
287 | } | 289 | } |
288 | 290 | ||
289 | static void mux_clone(int cpu) | 291 | static void mux_clone(int cpu) |
290 | { | 292 | { |
291 | if (!has_mux()) | 293 | if (!has_mux()) |
292 | return; | 294 | return; |
293 | 295 | ||
294 | memcpy(per_cpu(cpu_msrs, cpu).multiplex, | 296 | memcpy(per_cpu(cpu_msrs, cpu).multiplex, |
295 | per_cpu(cpu_msrs, 0).multiplex, | 297 | per_cpu(cpu_msrs, 0).multiplex, |
296 | sizeof(struct op_msr) * model->num_virt_counters); | 298 | sizeof(struct op_msr) * model->num_virt_counters); |
297 | } | 299 | } |
298 | 300 | ||
299 | #else | 301 | #else |
300 | 302 | ||
301 | inline int op_x86_phys_to_virt(int phys) { return phys; } | 303 | inline int op_x86_phys_to_virt(int phys) { return phys; } |
302 | inline int op_x86_virt_to_phys(int virt) { return virt; } | 304 | inline int op_x86_virt_to_phys(int virt) { return virt; } |
303 | static inline void nmi_shutdown_mux(void) { } | 305 | static inline void nmi_shutdown_mux(void) { } |
304 | static inline int nmi_setup_mux(void) { return 1; } | 306 | static inline int nmi_setup_mux(void) { return 1; } |
305 | static inline void | 307 | static inline void |
306 | nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) { } | 308 | nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) { } |
307 | static inline void mux_init(struct oprofile_operations *ops) { } | 309 | static inline void mux_init(struct oprofile_operations *ops) { } |
308 | static void mux_clone(int cpu) { } | 310 | static void mux_clone(int cpu) { } |
309 | 311 | ||
310 | #endif | 312 | #endif |
311 | 313 | ||
312 | static void free_msrs(void) | 314 | static void free_msrs(void) |
313 | { | 315 | { |
314 | int i; | 316 | int i; |
315 | for_each_possible_cpu(i) { | 317 | for_each_possible_cpu(i) { |
316 | kfree(per_cpu(cpu_msrs, i).counters); | 318 | kfree(per_cpu(cpu_msrs, i).counters); |
317 | per_cpu(cpu_msrs, i).counters = NULL; | 319 | per_cpu(cpu_msrs, i).counters = NULL; |
318 | kfree(per_cpu(cpu_msrs, i).controls); | 320 | kfree(per_cpu(cpu_msrs, i).controls); |
319 | per_cpu(cpu_msrs, i).controls = NULL; | 321 | per_cpu(cpu_msrs, i).controls = NULL; |
320 | } | 322 | } |
321 | nmi_shutdown_mux(); | 323 | nmi_shutdown_mux(); |
322 | } | 324 | } |
323 | 325 | ||
324 | static int allocate_msrs(void) | 326 | static int allocate_msrs(void) |
325 | { | 327 | { |
326 | size_t controls_size = sizeof(struct op_msr) * model->num_controls; | 328 | size_t controls_size = sizeof(struct op_msr) * model->num_controls; |
327 | size_t counters_size = sizeof(struct op_msr) * model->num_counters; | 329 | size_t counters_size = sizeof(struct op_msr) * model->num_counters; |
328 | 330 | ||
329 | int i; | 331 | int i; |
330 | for_each_possible_cpu(i) { | 332 | for_each_possible_cpu(i) { |
331 | per_cpu(cpu_msrs, i).counters = kzalloc(counters_size, | 333 | per_cpu(cpu_msrs, i).counters = kzalloc(counters_size, |
332 | GFP_KERNEL); | 334 | GFP_KERNEL); |
333 | if (!per_cpu(cpu_msrs, i).counters) | 335 | if (!per_cpu(cpu_msrs, i).counters) |
334 | goto fail; | 336 | goto fail; |
335 | per_cpu(cpu_msrs, i).controls = kzalloc(controls_size, | 337 | per_cpu(cpu_msrs, i).controls = kzalloc(controls_size, |
336 | GFP_KERNEL); | 338 | GFP_KERNEL); |
337 | if (!per_cpu(cpu_msrs, i).controls) | 339 | if (!per_cpu(cpu_msrs, i).controls) |
338 | goto fail; | 340 | goto fail; |
339 | } | 341 | } |
340 | 342 | ||
341 | if (!nmi_setup_mux()) | 343 | if (!nmi_setup_mux()) |
342 | goto fail; | 344 | goto fail; |
343 | 345 | ||
344 | return 1; | 346 | return 1; |
345 | 347 | ||
346 | fail: | 348 | fail: |
347 | free_msrs(); | 349 | free_msrs(); |
348 | return 0; | 350 | return 0; |
349 | } | 351 | } |
350 | 352 | ||
351 | static void nmi_cpu_setup(void *dummy) | 353 | static void nmi_cpu_setup(void *dummy) |
352 | { | 354 | { |
353 | int cpu = smp_processor_id(); | 355 | int cpu = smp_processor_id(); |
354 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); | 356 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); |
355 | nmi_cpu_save_registers(msrs); | 357 | nmi_cpu_save_registers(msrs); |
356 | spin_lock(&oprofilefs_lock); | 358 | spin_lock(&oprofilefs_lock); |
357 | model->setup_ctrs(model, msrs); | 359 | model->setup_ctrs(model, msrs); |
358 | nmi_cpu_setup_mux(cpu, msrs); | 360 | nmi_cpu_setup_mux(cpu, msrs); |
359 | spin_unlock(&oprofilefs_lock); | 361 | spin_unlock(&oprofilefs_lock); |
360 | per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC); | 362 | per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC); |
361 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 363 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
362 | } | 364 | } |
363 | 365 | ||
364 | static struct notifier_block profile_exceptions_nb = { | 366 | static struct notifier_block profile_exceptions_nb = { |
365 | .notifier_call = profile_exceptions_notify, | 367 | .notifier_call = profile_exceptions_notify, |
366 | .next = NULL, | 368 | .next = NULL, |
367 | .priority = NMI_LOCAL_LOW_PRIOR, | 369 | .priority = NMI_LOCAL_LOW_PRIOR, |
368 | }; | 370 | }; |
369 | 371 | ||
370 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) | 372 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) |
371 | { | 373 | { |
372 | struct op_msr *counters = msrs->counters; | 374 | struct op_msr *counters = msrs->counters; |
373 | struct op_msr *controls = msrs->controls; | 375 | struct op_msr *controls = msrs->controls; |
374 | unsigned int i; | 376 | unsigned int i; |
375 | 377 | ||
376 | for (i = 0; i < model->num_controls; ++i) { | 378 | for (i = 0; i < model->num_controls; ++i) { |
377 | if (controls[i].addr) | 379 | if (controls[i].addr) |
378 | wrmsrl(controls[i].addr, controls[i].saved); | 380 | wrmsrl(controls[i].addr, controls[i].saved); |
379 | } | 381 | } |
380 | 382 | ||
381 | for (i = 0; i < model->num_counters; ++i) { | 383 | for (i = 0; i < model->num_counters; ++i) { |
382 | if (counters[i].addr) | 384 | if (counters[i].addr) |
383 | wrmsrl(counters[i].addr, counters[i].saved); | 385 | wrmsrl(counters[i].addr, counters[i].saved); |
384 | } | 386 | } |
385 | } | 387 | } |
386 | 388 | ||
387 | static void nmi_cpu_shutdown(void *dummy) | 389 | static void nmi_cpu_shutdown(void *dummy) |
388 | { | 390 | { |
389 | unsigned int v; | 391 | unsigned int v; |
390 | int cpu = smp_processor_id(); | 392 | int cpu = smp_processor_id(); |
391 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); | 393 | struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); |
392 | 394 | ||
393 | /* restoring APIC_LVTPC can trigger an apic error because the delivery | 395 | /* restoring APIC_LVTPC can trigger an apic error because the delivery |
394 | * mode and vector nr combination can be illegal. That's by design: on | 396 | * mode and vector nr combination can be illegal. That's by design: on |
395 | * power on apic lvt contain a zero vector nr which are legal only for | 397 | * power on apic lvt contain a zero vector nr which are legal only for |
396 | * NMI delivery mode. So inhibit apic err before restoring lvtpc | 398 | * NMI delivery mode. So inhibit apic err before restoring lvtpc |
397 | */ | 399 | */ |
398 | v = apic_read(APIC_LVTERR); | 400 | v = apic_read(APIC_LVTERR); |
399 | apic_write(APIC_LVTERR, v | APIC_LVT_MASKED); | 401 | apic_write(APIC_LVTERR, v | APIC_LVT_MASKED); |
400 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); | 402 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); |
401 | apic_write(APIC_LVTERR, v); | 403 | apic_write(APIC_LVTERR, v); |
402 | nmi_cpu_restore_registers(msrs); | 404 | nmi_cpu_restore_registers(msrs); |
403 | if (model->cpu_down) | 405 | if (model->cpu_down) |
404 | model->cpu_down(); | 406 | model->cpu_down(); |
405 | } | 407 | } |
406 | 408 | ||
407 | static void nmi_cpu_up(void *dummy) | 409 | static void nmi_cpu_up(void *dummy) |
408 | { | 410 | { |
409 | if (nmi_enabled) | 411 | if (nmi_enabled) |
410 | nmi_cpu_setup(dummy); | 412 | nmi_cpu_setup(dummy); |
411 | if (ctr_running) | 413 | if (ctr_running) |
412 | nmi_cpu_start(dummy); | 414 | nmi_cpu_start(dummy); |
413 | } | 415 | } |
414 | 416 | ||
415 | static void nmi_cpu_down(void *dummy) | 417 | static void nmi_cpu_down(void *dummy) |
416 | { | 418 | { |
417 | if (ctr_running) | 419 | if (ctr_running) |
418 | nmi_cpu_stop(dummy); | 420 | nmi_cpu_stop(dummy); |
419 | if (nmi_enabled) | 421 | if (nmi_enabled) |
420 | nmi_cpu_shutdown(dummy); | 422 | nmi_cpu_shutdown(dummy); |
421 | } | 423 | } |
422 | 424 | ||
423 | static int nmi_create_files(struct super_block *sb, struct dentry *root) | 425 | static int nmi_create_files(struct super_block *sb, struct dentry *root) |
424 | { | 426 | { |
425 | unsigned int i; | 427 | unsigned int i; |
426 | 428 | ||
427 | for (i = 0; i < model->num_virt_counters; ++i) { | 429 | for (i = 0; i < model->num_virt_counters; ++i) { |
428 | struct dentry *dir; | 430 | struct dentry *dir; |
429 | char buf[4]; | 431 | char buf[4]; |
430 | 432 | ||
431 | /* quick little hack to _not_ expose a counter if it is not | 433 | /* quick little hack to _not_ expose a counter if it is not |
432 | * available for use. This should protect userspace app. | 434 | * available for use. This should protect userspace app. |
433 | * NOTE: assumes 1:1 mapping here (that counters are organized | 435 | * NOTE: assumes 1:1 mapping here (that counters are organized |
434 | * sequentially in their struct assignment). | 436 | * sequentially in their struct assignment). |
435 | */ | 437 | */ |
436 | if (!avail_to_resrv_perfctr_nmi_bit(op_x86_virt_to_phys(i))) | 438 | if (!avail_to_resrv_perfctr_nmi_bit(op_x86_virt_to_phys(i))) |
437 | continue; | 439 | continue; |
438 | 440 | ||
439 | snprintf(buf, sizeof(buf), "%d", i); | 441 | snprintf(buf, sizeof(buf), "%d", i); |
440 | dir = oprofilefs_mkdir(sb, root, buf); | 442 | dir = oprofilefs_mkdir(sb, root, buf); |
441 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); | 443 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); |
442 | oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); | 444 | oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); |
443 | oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); | 445 | oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); |
444 | oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); | 446 | oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); |
445 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); | 447 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); |
446 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); | 448 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); |
447 | oprofilefs_create_ulong(sb, dir, "extra", &counter_config[i].extra); | 449 | oprofilefs_create_ulong(sb, dir, "extra", &counter_config[i].extra); |
448 | } | 450 | } |
449 | 451 | ||
450 | return 0; | 452 | return 0; |
451 | } | 453 | } |
452 | 454 | ||
453 | static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action, | 455 | static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action, |
454 | void *data) | 456 | void *data) |
455 | { | 457 | { |
456 | int cpu = (unsigned long)data; | 458 | int cpu = (unsigned long)data; |
457 | switch (action) { | 459 | switch (action) { |
458 | case CPU_DOWN_FAILED: | 460 | case CPU_DOWN_FAILED: |
459 | case CPU_ONLINE: | 461 | case CPU_ONLINE: |
460 | smp_call_function_single(cpu, nmi_cpu_up, NULL, 0); | 462 | smp_call_function_single(cpu, nmi_cpu_up, NULL, 0); |
461 | break; | 463 | break; |
462 | case CPU_DOWN_PREPARE: | 464 | case CPU_DOWN_PREPARE: |
463 | smp_call_function_single(cpu, nmi_cpu_down, NULL, 1); | 465 | smp_call_function_single(cpu, nmi_cpu_down, NULL, 1); |
464 | break; | 466 | break; |
465 | } | 467 | } |
466 | return NOTIFY_DONE; | 468 | return NOTIFY_DONE; |
467 | } | 469 | } |
468 | 470 | ||
469 | static struct notifier_block oprofile_cpu_nb = { | 471 | static struct notifier_block oprofile_cpu_nb = { |
470 | .notifier_call = oprofile_cpu_notifier | 472 | .notifier_call = oprofile_cpu_notifier |
471 | }; | 473 | }; |
472 | 474 | ||
473 | static int nmi_setup(void) | 475 | static int nmi_setup(void) |
474 | { | 476 | { |
475 | int err = 0; | 477 | int err = 0; |
476 | int cpu; | 478 | int cpu; |
477 | 479 | ||
478 | if (!allocate_msrs()) | 480 | if (!allocate_msrs()) |
479 | return -ENOMEM; | 481 | return -ENOMEM; |
480 | 482 | ||
481 | /* We need to serialize save and setup for HT because the subset | 483 | /* We need to serialize save and setup for HT because the subset |
482 | * of msrs are distinct for save and setup operations | 484 | * of msrs are distinct for save and setup operations |
483 | */ | 485 | */ |
484 | 486 | ||
485 | /* Assume saved/restored counters are the same on all CPUs */ | 487 | /* Assume saved/restored counters are the same on all CPUs */ |
486 | err = model->fill_in_addresses(&per_cpu(cpu_msrs, 0)); | 488 | err = model->fill_in_addresses(&per_cpu(cpu_msrs, 0)); |
487 | if (err) | 489 | if (err) |
488 | goto fail; | 490 | goto fail; |
489 | 491 | ||
490 | for_each_possible_cpu(cpu) { | 492 | for_each_possible_cpu(cpu) { |
491 | if (!cpu) | 493 | if (!cpu) |
492 | continue; | 494 | continue; |
493 | 495 | ||
494 | memcpy(per_cpu(cpu_msrs, cpu).counters, | 496 | memcpy(per_cpu(cpu_msrs, cpu).counters, |
495 | per_cpu(cpu_msrs, 0).counters, | 497 | per_cpu(cpu_msrs, 0).counters, |
496 | sizeof(struct op_msr) * model->num_counters); | 498 | sizeof(struct op_msr) * model->num_counters); |
497 | 499 | ||
498 | memcpy(per_cpu(cpu_msrs, cpu).controls, | 500 | memcpy(per_cpu(cpu_msrs, cpu).controls, |
499 | per_cpu(cpu_msrs, 0).controls, | 501 | per_cpu(cpu_msrs, 0).controls, |
500 | sizeof(struct op_msr) * model->num_controls); | 502 | sizeof(struct op_msr) * model->num_controls); |
501 | 503 | ||
502 | mux_clone(cpu); | 504 | mux_clone(cpu); |
503 | } | 505 | } |
504 | 506 | ||
505 | nmi_enabled = 0; | 507 | nmi_enabled = 0; |
506 | ctr_running = 0; | 508 | ctr_running = 0; |
507 | barrier(); | 509 | /* make variables visible to the nmi handler: */ |
510 | smp_mb(); | ||
508 | err = register_die_notifier(&profile_exceptions_nb); | 511 | err = register_die_notifier(&profile_exceptions_nb); |
509 | if (err) | 512 | if (err) |
510 | goto fail; | 513 | goto fail; |
511 | 514 | ||
512 | get_online_cpus(); | 515 | get_online_cpus(); |
513 | register_cpu_notifier(&oprofile_cpu_nb); | 516 | register_cpu_notifier(&oprofile_cpu_nb); |
514 | on_each_cpu(nmi_cpu_setup, NULL, 1); | ||
515 | nmi_enabled = 1; | 517 | nmi_enabled = 1; |
518 | /* make nmi_enabled visible to the nmi handler: */ | ||
519 | smp_mb(); | ||
520 | on_each_cpu(nmi_cpu_setup, NULL, 1); | ||
516 | put_online_cpus(); | 521 | put_online_cpus(); |
517 | 522 | ||
518 | return 0; | 523 | return 0; |
519 | fail: | 524 | fail: |
520 | free_msrs(); | 525 | free_msrs(); |
521 | return err; | 526 | return err; |
522 | } | 527 | } |
523 | 528 | ||
524 | static void nmi_shutdown(void) | 529 | static void nmi_shutdown(void) |
525 | { | 530 | { |
526 | struct op_msrs *msrs; | 531 | struct op_msrs *msrs; |
527 | 532 | ||
528 | get_online_cpus(); | 533 | get_online_cpus(); |
529 | unregister_cpu_notifier(&oprofile_cpu_nb); | 534 | unregister_cpu_notifier(&oprofile_cpu_nb); |
530 | on_each_cpu(nmi_cpu_shutdown, NULL, 1); | 535 | on_each_cpu(nmi_cpu_shutdown, NULL, 1); |
531 | nmi_enabled = 0; | 536 | nmi_enabled = 0; |
532 | ctr_running = 0; | 537 | ctr_running = 0; |
533 | put_online_cpus(); | 538 | put_online_cpus(); |
534 | barrier(); | 539 | /* make variables visible to the nmi handler: */ |
540 | smp_mb(); | ||
535 | unregister_die_notifier(&profile_exceptions_nb); | 541 | unregister_die_notifier(&profile_exceptions_nb); |
536 | msrs = &get_cpu_var(cpu_msrs); | 542 | msrs = &get_cpu_var(cpu_msrs); |
537 | model->shutdown(msrs); | 543 | model->shutdown(msrs); |
538 | free_msrs(); | 544 | free_msrs(); |
539 | put_cpu_var(cpu_msrs); | 545 | put_cpu_var(cpu_msrs); |
540 | } | 546 | } |
541 | 547 | ||
542 | #ifdef CONFIG_PM | 548 | #ifdef CONFIG_PM |
543 | 549 | ||
544 | static int nmi_suspend(void) | 550 | static int nmi_suspend(void) |
545 | { | 551 | { |
546 | /* Only one CPU left, just stop that one */ | 552 | /* Only one CPU left, just stop that one */ |
547 | if (nmi_enabled == 1) | 553 | if (nmi_enabled == 1) |
548 | nmi_cpu_stop(NULL); | 554 | nmi_cpu_stop(NULL); |
549 | return 0; | 555 | return 0; |
550 | } | 556 | } |
551 | 557 | ||
552 | static void nmi_resume(void) | 558 | static void nmi_resume(void) |
553 | { | 559 | { |
554 | if (nmi_enabled == 1) | 560 | if (nmi_enabled == 1) |
555 | nmi_cpu_start(NULL); | 561 | nmi_cpu_start(NULL); |
556 | } | 562 | } |
557 | 563 | ||
558 | static struct syscore_ops oprofile_syscore_ops = { | 564 | static struct syscore_ops oprofile_syscore_ops = { |
559 | .resume = nmi_resume, | 565 | .resume = nmi_resume, |
560 | .suspend = nmi_suspend, | 566 | .suspend = nmi_suspend, |
561 | }; | 567 | }; |
562 | 568 | ||
563 | static void __init init_suspend_resume(void) | 569 | static void __init init_suspend_resume(void) |
564 | { | 570 | { |
565 | register_syscore_ops(&oprofile_syscore_ops); | 571 | register_syscore_ops(&oprofile_syscore_ops); |
566 | } | 572 | } |
567 | 573 | ||
568 | static void exit_suspend_resume(void) | 574 | static void exit_suspend_resume(void) |
569 | { | 575 | { |
570 | unregister_syscore_ops(&oprofile_syscore_ops); | 576 | unregister_syscore_ops(&oprofile_syscore_ops); |
571 | } | 577 | } |
572 | 578 | ||
573 | #else | 579 | #else |
574 | 580 | ||
575 | static inline void init_suspend_resume(void) { } | 581 | static inline void init_suspend_resume(void) { } |
576 | static inline void exit_suspend_resume(void) { } | 582 | static inline void exit_suspend_resume(void) { } |
577 | 583 | ||
578 | #endif /* CONFIG_PM */ | 584 | #endif /* CONFIG_PM */ |
579 | 585 | ||
580 | static int __init p4_init(char **cpu_type) | 586 | static int __init p4_init(char **cpu_type) |
581 | { | 587 | { |
582 | __u8 cpu_model = boot_cpu_data.x86_model; | 588 | __u8 cpu_model = boot_cpu_data.x86_model; |
583 | 589 | ||
584 | if (cpu_model > 6 || cpu_model == 5) | 590 | if (cpu_model > 6 || cpu_model == 5) |
585 | return 0; | 591 | return 0; |
586 | 592 | ||
587 | #ifndef CONFIG_SMP | 593 | #ifndef CONFIG_SMP |
588 | *cpu_type = "i386/p4"; | 594 | *cpu_type = "i386/p4"; |
589 | model = &op_p4_spec; | 595 | model = &op_p4_spec; |
590 | return 1; | 596 | return 1; |
591 | #else | 597 | #else |
592 | switch (smp_num_siblings) { | 598 | switch (smp_num_siblings) { |
593 | case 1: | 599 | case 1: |
594 | *cpu_type = "i386/p4"; | 600 | *cpu_type = "i386/p4"; |
595 | model = &op_p4_spec; | 601 | model = &op_p4_spec; |
596 | return 1; | 602 | return 1; |
597 | 603 | ||
598 | case 2: | 604 | case 2: |
599 | *cpu_type = "i386/p4-ht"; | 605 | *cpu_type = "i386/p4-ht"; |
600 | model = &op_p4_ht2_spec; | 606 | model = &op_p4_ht2_spec; |
601 | return 1; | 607 | return 1; |
602 | } | 608 | } |
603 | #endif | 609 | #endif |
604 | 610 | ||
605 | printk(KERN_INFO "oprofile: P4 HyperThreading detected with > 2 threads\n"); | 611 | printk(KERN_INFO "oprofile: P4 HyperThreading detected with > 2 threads\n"); |
606 | printk(KERN_INFO "oprofile: Reverting to timer mode.\n"); | 612 | printk(KERN_INFO "oprofile: Reverting to timer mode.\n"); |
607 | return 0; | 613 | return 0; |
608 | } | 614 | } |
609 | 615 | ||
610 | static int force_arch_perfmon; | 616 | static int force_arch_perfmon; |
611 | static int force_cpu_type(const char *str, struct kernel_param *kp) | 617 | static int force_cpu_type(const char *str, struct kernel_param *kp) |
612 | { | 618 | { |
613 | if (!strcmp(str, "arch_perfmon")) { | 619 | if (!strcmp(str, "arch_perfmon")) { |
614 | force_arch_perfmon = 1; | 620 | force_arch_perfmon = 1; |
615 | printk(KERN_INFO "oprofile: forcing architectural perfmon\n"); | 621 | printk(KERN_INFO "oprofile: forcing architectural perfmon\n"); |
616 | } | 622 | } |
617 | 623 | ||
618 | return 0; | 624 | return 0; |
619 | } | 625 | } |
620 | module_param_call(cpu_type, force_cpu_type, NULL, NULL, 0); | 626 | module_param_call(cpu_type, force_cpu_type, NULL, NULL, 0); |
621 | 627 | ||
622 | static int __init ppro_init(char **cpu_type) | 628 | static int __init ppro_init(char **cpu_type) |
623 | { | 629 | { |
624 | __u8 cpu_model = boot_cpu_data.x86_model; | 630 | __u8 cpu_model = boot_cpu_data.x86_model; |
625 | struct op_x86_model_spec *spec = &op_ppro_spec; /* default */ | 631 | struct op_x86_model_spec *spec = &op_ppro_spec; /* default */ |
626 | 632 | ||
627 | if (force_arch_perfmon && cpu_has_arch_perfmon) | 633 | if (force_arch_perfmon && cpu_has_arch_perfmon) |
628 | return 0; | 634 | return 0; |
629 | 635 | ||
630 | /* | 636 | /* |
631 | * Documentation on identifying Intel processors by CPU family | 637 | * Documentation on identifying Intel processors by CPU family |
632 | * and model can be found in the Intel Software Developer's | 638 | * and model can be found in the Intel Software Developer's |
633 | * Manuals (SDM): | 639 | * Manuals (SDM): |
634 | * | 640 | * |
635 | * http://www.intel.com/products/processor/manuals/ | 641 | * http://www.intel.com/products/processor/manuals/ |
636 | * | 642 | * |
637 | * As of May 2010 the documentation for this was in the: | 643 | * As of May 2010 the documentation for this was in the: |
638 | * "Intel 64 and IA-32 Architectures Software Developer's | 644 | * "Intel 64 and IA-32 Architectures Software Developer's |
639 | * Manual Volume 3B: System Programming Guide", "Table B-1 | 645 | * Manual Volume 3B: System Programming Guide", "Table B-1 |
640 | * CPUID Signature Values of DisplayFamily_DisplayModel". | 646 | * CPUID Signature Values of DisplayFamily_DisplayModel". |
641 | */ | 647 | */ |
642 | switch (cpu_model) { | 648 | switch (cpu_model) { |
643 | case 0 ... 2: | 649 | case 0 ... 2: |
644 | *cpu_type = "i386/ppro"; | 650 | *cpu_type = "i386/ppro"; |
645 | break; | 651 | break; |
646 | case 3 ... 5: | 652 | case 3 ... 5: |
647 | *cpu_type = "i386/pii"; | 653 | *cpu_type = "i386/pii"; |
648 | break; | 654 | break; |
649 | case 6 ... 8: | 655 | case 6 ... 8: |
650 | case 10 ... 11: | 656 | case 10 ... 11: |
651 | *cpu_type = "i386/piii"; | 657 | *cpu_type = "i386/piii"; |
652 | break; | 658 | break; |
653 | case 9: | 659 | case 9: |
654 | case 13: | 660 | case 13: |
655 | *cpu_type = "i386/p6_mobile"; | 661 | *cpu_type = "i386/p6_mobile"; |
656 | break; | 662 | break; |
657 | case 14: | 663 | case 14: |
658 | *cpu_type = "i386/core"; | 664 | *cpu_type = "i386/core"; |
659 | break; | 665 | break; |
660 | case 0x0f: | 666 | case 0x0f: |
661 | case 0x16: | 667 | case 0x16: |
662 | case 0x17: | 668 | case 0x17: |
663 | case 0x1d: | 669 | case 0x1d: |
664 | *cpu_type = "i386/core_2"; | 670 | *cpu_type = "i386/core_2"; |
665 | break; | 671 | break; |
666 | case 0x1a: | 672 | case 0x1a: |
667 | case 0x1e: | 673 | case 0x1e: |
668 | case 0x2e: | 674 | case 0x2e: |
669 | spec = &op_arch_perfmon_spec; | 675 | spec = &op_arch_perfmon_spec; |
670 | *cpu_type = "i386/core_i7"; | 676 | *cpu_type = "i386/core_i7"; |
671 | break; | 677 | break; |
672 | case 0x1c: | 678 | case 0x1c: |
673 | *cpu_type = "i386/atom"; | 679 | *cpu_type = "i386/atom"; |
674 | break; | 680 | break; |
675 | default: | 681 | default: |
676 | /* Unknown */ | 682 | /* Unknown */ |
677 | return 0; | 683 | return 0; |
678 | } | 684 | } |
679 | 685 | ||
680 | model = spec; | 686 | model = spec; |
681 | return 1; | 687 | return 1; |
682 | } | 688 | } |
683 | 689 | ||
684 | int __init op_nmi_init(struct oprofile_operations *ops) | 690 | int __init op_nmi_init(struct oprofile_operations *ops) |
685 | { | 691 | { |
686 | __u8 vendor = boot_cpu_data.x86_vendor; | 692 | __u8 vendor = boot_cpu_data.x86_vendor; |
687 | __u8 family = boot_cpu_data.x86; | 693 | __u8 family = boot_cpu_data.x86; |
688 | char *cpu_type = NULL; | 694 | char *cpu_type = NULL; |
689 | int ret = 0; | 695 | int ret = 0; |
690 | 696 | ||
691 | if (!cpu_has_apic) | 697 | if (!cpu_has_apic) |
692 | return -ENODEV; | 698 | return -ENODEV; |
693 | 699 | ||
694 | switch (vendor) { | 700 | switch (vendor) { |
695 | case X86_VENDOR_AMD: | 701 | case X86_VENDOR_AMD: |
696 | /* Needs to be at least an Athlon (or hammer in 32bit mode) */ | 702 | /* Needs to be at least an Athlon (or hammer in 32bit mode) */ |
697 | 703 | ||
698 | switch (family) { | 704 | switch (family) { |
699 | case 6: | 705 | case 6: |
700 | cpu_type = "i386/athlon"; | 706 | cpu_type = "i386/athlon"; |
701 | break; | 707 | break; |
702 | case 0xf: | 708 | case 0xf: |
703 | /* | 709 | /* |
704 | * Actually it could be i386/hammer too, but | 710 | * Actually it could be i386/hammer too, but |
705 | * give user space an consistent name. | 711 | * give user space an consistent name. |
706 | */ | 712 | */ |
707 | cpu_type = "x86-64/hammer"; | 713 | cpu_type = "x86-64/hammer"; |
708 | break; | 714 | break; |
709 | case 0x10: | 715 | case 0x10: |
710 | cpu_type = "x86-64/family10"; | 716 | cpu_type = "x86-64/family10"; |
711 | break; | 717 | break; |
712 | case 0x11: | 718 | case 0x11: |
713 | cpu_type = "x86-64/family11h"; | 719 | cpu_type = "x86-64/family11h"; |
714 | break; | 720 | break; |
715 | case 0x12: | 721 | case 0x12: |
716 | cpu_type = "x86-64/family12h"; | 722 | cpu_type = "x86-64/family12h"; |
717 | break; | 723 | break; |
718 | case 0x14: | 724 | case 0x14: |
719 | cpu_type = "x86-64/family14h"; | 725 | cpu_type = "x86-64/family14h"; |
720 | break; | 726 | break; |
721 | case 0x15: | 727 | case 0x15: |
722 | cpu_type = "x86-64/family15h"; | 728 | cpu_type = "x86-64/family15h"; |
723 | break; | 729 | break; |
724 | default: | 730 | default: |
725 | return -ENODEV; | 731 | return -ENODEV; |
726 | } | 732 | } |
727 | model = &op_amd_spec; | 733 | model = &op_amd_spec; |
728 | break; | 734 | break; |
729 | 735 | ||
730 | case X86_VENDOR_INTEL: | 736 | case X86_VENDOR_INTEL: |
731 | switch (family) { | 737 | switch (family) { |
732 | /* Pentium IV */ | 738 | /* Pentium IV */ |
733 | case 0xf: | 739 | case 0xf: |
734 | p4_init(&cpu_type); | 740 | p4_init(&cpu_type); |
735 | break; | 741 | break; |
736 | 742 | ||
737 | /* A P6-class processor */ | 743 | /* A P6-class processor */ |
738 | case 6: | 744 | case 6: |
739 | ppro_init(&cpu_type); | 745 | ppro_init(&cpu_type); |
740 | break; | 746 | break; |
741 | 747 | ||
742 | default: | 748 | default: |
743 | break; | 749 | break; |
744 | } | 750 | } |
745 | 751 | ||
746 | if (cpu_type) | 752 | if (cpu_type) |
747 | break; | 753 | break; |
748 | 754 | ||
749 | if (!cpu_has_arch_perfmon) | 755 | if (!cpu_has_arch_perfmon) |
750 | return -ENODEV; | 756 | return -ENODEV; |
751 | 757 | ||
752 | /* use arch perfmon as fallback */ | 758 | /* use arch perfmon as fallback */ |
753 | cpu_type = "i386/arch_perfmon"; | 759 | cpu_type = "i386/arch_perfmon"; |
754 | model = &op_arch_perfmon_spec; | 760 | model = &op_arch_perfmon_spec; |
755 | break; | 761 | break; |
756 | 762 | ||
757 | default: | 763 | default: |
758 | return -ENODEV; | 764 | return -ENODEV; |
759 | } | 765 | } |
760 | 766 | ||
761 | /* default values, can be overwritten by model */ | 767 | /* default values, can be overwritten by model */ |
762 | ops->create_files = nmi_create_files; | 768 | ops->create_files = nmi_create_files; |
763 | ops->setup = nmi_setup; | 769 | ops->setup = nmi_setup; |
764 | ops->shutdown = nmi_shutdown; | 770 | ops->shutdown = nmi_shutdown; |
765 | ops->start = nmi_start; | 771 | ops->start = nmi_start; |
766 | ops->stop = nmi_stop; | 772 | ops->stop = nmi_stop; |
767 | ops->cpu_type = cpu_type; | 773 | ops->cpu_type = cpu_type; |
768 | 774 | ||
769 | if (model->init) | 775 | if (model->init) |
770 | ret = model->init(ops); | 776 | ret = model->init(ops); |
771 | if (ret) | 777 | if (ret) |
772 | return ret; | 778 | return ret; |
773 | 779 | ||
774 | if (!model->num_virt_counters) | 780 | if (!model->num_virt_counters) |
775 | model->num_virt_counters = model->num_counters; | 781 | model->num_virt_counters = model->num_counters; |
776 | 782 | ||
777 | mux_init(ops); | 783 | mux_init(ops); |
778 | 784 | ||
779 | init_suspend_resume(); | 785 | init_suspend_resume(); |
780 | 786 | ||
781 | printk(KERN_INFO "oprofile: using NMI interrupt.\n"); | 787 | printk(KERN_INFO "oprofile: using NMI interrupt.\n"); |
782 | return 0; | 788 | return 0; |
783 | } | 789 | } |
784 | 790 | ||
785 | void op_nmi_exit(void) | 791 | void op_nmi_exit(void) |
786 | { | 792 | { |
787 | exit_suspend_resume(); | 793 | exit_suspend_resume(); |