Merge branch 'x86-mce-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 6 Jan 2011 19:05:21 +0000 (11:05 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 6 Jan 2011 19:05:21 +0000 (11:05 -0800)
* 'x86-mce-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  apic, amd: Make firmware bug messages more meaningful
  mce, amd: Remove goto in threshold_create_device()
  mce, amd: Add helper functions to setup APIC
  mce, amd: Shorten local variables mci_misc_{hi,lo}
  mce, amd: Implement mce_threshold_block_init() helper function

1  2 
arch/x86/kernel/apic/apic.c
arch/x86/oprofile/op_model_amd.c

@@@ -31,6 -31,7 +31,6 @@@
  #include <linux/init.h>
  #include <linux/cpu.h>
  #include <linux/dmi.h>
 -#include <linux/nmi.h>
  #include <linux/smp.h>
  #include <linux/mm.h>
  
@@@ -51,6 -52,7 +51,6 @@@
  #include <asm/mce.h>
  #include <asm/kvm_para.h>
  #include <asm/tsc.h>
 -#include <asm/atomic.h>
  
  unsigned int num_processors;
  
@@@ -431,17 -433,18 +431,18 @@@ int setup_APIC_eilvt(u8 offset, u8 vect
        reserved = reserve_eilvt_offset(offset, new);
  
        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);
+               pr_err(FW_BUG "cpu %d, try to use APIC%lX (LVT offset %d) for "
+                      "vector 0x%x, but the register is already in use for "
+                      "vector 0x%x on another cpu\n",
+                      smp_processor_id(), reg, offset, new, reserved);
                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);
+               pr_err(FW_BUG "cpu %d, try to use APIC%lX (LVT offset %d) for "
+                      "vector 0x%x, but the register is already in use for "
+                      "vector 0x%x on this cpu\n",
+                      smp_processor_id(), reg, offset, new, old);
                return -EBUSY;
        }
  
@@@ -798,7 -801,11 +799,7 @@@ void __init setup_boot_APIC_clock(void
         * PIT/HPET going.  Otherwise register lapic as a dummy
         * device.
         */
 -      if (nmi_watchdog != NMI_IO_APIC)
 -              lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;
 -      else
 -              pr_warning("APIC timer registered as dummy,"
 -                      " due to nmi_watchdog=%d!\n", nmi_watchdog);
 +      lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;
  
        /* Setup the lapic or request the broadcast */
        setup_APIC_timer();
@@@ -1382,15 -1389,8 +1383,15 @@@ void __cpuinit end_local_APIC_setup(voi
        }
  #endif
  
 -      setup_apic_nmi_watchdog(NULL);
        apic_pm_activate();
 +
 +      /*
 +       * Now that local APIC setup is completed for BP, configure the fault
 +       * handling for interrupt remapping.
 +       */
 +      if (!smp_processor_id() && intr_remapping_enabled)
 +              enable_drhd_fault_handling();
 +
  }
  
  #ifdef CONFIG_X86_X2APIC
