Commit ea431643 authored by Ingo Molnar's avatar Ingo Molnar

x86/mce: Fix CMCI preemption bugs

The following commit:

  27f6c573 ("x86, CMCI: Add proper detection of end of CMCI storms")

Added two preemption bugs:

 - machine_check_poll() does a get_cpu_var() without a matching
   put_cpu_var(), which causes preemption imbalance and crashes upon
   bootup.

 - it does percpu ops without disabling preemption. Preemption is not
   disabled due to the mistaken use of a raw spinlock.

To fix these bugs fix the imbalance and change
cmci_discover_lock to a regular spinlock.
Reported-by: default avatarOwen Kibel <qmewlo@gmail.com>
Reported-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Cc: Chen, Gong <gong.chen@linux.intel.com>
Cc: Josh Boyer <jwboyer@fedoraproject.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Alexander Todorov <atodorov@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Link: http://lkml.kernel.org/n/tip-jtjptvgigpfkpvtQxpEk1at2@git.kernel.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
--
 arch/x86/kernel/cpu/mcheck/mce.c       |    4 +---
 arch/x86/kernel/cpu/mcheck/mce_intel.c |   18 +++++++++---------
 2 files changed, 10 insertions(+), 12 deletions(-)
parent 5be44a6f
...@@ -598,7 +598,6 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b) ...@@ -598,7 +598,6 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
{ {
struct mce m; struct mce m;
int i; int i;
unsigned long *v;
this_cpu_inc(mce_poll_count); this_cpu_inc(mce_poll_count);
...@@ -618,8 +617,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b) ...@@ -618,8 +617,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
if (!(m.status & MCI_STATUS_VAL)) if (!(m.status & MCI_STATUS_VAL))
continue; continue;
v = &get_cpu_var(mce_polled_error); this_cpu_write(mce_polled_error, 1);
set_bit(0, v);
/* /*
* Uncorrected or signalled events are handled by the exception * Uncorrected or signalled events are handled by the exception
* handler when it is enabled, so don't process those here. * handler when it is enabled, so don't process those here.
......
...@@ -42,7 +42,7 @@ static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned); ...@@ -42,7 +42,7 @@ static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned);
* cmci_discover_lock protects against parallel discovery attempts * cmci_discover_lock protects against parallel discovery attempts
* which could race against each other. * which could race against each other.
*/ */
static DEFINE_RAW_SPINLOCK(cmci_discover_lock); static DEFINE_SPINLOCK(cmci_discover_lock);
#define CMCI_THRESHOLD 1 #define CMCI_THRESHOLD 1
#define CMCI_POLL_INTERVAL (30 * HZ) #define CMCI_POLL_INTERVAL (30 * HZ)
...@@ -144,14 +144,14 @@ static void cmci_storm_disable_banks(void) ...@@ -144,14 +144,14 @@ static void cmci_storm_disable_banks(void)
int bank; int bank;
u64 val; u64 val;
raw_spin_lock_irqsave(&cmci_discover_lock, flags); spin_lock_irqsave(&cmci_discover_lock, flags);
owned = __get_cpu_var(mce_banks_owned); owned = __get_cpu_var(mce_banks_owned);
for_each_set_bit(bank, owned, MAX_NR_BANKS) { for_each_set_bit(bank, owned, MAX_NR_BANKS) {
rdmsrl(MSR_IA32_MCx_CTL2(bank), val); rdmsrl(MSR_IA32_MCx_CTL2(bank), val);
val &= ~MCI_CTL2_CMCI_EN; val &= ~MCI_CTL2_CMCI_EN;
wrmsrl(MSR_IA32_MCx_CTL2(bank), val); wrmsrl(MSR_IA32_MCx_CTL2(bank), val);
} }
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); spin_unlock_irqrestore(&cmci_discover_lock, flags);
} }
static bool cmci_storm_detect(void) static bool cmci_storm_detect(void)
...@@ -211,7 +211,7 @@ static void cmci_discover(int banks) ...@@ -211,7 +211,7 @@ static void cmci_discover(int banks)
int i; int i;
int bios_wrong_thresh = 0; int bios_wrong_thresh = 0;
raw_spin_lock_irqsave(&cmci_discover_lock, flags); spin_lock_irqsave(&cmci_discover_lock, flags);
for (i = 0; i < banks; i++) { for (i = 0; i < banks; i++) {
u64 val; u64 val;
int bios_zero_thresh = 0; int bios_zero_thresh = 0;
...@@ -266,7 +266,7 @@ static void cmci_discover(int banks) ...@@ -266,7 +266,7 @@ static void cmci_discover(int banks)
WARN_ON(!test_bit(i, __get_cpu_var(mce_poll_banks))); WARN_ON(!test_bit(i, __get_cpu_var(mce_poll_banks)));
} }
} }
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); spin_unlock_irqrestore(&cmci_discover_lock, flags);
if (mca_cfg.bios_cmci_threshold && bios_wrong_thresh) { if (mca_cfg.bios_cmci_threshold && bios_wrong_thresh) {
pr_info_once( pr_info_once(
"bios_cmci_threshold: Some banks do not have valid thresholds set\n"); "bios_cmci_threshold: Some banks do not have valid thresholds set\n");
...@@ -316,10 +316,10 @@ void cmci_clear(void) ...@@ -316,10 +316,10 @@ void cmci_clear(void)
if (!cmci_supported(&banks)) if (!cmci_supported(&banks))
return; return;
raw_spin_lock_irqsave(&cmci_discover_lock, flags); spin_lock_irqsave(&cmci_discover_lock, flags);
for (i = 0; i < banks; i++) for (i = 0; i < banks; i++)
__cmci_disable_bank(i); __cmci_disable_bank(i);
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); spin_unlock_irqrestore(&cmci_discover_lock, flags);
} }
static void cmci_rediscover_work_func(void *arg) static void cmci_rediscover_work_func(void *arg)
...@@ -360,9 +360,9 @@ void cmci_disable_bank(int bank) ...@@ -360,9 +360,9 @@ void cmci_disable_bank(int bank)
if (!cmci_supported(&banks)) if (!cmci_supported(&banks))
return; return;
raw_spin_lock_irqsave(&cmci_discover_lock, flags); spin_lock_irqsave(&cmci_discover_lock, flags);
__cmci_disable_bank(bank); __cmci_disable_bank(bank);
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); spin_unlock_irqrestore(&cmci_discover_lock, flags);
} }
static void intel_init_cmci(void) static void intel_init_cmci(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment