diff options
Diffstat (limited to 'arch/i386/oprofile/nmi_int.c')
-rw-r--r-- | arch/i386/oprofile/nmi_int.c | 88 |
1 files changed, 59 insertions, 29 deletions
diff --git a/arch/i386/oprofile/nmi_int.c b/arch/i386/oprofile/nmi_int.c index 5f8dc8a21bd7..3700eef78743 100644 --- a/arch/i386/oprofile/nmi_int.c +++ b/arch/i386/oprofile/nmi_int.c | |||
@@ -17,14 +17,15 @@ | |||
17 | #include <asm/nmi.h> | 17 | #include <asm/nmi.h> |
18 | #include <asm/msr.h> | 18 | #include <asm/msr.h> |
19 | #include <asm/apic.h> | 19 | #include <asm/apic.h> |
20 | #include <asm/kdebug.h> | ||
20 | 21 | ||
21 | #include "op_counter.h" | 22 | #include "op_counter.h" |
22 | #include "op_x86_model.h" | 23 | #include "op_x86_model.h" |
23 | 24 | ||
24 | static struct op_x86_model_spec const * model; | 25 | static struct op_x86_model_spec const * model; |
25 | static struct op_msrs cpu_msrs[NR_CPUS]; | 26 | static struct op_msrs cpu_msrs[NR_CPUS]; |
26 | static unsigned long saved_lvtpc[NR_CPUS]; | 27 | static unsigned long saved_lvtpc[NR_CPUS]; |
27 | 28 | ||
28 | static int nmi_start(void); | 29 | static int nmi_start(void); |
29 | static void nmi_stop(void); | 30 | static void nmi_stop(void); |
30 | 31 | ||
@@ -82,13 +83,24 @@ static void exit_driverfs(void) | |||
82 | #define exit_driverfs() do { } while (0) | 83 | #define exit_driverfs() do { } while (0) |
83 | #endif /* CONFIG_PM */ | 84 | #endif /* CONFIG_PM */ |
84 | 85 | ||
85 | 86 | static int profile_exceptions_notify(struct notifier_block *self, | |
86 | static int nmi_callback(struct pt_regs * regs, int cpu) | 87 | unsigned long val, void *data) |
87 | { | 88 | { |
88 | return model->check_ctrs(regs, &cpu_msrs[cpu]); | 89 | struct die_args *args = (struct die_args *)data; |
90 | int ret = NOTIFY_DONE; | ||
91 | int cpu = smp_processor_id(); | ||
92 | |||
93 | switch(val) { | ||
94 | case DIE_NMI: | ||
95 | if (model->check_ctrs(args->regs, &cpu_msrs[cpu])) | ||
96 | ret = NOTIFY_STOP; | ||
97 | break; | ||
98 | default: | ||
99 | break; | ||
100 | } | ||
101 | return ret; | ||
89 | } | 102 | } |
90 | 103 | ||
91 | |||
92 | static void nmi_cpu_save_registers(struct op_msrs * msrs) | 104 | static void nmi_cpu_save_registers(struct op_msrs * msrs) |
93 | { | 105 | { |
94 | unsigned int const nr_ctrs = model->num_counters; | 106 | unsigned int const nr_ctrs = model->num_counters; |
@@ -98,15 +110,19 @@ static void nmi_cpu_save_registers(struct op_msrs * msrs) | |||
98 | unsigned int i; | 110 | unsigned int i; |
99 | 111 | ||
100 | for (i = 0; i < nr_ctrs; ++i) { | 112 | for (i = 0; i < nr_ctrs; ++i) { |
101 | rdmsr(counters[i].addr, | 113 | if (counters[i].addr){ |
102 | counters[i].saved.low, | 114 | rdmsr(counters[i].addr, |
103 | counters[i].saved.high); | 115 | counters[i].saved.low, |
116 | counters[i].saved.high); | ||
117 | } | ||
104 | } | 118 | } |
105 | 119 | ||
106 | for (i = 0; i < nr_ctrls; ++i) { | 120 | for (i = 0; i < nr_ctrls; ++i) { |
107 | rdmsr(controls[i].addr, | 121 | if (controls[i].addr){ |
108 | controls[i].saved.low, | 122 | rdmsr(controls[i].addr, |
109 | controls[i].saved.high); | 123 | controls[i].saved.low, |
124 | controls[i].saved.high); | ||
125 | } | ||
110 | } | 126 | } |
111 | } | 127 | } |
112 | 128 | ||
@@ -170,27 +186,29 @@ static void nmi_cpu_setup(void * dummy) | |||
170 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 186 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
171 | } | 187 | } |
172 | 188 | ||
189 | static struct notifier_block profile_exceptions_nb = { | ||
190 | .notifier_call = profile_exceptions_notify, | ||
191 | .next = NULL, | ||
192 | .priority = 0 | ||
193 | }; | ||
173 | 194 | ||
174 | static int nmi_setup(void) | 195 | static int nmi_setup(void) |
175 | { | 196 | { |
197 | int err=0; | ||
198 | |||
176 | if (!allocate_msrs()) | 199 | if (!allocate_msrs()) |
177 | return -ENOMEM; | 200 | return -ENOMEM; |
178 | 201 | ||
179 | /* We walk a thin line between law and rape here. | 202 | if ((err = register_die_notifier(&profile_exceptions_nb))){ |
180 | * We need to be careful to install our NMI handler | ||
181 | * without actually triggering any NMIs as this will | ||
182 | * break the core code horrifically. | ||
183 | */ | ||
184 | if (reserve_lapic_nmi() < 0) { | ||
185 | free_msrs(); | 203 | free_msrs(); |
186 | return -EBUSY; | 204 | return err; |
187 | } | 205 | } |
206 | |||
188 | /* We need to serialize save and setup for HT because the subset | 207 | /* We need to serialize save and setup for HT because the subset |
189 | * of msrs are distinct for save and setup operations | 208 | * of msrs are distinct for save and setup operations |
190 | */ | 209 | */ |
191 | on_each_cpu(nmi_save_registers, NULL, 0, 1); | 210 | on_each_cpu(nmi_save_registers, NULL, 0, 1); |
192 | on_each_cpu(nmi_cpu_setup, NULL, 0, 1); | 211 | on_each_cpu(nmi_cpu_setup, NULL, 0, 1); |
193 | set_nmi_callback(nmi_callback); | ||
194 | nmi_enabled = 1; | 212 | nmi_enabled = 1; |
195 | return 0; | 213 | return 0; |
196 | } | 214 | } |
@@ -205,15 +223,19 @@ static void nmi_restore_registers(struct op_msrs * msrs) | |||
205 | unsigned int i; | 223 | unsigned int i; |
206 | 224 | ||
207 | for (i = 0; i < nr_ctrls; ++i) { | 225 | for (i = 0; i < nr_ctrls; ++i) { |
208 | wrmsr(controls[i].addr, | 226 | if (controls[i].addr){ |
209 | controls[i].saved.low, | 227 | wrmsr(controls[i].addr, |
210 | controls[i].saved.high); | 228 | controls[i].saved.low, |
229 | controls[i].saved.high); | ||
230 | } | ||
211 | } | 231 | } |
212 | 232 | ||
213 | for (i = 0; i < nr_ctrs; ++i) { | 233 | for (i = 0; i < nr_ctrs; ++i) { |
214 | wrmsr(counters[i].addr, | 234 | if (counters[i].addr){ |
215 | counters[i].saved.low, | 235 | wrmsr(counters[i].addr, |
216 | counters[i].saved.high); | 236 | counters[i].saved.low, |
237 | counters[i].saved.high); | ||
238 | } | ||
217 | } | 239 | } |
218 | } | 240 | } |
219 | 241 | ||
@@ -234,6 +256,7 @@ static void nmi_cpu_shutdown(void * dummy) | |||
234 | apic_write(APIC_LVTPC, saved_lvtpc[cpu]); | 256 | apic_write(APIC_LVTPC, saved_lvtpc[cpu]); |
235 | apic_write(APIC_LVTERR, v); | 257 | apic_write(APIC_LVTERR, v); |
236 | nmi_restore_registers(msrs); | 258 | nmi_restore_registers(msrs); |
259 | model->shutdown(msrs); | ||
237 | } | 260 | } |
238 | 261 | ||
239 | 262 | ||
@@ -241,8 +264,7 @@ static void nmi_shutdown(void) | |||
241 | { | 264 | { |
242 | nmi_enabled = 0; | 265 | nmi_enabled = 0; |
243 | on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); | 266 | on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); |
244 | unset_nmi_callback(); | 267 | unregister_die_notifier(&profile_exceptions_nb); |
245 | release_lapic_nmi(); | ||
246 | free_msrs(); | 268 | free_msrs(); |
247 | } | 269 | } |
248 | 270 | ||
@@ -284,6 +306,14 @@ static int nmi_create_files(struct super_block * sb, struct dentry * root) | |||
284 | struct dentry * dir; | 306 | struct dentry * dir; |
285 | char buf[4]; | 307 | char buf[4]; |
286 | 308 | ||
309 | /* quick little hack to _not_ expose a counter if it is not | ||
310 | * available for use. This should protect userspace app. | ||
311 | * NOTE: assumes 1:1 mapping here (that counters are organized | ||
312 | * sequentially in their struct assignment). | ||
313 | */ | ||
314 | if (unlikely(!avail_to_resrv_perfctr_nmi_bit(i))) | ||
315 | continue; | ||
316 | |||
287 | snprintf(buf, sizeof(buf), "%d", i); | 317 | snprintf(buf, sizeof(buf), "%d", i); |
288 | dir = oprofilefs_mkdir(sb, root, buf); | 318 | dir = oprofilefs_mkdir(sb, root, buf); |
289 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); | 319 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); |