@@@ -1532,60 -1532,13 +1533,60 @@@ static int __init detect_init_APIC(void
        return 0;
  }
  #else
 +
 +static int apic_verify(void)
 +{
 +      u32 features, h, l;
 +
 +      /*
 +       * The APIC feature bit should now be enabled
 +       * in `cpuid'
 +       */
 +      features = cpuid_edx(1);
 +      if (!(features & (1 << X86_FEATURE_APIC))) {
 +              pr_warning("Could not enable APIC!\n");
 +              return -1;
 +      }
 +      set_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC);
 +      mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
 +
 +      /* The BIOS may have set up the APIC at some other address */
 +      rdmsr(MSR_IA32_APICBASE, l, h);
 +      if (l & MSR_IA32_APICBASE_ENABLE)
 +              mp_lapic_addr = l & MSR_IA32_APICBASE_BASE;
 +
 +      pr_info("Found and enabled local APIC!\n");
 +      return 0;
 +}
 +
 +int apic_force_enable(void)
 +{
 +      u32 h, l;
 +
 +      if (disable_apic)
 +              return -1;
 +
 +      /*
 +       * Some BIOSes disable the local APIC in the APIC_BASE
 +       * MSR. This can only be done in software for Intel P6 or later
 +       * and AMD K7 (Model > 1) or later.
 +       */
 +      rdmsr(MSR_IA32_APICBASE, l, h);
 +      if (!(l & MSR_IA32_APICBASE_ENABLE)) {
 +              pr_info("Local APIC disabled by BIOS -- reenabling.\n");
 +              l &= ~MSR_IA32_APICBASE_BASE;
 +              l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 +              wrmsr(MSR_IA32_APICBASE, l, h);
 +              enabled_via_apicbase = 1;
 +      }
 +      return apic_verify();
 +}
 +
  /*
   * Detect and initialize APIC
   */
  static int __init detect_init_APIC(void)
  {
 -      u32 h, l, features;
 -
        /* Disabled by kernel option? */
        if (disable_apic)
                return -1;
                                "you can enable it with \"lapic\"\n");
                        return -1;
                }
 -              /*
 -               * Some BIOSes disable the local APIC in the APIC_BASE
 -               * MSR. This can only be done in software for Intel P6 or later
 -               * and AMD K7 (Model > 1) or later.
 -               */
 -              rdmsr(MSR_IA32_APICBASE, l, h);
 -              if (!(l & MSR_IA32_APICBASE_ENABLE)) {
 -                      pr_info("Local APIC disabled by BIOS -- reenabling.\n");
 -                      l &= ~MSR_IA32_APICBASE_BASE;
 -                      l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 -                      wrmsr(MSR_IA32_APICBASE, l, h);
 -                      enabled_via_apicbase = 1;
 -              }
 -      }
 -      /*
 -       * The APIC feature bit should now be enabled
 -       * in `cpuid'
 -       */
 -      features = cpuid_edx(1);
 -      if (!(features & (1 << X86_FEATURE_APIC))) {
 -              pr_warning("Could not enable APIC!\n");
 -              return -1;
 +              if (apic_force_enable())
 +                      return -1;
 +      } else {
 +              if (apic_verify())
 +                      return -1;
        }
 -      set_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC);
 -      mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
 -
 -      /* The BIOS may have set up the APIC at some other address */
 -      rdmsr(MSR_IA32_APICBASE, l, h);
 -      if (l & MSR_IA32_APICBASE_ENABLE)
 -              mp_lapic_addr = l & MSR_IA32_APICBASE_BASE;
 -
 -      pr_info("Found and enabled local APIC!\n");
  
        apic_pm_activate();
  
