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 | |
| 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>
| -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(); |
