Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 19 Jan 2014 21:06:51 +0000 (13:06 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 19 Jan 2014 21:06:51 +0000 (13:06 -0800)
Pull perf fixes from Ingo Molnar:

 - an s2ram related fix on AMD systems

 - a perf fault handling bug that is relatively old but which has become
   much easier to trigger in v3.13 after commit e00b12e64be9 ("perf/x86:
   Further optimize copy_from_user_nmi()")

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  perf/x86/amd/ibs: Fix waking up from S3 for AMD family 10h
  x86, mm, perf: Allow recursive faults from interrupts

arch/x86/kernel/cpu/perf_event_amd_ibs.c
arch/x86/mm/fault.c

index e09f0bfb7b8f23b3206ed623289df4b9bff67605..4b8e4d3cd6ea62bc9c9047e869fa84cbd9cbe33a 100644 (file)
@@ -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>
 
@@ -816,6 +817,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 +864,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,18 +920,12 @@ 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;
 
+       perf_ibs_pm_init();
        get_online_cpus();
        ibs_caps = caps;
        /* make ibs_caps visible to other cpus: */
index 9ff85bb8dd698182fa6605ee565a00f7843e6303..9d591c895803101e2decbc85a0ce9f23a0b4eaeb 100644 (file)
@@ -641,6 +641,20 @@ no_context(struct pt_regs *regs, unsigned long error_code,
 
        /* Are we prepared to handle this kernel fault? */
        if (fixup_exception(regs)) {
+               /*
+                * Any interrupt that takes a fault gets the fixup. This makes
+                * the below recursive fault logic only apply to a faults from
+                * task context.
+                */
+               if (in_interrupt())
+                       return;
+
+               /*
+                * Per the above we're !in_interrupt(), aka. task context.
+                *
+                * In this case we need to make sure we're not recursively
+                * faulting through the emulate_vsyscall() logic.
+                */
                if (current_thread_info()->sig_on_uaccess_error && signal) {
                        tsk->thread.trap_nr = X86_TRAP_PF;
                        tsk->thread.error_code = error_code | PF_USER;
@@ -649,6 +663,10 @@ no_context(struct pt_regs *regs, unsigned long error_code,
                        /* XXX: hwpoison faults will set the wrong code. */
                        force_sig_info_fault(signal, si_code, address, tsk, 0);
                }
+
+               /*
+                * Barring that, we can do the fixup and be happy.
+                */
                return;
        }