x86/mce: Add per-bank CMCI storm mitigation
[linux-2.6-microblaze.git] / arch / x86 / kernel / cpu / mce / core.c
index b2ef487..fd5ce12 100644 (file)
@@ -686,6 +686,16 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
                barrier();
                m.status = mce_rdmsrl(mca_msr_reg(i, MCA_STATUS));
 
+               /*
+                * Update storm tracking here, before checking for the
+                * MCI_STATUS_VAL bit. Valid corrected errors count
+                * towards declaring, or maintaining, storm status. No
+                * error in a bank counts towards avoiding, or ending,
+                * storm status.
+                */
+               if (!mca_cfg.cmci_disabled)
+                       mce_track_storm(&m);
+
                /* If this entry is not valid, ignore it */
                if (!(m.status & MCI_STATUS_VAL))
                        continue;
@@ -1658,22 +1668,29 @@ static void mce_timer_fn(struct timer_list *t)
        else
                iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
 
-       __this_cpu_write(mce_next_interval, iv);
-       __start_timer(t, iv);
+       if (mce_get_storm_mode()) {
+               __start_timer(t, HZ);
+       } else {
+               __this_cpu_write(mce_next_interval, iv);
+               __start_timer(t, iv);
+       }
 }
 
 /*
- * Ensure that the timer is firing in @interval from now.
+ * When a storm starts on any bank on this CPU, switch to polling
+ * once per second. When the storm ends, revert to the default
+ * polling interval.
  */
-void mce_timer_kick(unsigned long interval)
+void mce_timer_kick(bool storm)
 {
        struct timer_list *t = this_cpu_ptr(&mce_timer);
-       unsigned long iv = __this_cpu_read(mce_next_interval);
 
-       __start_timer(t, interval);
+       mce_set_storm_mode(storm);
 
-       if (interval < iv)
-               __this_cpu_write(mce_next_interval, interval);
+       if (storm)
+               __start_timer(t, HZ);
+       else
+               __this_cpu_write(mce_next_interval, check_interval * HZ);
 }
 
 /* Must not be called in IRQ context where del_timer_sync() can deadlock */