]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/x86/oprofile/op_model_amd.c
sh64: asm/pgtable.h needs asm/mmu.h
[karo-tx-linux.git] / arch / x86 / oprofile / op_model_amd.c
index c3b8e24f2b16f4f6441c286268a61ac6a320b7c3..9fd8a567fe1e41a739dff21d02f9c2cd8a3a98d2 100644 (file)
@@ -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);