Merge tag 'amd-thresholding-fixes-for-3.5' of git://git.kernel.org/pub/scm/linux...
authorIngo Molnar <mingo@kernel.org>
Fri, 4 May 2012 07:53:34 +0000 (09:53 +0200)
committerIngo Molnar <mingo@kernel.org>
Fri, 4 May 2012 07:53:34 +0000 (09:53 +0200)
- make the APIC LVT interrupt optional because a subset
  of AMD F15h models don't support it.

Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mcheck/mce_amd.c

index d086a09c087d7fa3cf8d1ee3f20176623b1df9f6..888fbf9d0adf9bac4412454090bba5cac6ebf6c6 100644 (file)
@@ -1423,6 +1423,43 @@ static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
                 */
                 if (c->x86 == 6 && banks > 0)
                        mce_banks[0].ctl = 0;
+
+                /*
+                 * Turn off MC4_MISC thresholding banks on those models since
+                 * they're not supported there.
+                 */
+                if (c->x86 == 0x15 &&
+                    (c->x86_model >= 0x10 && c->x86_model <= 0x1f)) {
+                        int i;
+                        u64 val, hwcr;
+                        bool need_toggle;
+                        u32 msrs[] = {
+                               0x00000413, /* MC4_MISC0 */
+                               0xc0000408, /* MC4_MISC1 */
+                        };
+
+                        rdmsrl(MSR_K7_HWCR, hwcr);
+
+                        /* McStatusWrEn has to be set */
+                        need_toggle = !(hwcr & BIT(18));
+
+                        if (need_toggle)
+                                wrmsrl(MSR_K7_HWCR, hwcr | BIT(18));
+
+                        for (i = 0; i < ARRAY_SIZE(msrs); i++) {
+                                rdmsrl(msrs[i], val);
+
+                                /* CntP bit set? */
+                                if (val & BIT(62)) {
+                                        val &= ~BIT(62);
+                                        wrmsrl(msrs[i], val);
+                                }
+                        }
+
+                        /* restore old settings */
+                        if (need_toggle)
+                                wrmsrl(MSR_K7_HWCR, hwcr);
+                }
        }
 
        if (c->x86_vendor == X86_VENDOR_INTEL) {
index 99b57179f9129b770d0e3f069f09ab06f4f686ec..f4873a64f46dcb2dac8db0c3585afecc642e70ee 100644 (file)
@@ -51,6 +51,7 @@ struct threshold_block {
        unsigned int            cpu;
        u32                     address;
        u16                     interrupt_enable;
+       bool                    interrupt_capable;
        u16                     threshold_limit;
        struct kobject          kobj;
        struct list_head        miscj;
@@ -83,6 +84,21 @@ struct thresh_restart {
        u16                     old_limit;
 };
 
+static bool lvt_interrupt_supported(unsigned int bank, u32 msr_high_bits)
+{
+       /*
+        * bank 4 supports APIC LVT interrupts implicitly since forever.
+        */
+       if (bank == 4)
+               return true;
+
+       /*
+        * IntP: interrupt present; if this bit is set, the thresholding
+        * bank can generate APIC LVT interrupts
+        */
+       return msr_high_bits & BIT(28);
+}
+
 static int lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
 {
        int msr = (hi & MASK_LVTOFF_HI) >> 20;
@@ -104,8 +120,10 @@ static int lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
        return 1;
 };
 
-/* must be called with correct cpu affinity */
-/* Called via smp_call_function_single() */
+/*
+ * Called via smp_call_function_single(), must be called with correct
+ * cpu affinity.
+ */
 static void threshold_restart_bank(void *_tr)
 {
        struct thresh_restart *tr = _tr;
@@ -128,6 +146,12 @@ static void threshold_restart_bank(void *_tr)
                    (new_count & THRESHOLD_MAX);
        }
 
+       /* clear IntType */
+       hi &= ~MASK_INT_TYPE_HI;
+
+       if (!tr->b->interrupt_capable)
+               goto done;
+
        if (tr->set_lvt_off) {
                if (lvt_off_valid(tr->b, tr->lvt_off, lo, hi)) {
                        /* set new lvt offset */
@@ -136,9 +160,10 @@ static void threshold_restart_bank(void *_tr)
                }
        }
 
-       tr->b->interrupt_enable ?
-           (hi = (hi & ~MASK_INT_TYPE_HI) | INT_TYPE_APIC) :
-           (hi &= ~MASK_INT_TYPE_HI);
+       if (tr->b->interrupt_enable)
+               hi |= INT_TYPE_APIC;
+
+ done:
 
        hi |= MASK_COUNT_EN_HI;
        wrmsr(tr->b->address, lo, hi);
@@ -202,14 +227,17 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
                        if (shared_bank[bank] && c->cpu_core_id)
                                break;
 
-                       offset = setup_APIC_mce(offset,
-                                               (high & MASK_LVTOFF_HI) >> 20);
-
                        memset(&b, 0, sizeof(b));
-                       b.cpu           = cpu;
-                       b.bank          = bank;
-                       b.block         = block;
-                       b.address       = address;
+                       b.cpu                   = cpu;
+                       b.bank                  = bank;
+                       b.block                 = block;
+                       b.address               = address;
+                       b.interrupt_capable     = lvt_interrupt_supported(bank, high);
+
+                       if (b.interrupt_capable) {
+                               int new = (high & MASK_LVTOFF_HI) >> 20;
+                               offset  = setup_APIC_mce(offset, new);
+                       }
 
                        mce_threshold_block_init(&b, offset);
                        mce_threshold_vector = amd_threshold_interrupt;
@@ -309,6 +337,9 @@ store_interrupt_enable(struct threshold_block *b, const char *buf, size_t size)
        struct thresh_restart tr;
        unsigned long new;
 
+       if (!b->interrupt_capable)
+               return -EINVAL;
+
        if (strict_strtoul(buf, 0, &new) < 0)
                return -EINVAL;
 
@@ -390,10 +421,10 @@ RW_ATTR(threshold_limit);
 RW_ATTR(error_count);
 
 static struct attribute *default_attrs[] = {
-       &interrupt_enable.attr,
        &threshold_limit.attr,
        &error_count.attr,
-       NULL
+       NULL,   /* possibly interrupt_enable if supported, see below */
+       NULL,
 };
 
 #define to_block(k)    container_of(k, struct threshold_block, kobj)
@@ -467,8 +498,14 @@ static __cpuinit int allocate_threshold_blocks(unsigned int cpu,
        b->cpu                  = cpu;
        b->address              = address;
        b->interrupt_enable     = 0;
+       b->interrupt_capable    = lvt_interrupt_supported(bank, high);
        b->threshold_limit      = THRESHOLD_MAX;
 
+       if (b->interrupt_capable)
+               threshold_ktype.default_attrs[2] = &interrupt_enable.attr;
+       else
+               threshold_ktype.default_attrs[2] = NULL;
+
        INIT_LIST_HEAD(&b->miscj);
 
        if (per_cpu(threshold_banks, cpu)[bank]->blocks) {
This page took 0.03392 seconds and 5 git commands to generate.