oprofile/x86: implement lsfr pseudo-random number generator for IBS
[safe/jmp/linux-2.6] / arch / x86 / oprofile / op_model_amd.c
index 39604b4..97c84eb 100644 (file)
@@ -22,6 +22,9 @@
 #include <asm/ptrace.h>
 #include <asm/msr.h>
 #include <asm/nmi.h>
+#include <asm/apic.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
 
 #include "op_x86_model.h"
 #include "op_counter.h"
@@ -43,8 +46,6 @@
 
 static unsigned long reset_value[NUM_VIRT_COUNTERS];
 
-#ifdef CONFIG_OPROFILE_IBS
-
 /* IbsFetchCtl bits/masks */
 #define IBS_FETCH_RAND_EN              (1ULL<<57)
 #define IBS_FETCH_VAL                  (1ULL<<49)
@@ -59,7 +60,7 @@ static unsigned long reset_value[NUM_VIRT_COUNTERS];
 #define IBS_FETCH_SIZE                 6
 #define IBS_OP_SIZE                    12
 
-static int has_ibs;    /* AMD Family10h and later */
+static u32 ibs_caps;
 
 struct op_ibs_config {
        unsigned long op_enabled;
@@ -72,7 +73,39 @@ struct op_ibs_config {
 
 static struct op_ibs_config ibs_config;
 
-#endif
+/*
+ * IBS cpuid feature detection
+ */
+
+#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_OPCNT                 (1LL<<4)
+
+static u32 get_ibs_caps(void)
+{
+       u32 ibs_caps;
+       unsigned int max_level;
+
+       if (!boot_cpu_has(X86_FEATURE_IBS))
+               return 0;
+
+       /* check IBS cpuid feature flags */
+       max_level = cpuid_eax(0x80000000);
+       if (max_level < IBS_CPUID_FEATURES)
+               return IBS_CAPS_AVAIL;
+
+       ibs_caps = cpuid_eax(IBS_CPUID_FEATURES);
+       if (!(ibs_caps & IBS_CAPS_AVAIL))
+               /* cpuid flags not valid */
+               return IBS_CAPS_AVAIL;
+
+       return ibs_caps;
+}
 
 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
 
@@ -81,7 +114,7 @@ static void op_mux_fill_in_addresses(struct op_msrs * const msrs)
        int i;
 
        for (i = 0; i < NUM_VIRT_COUNTERS; i++) {
-               int hw_counter = i % NUM_COUNTERS;
+               int hw_counter = op_x86_virt_to_phys(i);
                if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
                        msrs->multiplex[i].addr = MSR_K7_PERFCTR0 + hw_counter;
                else
@@ -144,11 +177,10 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
 
        /* setup reset_value */
        for (i = 0; i < NUM_VIRT_COUNTERS; ++i) {
-               if (counter_config[i].enabled) {
+               if (counter_config[i].enabled)
                        reset_value[i] = counter_config[i].count;
-               } else {
+               else
                        reset_value[i] = 0;
-               }
        }
 
        /* clear all counters */
@@ -186,17 +218,38 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
        }
 }
 
-#ifdef CONFIG_OPROFILE_IBS
+/*
+ * 16-bit Linear Feedback Shift Register (LFSR)
+ *
+ *                       16   14   13    11
+ * Feedback polynomial = X  + X  + X  +  X  + 1
+ */
+static unsigned int lfsr_random(void)
+{
+       static unsigned int lfsr_value = 0xF00D;
+       unsigned int bit;
+
+       /* Compute next bit to shift in */
+       bit = ((lfsr_value >> 0) ^
+              (lfsr_value >> 2) ^
+              (lfsr_value >> 3) ^
+              (lfsr_value >> 5)) & 0x0001;
 
-static inline int
+       /* Advance to next register value */
+       lfsr_value = (lfsr_value >> 1) | (bit << 15);
+
+       return lfsr_value;
+}
+
+static inline void
 op_amd_handle_ibs(struct pt_regs * const regs,
                  struct op_msrs const * const msrs)
 {
        u64 val, ctl;
        struct op_entry entry;
 
-       if (!has_ibs)
-               return 0;
+       if (!ibs_caps)
+               return;
 
        if (ibs_config.fetch_enabled) {
                rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl);
@@ -242,23 +295,26 @@ op_amd_handle_ibs(struct pt_regs * const regs,
                        wrmsrl(MSR_AMD64_IBSOPCTL, ctl);
                }
        }
-
-       return 1;
 }
 
 static inline void op_amd_start_ibs(void)
 {
        u64 val;
-       if (has_ibs && ibs_config.fetch_enabled) {
+
+       if (!ibs_caps)
+               return;
+
+       if (ibs_config.fetch_enabled) {
                val = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
                val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0;
                val |= IBS_FETCH_ENABLE;
                wrmsrl(MSR_AMD64_IBSFETCHCTL, val);
        }
 
-       if (has_ibs && ibs_config.op_enabled) {
+       if (ibs_config.op_enabled) {
                val = (ibs_config.max_cnt_op >> 4) & 0xFFFF;
-               val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0;
+               if (ibs_caps & IBS_CAPS_OPCNT && ibs_config.dispatched_ops)
+                       val |= IBS_OP_CNT_CTL;
                val |= IBS_OP_ENABLE;
                wrmsrl(MSR_AMD64_IBSOPCTL, val);
        }
@@ -266,27 +322,18 @@ static inline void op_amd_start_ibs(void)
 
 static void op_amd_stop_ibs(void)
 {
-       if (has_ibs && ibs_config.fetch_enabled)
+       if (!ibs_caps)
+               return;
+
+       if (ibs_config.fetch_enabled)
                /* clear max count and enable */
                wrmsrl(MSR_AMD64_IBSFETCHCTL, 0);
 
-       if (has_ibs && ibs_config.op_enabled)
+       if (ibs_config.op_enabled)
                /* clear max count and enable */
                wrmsrl(MSR_AMD64_IBSOPCTL, 0);
 }
 
-#else
-
-static inline int op_amd_handle_ibs(struct pt_regs * const regs,
-                                   struct op_msrs const * const msrs)
-{
-       return 0;
-}
-static inline void op_amd_start_ibs(void) { }
-static inline void op_amd_stop_ibs(void) { }
-
-#endif
-
 static int op_amd_check_ctrs(struct pt_regs * const regs,
                             struct op_msrs const * const msrs)
 {
@@ -361,8 +408,6 @@ static void op_amd_shutdown(struct op_msrs const * const msrs)
        }
 }
 
-#ifdef CONFIG_OPROFILE_IBS
-
 static u8 ibs_eilvt_off;
 
 static inline void apic_init_ibs_nmi_per_cpu(void *arg)
@@ -411,45 +456,36 @@ static int init_ibs_nmi(void)
                return 1;
        }
 
-#ifdef CONFIG_NUMA
-       /* Sanity check */
-       /* Works only for 64bit with proper numa implementation. */
-       if (nodes != num_possible_nodes()) {
-               printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, "
-                       "found: %d, expected %d",
-                       nodes, num_possible_nodes());
-               return 1;
-       }
-#endif
        return 0;
 }
 
 /* uninitialize the APIC for the IBS interrupts if needed */
 static void clear_ibs_nmi(void)
 {
-       if (has_ibs)
+       if (ibs_caps)
                on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
 }
 
 /* initialize the APIC for the IBS interrupts if available */
 static void ibs_init(void)
 {
-       has_ibs = boot_cpu_has(X86_FEATURE_IBS);
+       ibs_caps = get_ibs_caps();
 
-       if (!has_ibs)
+       if (!ibs_caps)
                return;
 
        if (init_ibs_nmi()) {
-               has_ibs = 0;
+               ibs_caps = 0;
                return;
        }
 
-       printk(KERN_INFO "oprofile: AMD IBS detected\n");
+       printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n",
+              (unsigned)ibs_caps);
 }
 
 static void ibs_exit(void)
 {
-       if (!has_ibs)
+       if (!ibs_caps)
                return;
 
        clear_ibs_nmi();
@@ -469,7 +505,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
        if (ret)
                return ret;
 
-       if (!has_ibs)
+       if (!ibs_caps)
                return ret;
 
        /* model specific files */
@@ -479,7 +515,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
        ibs_config.fetch_enabled = 0;
        ibs_config.max_cnt_op = 250000;
        ibs_config.op_enabled = 0;
-       ibs_config.dispatched_ops = 1;
+       ibs_config.dispatched_ops = 0;
 
        dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
        oprofilefs_create_ulong(sb, dir, "enable",
@@ -494,8 +530,9 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
                                &ibs_config.op_enabled);
        oprofilefs_create_ulong(sb, dir, "max_count",
                                &ibs_config.max_cnt_op);
-       oprofilefs_create_ulong(sb, dir, "dispatched_ops",
-                               &ibs_config.dispatched_ops);
+       if (ibs_caps & IBS_CAPS_OPCNT)
+               oprofilefs_create_ulong(sb, dir, "dispatched_ops",
+                                       &ibs_config.dispatched_ops);
 
        return 0;
 }
@@ -513,24 +550,10 @@ static void op_amd_exit(void)
        ibs_exit();
 }
 
-#else
-
-/* no IBS support */
-
-static int op_amd_init(struct oprofile_operations *ops)
-{
-       return 0;
-}
-
-static void op_amd_exit(void) {}
-
-#endif /* CONFIG_OPROFILE_IBS */
-
 struct op_x86_model_spec op_amd_spec = {
        .num_counters           = NUM_COUNTERS,
        .num_controls           = NUM_CONTROLS,
        .num_virt_counters      = NUM_VIRT_COUNTERS,
-       .num_virt_controls      = NUM_VIRT_CONTROLS,
        .reserved               = MSR_AMD_EVENTSEL_RESERVED,
        .event_mask             = OP_EVENT_MASK,
        .init                   = op_amd_init,