|
@@ -316,16 +316,23 @@ static void op_amd_stop_ibs(void)
|
|
|
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
|
|
|
}
|
|
|
|
|
|
-static inline int eilvt_is_available(int offset)
|
|
|
+static inline int get_eilvt(int offset)
|
|
|
{
|
|
|
- /* check if we may assign a vector */
|
|
|
return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
|
|
|
}
|
|
|
|
|
|
+static inline int put_eilvt(int offset)
|
|
|
+{
|
|
|
+ return !setup_APIC_eilvt(offset, 0, 0, 1);
|
|
|
+}
|
|
|
+
|
|
|
static inline int ibs_eilvt_valid(void)
|
|
|
{
|
|
|
int offset;
|
|
|
u64 val;
|
|
|
+ int valid = 0;
|
|
|
+
|
|
|
+ preempt_disable();
|
|
|
|
|
|
rdmsrl(MSR_AMD64_IBSCTL, val);
|
|
|
offset = val & IBSCTL_LVT_OFFSET_MASK;
|
|
@@ -333,16 +340,20 @@ static inline int ibs_eilvt_valid(void)
|
|
|
if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
|
|
|
pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n",
|
|
|
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
|
|
|
- return 0;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- if (!eilvt_is_available(offset)) {
|
|
|
+ if (!get_eilvt(offset)) {
|
|
|
pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n",
|
|
|
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
|
|
|
- return 0;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- return 1;
|
|
|
+ valid = 1;
|
|
|
+out:
|
|
|
+ preempt_enable();
|
|
|
+
|
|
|
+ return valid;
|
|
|
}
|
|
|
|
|
|
static inline int get_ibs_offset(void)
|
|
@@ -600,67 +611,69 @@ static int setup_ibs_ctl(int ibs_eilvt_off)
|
|
|
|
|
|
static int force_ibs_eilvt_setup(void)
|
|
|
{
|
|
|
- int i;
|
|
|
+ int offset;
|
|
|
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;
|
|
|
- pr_err(FW_BUG "using offset %d for IBS interrupts\n", i);
|
|
|
- return 0;
|
|
|
+ /*
|
|
|
+ * find the next free available EILVT entry, skip offset 0,
|
|
|
+ * pin search to this cpu
|
|
|
+ */
|
|
|
+ preempt_disable();
|
|
|
+ for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) {
|
|
|
+ if (get_eilvt(offset))
|
|
|
+ break;
|
|
|
}
|
|
|
+ preempt_enable();
|
|
|
|
|
|
- printk(KERN_DEBUG "No EILVT entry available\n");
|
|
|
-
|
|
|
- return -EBUSY;
|
|
|
-}
|
|
|
-
|
|
|
-static int __init_ibs_nmi(void)
|
|
|
-{
|
|
|
- int ret;
|
|
|
-
|
|
|
- if (ibs_eilvt_valid())
|
|
|
- return 0;
|
|
|
+ if (offset == APIC_EILVT_NR_MAX) {
|
|
|
+ printk(KERN_DEBUG "No EILVT entry available\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
|
|
|
- ret = force_ibs_eilvt_setup();
|
|
|
+ ret = setup_ibs_ctl(offset);
|
|
|
if (ret)
|
|
|
- return ret;
|
|
|
+ goto out;
|
|
|
|
|
|
- if (!ibs_eilvt_valid())
|
|
|
- return -EFAULT;
|
|
|
+ if (!ibs_eilvt_valid()) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
+ pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset);
|
|
|
pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
|
|
|
|
|
|
return 0;
|
|
|
+out:
|
|
|
+ preempt_disable();
|
|
|
+ put_eilvt(offset);
|
|
|
+ preempt_enable();
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* check and reserve APIC extended interrupt LVT offset for IBS if
|
|
|
* available
|
|
|
- *
|
|
|
- * init_ibs() preforms implicitly cpu-local operations, so pin this
|
|
|
- * thread to its current CPU
|
|
|
*/
|
|
|
|
|
|
static void init_ibs(void)
|
|
|
{
|
|
|
- preempt_disable();
|
|
|
-
|
|
|
ibs_caps = get_ibs_caps();
|
|
|
+
|
|
|
if (!ibs_caps)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (ibs_eilvt_valid())
|
|
|
goto out;
|
|
|
|
|
|
- if (__init_ibs_nmi() < 0)
|
|
|
- ibs_caps = 0;
|
|
|
- else
|
|
|
- printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
|
|
|
+ if (!force_ibs_eilvt_setup())
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Failed to setup ibs */
|
|
|
+ ibs_caps = 0;
|
|
|
+ return;
|
|
|
|
|
|
out:
|
|
|
- preempt_enable();
|
|
|
+ printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
|
|
|
}
|
|
|
|
|
|
static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
|