@@@ -1708,7 -1687,7 +1709,7 @@@ void __init init_apic_mappings(void
   * This initializes the IO-APIC and APIC hardware if this is
   * a UP kernel.
   */
 -int apic_version[MAX_APICS];
 +int apic_version[MAX_LOCAL_APIC];
  
  int __init APIC_init_uniprocessor(void)
  {
                setup_IO_APIC();
        else {
                nr_ioapics = 0;
 -              localise_nmi_watchdog();
        }
 -#else
 -      localise_nmi_watchdog();
  #endif
  
        x86_init.timers.setup_percpu_clockev();
 -#ifdef CONFIG_X86_64
 -      check_nmi_watchdog();
 -#endif
 -
        return 0;
  }
  
  #include "op_x86_model.h"
  #include "op_counter.h"
  
 -#define NUM_COUNTERS 4
 +#define NUM_COUNTERS          4
 +#define NUM_COUNTERS_F15H     6
  #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
 -#define NUM_VIRT_COUNTERS 32
 +#define NUM_VIRT_COUNTERS     32
  #else
 -#define NUM_VIRT_COUNTERS NUM_COUNTERS
 +#define NUM_VIRT_COUNTERS     0
  #endif
  
  #define OP_EVENT_MASK                 0x0FFF
  
  #define MSR_AMD_EVENTSEL_RESERVED     ((0xFFFFFCF0ULL<<32)|(1ULL<<21))
  
 -static unsigned long reset_value[NUM_VIRT_COUNTERS];
 +static int num_counters;
 +static unsigned long reset_value[OP_MAX_COUNTER];
  
  #define IBS_FETCH_SIZE                        6
  #define IBS_OP_SIZE                   12
  
  static u32 ibs_caps;
  
 -struct op_ibs_config {
 +struct ibs_config {
        unsigned long op_enabled;
        unsigned long fetch_enabled;
        unsigned long max_cnt_fetch;
        unsigned long max_cnt_op;
        unsigned long rand_en;
        unsigned long dispatched_ops;
 +      unsigned long branch_target;
  };
  
 -static struct op_ibs_config ibs_config;
 -static u64 ibs_op_ctl;
 +struct ibs_state {
 +      u64             ibs_op_ctl;
 +      int             branch_target;
 +      unsigned long   sample_size;
 +};
 +
 +static struct ibs_config ibs_config;
 +static struct ibs_state ibs_state;
  
  /*
   * IBS cpuid feature detection
   * bit 0 is used to indicate the existence of IBS.
   */
  #define IBS_CAPS_AVAIL                        (1U<<0)
 +#define IBS_CAPS_FETCHSAM             (1U<<1)
 +#define IBS_CAPS_OPSAM                        (1U<<2)
  #define IBS_CAPS_RDWROPCNT            (1U<<3)
  #define IBS_CAPS_OPCNT                        (1U<<4)
 +#define IBS_CAPS_BRNTRGT              (1U<<5)
 +#define IBS_CAPS_OPCNTEXT             (1U<<6)
 +
 +#define IBS_CAPS_DEFAULT              (IBS_CAPS_AVAIL         \
 +                                       | IBS_CAPS_FETCHSAM    \
 +                                       | IBS_CAPS_OPSAM)
  
  /*
   * IBS APIC setup
@@@ -116,12 -99,12 +116,12 @@@ static u32 get_ibs_caps(void
        /* check IBS cpuid feature flags */
        max_level = cpuid_eax(0x80000000);
        if (max_level < IBS_CPUID_FEATURES)
 -              return IBS_CAPS_AVAIL;
 +              return IBS_CAPS_DEFAULT;
  
        ibs_caps = cpuid_eax(IBS_CPUID_FEATURES);
        if (!(ibs_caps & IBS_CAPS_AVAIL))
                /* cpuid flags not valid */
 -              return IBS_CAPS_AVAIL;
 +              return IBS_CAPS_DEFAULT;
  
        return ibs_caps;
  }
@@@ -214,8 -197,8 +214,8 @@@ op_amd_handle_ibs(struct pt_regs * cons
                rdmsrl(MSR_AMD64_IBSOPCTL, ctl);
                if (ctl & IBS_OP_VAL) {
                        rdmsrl(MSR_AMD64_IBSOPRIP, val);
 -                      oprofile_write_reserve(&entry, regs, val,
 -                                             IBS_OP_CODE, IBS_OP_SIZE);
 +                      oprofile_write_reserve(&entry, regs, val, IBS_OP_CODE,
 +                                             ibs_state.sample_size);
                        oprofile_add_data64(&entry, val);
                        rdmsrl(MSR_AMD64_IBSOPDATA, val);
                        oprofile_add_data64(&entry, val);
                        oprofile_add_data64(&entry, val);
                        rdmsrl(MSR_AMD64_IBSDCPHYSAD, val);
                        oprofile_add_data64(&entry, val);
 +                      if (ibs_state.branch_target) {
 +                              rdmsrl(MSR_AMD64_IBSBRTARGET, val);
 +                              oprofile_add_data(&entry, (unsigned long)val);
 +                      }
                        oprofile_write_commit(&entry);
  
                        /* reenable the IRQ */
 -                      ctl = op_amd_randomize_ibs_op(ibs_op_ctl);
 +                      ctl = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
                        wrmsrl(MSR_AMD64_IBSOPCTL, ctl);
                }
        }
