diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event_amd_ibs.c')
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event_amd_ibs.c | 62 | 
1 files changed, 50 insertions, 12 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index e09f0bfb7b8..cbb1be3ed9e 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -10,6 +10,7 @@  #include <linux/module.h>  #include <linux/pci.h>  #include <linux/ptrace.h> +#include <linux/syscore_ops.h>  #include <asm/apic.h> @@ -592,7 +593,7 @@ out:  	return 1;  } -static int __kprobes +static int  perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)  {  	int handled = 0; @@ -605,6 +606,7 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)  	return handled;  } +NOKPROBE_SYMBOL(perf_ibs_nmi_handler);  static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)  { @@ -816,6 +818,18 @@ out:  	return ret;  } +static void ibs_eilvt_setup(void) +{ +	/* +	 * Force LVT offset assignment for family 10h: The offsets are +	 * not assigned by the BIOS for this family, so the OS is +	 * responsible for doing it. If the OS assignment fails, fall +	 * back to BIOS settings and try to setup this. +	 */ +	if (boot_cpu_data.x86 == 0x10) +		force_ibs_eilvt_setup(); +} +  static inline int get_ibs_lvt_offset(void)  {  	u64 val; @@ -851,6 +865,36 @@ static void clear_APIC_ibs(void *dummy)  		setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);  } +#ifdef CONFIG_PM + +static int perf_ibs_suspend(void) +{ +	clear_APIC_ibs(NULL); +	return 0; +} + +static void perf_ibs_resume(void) +{ +	ibs_eilvt_setup(); +	setup_APIC_ibs(NULL); +} + +static struct syscore_ops perf_ibs_syscore_ops = { +	.resume		= perf_ibs_resume, +	.suspend	= perf_ibs_suspend, +}; + +static void perf_ibs_pm_init(void) +{ +	register_syscore_ops(&perf_ibs_syscore_ops); +} + +#else + +static inline void perf_ibs_pm_init(void) { } + +#endif +  static int  perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)  { @@ -877,25 +921,19 @@ static __init int amd_ibs_init(void)  	if (!caps)  		return -ENODEV;	/* ibs not supported by the cpu */ -	/* -	 * Force LVT offset assignment for family 10h: The offsets are -	 * not assigned by the BIOS for this family, so the OS is -	 * responsible for doing it. If the OS assignment fails, fall -	 * back to BIOS settings and try to setup this. -	 */ -	if (boot_cpu_data.x86 == 0x10) -		force_ibs_eilvt_setup(); +	ibs_eilvt_setup();  	if (!ibs_eilvt_valid())  		goto out; -	get_online_cpus(); +	perf_ibs_pm_init(); +	cpu_notifier_register_begin();  	ibs_caps = caps;  	/* make ibs_caps visible to other cpus: */  	smp_mb(); -	perf_cpu_notifier(perf_ibs_cpu_notifier);  	smp_call_function(setup_APIC_ibs, NULL, 1); -	put_online_cpus(); +	__perf_cpu_notifier(perf_ibs_cpu_notifier); +	cpu_notifier_register_done();  	ret = perf_event_ibs_init();  out:  | 
