diff options
Diffstat (limited to 'arch/x86/oprofile/nmi_int.c')
-rw-r--r-- | arch/x86/oprofile/nmi_int.c | 84 |
1 files changed, 38 insertions, 46 deletions
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index f1575c9a2572..68894fdc034b 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c | |||
@@ -15,7 +15,7 @@ | |||
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/sysdev.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> |
@@ -49,6 +49,10 @@ u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, | |||
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 | | ||
53 | ARCH_PERFMON_EVENTSEL_EDGE | | ||
54 | ARCH_PERFMON_EVENTSEL_CMASK); | ||
55 | val |= counter_config->extra; | ||
52 | event &= model->event_mask ? model->event_mask : 0xFF; | 56 | event &= model->event_mask ? model->event_mask : 0xFF; |
53 | val |= event & 0xFF; | 57 | val |= event & 0xFF; |
54 | val |= (event & 0x0F00) << 24; | 58 | val |= (event & 0x0F00) << 24; |
@@ -65,7 +69,6 @@ static int profile_exceptions_notify(struct notifier_block *self, | |||
65 | 69 | ||
66 | switch (val) { | 70 | switch (val) { |
67 | case DIE_NMI: | 71 | case DIE_NMI: |
68 | case DIE_NMI_IPI: | ||
69 | if (ctr_running) | 72 | if (ctr_running) |
70 | model->check_ctrs(args->regs, &__get_cpu_var(cpu_msrs)); | 73 | model->check_ctrs(args->regs, &__get_cpu_var(cpu_msrs)); |
71 | else if (!nmi_enabled) | 74 | else if (!nmi_enabled) |
@@ -109,8 +112,10 @@ static void nmi_cpu_start(void *dummy) | |||
109 | static int nmi_start(void) | 112 | static int nmi_start(void) |
110 | { | 113 | { |
111 | get_online_cpus(); | 114 | get_online_cpus(); |
112 | on_each_cpu(nmi_cpu_start, NULL, 1); | ||
113 | 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); | ||
114 | put_online_cpus(); | 119 | put_online_cpus(); |
115 | return 0; | 120 | return 0; |
116 | } | 121 | } |
@@ -143,7 +148,7 @@ static inline int has_mux(void) | |||
143 | 148 | ||
144 | inline int op_x86_phys_to_virt(int phys) | 149 | inline int op_x86_phys_to_virt(int phys) |
145 | { | 150 | { |
146 | return __get_cpu_var(switch_index) + phys; | 151 | return __this_cpu_read(switch_index) + phys; |
147 | } | 152 | } |
148 | 153 | ||
149 | inline int op_x86_virt_to_phys(int virt) | 154 | inline int op_x86_virt_to_phys(int virt) |
@@ -361,7 +366,7 @@ static void nmi_cpu_setup(void *dummy) | |||
361 | static struct notifier_block profile_exceptions_nb = { | 366 | static struct notifier_block profile_exceptions_nb = { |
362 | .notifier_call = profile_exceptions_notify, | 367 | .notifier_call = profile_exceptions_notify, |
363 | .next = NULL, | 368 | .next = NULL, |
364 | .priority = 2 | 369 | .priority = NMI_LOCAL_LOW_PRIOR, |
365 | }; | 370 | }; |
366 | 371 | ||
367 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) | 372 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) |
@@ -441,6 +446,7 @@ static int nmi_create_files(struct super_block *sb, struct dentry *root) | |||
441 | 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); |
442 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); | 447 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); |
443 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); | 448 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); |
449 | oprofilefs_create_ulong(sb, dir, "extra", &counter_config[i].extra); | ||
444 | } | 450 | } |
445 | 451 | ||
446 | return 0; | 452 | return 0; |
@@ -500,15 +506,18 @@ static int nmi_setup(void) | |||
500 | 506 | ||
501 | nmi_enabled = 0; | 507 | nmi_enabled = 0; |
502 | ctr_running = 0; | 508 | ctr_running = 0; |
503 | barrier(); | 509 | /* make variables visible to the nmi handler: */ |
510 | smp_mb(); | ||
504 | err = register_die_notifier(&profile_exceptions_nb); | 511 | err = register_die_notifier(&profile_exceptions_nb); |
505 | if (err) | 512 | if (err) |
506 | goto fail; | 513 | goto fail; |
507 | 514 | ||
508 | get_online_cpus(); | 515 | get_online_cpus(); |
509 | register_cpu_notifier(&oprofile_cpu_nb); | 516 | register_cpu_notifier(&oprofile_cpu_nb); |
510 | on_each_cpu(nmi_cpu_setup, NULL, 1); | ||
511 | 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); | ||
512 | put_online_cpus(); | 521 | put_online_cpus(); |
513 | 522 | ||
514 | return 0; | 523 | return 0; |
@@ -527,7 +536,8 @@ static void nmi_shutdown(void) | |||
527 | nmi_enabled = 0; | 536 | nmi_enabled = 0; |
528 | ctr_running = 0; | 537 | ctr_running = 0; |
529 | put_online_cpus(); | 538 | put_online_cpus(); |
530 | barrier(); | 539 | /* make variables visible to the nmi handler: */ |
540 | smp_mb(); | ||
531 | unregister_die_notifier(&profile_exceptions_nb); | 541 | unregister_die_notifier(&profile_exceptions_nb); |
532 | msrs = &get_cpu_var(cpu_msrs); | 542 | msrs = &get_cpu_var(cpu_msrs); |
533 | model->shutdown(msrs); | 543 | model->shutdown(msrs); |
@@ -537,7 +547,7 @@ static void nmi_shutdown(void) | |||
537 | 547 | ||
538 | #ifdef CONFIG_PM | 548 | #ifdef CONFIG_PM |
539 | 549 | ||
540 | static int nmi_suspend(struct sys_device *dev, pm_message_t state) | 550 | static int nmi_suspend(void) |
541 | { | 551 | { |
542 | /* Only one CPU left, just stop that one */ | 552 | /* Only one CPU left, just stop that one */ |
543 | if (nmi_enabled == 1) | 553 | if (nmi_enabled == 1) |
@@ -545,49 +555,31 @@ static int nmi_suspend(struct sys_device *dev, pm_message_t state) | |||
545 | return 0; | 555 | return 0; |
546 | } | 556 | } |
547 | 557 | ||
548 | static int nmi_resume(struct sys_device *dev) | 558 | static void nmi_resume(void) |
549 | { | 559 | { |
550 | if (nmi_enabled == 1) | 560 | if (nmi_enabled == 1) |
551 | nmi_cpu_start(NULL); | 561 | nmi_cpu_start(NULL); |
552 | return 0; | ||
553 | } | 562 | } |
554 | 563 | ||
555 | static struct sysdev_class oprofile_sysclass = { | 564 | static struct syscore_ops oprofile_syscore_ops = { |
556 | .name = "oprofile", | ||
557 | .resume = nmi_resume, | 565 | .resume = nmi_resume, |
558 | .suspend = nmi_suspend, | 566 | .suspend = nmi_suspend, |
559 | }; | 567 | }; |
560 | 568 | ||
561 | static struct sys_device device_oprofile = { | 569 | static void __init init_suspend_resume(void) |
562 | .id = 0, | ||
563 | .cls = &oprofile_sysclass, | ||
564 | }; | ||
565 | |||
566 | static int __init init_sysfs(void) | ||
567 | { | 570 | { |
568 | int error; | 571 | register_syscore_ops(&oprofile_syscore_ops); |
569 | |||
570 | error = sysdev_class_register(&oprofile_sysclass); | ||
571 | if (error) | ||
572 | return error; | ||
573 | |||
574 | error = sysdev_register(&device_oprofile); | ||
575 | if (error) | ||
576 | sysdev_class_unregister(&oprofile_sysclass); | ||
577 | |||
578 | return error; | ||
579 | } | 572 | } |
580 | 573 | ||
581 | static void exit_sysfs(void) | 574 | static void exit_suspend_resume(void) |
582 | { | 575 | { |
583 | sysdev_unregister(&device_oprofile); | 576 | unregister_syscore_ops(&oprofile_syscore_ops); |
584 | sysdev_class_unregister(&oprofile_sysclass); | ||
585 | } | 577 | } |
586 | 578 | ||
587 | #else | 579 | #else |
588 | 580 | ||
589 | static inline int init_sysfs(void) { return 0; } | 581 | static inline void init_suspend_resume(void) { } |
590 | static inline void exit_sysfs(void) { } | 582 | static inline void exit_suspend_resume(void) { } |
591 | 583 | ||
592 | #endif /* CONFIG_PM */ | 584 | #endif /* CONFIG_PM */ |
593 | 585 | ||
@@ -695,9 +687,6 @@ static int __init ppro_init(char **cpu_type) | |||
695 | return 1; | 687 | return 1; |
696 | } | 688 | } |
697 | 689 | ||
698 | /* in order to get sysfs right */ | ||
699 | static int using_nmi; | ||
700 | |||
701 | int __init op_nmi_init(struct oprofile_operations *ops) | 690 | int __init op_nmi_init(struct oprofile_operations *ops) |
702 | { | 691 | { |
703 | __u8 vendor = boot_cpu_data.x86_vendor; | 692 | __u8 vendor = boot_cpu_data.x86_vendor; |
@@ -705,8 +694,6 @@ int __init op_nmi_init(struct oprofile_operations *ops) | |||
705 | char *cpu_type = NULL; | 694 | char *cpu_type = NULL; |
706 | int ret = 0; | 695 | int ret = 0; |
707 | 696 | ||
708 | using_nmi = 0; | ||
709 | |||
710 | if (!cpu_has_apic) | 697 | if (!cpu_has_apic) |
711 | return -ENODEV; | 698 | return -ENODEV; |
712 | 699 | ||
@@ -731,6 +718,15 @@ int __init op_nmi_init(struct oprofile_operations *ops) | |||
731 | case 0x11: | 718 | case 0x11: |
732 | cpu_type = "x86-64/family11h"; | 719 | cpu_type = "x86-64/family11h"; |
733 | break; | 720 | break; |
721 | case 0x12: | ||
722 | cpu_type = "x86-64/family12h"; | ||
723 | break; | ||
724 | case 0x14: | ||
725 | cpu_type = "x86-64/family14h"; | ||
726 | break; | ||
727 | case 0x15: | ||
728 | cpu_type = "x86-64/family15h"; | ||
729 | break; | ||
734 | default: | 730 | default: |
735 | return -ENODEV; | 731 | return -ENODEV; |
736 | } | 732 | } |
@@ -786,17 +782,13 @@ int __init op_nmi_init(struct oprofile_operations *ops) | |||
786 | 782 | ||
787 | mux_init(ops); | 783 | mux_init(ops); |
788 | 784 | ||
789 | ret = init_sysfs(); | 785 | init_suspend_resume(); |
790 | if (ret) | ||
791 | return ret; | ||
792 | 786 | ||
793 | using_nmi = 1; | ||
794 | printk(KERN_INFO "oprofile: using NMI interrupt.\n"); | 787 | printk(KERN_INFO "oprofile: using NMI interrupt.\n"); |
795 | return 0; | 788 | return 0; |
796 | } | 789 | } |
797 | 790 | ||
798 | void op_nmi_exit(void) | 791 | void op_nmi_exit(void) |
799 | { | 792 | { |
800 | if (using_nmi) | 793 | exit_suspend_resume(); |
801 | exit_sysfs(); | ||
802 | } | 794 | } |