perf/x86/amd/ibs: Fix waking up from S3 for AMD family 10h

On AMD family 10h we see following error messages while waking up from
S3 for all non-boot CPUs leading to a failed IBS initialization:

Enabling non-boot CPUs ...
smpboot: Booting Node 0 Processor 1 APIC 0x1
[Firmware Bug]: cpu 1, try to use APIC500 (LVT offset 0) for vector 0x400, but the register is already in use for vector 0xf9 on another cpu
perf: IBS APIC setup failed on cpu #1
process: Switch to broadcast mode on CPU1
CPU1 is up
...
ACPI: Waking up from system sleep state S3

Reason for this is that during suspend the LVT offset for the IBS
vector gets lost and needs to be reinialized while resuming.

The offset is read from the IBSCTL msr. On family 10h the offset needs
to be 1 as offset 0 is used for the MCE threshold interrupt, but
firmware assings it for IBS to 0 too. The kernel needs to reprogram
the vector. The msr is a readonly node msr, but a new value can be
written via pci config space access. The reinitialization is
implemented for family 10h in setup_ibs_ctl() which is forced during
IBS setup.

This patch fixes IBS setup after waking up from S3 by adding
resume/supend hooks for the boot cpu which does the offset
reinitialization.

Marking it as stable to let distros pick up this fix.

Signed-off-by: Robert Richter <rric@kernel.org>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Cc: <stable@vger.kernel.org> v3.2..
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/1389797849-5565-1-git-send-email-rric.net@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by Robert Richter and committed by Ingo Molnar bee09ed9 c026b359

Changed files
+45 -8
arch
x86
+45 -8
arch/x86/kernel/cpu/perf_event_amd_ibs.c
··· 10 10 #include <linux/module.h> 11 11 #include <linux/pci.h> 12 12 #include <linux/ptrace.h> 13 + #include <linux/syscore_ops.h> 13 14 14 15 #include <asm/apic.h> 15 16 ··· 817 816 return ret; 818 817 } 819 818 819 + static void ibs_eilvt_setup(void) 820 + { 821 + /* 822 + * Force LVT offset assignment for family 10h: The offsets are 823 + * not assigned by the BIOS for this family, so the OS is 824 + * responsible for doing it. If the OS assignment fails, fall 825 + * back to BIOS settings and try to setup this. 826 + */ 827 + if (boot_cpu_data.x86 == 0x10) 828 + force_ibs_eilvt_setup(); 829 + } 830 + 820 831 static inline int get_ibs_lvt_offset(void) 821 832 { 822 833 u64 val; ··· 864 851 setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); 865 852 } 866 853 854 + #ifdef CONFIG_PM 855 + 856 + static int perf_ibs_suspend(void) 857 + { 858 + clear_APIC_ibs(NULL); 859 + return 0; 860 + } 861 + 862 + static void perf_ibs_resume(void) 863 + { 864 + ibs_eilvt_setup(); 865 + setup_APIC_ibs(NULL); 866 + } 867 + 868 + static struct syscore_ops perf_ibs_syscore_ops = { 869 + .resume = perf_ibs_resume, 870 + .suspend = perf_ibs_suspend, 871 + }; 872 + 873 + static void perf_ibs_pm_init(void) 874 + { 875 + register_syscore_ops(&perf_ibs_syscore_ops); 876 + } 877 + 878 + #else 879 + 880 + static inline void perf_ibs_pm_init(void) { } 881 + 882 + #endif 883 + 867 884 static int 868 885 perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) 869 886 { ··· 920 877 if (!caps) 921 878 return -ENODEV; /* ibs not supported by the cpu */ 922 879 923 - /* 924 - * Force LVT offset assignment for family 10h: The offsets are 925 - * not assigned by the BIOS for this family, so the OS is 926 - * responsible for doing it. If the OS assignment fails, fall 927 - * back to BIOS settings and try to setup this. 928 - */ 929 - if (boot_cpu_data.x86 == 0x10) 930 - force_ibs_eilvt_setup(); 880 + ibs_eilvt_setup(); 931 881 932 882 if (!ibs_eilvt_valid()) 933 883 goto out; 934 884 885 + perf_ibs_pm_init(); 935 886 get_online_cpus(); 936 887 ibs_caps = caps; 937 888 /* make ibs_caps visible to other cpus: */