A three-set which makes the APIC LVT interrupt optional because a subset

of F15h models don't support it.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJPno0hAAoJEBLB8Bhh3lVKSJkP+gJTnMUOTzLgW47zuo/aadkk
 r8YxPWZ135Lijvv7fdB7TijT0X9W4v1oIwjzQPcULW88Oig63eo2O+TocYEANq4A
 Vj4CaLePGTPZ4tX7KT/qaafelyp58lx/FOJWDshhvvnCT6UqfhYlX+9Coj+SGPaA
 97PlDGWvMoVlpTQouurQjUqs2NQYelnw4eEPpAP0b1zi+DE2W0DuAcYE1cc44qqy
 GNJZr9ZdFZ7mdoDO0gcZp/WQVeDD7fCId2nrvi0wGRFoS+oXr8jbyNHGhrHu84DW
 cgr+OuHQXuNe+ZhRA3PpqCWIFdp9jT3/zDKR0NCDn2PdBmzLdpTDq8ZmXHQhEjHn
 gGH2pOVqgQbT9+2Tt1pzqjIG8iNMoj5C3WCoVPZ2TD+EHWv0Ao2MNyS3UGmE8EOu
 tplKu24mf1J+1SmuDcncyHGrU5KWE9p1WbM4YpSAiTFnz7Qs1GJzHmJOK4j2jK0O
 2IqPlPPsFSPMrU2JX7wjsmvoAIg4Q1FAjev8y1+bfcZW14YrcLMzBbmIgedb7zef
 nVnpCfaSa+bXJ8i2lh12+CKBZEfefU4aMOwPKWwe4ZwTzd1JVThfLhi0e2b76KZj
 ut2mez9iIXp9RrmUwj2L+hqT3McXgDqWG6OS8Or/wzGP5XIkCeHpMYuN9iF8jlfQ
 vhHVzbeALV7rpovpxQhu
 =WJEO
 -----END PGP SIGNATURE-----

Merge tag 'amd-thresholding-fixes-for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras into x86/mce

- 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>
This commit is contained in:
Ingo Molnar 2012-05-04 09:53:34 +02:00
commit 239e7bad43
2 changed files with 88 additions and 14 deletions

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

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