|
@@ -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) {
|