@@@ -247,32 -226,21 +247,32 @@@ static inline void op_amd_start_ibs(voi
        if (!ibs_caps)
                return;
  
 +      memset(&ibs_state, 0, sizeof(ibs_state));
 +
 +      /*
 +       * Note: Since the max count settings may out of range we
 +       * write back the actual used values so that userland can read
 +       * it.
 +       */
 +
        if (ibs_config.fetch_enabled) {
 -              val = (ibs_config.max_cnt_fetch >> 4) & IBS_FETCH_MAX_CNT;
 +              val = ibs_config.max_cnt_fetch >> 4;
 +              val = min(val, IBS_FETCH_MAX_CNT);
 +              ibs_config.max_cnt_fetch = val << 4;
                val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0;
                val |= IBS_FETCH_ENABLE;
                wrmsrl(MSR_AMD64_IBSFETCHCTL, val);
        }
  
        if (ibs_config.op_enabled) {
 -              ibs_op_ctl = ibs_config.max_cnt_op >> 4;
 +              val = ibs_config.max_cnt_op >> 4;
                if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) {
                        /*
                         * IbsOpCurCnt not supported.  See
                         * op_amd_randomize_ibs_op() for details.
                         */
 -                      ibs_op_ctl = clamp(ibs_op_ctl, 0x0081ULL, 0xFF80ULL);
 +                      val = clamp(val, 0x0081ULL, 0xFF80ULL);
 +                      ibs_config.max_cnt_op = val << 4;
                } else {
                        /*
                         * The start value is randomized with a
                         * with the half of the randomized range. Also
                         * avoid underflows.
                         */
 -                      ibs_op_ctl = min(ibs_op_ctl + IBS_RANDOM_MAXCNT_OFFSET,
 -                                       IBS_OP_MAX_CNT);
 +                      val += IBS_RANDOM_MAXCNT_OFFSET;
 +                      if (ibs_caps & IBS_CAPS_OPCNTEXT)
 +                              val = min(val, IBS_OP_MAX_CNT_EXT);
 +                      else
 +                              val = min(val, IBS_OP_MAX_CNT);
 +                      ibs_config.max_cnt_op =
 +                              (val - IBS_RANDOM_MAXCNT_OFFSET) << 4;
 +              }
 +              val = ((val & ~IBS_OP_MAX_CNT) << 4) | (val & IBS_OP_MAX_CNT);
 +              val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0;
 +              val |= IBS_OP_ENABLE;
 +              ibs_state.ibs_op_ctl = val;
 +              ibs_state.sample_size = IBS_OP_SIZE;
 +              if (ibs_config.branch_target) {
 +                      ibs_state.branch_target = 1;
 +                      ibs_state.sample_size++;
                }
 -              if (ibs_caps & IBS_CAPS_OPCNT && ibs_config.dispatched_ops)
 -                      ibs_op_ctl |= IBS_OP_CNT_CTL;
 -              ibs_op_ctl |= IBS_OP_ENABLE;
 -              val = op_amd_randomize_ibs_op(ibs_op_ctl);
 +              val = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
                wrmsrl(MSR_AMD64_IBSOPCTL, val);
        }
  }
@@@ -324,25 -281,29 +324,25 @@@ static inline int eilvt_is_available(in
  
  static inline int ibs_eilvt_valid(void)
  {
 -      u64 val;
        int offset;
 +      u64 val;
  
        rdmsrl(MSR_AMD64_IBSCTL, val);
 +      offset = val & IBSCTL_LVT_OFFSET_MASK;
 +
        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);
 +              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;
        }
  
 -      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);
 +      if (!eilvt_is_available(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;
 +      }
  
 -      return 0;
 +      return 1;
  }
  
  static inline int get_ibs_offset(void)
