* [PATCH 0/2] apic, x86: Use BIOS settings to setup AMD EILVT APIC registers
@ 2010-10-06 10:27 Robert Richter
2010-10-06 10:27 ` [PATCH 1/2] apic, x86: Check if EILVT APIC registers are available (AMD only) Robert Richter
2010-10-06 10:27 ` [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets Robert Richter
0 siblings, 2 replies; 10+ messages in thread
From: Robert Richter @ 2010-10-06 10:27 UTC (permalink / raw)
To: Ingo Molnar; +Cc: LKML
This patch set changes the way of setting up EILVT APIC registers
(APIC500-530). See family 10h bkdg:
http://support.amd.com/us/Processor_TechDocs/31116.pdf
Until now, Linux has assigned fixed LVT offsets for IBS and MCE
threshold. With the introduction of new cpu families we want to be
more flexible and assign those LVT offsets dynamically. The general
apporach here is to let the BIOS decide which offsets to use. Linux
will then detect this by reading the corresponding hw registers that
contain the LVT offsets.
This requires to check if the BIOS is correctly setting up the LVT
offsets. Otherwise a firmware bug message will be thrown. This is
implemented in patch #1.
Patch #2 implements the detection of MCE threshold and IBS LVT offsets
and changes the subsystem initialization. The code to setup the IBS
LVT offset on family 10h systems will remain as a workaround that will
be only applied in case of an invalid IBS LVT BIOS setup.
-Robert
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] apic, x86: Check if EILVT APIC registers are available (AMD only)
2010-10-06 10:27 [PATCH 0/2] apic, x86: Use BIOS settings to setup AMD EILVT APIC registers Robert Richter
@ 2010-10-06 10:27 ` Robert Richter
2010-10-20 5:01 ` [tip:irq/core] " tip-bot for Robert Richter
2010-10-06 10:27 ` [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets Robert Richter
1 sibling, 1 reply; 10+ messages in thread
From: Robert Richter @ 2010-10-06 10:27 UTC (permalink / raw)
To: Ingo Molnar; +Cc: LKML, Robert Richter
This patch implements checks for the availability of LVT entries
(APIC500-530) and reserves it if used. The check becomes necessary
since we want to let the BIOS provide the LVT offsets. The offsets
should be determined by the subsystems using it like those for MCE
threshold or IBS. On K8 only offset 0 (APIC500) and MCE interrupts
are supported. Beginning with family 10h at least 4 offsets are
available.
Since offsets must be consistent for all cores, we keep track of the
LVT offsets in software and reserve the offset for the same vector
also to be used on other cores. An offset is freed by setting the
entry to APIC_EILVT_MASKED.
If the BIOS is right, there should be no conflicts. Otherwise a
"[Firmware Bug]: ..." error message is generated. However, if software
does not properly determines the offsets, it is not necessarily a BIOS
bug.
Signed-off-by: Robert Richter <robert.richter@amd.com>
---
arch/x86/include/asm/apicdef.h | 1 +
arch/x86/kernel/apic/apic.c | 83 +++++++++++++++++++++++++++++++++++----
2 files changed, 75 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h
index 7fe3b30..a859ca4 100644
--- a/arch/x86/include/asm/apicdef.h
+++ b/arch/x86/include/asm/apicdef.h
@@ -131,6 +131,7 @@
#define APIC_EILVTn(n) (0x500 + 0x10 * n)
#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */
#define APIC_EILVT_NR_AMD_10H 4
+#define APIC_EILVT_NR_MAX APIC_EILVT_NR_AMD_10H
#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF)
#define APIC_EILVT_MSG_FIX 0x0
#define APIC_EILVT_MSG_SMI 0x2
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index e3b534c..2876f95 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -52,6 +52,7 @@
#include <asm/mce.h>
#include <asm/kvm_para.h>
#include <asm/tsc.h>
+#include <asm/atomic.h>
unsigned int num_processors;
@@ -370,24 +371,88 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
}
/*
- * Setup extended LVT, AMD specific (K8, family 10h)
+ * Setup extended LVT, AMD specific
*
- * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
- * MCE interrupts are supported. Thus MCE offset must be set to 0.
+ * Software should use the LVT offsets the BIOS provides. The offsets
+ * are determined by the subsystems using it like those for MCE
+ * threshold or IBS. On K8 only offset 0 (APIC500) and MCE interrupts
+ * are supported. Beginning with family 10h at least 4 offsets are
+ * available.
*
- * If mask=1, the LVT entry does not generate interrupts while mask=0
- * enables the vector. See also the BKDGs.
+ * Since the offsets must be consistent for all cores, we keep track
+ * of the LVT offsets in software and reserve the offset for the same
+ * vector also to be used on other cores. An offset is freed by
+ * setting the entry to APIC_EILVT_MASKED.
+ *
+ * If the BIOS is right, there should be no conflicts. Otherwise a
+ * "[Firmware Bug]: ..." error message is generated. However, if
+ * software does not properly determines the offsets, it is not
+ * necessarily a BIOS bug.
*/
#define APIC_EILVT_LVTOFF_MCE 0
#define APIC_EILVT_LVTOFF_IBS 1
-static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask)
+static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
+
+static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
+{
+ return (old & APIC_EILVT_MASKED)
+ || (new == APIC_EILVT_MASKED)
+ || ((new & ~APIC_EILVT_MASKED) == old);
+}
+
+static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
+{
+ unsigned int rsvd; /* 0: uninitialized */
+
+ if (offset >= APIC_EILVT_NR_MAX)
+ return ~0;
+
+ rsvd = atomic_read(&eilvt_offsets[offset]) & ~APIC_EILVT_MASKED;
+ do {
+ if (rsvd &&
+ !eilvt_entry_is_changeable(rsvd, new))
+ /* may not change if vectors are different */
+ return rsvd;
+ rsvd = atomic_cmpxchg(&eilvt_offsets[offset], rsvd, new);
+ } while (rsvd != new);
+
+ return new;
+}
+
+/*
+ * If mask=1, the LVT entry does not generate interrupts while mask=0
+ * enables the vector. See also the BKDGs.
+ */
+
+static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
{
- unsigned long reg = (lvt_off << 4) + APIC_EILVTn(0);
- unsigned int v = (mask << 16) | (msg_type << 8) | vector;
+ unsigned long reg = APIC_EILVTn(offset);
+ unsigned int new, old, reserved;
+
+ new = (mask << 16) | (msg_type << 8) | vector;
+ old = apic_read(reg);
+ reserved = reserve_eilvt_offset(offset, new);
- apic_write(reg, v);
+ if (reserved != new) {
+ pr_err(FW_BUG "cpu %d, try to setup vector 0x%x, but "
+ "vector 0x%x was already reserved by another core, "
+ "APIC%lX=0x%x\n",
+ smp_processor_id(), new, reserved, reg, old);
+ return -EINVAL;
+ }
+
+ if (!eilvt_entry_is_changeable(old, new)) {
+ pr_err(FW_BUG "cpu %d, try to setup vector 0x%x but "
+ "register already in use, APIC%lX=0x%x\n",
+ smp_processor_id(), new, reg, old);
+ return -EBUSY;
+ }
+
+ apic_write(reg, new);
+
+ return 0;
}
u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
--
1.7.2.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-06 10:27 [PATCH 0/2] apic, x86: Use BIOS settings to setup AMD EILVT APIC registers Robert Richter
2010-10-06 10:27 ` [PATCH 1/2] apic, x86: Check if EILVT APIC registers are available (AMD only) Robert Richter
@ 2010-10-06 10:27 ` Robert Richter
2010-10-06 19:41 ` Cyrill Gorcunov
2010-10-20 5:01 ` [tip:irq/core] " tip-bot for Robert Richter
1 sibling, 2 replies; 10+ messages in thread
From: Robert Richter @ 2010-10-06 10:27 UTC (permalink / raw)
To: Ingo Molnar; +Cc: LKML, Robert Richter
We want the BIOS to setup the EILVT APIC registers. The offsets were
hardcoded and BIOS settings were overwritten by the OS. Now, the
subsystems for MCE threshold and IBS determine the LVT offset from the
registers the BIOS has setup. If the BIOS setup is buggy on a family
10h system, a workaround enables IBS. If the OS determines an invalid
register setup, a "[Firmware Bug]: " error message is reported.
We need this change also for upcomming cpu families.
Signed-off-by: Robert Richter <robert.richter@amd.com>
---
arch/x86/include/asm/apic.h | 4 +-
arch/x86/kernel/apic/apic.c | 19 +----
arch/x86/kernel/cpu/mcheck/mce_amd.c | 27 ++++++-
arch/x86/oprofile/op_model_amd.c | 145 +++++++++++++++++++++++++++++----
4 files changed, 154 insertions(+), 41 deletions(-)
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 1fa03e0..286de34 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -252,9 +252,7 @@ static inline int apic_is_clustered_box(void)
}
#endif
-extern u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask);
-extern u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask);
-
+extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask);
#else /* !CONFIG_X86_LOCAL_APIC */
static inline void lapic_shutdown(void) { }
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 2876f95..06ec876 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -390,9 +390,6 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
* necessarily a BIOS bug.
*/
-#define APIC_EILVT_LVTOFF_MCE 0
-#define APIC_EILVT_LVTOFF_IBS 1
-
static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
@@ -426,7 +423,7 @@ static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
* enables the vector. See also the BKDGs.
*/
-static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
+int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
{
unsigned long reg = APIC_EILVTn(offset);
unsigned int new, old, reserved;
@@ -454,19 +451,7 @@ static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
return 0;
}
-
-u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_MCE;
-}
-
-u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_IBS;
-}
-EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
+EXPORT_SYMBOL_GPL(setup_APIC_eilvt);
/*
* Program the next event, relative to now
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 5e97529..e13d4bd 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
u32 low = 0, high = 0, address = 0;
unsigned int bank, block;
struct thresh_restart tr;
- u8 lvt_off;
+ int lvt_off = -1;
+ u8 offset;
for (bank = 0; bank < NR_BANKS; ++bank) {
for (block = 0; block < NR_BLOCKS; ++block) {
@@ -165,8 +166,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
if (shared_bank[bank] && c->cpu_core_id)
break;
#endif
- lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR,
- APIC_EILVT_MSG_FIX, 0);
+ offset = (high & MASK_LVTOFF_HI) >> 20;
+ if (lvt_off < 0) {
+ if (setup_APIC_eilvt(offset,
+ THRESHOLD_APIC_VECTOR,
+ APIC_EILVT_MSG_FIX, 0)) {
+ pr_err(FW_BUG "cpu %d, failed to "
+ "setup threshold interrupt "
+ "for bank %d, block %d "
+ "(MSR%08X=0x%x%08x)",
+ smp_processor_id(), bank, block,
+ address, high, low);
+ continue;
+ }
+ lvt_off = offset;
+ } else if (lvt_off != offset) {
+ pr_err(FW_BUG "cpu %d, invalid threshold "
+ "interrupt offset %d for bank %d,"
+ "block %d (MSR%08X=0x%x%08x)",
+ smp_processor_id(), lvt_off, bank,
+ block, address, high, low);
+ continue;
+ }
high &= ~MASK_LVTOFF_HI;
high |= lvt_off << 20;
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
index b67a6b5..42fb46f 100644
--- a/arch/x86/oprofile/op_model_amd.c
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -64,15 +64,22 @@ static u64 ibs_op_ctl;
* IBS cpuid feature detection
*/
-#define IBS_CPUID_FEATURES 0x8000001b
+#define IBS_CPUID_FEATURES 0x8000001b
/*
* Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but
* bit 0 is used to indicate the existence of IBS.
*/
-#define IBS_CAPS_AVAIL (1LL<<0)
-#define IBS_CAPS_RDWROPCNT (1LL<<3)
-#define IBS_CAPS_OPCNT (1LL<<4)
+#define IBS_CAPS_AVAIL (1U<<0)
+#define IBS_CAPS_RDWROPCNT (1U<<3)
+#define IBS_CAPS_OPCNT (1U<<4)
+
+/*
+ * IBS APIC setup
+ */
+#define IBSCTL 0x1cc
+#define IBSCTL_LVT_OFFSET_VALID (1ULL<<8)
+#define IBSCTL_LVT_OFFSET_MASK 0x0F
/*
* IBS randomization macros
@@ -266,6 +273,74 @@ static void op_amd_stop_ibs(void)
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
}
+static inline int eilvt_is_available(int offset)
+{
+ /* check if we may assign a vector */
+ return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
+}
+
+static inline int ibs_eilvt_valid(void)
+{
+ u64 val;
+ int offset;
+
+ rdmsrl(MSR_AMD64_IBSCTL, val);
+ if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
+ pr_err(FW_BUG "cpu %d, invalid IBS "
+ "interrupt offset %d (MSR%08X=0x%016llx)",
+ smp_processor_id(), offset,
+ MSR_AMD64_IBSCTL, val);
+ return 0;
+ }
+
+ offset = val & IBSCTL_LVT_OFFSET_MASK;
+
+ if (eilvt_is_available(offset))
+ return !0;
+
+ pr_err(FW_BUG "cpu %d, IBS interrupt offset %d "
+ "not available (MSR%08X=0x%016llx)",
+ smp_processor_id(), offset,
+ MSR_AMD64_IBSCTL, val);
+
+ return 0;
+}
+
+static inline int get_ibs_offset(void)
+{
+ u64 val;
+
+ rdmsrl(MSR_AMD64_IBSCTL, val);
+ if (!(val & IBSCTL_LVT_OFFSET_VALID))
+ return -EINVAL;
+
+ return val & IBSCTL_LVT_OFFSET_MASK;
+}
+
+static void setup_APIC_ibs(void)
+{
+ int offset;
+
+ offset = get_ibs_offset();
+ if (offset < 0)
+ goto failed;
+
+ if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0))
+ return;
+failed:
+ pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n",
+ smp_processor_id());
+}
+
+static void clear_APIC_ibs(void)
+{
+ int offset;
+
+ offset = get_ibs_offset();
+ if (offset >= 0)
+ setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);
+}
+
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
static void op_mux_switch_ctrl(struct op_x86_model_spec const *model,
@@ -376,13 +451,13 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
}
if (ibs_caps)
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
+ setup_APIC_ibs();
}
static void op_amd_cpu_shutdown(void)
{
if (ibs_caps)
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
+ clear_APIC_ibs();
}
static int op_amd_check_ctrs(struct pt_regs * const regs,
@@ -445,16 +520,11 @@ static void op_amd_stop(struct op_msrs const * const msrs)
op_amd_stop_ibs();
}
-static int __init_ibs_nmi(void)
+static int setup_ibs_ctl(int ibs_eilvt_off)
{
-#define IBSCTL_LVTOFFSETVAL (1 << 8)
-#define IBSCTL 0x1cc
struct pci_dev *cpu_cfg;
int nodes;
u32 value = 0;
- u8 ibs_eilvt_off;
-
- ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
nodes = 0;
cpu_cfg = NULL;
@@ -466,21 +536,60 @@ static int __init_ibs_nmi(void)
break;
++nodes;
pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
- | IBSCTL_LVTOFFSETVAL);
+ | IBSCTL_LVT_OFFSET_VALID);
pci_read_config_dword(cpu_cfg, IBSCTL, &value);
- if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
+ if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) {
pci_dev_put(cpu_cfg);
printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
- "IBSCTL = 0x%08x", value);
- return 1;
+ "IBSCTL = 0x%08x\n", value);
+ return -EINVAL;
}
} while (1);
if (!nodes) {
- printk(KERN_DEBUG "No CPU node configured for IBS");
- return 1;
+ printk(KERN_DEBUG "No CPU node configured for IBS\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int force_ibs_eilvt_setup(void)
+{
+ int i;
+ int ret;
+
+ /* find the next free available EILVT entry */
+ for (i = 1; i < 4; i++) {
+ if (!eilvt_is_available(i))
+ continue;
+ ret = setup_ibs_ctl(i);
+ if (ret)
+ return ret;
+ return 0;
}
+ printk(KERN_DEBUG "No EILVT entry available\n");
+
+ return -EBUSY;
+}
+
+static int __init_ibs_nmi(void)
+{
+ int ret;
+
+ if (ibs_eilvt_valid())
+ return 0;
+
+ ret = force_ibs_eilvt_setup();
+ if (ret)
+ return ret;
+
+ if (!ibs_eilvt_valid())
+ return -EFAULT;
+
+ pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
+
return 0;
}
--
1.7.2.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-06 10:27 ` [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets Robert Richter
@ 2010-10-06 19:41 ` Cyrill Gorcunov
2010-10-08 9:24 ` Robert Richter
2010-10-20 5:01 ` [tip:irq/core] " tip-bot for Robert Richter
1 sibling, 1 reply; 10+ messages in thread
From: Cyrill Gorcunov @ 2010-10-06 19:41 UTC (permalink / raw)
To: Robert Richter; +Cc: Ingo Molnar, LKML
On Wed, Oct 06, 2010 at 12:27:54PM +0200, Robert Richter wrote:
> We want the BIOS to setup the EILVT APIC registers. The offsets were
> hardcoded and BIOS settings were overwritten by the OS. Now, the
> subsystems for MCE threshold and IBS determine the LVT offset from the
> registers the BIOS has setup. If the BIOS setup is buggy on a family
> 10h system, a workaround enables IBS. If the OS determines an invalid
> register setup, a "[Firmware Bug]: " error message is reported.
>
> We need this change also for upcomming cpu families.
>
> Signed-off-by: Robert Richter <robert.richter@amd.com>
> ---
Hi Robert, a few comments
...
> /*
> * Program the next event, relative to now
> diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
> index 5e97529..e13d4bd 100644
> --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
> +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
> @@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
> u32 low = 0, high = 0, address = 0;
> unsigned int bank, block;
> struct thresh_restart tr;
> - u8 lvt_off;
> + int lvt_off = -1;
> + u8 offset;
>
> for (bank = 0; bank < NR_BANKS; ++bank) {
> for (block = 0; block < NR_BLOCKS; ++block) {
> @@ -165,8 +166,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
> if (shared_bank[bank] && c->cpu_core_id)
> break;
> #endif
> - lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR,
> - APIC_EILVT_MSG_FIX, 0);
> + offset = (high & MASK_LVTOFF_HI) >> 20;
> + if (lvt_off < 0) {
> + if (setup_APIC_eilvt(offset,
> + THRESHOLD_APIC_VECTOR,
> + APIC_EILVT_MSG_FIX, 0)) {
> + pr_err(FW_BUG "cpu %d, failed to "
> + "setup threshold interrupt "
> + "for bank %d, block %d "
> + "(MSR%08X=0x%x%08x)",
> + smp_processor_id(), bank, block,
> + address, high, low);
> + continue;
> + }
> + lvt_off = offset;
> + } else if (lvt_off != offset) {
Could we put explicit type specificator here? For better readbility.
...
> +static int force_ibs_eilvt_setup(void)
> +{
> + int i;
> + int ret;
> +
> + /* find the next free available EILVT entry */
> + for (i = 1; i < 4; i++) {
APIC_EILVT_NR_MAX here, no?
> + if (!eilvt_is_available(i))
> + continue;
> + ret = setup_ibs_ctl(i);
> + if (ret)
> + return ret;
> + return 0;
> }
>
Cyrill
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-06 19:41 ` Cyrill Gorcunov
@ 2010-10-08 9:24 ` Robert Richter
2010-10-08 9:37 ` Cyrill Gorcunov
0 siblings, 1 reply; 10+ messages in thread
From: Robert Richter @ 2010-10-08 9:24 UTC (permalink / raw)
To: Cyrill Gorcunov; +Cc: Ingo Molnar, LKML
On 06.10.10 15:41:50, Cyrill Gorcunov wrote:
> On Wed, Oct 06, 2010 at 12:27:54PM +0200, Robert Richter wrote:
> > We want the BIOS to setup the EILVT APIC registers. The offsets were
> > hardcoded and BIOS settings were overwritten by the OS. Now, the
> > subsystems for MCE threshold and IBS determine the LVT offset from the
> > registers the BIOS has setup. If the BIOS setup is buggy on a family
> > 10h system, a workaround enables IBS. If the OS determines an invalid
> > register setup, a "[Firmware Bug]: " error message is reported.
> >
> > We need this change also for upcomming cpu families.
> >
> > Signed-off-by: Robert Richter <robert.richter@amd.com>
> > ---
>
> Hi Robert, a few comments
>
> ...
> > /*
> > * Program the next event, relative to now
> > diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
> > index 5e97529..e13d4bd 100644
> > --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
> > +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
> > @@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
> > u32 low = 0, high = 0, address = 0;
> > unsigned int bank, block;
> > struct thresh_restart tr;
> > - u8 lvt_off;
> > + int lvt_off = -1;
> > + u8 offset;
> >
> > for (bank = 0; bank < NR_BANKS; ++bank) {
> > for (block = 0; block < NR_BLOCKS; ++block) {
> > @@ -165,8 +166,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
> > if (shared_bank[bank] && c->cpu_core_id)
> > break;
> > #endif
> > - lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR,
> > - APIC_EILVT_MSG_FIX, 0);
> > + offset = (high & MASK_LVTOFF_HI) >> 20;
> > + if (lvt_off < 0) {
> > + if (setup_APIC_eilvt(offset,
> > + THRESHOLD_APIC_VECTOR,
> > + APIC_EILVT_MSG_FIX, 0)) {
> > + pr_err(FW_BUG "cpu %d, failed to "
> > + "setup threshold interrupt "
> > + "for bank %d, block %d "
> > + "(MSR%08X=0x%x%08x)",
> > + smp_processor_id(), bank, block,
> > + address, high, low);
> > + continue;
> > + }
> > + lvt_off = offset;
> > + } else if (lvt_off != offset) {
>
> Could we put explicit type specificator here? For better readbility.
> ...
Cyrill,
Do you mean an explicit type cast here, or something else?
>
> > +static int force_ibs_eilvt_setup(void)
> > +{
> > + int i;
> > + int ret;
> > +
> > + /* find the next free available EILVT entry */
> > + for (i = 1; i < 4; i++) {
>
> APIC_EILVT_NR_MAX here, no?
Yes, will update this if a -v2 patch set will be necessary.
Thanks for review,
-Robert
>
> > + if (!eilvt_is_available(i))
> > + continue;
> > + ret = setup_ibs_ctl(i);
> > + if (ret)
> > + return ret;
> > + return 0;
> > }
> >
>
> Cyrill
>
--
Advanced Micro Devices, Inc.
Operating System Research Center
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-08 9:24 ` Robert Richter
@ 2010-10-08 9:37 ` Cyrill Gorcunov
2010-10-08 10:21 ` Robert Richter
0 siblings, 1 reply; 10+ messages in thread
From: Cyrill Gorcunov @ 2010-10-08 9:37 UTC (permalink / raw)
To: Robert Richter; +Cc: Ingo Molnar, LKML
On Fri, Oct 8, 2010 at 1:24 PM, Robert Richter <robert.richter@amd.com> wrote:
...
>> > + } else if (lvt_off != offset) {
>>
>> Could we put explicit type specificator here? For better readbility.
>> ...
>
> Cyrill,
>
> Do you mean an explicit type cast here, or something else?
>
Yeah, explicit type cast (not a big deal though since at moment
of course nothing can fire in this snippet, but I think for future
modifications better to have explicit cast).
>
> Thanks for review,
>
> -Robert
>
Cyrill
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-08 9:37 ` Cyrill Gorcunov
@ 2010-10-08 10:21 ` Robert Richter
2010-10-08 18:27 ` Cyrill Gorcunov
0 siblings, 1 reply; 10+ messages in thread
From: Robert Richter @ 2010-10-08 10:21 UTC (permalink / raw)
To: Cyrill Gorcunov; +Cc: Ingo Molnar, LKML
On 08.10.10 05:37:24, Cyrill Gorcunov wrote:
> On Fri, Oct 8, 2010 at 1:24 PM, Robert Richter <robert.richter@amd.com> wrote:
> ...
> >> > + } else if (lvt_off != offset) {
> >>
> >> Could we put explicit type specificator here? For better readbility.
> >> ...
> >
> > Cyrill,
> >
> > Do you mean an explicit type cast here, or something else?
> >
>
> Yeah, explicit type cast (not a big deal though since at moment
> of course nothing can fire in this snippet, but I think for future
> modifications better to have explicit cast).
Hmm, don't think this is really necessary. As an alternative we could
make variable offset an int too and then do an explicit cast when
calling setup_APIC_eilvt().
But I rather tend to leave it as it is.
-Robert
>
> >
> > Thanks for review,
> >
> > -Robert
> >
>
> Cyrill
>
--
Advanced Micro Devices, Inc.
Operating System Research Center
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-08 10:21 ` Robert Richter
@ 2010-10-08 18:27 ` Cyrill Gorcunov
0 siblings, 0 replies; 10+ messages in thread
From: Cyrill Gorcunov @ 2010-10-08 18:27 UTC (permalink / raw)
To: Robert Richter; +Cc: Ingo Molnar, LKML
On Fri, Oct 08, 2010 at 12:21:15PM +0200, Robert Richter wrote:
> On 08.10.10 05:37:24, Cyrill Gorcunov wrote:
> > On Fri, Oct 8, 2010 at 1:24 PM, Robert Richter <robert.richter@amd.com> wrote:
> > ...
> > >> > + } else if (lvt_off != offset) {
> > >>
> > >> Could we put explicit type specificator here? For better readbility.
> > >> ...
> > >
> > > Cyrill,
> > >
> > > Do you mean an explicit type cast here, or something else?
> > >
> >
> > Yeah, explicit type cast (not a big deal though since at moment
> > of course nothing can fire in this snippet, but I think for future
> > modifications better to have explicit cast).
>
> Hmm, don't think this is really necessary. As an alternative we could
> make variable offset an int too and then do an explicit cast when
> calling setup_APIC_eilvt().
>
> But I rather tend to leave it as it is.
Well, I don't insist at all. Lets leave it as is.
Cyrill
^ permalink raw reply [flat|nested] 10+ messages in thread
* [tip:irq/core] apic, x86: Check if EILVT APIC registers are available (AMD only)
2010-10-06 10:27 ` [PATCH 1/2] apic, x86: Check if EILVT APIC registers are available (AMD only) Robert Richter
@ 2010-10-20 5:01 ` tip-bot for Robert Richter
0 siblings, 0 replies; 10+ messages in thread
From: tip-bot for Robert Richter @ 2010-10-20 5:01 UTC (permalink / raw)
To: linux-tip-commits; +Cc: linux-kernel, hpa, mingo, robert.richter, tglx, mingo
Commit-ID: a68c439b1966c91f0ef474e2bf275d6792312726
Gitweb: http://git.kernel.org/tip/a68c439b1966c91f0ef474e2bf275d6792312726
Author: Robert Richter <robert.richter@amd.com>
AuthorDate: Wed, 6 Oct 2010 12:27:53 +0200
Committer: Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 20 Oct 2010 04:42:13 +0200
apic, x86: Check if EILVT APIC registers are available (AMD only)
This patch implements checks for the availability of LVT entries
(APIC500-530) and reserves it if used. The check becomes
necessary since we want to let the BIOS provide the LVT offsets.
The offsets should be determined by the subsystems using it
like those for MCE threshold or IBS. On K8 only offset 0
(APIC500) and MCE interrupts are supported. Beginning with
family 10h at least 4 offsets are available.
Since offsets must be consistent for all cores, we keep track of
the LVT offsets in software and reserve the offset for the same
vector also to be used on other cores. An offset is freed by
setting the entry to APIC_EILVT_MASKED.
If the BIOS is right, there should be no conflicts. Otherwise a
"[Firmware Bug]: ..." error message is generated. However, if
software does not properly determines the offsets, it is not
necessarily a BIOS bug.
Signed-off-by: Robert Richter <robert.richter@amd.com>
LKML-Reference: <1286360874-1471-2-git-send-email-robert.richter@amd.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
arch/x86/include/asm/apicdef.h | 1 +
arch/x86/kernel/apic/apic.c | 83 +++++++++++++++++++++++++++++++++++----
2 files changed, 75 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h
index 7fe3b30..a859ca4 100644
--- a/arch/x86/include/asm/apicdef.h
+++ b/arch/x86/include/asm/apicdef.h
@@ -131,6 +131,7 @@
#define APIC_EILVTn(n) (0x500 + 0x10 * n)
#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */
#define APIC_EILVT_NR_AMD_10H 4
+#define APIC_EILVT_NR_MAX APIC_EILVT_NR_AMD_10H
#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF)
#define APIC_EILVT_MSG_FIX 0x0
#define APIC_EILVT_MSG_SMI 0x2
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 8cf86fb..2bfeafd 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -52,6 +52,7 @@
#include <asm/mce.h>
#include <asm/kvm_para.h>
#include <asm/tsc.h>
+#include <asm/atomic.h>
unsigned int num_processors;
@@ -370,24 +371,88 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
}
/*
- * Setup extended LVT, AMD specific (K8, family 10h)
+ * Setup extended LVT, AMD specific
*
- * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
- * MCE interrupts are supported. Thus MCE offset must be set to 0.
+ * Software should use the LVT offsets the BIOS provides. The offsets
+ * are determined by the subsystems using it like those for MCE
+ * threshold or IBS. On K8 only offset 0 (APIC500) and MCE interrupts
+ * are supported. Beginning with family 10h at least 4 offsets are
+ * available.
*
- * If mask=1, the LVT entry does not generate interrupts while mask=0
- * enables the vector. See also the BKDGs.
+ * Since the offsets must be consistent for all cores, we keep track
+ * of the LVT offsets in software and reserve the offset for the same
+ * vector also to be used on other cores. An offset is freed by
+ * setting the entry to APIC_EILVT_MASKED.
+ *
+ * If the BIOS is right, there should be no conflicts. Otherwise a
+ * "[Firmware Bug]: ..." error message is generated. However, if
+ * software does not properly determines the offsets, it is not
+ * necessarily a BIOS bug.
*/
#define APIC_EILVT_LVTOFF_MCE 0
#define APIC_EILVT_LVTOFF_IBS 1
-static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask)
+static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
+
+static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
+{
+ return (old & APIC_EILVT_MASKED)
+ || (new == APIC_EILVT_MASKED)
+ || ((new & ~APIC_EILVT_MASKED) == old);
+}
+
+static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
+{
+ unsigned int rsvd; /* 0: uninitialized */
+
+ if (offset >= APIC_EILVT_NR_MAX)
+ return ~0;
+
+ rsvd = atomic_read(&eilvt_offsets[offset]) & ~APIC_EILVT_MASKED;
+ do {
+ if (rsvd &&
+ !eilvt_entry_is_changeable(rsvd, new))
+ /* may not change if vectors are different */
+ return rsvd;
+ rsvd = atomic_cmpxchg(&eilvt_offsets[offset], rsvd, new);
+ } while (rsvd != new);
+
+ return new;
+}
+
+/*
+ * If mask=1, the LVT entry does not generate interrupts while mask=0
+ * enables the vector. See also the BKDGs.
+ */
+
+static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
{
- unsigned long reg = (lvt_off << 4) + APIC_EILVTn(0);
- unsigned int v = (mask << 16) | (msg_type << 8) | vector;
+ unsigned long reg = APIC_EILVTn(offset);
+ unsigned int new, old, reserved;
+
+ new = (mask << 16) | (msg_type << 8) | vector;
+ old = apic_read(reg);
+ reserved = reserve_eilvt_offset(offset, new);
- apic_write(reg, v);
+ if (reserved != new) {
+ pr_err(FW_BUG "cpu %d, try to setup vector 0x%x, but "
+ "vector 0x%x was already reserved by another core, "
+ "APIC%lX=0x%x\n",
+ smp_processor_id(), new, reserved, reg, old);
+ return -EINVAL;
+ }
+
+ if (!eilvt_entry_is_changeable(old, new)) {
+ pr_err(FW_BUG "cpu %d, try to setup vector 0x%x but "
+ "register already in use, APIC%lX=0x%x\n",
+ smp_processor_id(), new, reg, old);
+ return -EBUSY;
+ }
+
+ apic_write(reg, new);
+
+ return 0;
}
u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [tip:irq/core] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
2010-10-06 10:27 ` [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets Robert Richter
2010-10-06 19:41 ` Cyrill Gorcunov
@ 2010-10-20 5:01 ` tip-bot for Robert Richter
1 sibling, 0 replies; 10+ messages in thread
From: tip-bot for Robert Richter @ 2010-10-20 5:01 UTC (permalink / raw)
To: linux-tip-commits; +Cc: linux-kernel, hpa, mingo, robert.richter, tglx, mingo
Commit-ID: 27afdf2008da0b8878a73e32e4eb12381b84e224
Gitweb: http://git.kernel.org/tip/27afdf2008da0b8878a73e32e4eb12381b84e224
Author: Robert Richter <robert.richter@amd.com>
AuthorDate: Wed, 6 Oct 2010 12:27:54 +0200
Committer: Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 20 Oct 2010 04:42:13 +0200
apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
We want the BIOS to setup the EILVT APIC registers. The offsets
were hardcoded and BIOS settings were overwritten by the OS.
Now, the subsystems for MCE threshold and IBS determine the LVT
offset from the registers the BIOS has setup. If the BIOS setup
is buggy on a family 10h system, a workaround enables IBS. If
the OS determines an invalid register setup, a "[Firmware Bug]:
" error message is reported.
We need this change also for upcomming cpu families.
Signed-off-by: Robert Richter <robert.richter@amd.com>
LKML-Reference: <1286360874-1471-3-git-send-email-robert.richter@amd.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
arch/x86/include/asm/apic.h | 4 +-
arch/x86/kernel/apic/apic.c | 19 +----
arch/x86/kernel/cpu/mcheck/mce_amd.c | 27 ++++++-
arch/x86/oprofile/op_model_amd.c | 145 +++++++++++++++++++++++++++++----
4 files changed, 154 insertions(+), 41 deletions(-)
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 1fa03e0..286de34 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -252,9 +252,7 @@ static inline int apic_is_clustered_box(void)
}
#endif
-extern u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask);
-extern u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask);
-
+extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask);
#else /* !CONFIG_X86_LOCAL_APIC */
static inline void lapic_shutdown(void) { }
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 2bfeafd..850657d 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -390,9 +390,6 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
* necessarily a BIOS bug.
*/
-#define APIC_EILVT_LVTOFF_MCE 0
-#define APIC_EILVT_LVTOFF_IBS 1
-
static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
@@ -426,7 +423,7 @@ static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
* enables the vector. See also the BKDGs.
*/
-static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
+int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
{
unsigned long reg = APIC_EILVTn(offset);
unsigned int new, old, reserved;
@@ -454,19 +451,7 @@ static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
return 0;
}
-
-u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_MCE;
-}
-
-u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_IBS;
-}
-EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
+EXPORT_SYMBOL_GPL(setup_APIC_eilvt);
/*
* Program the next event, relative to now
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 39aaee5..80c4823 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
u32 low = 0, high = 0, address = 0;
unsigned int bank, block;
struct thresh_restart tr;
- u8 lvt_off;
+ int lvt_off = -1;
+ u8 offset;
for (bank = 0; bank < NR_BANKS; ++bank) {
for (block = 0; block < NR_BLOCKS; ++block) {
@@ -162,8 +163,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
if (shared_bank[bank] && c->cpu_core_id)
break;
#endif
- lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR,
- APIC_EILVT_MSG_FIX, 0);
+ offset = (high & MASK_LVTOFF_HI) >> 20;
+ if (lvt_off < 0) {
+ if (setup_APIC_eilvt(offset,
+ THRESHOLD_APIC_VECTOR,
+ APIC_EILVT_MSG_FIX, 0)) {
+ pr_err(FW_BUG "cpu %d, failed to "
+ "setup threshold interrupt "
+ "for bank %d, block %d "
+ "(MSR%08X=0x%x%08x)",
+ smp_processor_id(), bank, block,
+ address, high, low);
+ continue;
+ }
+ lvt_off = offset;
+ } else if (lvt_off != offset) {
+ pr_err(FW_BUG "cpu %d, invalid threshold "
+ "interrupt offset %d for bank %d,"
+ "block %d (MSR%08X=0x%x%08x)",
+ smp_processor_id(), lvt_off, bank,
+ block, address, high, low);
+ continue;
+ }
high &= ~MASK_LVTOFF_HI;
high |= lvt_off << 20;
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
index b67a6b5..42fb46f 100644
--- a/arch/x86/oprofile/op_model_amd.c
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -64,15 +64,22 @@ static u64 ibs_op_ctl;
* IBS cpuid feature detection
*/
-#define IBS_CPUID_FEATURES 0x8000001b
+#define IBS_CPUID_FEATURES 0x8000001b
/*
* Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but
* bit 0 is used to indicate the existence of IBS.
*/
-#define IBS_CAPS_AVAIL (1LL<<0)
-#define IBS_CAPS_RDWROPCNT (1LL<<3)
-#define IBS_CAPS_OPCNT (1LL<<4)
+#define IBS_CAPS_AVAIL (1U<<0)
+#define IBS_CAPS_RDWROPCNT (1U<<3)
+#define IBS_CAPS_OPCNT (1U<<4)
+
+/*
+ * IBS APIC setup
+ */
+#define IBSCTL 0x1cc
+#define IBSCTL_LVT_OFFSET_VALID (1ULL<<8)
+#define IBSCTL_LVT_OFFSET_MASK 0x0F
/*
* IBS randomization macros
@@ -266,6 +273,74 @@ static void op_amd_stop_ibs(void)
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
}
+static inline int eilvt_is_available(int offset)
+{
+ /* check if we may assign a vector */
+ return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
+}
+
+static inline int ibs_eilvt_valid(void)
+{
+ u64 val;
+ int offset;
+
+ rdmsrl(MSR_AMD64_IBSCTL, val);
+ if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
+ pr_err(FW_BUG "cpu %d, invalid IBS "
+ "interrupt offset %d (MSR%08X=0x%016llx)",
+ smp_processor_id(), offset,
+ MSR_AMD64_IBSCTL, val);
+ return 0;
+ }
+
+ offset = val & IBSCTL_LVT_OFFSET_MASK;
+
+ if (eilvt_is_available(offset))
+ return !0;
+
+ pr_err(FW_BUG "cpu %d, IBS interrupt offset %d "
+ "not available (MSR%08X=0x%016llx)",
+ smp_processor_id(), offset,
+ MSR_AMD64_IBSCTL, val);
+
+ return 0;
+}
+
+static inline int get_ibs_offset(void)
+{
+ u64 val;
+
+ rdmsrl(MSR_AMD64_IBSCTL, val);
+ if (!(val & IBSCTL_LVT_OFFSET_VALID))
+ return -EINVAL;
+
+ return val & IBSCTL_LVT_OFFSET_MASK;
+}
+
+static void setup_APIC_ibs(void)
+{
+ int offset;
+
+ offset = get_ibs_offset();
+ if (offset < 0)
+ goto failed;
+
+ if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0))
+ return;
+failed:
+ pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n",
+ smp_processor_id());
+}
+
+static void clear_APIC_ibs(void)
+{
+ int offset;
+
+ offset = get_ibs_offset();
+ if (offset >= 0)
+ setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);
+}
+
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
static void op_mux_switch_ctrl(struct op_x86_model_spec const *model,
@@ -376,13 +451,13 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
}
if (ibs_caps)
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
+ setup_APIC_ibs();
}
static void op_amd_cpu_shutdown(void)
{
if (ibs_caps)
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
+ clear_APIC_ibs();
}
static int op_amd_check_ctrs(struct pt_regs * const regs,
@@ -445,16 +520,11 @@ static void op_amd_stop(struct op_msrs const * const msrs)
op_amd_stop_ibs();
}
-static int __init_ibs_nmi(void)
+static int setup_ibs_ctl(int ibs_eilvt_off)
{
-#define IBSCTL_LVTOFFSETVAL (1 << 8)
-#define IBSCTL 0x1cc
struct pci_dev *cpu_cfg;
int nodes;
u32 value = 0;
- u8 ibs_eilvt_off;
-
- ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
nodes = 0;
cpu_cfg = NULL;
@@ -466,21 +536,60 @@ static int __init_ibs_nmi(void)
break;
++nodes;
pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
- | IBSCTL_LVTOFFSETVAL);
+ | IBSCTL_LVT_OFFSET_VALID);
pci_read_config_dword(cpu_cfg, IBSCTL, &value);
- if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
+ if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) {
pci_dev_put(cpu_cfg);
printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
- "IBSCTL = 0x%08x", value);
- return 1;
+ "IBSCTL = 0x%08x\n", value);
+ return -EINVAL;
}
} while (1);
if (!nodes) {
- printk(KERN_DEBUG "No CPU node configured for IBS");
- return 1;
+ printk(KERN_DEBUG "No CPU node configured for IBS\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int force_ibs_eilvt_setup(void)
+{
+ int i;
+ int ret;
+
+ /* find the next free available EILVT entry */
+ for (i = 1; i < 4; i++) {
+ if (!eilvt_is_available(i))
+ continue;
+ ret = setup_ibs_ctl(i);
+ if (ret)
+ return ret;
+ return 0;
}
+ printk(KERN_DEBUG "No EILVT entry available\n");
+
+ return -EBUSY;
+}
+
+static int __init_ibs_nmi(void)
+{
+ int ret;
+
+ if (ibs_eilvt_valid())
+ return 0;
+
+ ret = force_ibs_eilvt_setup();
+ if (ret)
+ return ret;
+
+ if (!ibs_eilvt_valid())
+ return -EFAULT;
+
+ pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
+
return 0;
}
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2010-10-20 5:02 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-06 10:27 [PATCH 0/2] apic, x86: Use BIOS settings to setup AMD EILVT APIC registers Robert Richter
2010-10-06 10:27 ` [PATCH 1/2] apic, x86: Check if EILVT APIC registers are available (AMD only) Robert Richter
2010-10-20 5:01 ` [tip:irq/core] " tip-bot for Robert Richter
2010-10-06 10:27 ` [PATCH 2/2] apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets Robert Richter
2010-10-06 19:41 ` Cyrill Gorcunov
2010-10-08 9:24 ` Robert Richter
2010-10-08 9:37 ` Cyrill Gorcunov
2010-10-08 10:21 ` Robert Richter
2010-10-08 18:27 ` Cyrill Gorcunov
2010-10-20 5:01 ` [tip:irq/core] " tip-bot for Robert Richter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox