Signed-off-by: Robert Richter <[EMAIL PROTECTED]>

---
 arch/i386/perfmon/perfmon.c         |  234 +++++++++++++++++++++++++++++++++---
 arch/x86_64/perfmon/perfmon_amd64.c |  123 ++++++++++++++++++
 include/asm-i386/perfmon.h          |   39 ++++++
 include/linux/perfmon_pmu.h         |    5 
 perfmon/perfmon_intr.c              |   17 ++
 5 files changed, 395 insertions(+), 23 deletions(-)

Index: linux-2.6.22-rc4/arch/i386/perfmon/perfmon.c
===================================================================
--- linux-2.6.22-rc4.orig/arch/i386/perfmon/perfmon.c
+++ linux-2.6.22-rc4/arch/i386/perfmon/perfmon.c
@@ -4,6 +4,9 @@
  * Copyright (c) 2005-2007 Hewlett-Packard Development Company, L.P.
  * Contributed by Stephane Eranian <[EMAIL PROTECTED]>
  *
+ * Copyright (c) 2007 Advanced Micro Devices, Inc.
+ * Contributed by Robert Richter <[EMAIL PROTECTED]>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
  * License as published by the Free Software Foundation.
@@ -27,6 +30,12 @@
 #include <asm/apic.h>
 #include <asm/perfctr.h>
 
+#define IBS_CHECK_FETCHCTL     0x01
+#define IBS_CHECK_FETCHCTR     0x02
+#define IBS_CHECK_OPCTL                0x04
+#define IBS_CHECK_OPCTR                0x08
+#define IBS_CHECK_ALL          0x0f
+
 DEFINE_PER_CPU(unsigned long, real_iip);
 
 static int pfm_using_nmi;
@@ -57,6 +66,49 @@ static inline int get_smt_id(void)
 #endif
 }
 
+static inline u64 __pfm_pmd_sread(unsigned int vpmd)
+{
+       struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+       return arch_info->vpmds[get_smt_id()][vpmd];
+}
+
+static inline void __pfm_pmd_swrite(unsigned int vpmd, u64 val)
+{
+       struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+       arch_info->vpmds[get_smt_id()][vpmd] = val;
+}
+
+/* Context must be locked for atomic read and write operations */
+
+/* hw_addr is used as index for the virtual PMDs. If index is out of
+   range, there is a bug in the PMU description. */
+
+u64 pfm_pmd_sread(struct pfm_context *ctx, unsigned int cnum)
+{
+       unsigned int vpmd = pfm_pmu_conf->pmd_desc[cnum].hw_addr;
+       BUG_ON(!spin_is_locked(&ctx->lock));
+       BUG_ON(vpmd >= PFM_NUM_VPMDS);
+       return __pfm_pmd_sread(vpmd);
+}
+
+void pfm_pmd_swrite(struct pfm_context *ctx, unsigned int cnum, u64 val)
+{
+       unsigned int vpmd = pfm_pmu_conf->pmd_desc[cnum].hw_addr;
+       BUG_ON(!spin_is_locked(&ctx->lock));
+       BUG_ON(vpmd >= PFM_NUM_VPMDS);
+       __pfm_pmd_swrite(vpmd, val);
+}
+
+static void pfm_pmd_sinc(struct pfm_context *ctx, unsigned int cnum)
+{
+       unsigned int vpmd = pfm_pmu_conf->pmd_desc[cnum].hw_addr;
+       u64 val = 0;
+       BUG_ON(!spin_is_locked(&ctx->lock));
+       BUG_ON(vpmd >= PFM_NUM_VPMDS);
+       val = __pfm_pmd_sread(vpmd);
+       __pfm_pmd_swrite(vpmd, val + 1);
+}
+
 void __pfm_write_reg_p4(const struct pfm_arch_ext_reg *xreg, u64 val)
 {
        u64 pmi;
@@ -248,7 +300,13 @@ static int pfm_stop_save_p6(struct pfm_c
        count = set->nused_pmds;
        for (i = 0; count; i++) {
                if (test_bit(i, cast_ulp(set->used_pmds))) {
-                       val = pfm_arch_read_pmd(ctx, i);
+                       count--;
+                       /* Skip for IBS event counter */
+                       if (unlikely(i == arch_info->ibsfetchctr_idx))
+                               continue;
+                       if (unlikely(i == arch_info->ibsopctr_idx))
+                               continue;
+                       val = pfm_read_pmd(ctx, i);
                        if (likely(test_bit(i, cast_ulp(cnt_mask)))) {
                                if (!(val & wmask)) {
                                        __set_bit(i, cast_ulp(set->povfl_pmds));
@@ -257,16 +315,60 @@ static int pfm_stop_save_p6(struct pfm_c
                                val = (set->pmds[i].value & ~ovfl_mask) | (val 
& ovfl_mask);
                        }
                        set->pmds[i].value = val;
-                       count--;
                }
        }
        /* 0 means: no need to save PMDs at upper level */
        return 0;
 }
 
+/* Check for IBS events */
+/* ctx->lock must be locked for pfm_check_ibs_event() */
+
+static inline void pfm_check_ibs_event(struct pfm_context *ctx,
+                                      struct pfm_event_set *set,
+                                      u16 ctl, u64 ctl_mask, u16 ctr)
+{
+       struct pfm_regmap_desc *pmc_desc = pfm_pmu_conf->pmc_desc;
+       u64 val = 0;
+
+       if (unlikely(test_bit(ctr, cast_ulp(set->povfl_pmds)))) {
+               PFM_DBG("Warning: IBS event already pending for counter %d", 
ctr);
+               return;
+       }
+
+       /* Check event bits in control register */
+       rdmsrl(pmc_desc[ctl].hw_addr, val);
+       if (!(val & ctl_mask))
+               return;
+
+       PFM_DBG_ovfl("IBS event occurred for counter %d", ctr);
+       __set_bit(ctr, cast_ulp(set->povfl_pmds));
+       set->npend_ovfls++;
+       /* Increment event counter, update PMD */
+       pfm_pmd_sinc(ctx, ctr);
+       set->pmds[ctr].value = pfm_pmd_sread(ctx, ctr);
+}
+
 static int pfm_stop_save_amd64(struct pfm_context *ctx,
                               struct pfm_event_set *set)
 {
+       struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+
+       if (arch_info->flags & PFM_X86_FL_IBS) {
+               BUG_ON(!spin_is_locked(&ctx->lock));
+               /* Check for IBS fetch data */
+               pfm_check_ibs_event(ctx, set,
+                                   arch_info->ibsfetchctl_idx,
+                                   PFM_AMD64_IBSFETCHVAL,
+                                   arch_info->ibsfetchctr_idx);
+               /* Check for IBS execution data */
+               pfm_check_ibs_event(ctx, set,
+                                   arch_info->ibsopctl_idx,
+                                   PFM_AMD64_IBSOPVAL,
+                                   arch_info->ibsopctr_idx);
+       }
+
+       /* IbsFetchCtl/IbsOpCtl is cleard in pfm_stop_save_p6() */
        return pfm_stop_save_p6(ctx, set);
 }
 
@@ -580,6 +682,7 @@ void pfm_arch_start(struct task_struct *
                    struct pfm_event_set *set)
 {
        struct pfm_arch_context *ctx_arch;
+       struct pfm_regmap_desc *pmc_desc;
        u64 *mask;
        u16 i, num;
 
@@ -616,11 +719,18 @@ void pfm_arch_start(struct task_struct *
         */
        num = pfm_pmu_conf->regs.num_pmcs;
        mask = pfm_pmu_conf->regs.pmcs;
+       pmc_desc = pfm_pmu_conf->pmc_desc;
        for (i = 0; num; i++) {
-               if (test_bit(i, cast_ulp(mask))) {
+               if (!test_bit(i, cast_ulp(mask)))
+                       continue;
+               num--;
+               if (!test_bit(i, cast_ulp(set->used_pmcs)))
+                       /* If the PMC is not used, we initialize with
+                        * interrupts disabled. */
+                       pfm_arch_write_pmc(ctx, i, set->pmcs[i]
+                                          & ~pmc_desc[i].no_emul64_msk);
+               else
                        pfm_arch_write_pmc(ctx, i, set->pmcs[i]);
-                       num--;
-               }
        }
 
        /*
@@ -826,18 +936,37 @@ static int __kprobes pfm_has_ovfl_p6(str
        xrd = arch_info->pmd_addrs;
 
        for (i = 0; num; i++) {
-               if (test_bit(i, cast_ulp(cnt_mask))) {
-                       rdmsrl(xrd[i].addrs[0], val);
-                       if (!(val & wmask))
-                               return 1;
-                       num--;
-               }
+               if (!test_bit(i, cast_ulp(cnt_mask)))
+                       continue;
+               num--;
+               /* Skip for IBS event counter */
+               if (unlikely(i == arch_info->ibsfetchctr_idx))
+                       continue;
+               if (unlikely(i == arch_info->ibsopctr_idx))
+                       continue;
+               rdmsrl(xrd[i].addrs[0], val);
+               if (!(val & wmask))
+                       return 1;
        }
        return 0;
 }
 
 static int __kprobes pfm_has_ovfl_amd64(struct pfm_context *ctx)
 {
+       struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+       u64 val = 0;
+
+       if (!(arch_info->flags & PFM_X86_FL_IBS))
+               return pfm_has_ovfl_p6(ctx);
+
+       /* Check for IBS events */
+       rdmsrl(pfm_pmu_conf->pmc_desc[arch_info->ibsfetchctl_idx].hw_addr, val);
+       if (val & PFM_AMD64_IBSFETCHVAL)
+               return 1;
+       rdmsrl(pfm_pmu_conf->pmc_desc[arch_info->ibsopctl_idx].hw_addr, val);
+       if (val & PFM_AMD64_IBSOPVAL)
+               return 1;
+
        return pfm_has_ovfl_p6(ctx);
 }
 
@@ -1052,9 +1181,10 @@ int pfm_arch_pmu_config_init(struct pfm_
        /*
         * adust stop routine based on PMU model
         *
-        * P6  : P6, Pentium M, AMD K8, Intel architectural perfmon
+        * P6  : P6, Pentium M, Intel architectural perfmon
         * P4  : Xeon, EM64T, P4
         * Core: Core 2,
+        * AMD64: AMD64 (K8, family 10h)
         */
        switch(arch_info->pmu_style) {
        case PFM_X86_PMU_P4:
@@ -1198,11 +1328,12 @@ int pfm_arch_pmu_acquire(void)
 {
        struct pfm_arch_pmu_info *arch_info;
        struct pfm_regmap_desc *d;
-       struct pfm_arch_ext_reg *pc;
-       u16 i, n, ena = 0;
+       struct pfm_arch_ext_reg *pc, *pd;
+       u16 i, n, ena = 0, ibs_check = 0;
 
        arch_info = pfm_pmu_conf->arch_info;
        pc = arch_info->pmc_addrs;
+       pd = arch_info->pmd_addrs;
 
        bitmap_zero(cast_ulp(arch_info->enable_mask), PFM_MAX_PMCS);
 
@@ -1227,13 +1358,39 @@ int pfm_arch_pmu_acquire(void)
                if (!reserve_evntsel(d->hw_addr)) {
                        PFM_DBG("pmc%d (%s) in use elsewhere, disabling", i, 
d->desc);
                        __clear_bit(i, cast_ulp(pfm_pmu_conf->regs.pmcs));
-               } else {
-                       if (pc[i].reg_type & PFM_REGT_EN) {
-                               __set_bit(i, cast_ulp(arch_info->enable_mask));
-                               ena++;
-                               arch_info->max_ena = i + 1;
+                       continue;
+               }
+
+               if (!(pc[i].reg_type & PFM_REGT_EN))
+                       continue;
+               if (pc[i].reg_type & PFM_REGT_IBS) {
+                       if (!(arch_info->flags & PFM_X86_FL_IBS)) {
+                               PFM_DBG("Skipping PMD%d, IBS not supported by 
the PMU", i);
+                               continue;
+                       }
+                       if (arch_info->pmc_addrs[i].addrs[0] == 
MSR_AMD64_IBSFETCHCTL) {
+                               PFM_DBG("IBS fetch control register detected 
(PMC%d)", i);
+                               if (ibs_check & IBS_CHECK_FETCHCTL) {
+                                       PFM_INFO("Invalid PMU configuration 
(PMC%d), "
+                                                "IBS fetch control already 
configured", i);
+                                       return -EINVAL;
+                               }
+                               ibs_check |= IBS_CHECK_FETCHCTL;
+                               arch_info->ibsfetchctl_idx = i;
+                       } else if (arch_info->pmc_addrs[i].addrs[0] == 
MSR_AMD64_IBSOPCTL) {
+                               PFM_DBG("IBS execution control register 
detected (PMC%d)", i);
+                               if (ibs_check & IBS_CHECK_OPCTL) {
+                                       PFM_INFO("Invalid PMU configuration 
(PMC%d), "
+                                                "IBS execution control already 
configured", i);
+                                       return -EINVAL;
+                               }
+                               ibs_check |= IBS_CHECK_OPCTL;
+                               arch_info->ibsopctl_idx = i;
                        }
                }
+               __set_bit(i, cast_ulp(arch_info->enable_mask));
+               ena++;
+               arch_info->max_ena = i + 1;
        }
 
        PFM_DBG("%u PMCs with enable capability", ena);
@@ -1250,6 +1407,36 @@ int pfm_arch_pmu_acquire(void)
                        continue;
                n--;
 
+               /* Configure IBS registers */
+               if (!arch_info->flags & PFM_X86_FL_IBS) {
+                       /* No IBS support */
+                       ;
+               } else if (!(pd[i].reg_type & PFM_REGT_IBS)) {
+                       /* No IBS register */
+                       ;
+               } else if (d->type != PFM_REG_ICV) {
+                       /* No virtual IBS counter */
+                       ;
+               } else if (d->hw_addr == PFM_VPMD_AMD64_IBSFETCHCTR) {
+                       PFM_DBG("IBS fetch counter detected (PMD%d)", i);
+                       if (ibs_check & IBS_CHECK_FETCHCTR) {
+                               PFM_INFO("Invalid PMU configuration (PMD%d), "
+                                        "IBS fetch counter already 
configured", i);
+                               return -EINVAL;
+                       }
+                       ibs_check |= IBS_CHECK_FETCHCTR;
+                       arch_info->ibsfetchctr_idx = i;
+               } else if (d->hw_addr == PFM_VPMD_AMD64_IBSOPCTR) {
+                       PFM_DBG("IBS execution counter detected (PMD%d)", i);
+                       if (ibs_check & IBS_CHECK_OPCTR) {
+                               PFM_INFO("Invalid PMU configuration (PMD%d), "
+                                        "IBS execution counter already 
configured", i);
+                               return -EINVAL;
+                       }
+                       ibs_check |= IBS_CHECK_OPCTR;
+                       arch_info->ibsopctr_idx = i;
+               }
+
                if (d->type & PFM_REG_V)
                        continue;
 
@@ -1261,6 +1448,15 @@ int pfm_arch_pmu_acquire(void)
                }
        }
 
+       if (arch_info->flags & PFM_X86_FL_IBS) {
+               if ((ibs_check & IBS_CHECK_ALL) != IBS_CHECK_ALL) {
+                       PFM_INFO("Invalid PMU configuration, "
+                                "Missing settings: 0x%02x", ibs_check);
+                       return -EINVAL;
+               }
+               PFM_DBG("IBS configuration successfully finished");
+       }
+
        /*
         * program APIC
         */
Index: linux-2.6.22-rc4/arch/x86_64/perfmon/perfmon_amd64.c
===================================================================
--- linux-2.6.22-rc4.orig/arch/x86_64/perfmon/perfmon_amd64.c
+++ linux-2.6.22-rc4/arch/x86_64/perfmon/perfmon_amd64.c
@@ -5,6 +5,9 @@
  * Copyright (c) 2005-2007 Hewlett-Packard Development Company, L.P.
  * Contributed by Stephane Eranian <[EMAIL PROTECTED]>
  *
+ * Copyright (c) 2007 Advanced Micro Devices, Inc.
+ * Contributed by Robert Richter <[EMAIL PROTECTED]>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
  * License as published by the Free Software Foundation.
@@ -25,6 +28,7 @@
 #include <asm/nmi.h>
 
 MODULE_AUTHOR("Stephane Eranian <[EMAIL PROTECTED]>");
+MODULE_AUTHOR("Robert Richter <[EMAIL PROTECTED]>");
 MODULE_DESCRIPTION("AMD64 PMU description table");
 MODULE_LICENSE("GPL");
 
@@ -38,12 +42,26 @@ static struct pfm_arch_pmu_info pfm_amd6
 /* pmc1  */    {{MSR_K7_EVNTSEL1, 0}, 1, PFM_REGT_EN},
 /* pmc2  */    {{MSR_K7_EVNTSEL2, 0}, 2, PFM_REGT_EN},
 /* pmc3  */    {{MSR_K7_EVNTSEL3, 0}, 3, PFM_REGT_EN},
+/* pmc4  */    {{MSR_AMD64_IBSFETCHCTL, 0}, 0, PFM_REGT_EN|PFM_REGT_IBS},
+/* pmc5  */    {{MSR_AMD64_IBSOPCTL, 0}, 0, PFM_REGT_EN|PFM_REGT_IBS},
        },
        .pmd_addrs = {
 /* pmd0  */    {{MSR_K7_PERFCTR0, 0}, 0, PFM_REGT_CTR},
 /* pmd1  */    {{MSR_K7_PERFCTR1, 0}, 0, PFM_REGT_CTR},
 /* pmd2  */    {{MSR_K7_PERFCTR2, 0}, 0, PFM_REGT_CTR},
 /* pmd3  */    {{MSR_K7_PERFCTR3, 0}, 0, PFM_REGT_CTR},
+/* pmd4  */    {{0, 0}, 0, PFM_REGT_CTR|PFM_REGT_IBS},
+/* pmd5  */    {{MSR_AMD64_IBSFETCHCTL, 0}, 0, PFM_REGT_IBS},
+/* pmd6  */    {{MSR_AMD64_IBSFETCHLINAD, 0}, 0, PFM_REGT_IBS},
+/* pmd7  */    {{MSR_AMD64_IBSFETCHPHYSAD, 0}, 0, PFM_REGT_IBS},
+/* pmd8  */    {{0, 0}, 0, PFM_REGT_CTR|PFM_REGT_IBS},
+/* pmd9  */    {{MSR_AMD64_IBSOPCTL, 0}, 0, PFM_REGT_IBS},
+/* pmd10 */    {{MSR_AMD64_IBSOPRIP, 0}, 0, PFM_REGT_IBS},
+/* pmd11 */    {{MSR_AMD64_IBSOPDATA, 0}, 0, PFM_REGT_IBS},
+/* pmd12 */    {{MSR_AMD64_IBSOPDATA2, 0}, 0, PFM_REGT_IBS},
+/* pmd13 */    {{MSR_AMD64_IBSOPDATA3, 0}, 0, PFM_REGT_IBS|PFM_REGT_IBS_EXT},
+/* pmd14 */    {{MSR_AMD64_IBSDCLINAD, 0}, 0, PFM_REGT_IBS|PFM_REGT_IBS_EXT},
+/* pmd15 */    {{MSR_AMD64_IBSDCPHYSAD, 0}, 0, PFM_REGT_IBS|PFM_REGT_IBS_EXT},
        },
        .pmu_style = PFM_X86_PMU_AMD64
 };
@@ -65,11 +83,26 @@ static struct pfm_arch_pmu_info pfm_amd6
                        | (1ULL<<20)       \
                        | (1ULL<<21))
 
+/*
+ * We mark readonly bits as reserved and use the PMC for control
+ * operations only.  Interrupt enable and clear bits are reserved too.
+ * IBSFETCHCTL is also implemented as PMD, where data can be read
+ * from. Same applies to IBSOPCTR.
+ */
+#define PFM_AMD64_IBSFETCHCTL_VAL      PFM_AMD64_IBSFETCHEN
+#define PFM_AMD64_IBSFETCHCTL_NO64     PFM_AMD64_IBSFETCHEN
+#define PFM_AMD64_IBSFETCHCTL_RSVD     (~((1ULL<<16)-1))
+#define PFM_AMD64_IBSOPCTL_VAL         PFM_AMD64_IBSOPEN
+#define PFM_AMD64_IBSOPCTL_NO64                PFM_AMD64_IBSOPEN
+#define PFM_AMD64_IBSOPCTL_RSVD                (~((1ULL<<16)-1))
+
 static struct pfm_regmap_desc pfm_amd64_pmc_desc[] = {
 /* pmc0  */ PMC_D(PFM_REG_I64, "PERFSEL0", PFM_K8_VAL, PFM_K8_RSVD, 
PFM_K8_NO64, MSR_K7_EVNTSEL0),
 /* pmc1  */ PMC_D(PFM_REG_I64, "PERFSEL1", PFM_K8_VAL, PFM_K8_RSVD, 
PFM_K8_NO64, MSR_K7_EVNTSEL1),
 /* pmc2  */ PMC_D(PFM_REG_I64, "PERFSEL2", PFM_K8_VAL, PFM_K8_RSVD, 
PFM_K8_NO64, MSR_K7_EVNTSEL2),
 /* pmc3  */ PMC_D(PFM_REG_I64, "PERFSEL3", PFM_K8_VAL, PFM_K8_RSVD, 
PFM_K8_NO64, MSR_K7_EVNTSEL3),
+/* pmc4  */ PMC_D(PFM_REG_I, "IBSFETCHCTL", PFM_AMD64_IBSFETCHCTL_VAL, 
PFM_AMD64_IBSFETCHCTL_RSVD, PFM_AMD64_IBSFETCHCTL_NO64, MSR_AMD64_IBSFETCHCTL),
+/* pmc5  */ PMC_D(PFM_REG_I, "IBSOPCTL", PFM_AMD64_IBSOPCTL_VAL, 
PFM_AMD64_IBSOPCTL_RSVD, PFM_AMD64_IBSOPCTL_NO64, MSR_AMD64_IBSOPCTL),
 };
 #define PFM_AMD_NUM_PMCS ARRAY_SIZE(pfm_amd64_pmc_desc)
 
@@ -77,7 +110,19 @@ static struct pfm_regmap_desc pfm_amd64_
 /* pmd0  */ PMD_D(PFM_REG_C,   "PERFCTR0",     MSR_K7_PERFCTR0),
 /* pmd1  */ PMD_D(PFM_REG_C,   "PERFCTR1",     MSR_K7_PERFCTR1),
 /* pmd2  */ PMD_D(PFM_REG_C,   "PERFCTR2",     MSR_K7_PERFCTR2),
-/* pmd3  */ PMD_D(PFM_REG_C,   "PERFCTR3",     MSR_K7_PERFCTR3)
+/* pmd3  */ PMD_D(PFM_REG_C,   "PERFCTR3",     MSR_K7_PERFCTR3),
+/* pmd4  */ PMD_D(PFM_REG_ICV, "IBSFETCHCTR",  PFM_VPMD_AMD64_IBSFETCHCTR),
+/* pmd5  */ PMD_D(PFM_REG_IRO, "IBSFETCHCTL",  MSR_AMD64_IBSFETCHCTL),
+/* pmd6  */ PMD_D(PFM_REG_IRO, "IBSFETCHLINAD",        
MSR_AMD64_IBSFETCHLINAD),
+/* pmd7  */ PMD_D(PFM_REG_IRO, "IBSFETCHPHYSAD", MSR_AMD64_IBSFETCHPHYSAD),
+/* pmd8  */ PMD_D(PFM_REG_ICV, "IBSOPCTR",     PFM_VPMD_AMD64_IBSOPCTR),
+/* pmd9  */ PMD_D(PFM_REG_IRO, "IBSOPCTL",     MSR_AMD64_IBSOPCTL),
+/* pmd10 */ PMD_D(PFM_REG_IRO, "IBSOPRIP",     MSR_AMD64_IBSOPRIP),
+/* pmd11 */ PMD_D(PFM_REG_IRO, "IBSOPDATA",    MSR_AMD64_IBSOPDATA),
+/* pmd12 */ PMD_D(PFM_REG_IRO, "IBSOPDATA2",   MSR_AMD64_IBSOPDATA2),
+/* pmd13 */ PMD_D(PFM_REG_IRO, "IBSOPDATA3",   MSR_AMD64_IBSOPDATA3),
+/* pmd14 */ PMD_D(PFM_REG_IRO, "IBSDCLINAD",   MSR_AMD64_IBSDCLINAD),
+/* pmd15 */ PMD_D(PFM_REG_IRO, "IBSDCPHYSAD",  MSR_AMD64_IBSDCPHYSAD),
 };
 #define PFM_AMD_NUM_PMDS ARRAY_SIZE(pfm_amd64_pmd_desc)
 
@@ -296,8 +341,10 @@ static int pfm_amd64_probe_pmu(void)
        }
 
        if (current_cpu_data.x86_max_cores > 1)
+               /* XXX: Central exit at end of function if ERRNO */
                return pfm_amd64_setup_nb_event_control();
 
+       /* XXX: Only single-core systems here? */
        PFM_INFO("nmi_watchdog=%d nmi_active=%d force_nmi=%d",
                nmi_watchdog, atomic_read(&nmi_active), force_nmi);
        /*
@@ -307,9 +354,76 @@ static int pfm_amd64_probe_pmu(void)
        if (nmi_watchdog == NMI_LOCAL_APIC || force_nmi)
                pfm_amd64_pmu_info.flags |= PFM_X86_FL_USE_NMI;
 
+       PFM_INFO("Using AMD64 PMU");
+       if (pfm_amd64_pmu_info.flags & PFM_X86_FL_IBS)
+               PFM_INFO("IBS is supported by processor");
+       if (pfm_amd64_pmu_info.flags & PFM_X86_FL_IBS_EXT)
+               PFM_INFO("IBS extended registers are supported by processor");
+
        return 0;
 }
 
+static inline void
+pfm_amd64_check_register(struct pfm_pmu_config *cfg,
+                        struct pfm_regmap_desc *reg,
+                        struct pfm_arch_ext_reg *ext_reg)
+{
+       struct pfm_arch_pmu_info *arch_info = cfg->arch_info;
+
+       if (!(ext_reg->reg_type & PFM_REGT_AMD64))
+               /* No special AMD64 PMU register */
+               return;
+
+       /* Disable register */
+       reg->type &= ~PFM_REG_I;
+
+       switch (ext_reg->reg_type & PFM_REGT_AMD64) {
+       case (PFM_REGT_IBS):
+               /* IBS register */
+               if (!(arch_info->flags & PFM_X86_FL_IBS))
+                       return;
+               break;
+       case (PFM_REGT_IBS|PFM_REGT_IBS_EXT):
+               /* IBS extended register */
+               if (!(arch_info->flags & PFM_X86_FL_IBS_EXT))
+                       return;
+               break;
+       default:
+               return;
+       }
+
+       /* Enable register */
+       reg->type |= PFM_REG_I;
+}
+
+static void pfm_amd64_setup_pmu(struct pfm_pmu_config *cfg)
+{
+       u16 i;
+       struct pfm_arch_pmu_info *arch_info = cfg->arch_info;
+
+       /* set PMU features depending on CPUID */
+       arch_info->flags &= ~(PFM_X86_FL_IBS|PFM_X86_FL_IBS_EXT);
+       switch (current_cpu_data.x86) {
+       case 15:
+               break;
+       case 16:
+               arch_info->flags |= PFM_X86_FL_IBS;
+               break;
+       default:
+               break;
+       }
+
+       /* Disable unsupported PMC/PMD registers */
+       for (i = 0; i < cfg->num_pmc_entries;  i++) {
+               pfm_amd64_check_register(cfg, &cfg->pmc_desc[i],
+                                        &arch_info->pmc_addrs[i]);
+       }
+       for (i = 0; i < cfg->num_pmd_entries;  i++) {
+               pfm_amd64_check_register(cfg, &cfg->pmd_desc[i],
+                                        &arch_info->pmd_addrs[i]);
+       }
+}
+
 static struct pfm_pmu_config pfm_amd64_pmu_conf = {
        .pmu_name = "AMD64",
        .counter_width = 47,
@@ -318,14 +432,17 @@ static struct pfm_pmu_config pfm_amd64_p
        .num_pmc_entries = PFM_AMD_NUM_PMCS,
        .num_pmd_entries = PFM_AMD_NUM_PMDS,
        .probe_pmu = pfm_amd64_probe_pmu,
-       .version = "1.1",
+       .version = "1.2",
        .arch_info = &pfm_amd64_pmu_info,
        .flags = PFM_PMU_BUILTIN_FLAG,
-       .owner = THIS_MODULE
+       .owner = THIS_MODULE,
+       .pmd_sread = pfm_pmd_sread,
+       .pmd_swrite = pfm_pmd_swrite,
 };
 
 static int __init pfm_amd64_pmu_init_module(void)
 {
+       pfm_amd64_setup_pmu(&pfm_amd64_pmu_conf);
        return pfm_pmu_register(&pfm_amd64_pmu_conf);
 }
 
Index: linux-2.6.22-rc4/include/asm-i386/perfmon.h
===================================================================
--- linux-2.6.22-rc4.orig/include/asm-i386/perfmon.h
+++ linux-2.6.22-rc4/include/asm-i386/perfmon.h
@@ -2,6 +2,9 @@
  * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P.
  * Contributed by Stephane Eranian <[EMAIL PROTECTED]>
  *
+ * Copyright (c) 2007 Advanced Micro Devices, Inc.
+ * Contributed by Robert Richter <[EMAIL PROTECTED]>
+ *
  * This file contains X86 Processor Family specific definitions
  * for the perfmon interface. This covers P6, Pentium M, P4/Xeon
  * (32-bit and 64-bit, i.e., EM64T) and AMD X86-64.
@@ -27,6 +30,8 @@
 
 #ifdef __KERNEL__
 
+#include <linux/threads.h> /* NR_CPUS */
+
 #ifdef CONFIG_4KSTACKS
 #define PFM_ARCH_PMD_STK_ARG   2
 #define PFM_ARCH_PMC_STK_ARG   2
@@ -45,6 +50,15 @@
 #define PFM_ESCR_RSVD  ~0x000000007ffffffcULL
 
 /*
+ * For AMD64
+ */
+/* Familiy 10h */
+#define PFM_AMD64_IBSFETCHEN   (1ULL<<48)
+#define PFM_AMD64_IBSFETCHVAL  (1ULL<<49)
+#define PFM_AMD64_IBSOPEN      (1ULL<<17)
+#define PFM_AMD64_IBSOPVAL     (1ULL<<18)
+
+/*
  * bitmask for reg_type
  */
 #define PFM_REGT_NA            0x0000  /* not available */
@@ -55,6 +69,17 @@
 #define PFM_REGT_NOHT          0x0020  /* unavailable with HT */
 #define PFM_REGT_CTR           0x0040  /* counter */
 #define PFM_REGT_OTH           0x0080  /* other type of register */
+#define PFM_REGT_IBS           0x0100  /* IBS register set */
+#define PFM_REGT_IBS_EXT       0x0200  /* IBS extended register set */
+
+/* AMD64 PMU features */
+#define PFM_REGT_AMD64         (PFM_REGT_IBS|PFM_REGT_IBS_EXT)
+
+/* We count IBS events in virtual PMDs to use implemented sampling
+ * features */
+#define PFM_VPMD_AMD64_IBSFETCHCTR     0
+#define PFM_VPMD_AMD64_IBSOPCTR                1
+#define PFM_NUM_VPMDS                  2
 
 /*
  * This design and the partitioning of resources for SMT (hyper threads)
@@ -99,6 +124,11 @@ struct pfm_arch_pmu_info {
        int (*unload_context)(struct pfm_context *ctx);
 
        u8  pmu_style;     /* type of PMU interface (P4, P6, CORE) */
+       u64 vpmds[NR_CPUS][PFM_NUM_VPMDS];      /* virt. sw PMDs per cpu */
+       u16 ibsfetchctl_idx;    /* IBS: index of IBS fetch control register */
+       u16 ibsfetchctr_idx;    /* IBS: index of IBS fetch counter (virtual) */
+       u16 ibsopctl_idx;       /* IBS: index of IBS execution control register 
*/
+       u16 ibsopctr_idx;       /* IBS: index of IBS execution counter 
(virtual) */
 };
 
 /*
@@ -115,6 +145,12 @@ struct pfm_arch_pmu_info {
 #define PFM_X86_FL_PMU_DS      0x01    /* Intel: support for Data Save Area 
(DS) */
 #define PFM_X86_FL_PMU_PEBS    0x02    /* Intel: support PEBS (implies DS) */
 #define PFM_X86_FL_USE_NMI     0x04    /* must use NMI interrupt */
+#define PFM_X86_FL_IBS         0x08    /* AMD64: support for IBS */
+#define PFM_X86_FL_IBS_EXT     0x10    /* AMD64: support for IBS extended 
registers */
+
+/* Virtual PMDs access functions */
+u64 pfm_pmd_sread(struct pfm_context *ctx, unsigned int cnum);
+void pfm_pmd_swrite(struct pfm_context *ctx, unsigned int cnum, u64 val);
 
 void __pfm_read_reg_p4(const struct pfm_arch_ext_reg *xreg, u64 *val);
 void __pfm_write_reg_p4(const struct pfm_arch_ext_reg *xreg, u64 val);
@@ -365,6 +401,9 @@ void pfm_arch_pmu_release(void);
 static inline void pfm_arch_ovfl_reset_pmd(struct pfm_context *ctx, unsigned 
int cnum)
 {
        u64 val;
+       if (unlikely(pfm_pmu_conf->pmd_desc[cnum].type & PFM_REG_V))
+               /* skip virtual counters */
+               return;
        val = pfm_arch_read_pmd(ctx, cnum);
        pfm_arch_write_pmd(ctx, cnum, val);
 }
Index: linux-2.6.22-rc4/include/linux/perfmon_pmu.h
===================================================================
--- linux-2.6.22-rc4.orig/include/linux/perfmon_pmu.h
+++ linux-2.6.22-rc4/include/linux/perfmon_pmu.h
@@ -2,6 +2,9 @@
  * Copyright (c) 2006 Hewlett-Packard Development Company, L.P.
  * Contributed by Stephane Eranian <[EMAIL PROTECTED]>
  *
+ * Copyright (c) 2007 Advanced Micro Devices, Inc.
+ * Contributed by Robert Richter <[EMAIL PROTECTED]>
+ *
  * Interface for PMU description modules
  *
  * This program is free software; you can redistribute it and/or
@@ -78,6 +81,8 @@ struct pfm_regmap_desc {
 #define PFM_REG_W64    (PFM_REG_WC|PFM_REG_NO64|PFM_REG_I)
 #define PFM_REG_C      (PFM_REG_C64|PFM_REG_I)
 #define PFM_REG_I64    (PFM_REG_NO64|PFM_REG_I)
+#define PFM_REG_IRO    (PFM_REG_RO|PFM_REG_I)
+#define PFM_REG_ICV    (PFM_REG_C64|PFM_REG_I|PFM_REG_V) /* virtual (sw) 
counter */
 
 typedef int (*pfm_pmc_check_t)(struct pfm_context *ctx,
                               struct pfm_event_set *set,
Index: linux-2.6.22-rc4/perfmon/perfmon_intr.c
===================================================================
--- linux-2.6.22-rc4.orig/perfmon/perfmon_intr.c
+++ linux-2.6.22-rc4/perfmon/perfmon_intr.c
@@ -18,6 +18,9 @@
  * Contributed by Stephane Eranian <[EMAIL PROTECTED]>
  *                David Mosberger-Tang <[EMAIL PROTECTED]>
  *
+ * Copyright (c) 2007 Advanced Micro Devices, Inc.
+ * Contributed by Robert Richter <[EMAIL PROTECTED]>
+ *
  * More information about perfmon available at:
  *     http://perfmon2.sf.net
  *
@@ -158,7 +161,19 @@ static void pfm_overflow_handler(struct 
                 */
                old_val = new_val = set->pmds[i].value;
                ovfl_thres = set->pmds[i].ovflsw_thres;
-               new_val += 1 + ovfl_mask;
+               if (likely(!(pfm_pmu_conf->pmd_desc[i].type & PFM_REG_V)))
+                       new_val += 1 + ovfl_mask;
+               else {
+                       /* No hardware counter */
+                       /*
+                        * Since the previous value is unknown, 64 bit
+                        * overflows can only detected for zero
+                        * values. Thus, increments of more than 1
+                        * will not be detected.
+                        */
+                       if (!new_val)
+                               old_val = ~0;
+               }
                set->pmds[i].value = new_val;
 
                /*

-- 
AMD Saxony, Dresden, Germany
Operating System Research Center
email: [EMAIL PROTECTED]



_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/

Reply via email to