@@@ -389,7 -350,7 +389,7 @@@ static void op_mux_switch_ctrl(struct o
        int i;
  
        /* enable active counters */
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                int virt = op_x86_phys_to_virt(i);
                if (!reset_value[virt])
                        continue;
@@@ -408,7 -369,7 +408,7 @@@ static void op_amd_shutdown(struct op_m
  {
        int i;
  
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                if (!msrs->counters[i].addr)
                        continue;
                release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
@@@ -420,7 -381,7 +420,7 @@@ static int op_amd_fill_in_addresses(str
  {
        int i;
  
 -      for (i = 0; i < NUM_COUNTERS; i++) {
 +      for (i = 0; i < num_counters; i++) {
                if (!reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
                        goto fail;
                if (!reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) {
                        goto fail;
                }
                /* both registers must be reserved */
 -              msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
 -              msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
 +              if (num_counters == NUM_COUNTERS_F15H) {
 +                      msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1);
 +                      msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1);
 +              } else {
 +                      msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
 +                      msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
 +              }
                continue;
        fail:
                if (!counter_config[i].enabled)
@@@ -454,7 -410,7 +454,7 @@@ static void op_amd_setup_ctrs(struct op
        int i;
  
        /* setup reset_value */
 -      for (i = 0; i < NUM_VIRT_COUNTERS; ++i) {
 +      for (i = 0; i < OP_MAX_COUNTER; ++i) {
                if (counter_config[i].enabled
                    && msrs->counters[op_x86_virt_to_phys(i)].addr)
                        reset_value[i] = counter_config[i].count;
        }
  
        /* clear all counters */
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                if (!msrs->controls[i].addr)
                        continue;
                rdmsrl(msrs->controls[i].addr, val);
        }
  
        /* enable active counters */
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                int virt = op_x86_phys_to_virt(i);
                if (!reset_value[virt])
                        continue;
@@@ -510,7 -466,7 +510,7 @@@ static int op_amd_check_ctrs(struct pt_
        u64 val;
        int i;
  
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                int virt = op_x86_phys_to_virt(i);
                if (!reset_value[virt])
                        continue;
@@@ -533,7 -489,7 +533,7 @@@ static void op_amd_start(struct op_msr
        u64 val;
        int i;
  
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                if (!reset_value[op_x86_phys_to_virt(i)])
                        continue;
                rdmsrl(msrs->controls[i].addr, val);
@@@ -553,7 -509,7 +553,7 @@@ static void op_amd_stop(struct op_msrs 
         * Subtle: stop on all counters to avoid race with setting our
         * pm callback
         */
 -      for (i = 0; i < NUM_COUNTERS; ++i) {
 +      for (i = 0; i < num_counters; ++i) {
                if (!reset_value[op_x86_phys_to_virt(i)])
                        continue;
                rdmsrl(msrs->controls[i].addr, val);
@@@ -610,6 -566,7 +610,7 @@@ static int force_ibs_eilvt_setup(void
                ret = setup_ibs_ctl(i);
                if (ret)
                        return ret;
+               pr_err(FW_BUG "using offset %d for IBS interrupts\n", i);
                return 0;
        }
  
@@@ -637,29 -594,21 +638,29 @@@ static int __init_ibs_nmi(void
        return 0;
  }
  
 -/* initialize the APIC for the IBS interrupts if available */
 +/*
 + * 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)
  {
 -      ibs_caps = get_ibs_caps();
 +      preempt_disable();
  
 +      ibs_caps = get_ibs_caps();
        if (!ibs_caps)
 -              return;
 +              goto out;
  
 -      if (__init_ibs_nmi()) {
 +      if (__init_ibs_nmi() < 0)
                ibs_caps = 0;
 -              return;
 -      }
 +      else
 +              printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps);
  
 -      printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n",
 -             (unsigned)ibs_caps);
 +out:
 +      preempt_enable();
  }
  
  static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
@@@ -682,60 -631,44 +683,60 @@@ static int setup_ibs_files(struct super
        /* model specific files */
  
        /* setup some reasonable defaults */
 +      memset(&ibs_config, 0, sizeof(ibs_config));
        ibs_config.max_cnt_fetch = 250000;
 -      ibs_config.fetch_enabled = 0;
        ibs_config.max_cnt_op = 250000;
 -      ibs_config.op_enabled = 0;
 -      ibs_config.dispatched_ops = 0;
 -
 -      dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
 -      oprofilefs_create_ulong(sb, dir, "enable",
 -                              &ibs_config.fetch_enabled);
 -      oprofilefs_create_ulong(sb, dir, "max_count",
 -                              &ibs_config.max_cnt_fetch);
 -      oprofilefs_create_ulong(sb, dir, "rand_enable",
 -                              &ibs_config.rand_en);
 -
 -      dir = oprofilefs_mkdir(sb, root, "ibs_op");
 -      oprofilefs_create_ulong(sb, dir, "enable",
 -                              &ibs_config.op_enabled);
 -      oprofilefs_create_ulong(sb, dir, "max_count",
 -                              &ibs_config.max_cnt_op);
 -      if (ibs_caps & IBS_CAPS_OPCNT)
 -              oprofilefs_create_ulong(sb, dir, "dispatched_ops",
 -                                      &ibs_config.dispatched_ops);
 +
 +      if (ibs_caps & IBS_CAPS_FETCHSAM) {
 +              dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
 +              oprofilefs_create_ulong(sb, dir, "enable",
 +                                      &ibs_config.fetch_enabled);
 +              oprofilefs_create_ulong(sb, dir, "max_count",
 +                                      &ibs_config.max_cnt_fetch);
 +              oprofilefs_create_ulong(sb, dir, "rand_enable",
 +                                      &ibs_config.rand_en);
 +      }
 +
 +      if (ibs_caps & IBS_CAPS_OPSAM) {
 +              dir = oprofilefs_mkdir(sb, root, "ibs_op");
 +              oprofilefs_create_ulong(sb, dir, "enable",
 +                                      &ibs_config.op_enabled);
 +              oprofilefs_create_ulong(sb, dir, "max_count",
 +                                      &ibs_config.max_cnt_op);
 +              if (ibs_caps & IBS_CAPS_OPCNT)
 +                      oprofilefs_create_ulong(sb, dir, "dispatched_ops",
 +                                              &ibs_config.dispatched_ops);
 +              if (ibs_caps & IBS_CAPS_BRNTRGT)
 +                      oprofilefs_create_ulong(sb, dir, "branch_target",
 +                                              &ibs_config.branch_target);
 +      }
  
        return 0;
  }
  
 +struct op_x86_model_spec op_amd_spec;
 +
  static int op_amd_init(struct oprofile_operations *ops)
  {
        init_ibs();
        create_arch_files = ops->create_files;
        ops->create_files = setup_ibs_files;
 +
 +      if (boot_cpu_data.x86 == 0x15) {
 +              num_counters = NUM_COUNTERS_F15H;
 +      } else {
 +              num_counters = NUM_COUNTERS;
 +      }
 +
 +      op_amd_spec.num_counters = num_counters;
 +      op_amd_spec.num_controls = num_counters;
 +      op_amd_spec.num_virt_counters = max(num_counters, NUM_VIRT_COUNTERS);
 +
        return 0;
  }
  
  struct op_x86_model_spec op_amd_spec = {
 -      .num_counters           = NUM_COUNTERS,
 -      .num_controls           = NUM_COUNTERS,
 -      .num_virt_counters      = NUM_VIRT_COUNTERS,
 +      /* num_counters/num_controls filled in at runtime */
        .reserved               = MSR_AMD_EVENTSEL_RESERVED,
        .event_mask             = OP_EVENT_MASK,
        .init                   = op_amd_init,