diff options
author | Robert Richter <robert.richter@amd.com> | 2010-04-29 08:55:55 -0400 |
---|---|---|
committer | Robert Richter <robert.richter@amd.com> | 2010-05-04 05:37:56 -0400 |
commit | 6ae56b55bc364bc2f2342f599b46581627ba22da (patch) | |
tree | 0457732ebc2827aeb976881345e620c656fbb338 /arch/x86/oprofile | |
parent | 216f3d9b4e5121feea4b13fae9d4c83e8d7e1c8a (diff) |
oprofile/x86: protect cpu hotplug sections
This patch reworks oprofile cpu hotplug code as follows:
Introduce ctr_running variable to check, if counters are running or
not. The state must be known for taking a cpu on or offline and when
switching counters during counter multiplexing.
Protect on_each_cpu() sections with get_online_cpus()/put_online_cpu()
functions. This is necessary if notifiers or states are
modified. Within these sections the cpu mask may not change.
Switch only between counters in nmi_cpu_switch(), if counters are
running. Otherwise the switch may restart a counter though they are
disabled.
Add nmi_cpu_setup() and nmi_cpu_shutdown() to cpu hotplug code. The
function must also be called to avoid uninitialzed counter usage.
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Robert Richter <robert.richter@amd.com>
Diffstat (limited to 'arch/x86/oprofile')
-rw-r--r-- | arch/x86/oprofile/nmi_int.c | 52 |
1 files changed, 46 insertions, 6 deletions
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index c5df8ee76ee4..b56601eaf29d 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c | |||
@@ -31,8 +31,9 @@ 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 | /* 0 == registered but off, 1 == registered and on */ | 34 | /* must be protected with get_online_cpus()/put_online_cpus(): */ |
35 | static int nmi_enabled = 0; | 35 | static int nmi_enabled; |
36 | static int ctr_running; | ||
36 | 37 | ||
37 | struct op_counter_config counter_config[OP_MAX_COUNTER]; | 38 | struct op_counter_config counter_config[OP_MAX_COUNTER]; |
38 | 39 | ||
@@ -103,7 +104,10 @@ static void nmi_cpu_start(void *dummy) | |||
103 | 104 | ||
104 | static int nmi_start(void) | 105 | static int nmi_start(void) |
105 | { | 106 | { |
107 | get_online_cpus(); | ||
106 | on_each_cpu(nmi_cpu_start, NULL, 1); | 108 | on_each_cpu(nmi_cpu_start, NULL, 1); |
109 | ctr_running = 1; | ||
110 | put_online_cpus(); | ||
107 | return 0; | 111 | return 0; |
108 | } | 112 | } |
109 | 113 | ||
@@ -118,7 +122,10 @@ static void nmi_cpu_stop(void *dummy) | |||
118 | 122 | ||
119 | static void nmi_stop(void) | 123 | static void nmi_stop(void) |
120 | { | 124 | { |
125 | get_online_cpus(); | ||
121 | on_each_cpu(nmi_cpu_stop, NULL, 1); | 126 | on_each_cpu(nmi_cpu_stop, NULL, 1); |
127 | ctr_running = 0; | ||
128 | put_online_cpus(); | ||
122 | } | 129 | } |
123 | 130 | ||
124 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 131 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
@@ -258,7 +265,10 @@ static int nmi_switch_event(void) | |||
258 | if (nmi_multiplex_on() < 0) | 265 | if (nmi_multiplex_on() < 0) |
259 | return -EINVAL; /* not necessary */ | 266 | return -EINVAL; /* not necessary */ |
260 | 267 | ||
261 | on_each_cpu(nmi_cpu_switch, NULL, 1); | 268 | get_online_cpus(); |
269 | if (ctr_running) | ||
270 | on_each_cpu(nmi_cpu_switch, NULL, 1); | ||
271 | put_online_cpus(); | ||
262 | 272 | ||
263 | return 0; | 273 | return 0; |
264 | } | 274 | } |
@@ -386,8 +396,11 @@ static int nmi_setup(void) | |||
386 | if (err) | 396 | if (err) |
387 | goto fail; | 397 | goto fail; |
388 | 398 | ||
399 | get_online_cpus(); | ||
389 | on_each_cpu(nmi_cpu_setup, NULL, 1); | 400 | on_each_cpu(nmi_cpu_setup, NULL, 1); |
390 | nmi_enabled = 1; | 401 | nmi_enabled = 1; |
402 | put_online_cpus(); | ||
403 | |||
391 | return 0; | 404 | return 0; |
392 | fail: | 405 | fail: |
393 | free_msrs(); | 406 | free_msrs(); |
@@ -433,8 +446,11 @@ static void nmi_shutdown(void) | |||
433 | { | 446 | { |
434 | struct op_msrs *msrs; | 447 | struct op_msrs *msrs; |
435 | 448 | ||
436 | nmi_enabled = 0; | 449 | get_online_cpus(); |
437 | on_each_cpu(nmi_cpu_shutdown, NULL, 1); | 450 | on_each_cpu(nmi_cpu_shutdown, NULL, 1); |
451 | nmi_enabled = 0; | ||
452 | ctr_running = 0; | ||
453 | put_online_cpus(); | ||
438 | unregister_die_notifier(&profile_exceptions_nb); | 454 | unregister_die_notifier(&profile_exceptions_nb); |
439 | msrs = &get_cpu_var(cpu_msrs); | 455 | msrs = &get_cpu_var(cpu_msrs); |
440 | model->shutdown(msrs); | 456 | model->shutdown(msrs); |
@@ -442,6 +458,22 @@ static void nmi_shutdown(void) | |||
442 | put_cpu_var(cpu_msrs); | 458 | put_cpu_var(cpu_msrs); |
443 | } | 459 | } |
444 | 460 | ||
461 | static void nmi_cpu_up(void *dummy) | ||
462 | { | ||
463 | if (nmi_enabled) | ||
464 | nmi_cpu_setup(dummy); | ||
465 | if (ctr_running) | ||
466 | nmi_cpu_start(dummy); | ||
467 | } | ||
468 | |||
469 | static void nmi_cpu_down(void *dummy) | ||
470 | { | ||
471 | if (ctr_running) | ||
472 | nmi_cpu_stop(dummy); | ||
473 | if (nmi_enabled) | ||
474 | nmi_cpu_shutdown(dummy); | ||
475 | } | ||
476 | |||
445 | static int nmi_create_files(struct super_block *sb, struct dentry *root) | 477 | static int nmi_create_files(struct super_block *sb, struct dentry *root) |
446 | { | 478 | { |
447 | unsigned int i; | 479 | unsigned int i; |
@@ -478,10 +510,10 @@ static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action, | |||
478 | switch (action) { | 510 | switch (action) { |
479 | case CPU_DOWN_FAILED: | 511 | case CPU_DOWN_FAILED: |
480 | case CPU_ONLINE: | 512 | case CPU_ONLINE: |
481 | smp_call_function_single(cpu, nmi_cpu_start, NULL, 0); | 513 | smp_call_function_single(cpu, nmi_cpu_up, NULL, 0); |
482 | break; | 514 | break; |
483 | case CPU_DOWN_PREPARE: | 515 | case CPU_DOWN_PREPARE: |
484 | smp_call_function_single(cpu, nmi_cpu_stop, NULL, 1); | 516 | smp_call_function_single(cpu, nmi_cpu_down, NULL, 1); |
485 | break; | 517 | break; |
486 | } | 518 | } |
487 | return NOTIFY_DONE; | 519 | return NOTIFY_DONE; |
@@ -699,7 +731,11 @@ int __init op_nmi_init(struct oprofile_operations *ops) | |||
699 | return -ENODEV; | 731 | return -ENODEV; |
700 | } | 732 | } |
701 | 733 | ||
734 | get_online_cpus(); | ||
702 | register_cpu_notifier(&oprofile_cpu_nb); | 735 | register_cpu_notifier(&oprofile_cpu_nb); |
736 | nmi_enabled = 0; | ||
737 | ctr_running = 0; | ||
738 | put_online_cpus(); | ||
703 | 739 | ||
704 | /* default values, can be overwritten by model */ | 740 | /* default values, can be overwritten by model */ |
705 | ops->create_files = nmi_create_files; | 741 | ops->create_files = nmi_create_files; |
@@ -729,7 +765,11 @@ void op_nmi_exit(void) | |||
729 | { | 765 | { |
730 | if (using_nmi) { | 766 | if (using_nmi) { |
731 | exit_sysfs(); | 767 | exit_sysfs(); |
768 | get_online_cpus(); | ||
732 | unregister_cpu_notifier(&oprofile_cpu_nb); | 769 | unregister_cpu_notifier(&oprofile_cpu_nb); |
770 | nmi_enabled = 0; | ||
771 | ctr_running = 0; | ||
772 | put_online_cpus(); | ||
733 | } | 773 | } |
734 | if (model->exit) | 774 | if (model->exit) |
735 | model->exit(); | 775 | model->exit(); |