diff mbox series

[v2,05/16] target/ppc/power8_pmu.c: enable PMC1-PMC4 events

Message ID 20210824163032.394099-6-danielhb413@gmail.com
State New
Headers show
Series PMU-EBB support for PPC64 TCG | expand

Commit Message

Daniel Henrique Barboza Aug. 24, 2021, 4:30 p.m. UTC
This patch enable all PMCs but PMC5 to count cycles. To do that we
need to implement MMCR1 bits where the event are stored, retrieve
them, see if the PMC was configured with a PM_CYC event, and
calculate cycles if that's the case.

PowerISA v3.1 defines the following conditions to count cycles:

- PMC1 set with the event 0xF0;
- PMC6, which always count cycles

However, the PowerISA also defines a range of 'implementation dependent'
events that the chip can use in the 0x01-0xBF range. Turns out that IBM
POWER chips implements some non-ISA events, and the Linux kernel makes uses
of them. For instance, 0x1E is an implementation specific event that
counts cycles in PMCs 1-4 that the kernel uses. Let's also support 0x1E
to count cycles to allow for existing kernels to behave properly with the
PMU.

Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
---
 target/ppc/cpu.h        |  8 +++++++
 target/ppc/power8_pmu.c | 53 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

Comments

David Gibson Aug. 25, 2021, 5:23 a.m. UTC | #1
On Tue, Aug 24, 2021 at 01:30:21PM -0300, Daniel Henrique Barboza wrote:
> This patch enable all PMCs but PMC5 to count cycles. To do that we
> need to implement MMCR1 bits where the event are stored, retrieve
> them, see if the PMC was configured with a PM_CYC event, and
> calculate cycles if that's the case.
> 
> PowerISA v3.1 defines the following conditions to count cycles:
> 
> - PMC1 set with the event 0xF0;
> - PMC6, which always count cycles
> 
> However, the PowerISA also defines a range of 'implementation dependent'
> events that the chip can use in the 0x01-0xBF range. Turns out that IBM
> POWER chips implements some non-ISA events, and the Linux kernel makes uses
> of them. For instance, 0x1E is an implementation specific event that
> counts cycles in PMCs 1-4 that the kernel uses. Let's also support 0x1E
> to count cycles to allow for existing kernels to behave properly with the
> PMU.
> 
> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
> ---
>  target/ppc/cpu.h        |  8 +++++++
>  target/ppc/power8_pmu.c | 53 +++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 61 insertions(+)
> 
> diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
> index 6878d950de..e5df644a3c 100644
> --- a/target/ppc/cpu.h
> +++ b/target/ppc/cpu.h
> @@ -350,6 +350,14 @@ typedef struct ppc_v3_pate_t {
>  #define MMCR0_FCECE PPC_BIT(38)         /* FC on Enabled Cond or Event */
>  #define MMCR0_PMCC  PPC_BITMASK(44, 45) /* PMC Control */
>  
> +#define MMCR1_PMC1SEL_SHIFT (63 - 39)
> +#define MMCR1_PMC1SEL PPC_BITMASK(32, 39)
> +#define MMCR1_PMC2SEL_SHIFT (63 - 47)
> +#define MMCR1_PMC2SEL PPC_BITMASK(40, 47)
> +#define MMCR1_PMC3SEL_SHIFT (63 - 55)
> +#define MMCR1_PMC3SEL PPC_BITMASK(48, 55)
> +#define MMCR1_PMC4SEL PPC_BITMASK(56, 63)

Defining macros to actually pull out the event values based on
extract64() is probably a bit more qemu-idiomatic.

>  /* LPCR bits */
>  #define LPCR_VPM0         PPC_BIT(0)
>  #define LPCR_VPM1         PPC_BIT(1)
> diff --git a/target/ppc/power8_pmu.c b/target/ppc/power8_pmu.c
> index 47de38a99e..007007824d 100644
> --- a/target/ppc/power8_pmu.c
> +++ b/target/ppc/power8_pmu.c
> @@ -31,10 +31,63 @@ static void update_PMC_PM_CYC(CPUPPCState *env, int sprn,
>      env->spr[sprn] += time_delta;
>  }
>  
> +static void update_programmable_PMC_reg(CPUPPCState *env, int sprn,
> +                                        uint64_t time_delta)
> +{
> +    uint8_t event;
> +
> +    switch (sprn) {
> +    case SPR_POWER_PMC1:
> +        event = MMCR1_PMC1SEL & env->spr[SPR_POWER_MMCR1];
> +        event = event >> MMCR1_PMC1SEL_SHIFT;
> +        break;
> +    case SPR_POWER_PMC2:
> +        event = MMCR1_PMC2SEL & env->spr[SPR_POWER_MMCR1];
> +        event = event >> MMCR1_PMC2SEL_SHIFT;
> +        break;
> +    case SPR_POWER_PMC3:
> +        event = MMCR1_PMC3SEL & env->spr[SPR_POWER_MMCR1];
> +        event = event >> MMCR1_PMC3SEL_SHIFT;
> +        break;
> +    case SPR_POWER_PMC4:
> +        event = MMCR1_PMC4SEL & env->spr[SPR_POWER_MMCR1];
> +        break;
> +    default:
> +        return;
> +    }
> +
> +    /*
> +     * MMCR0_PMC1SEL = 0xF0 is the architected PowerISA v3.1 event
> +     * that counts cycles using PMC1.
> +     *
> +     * IBM POWER chips also has support for an implementation dependent
> +     * event, 0x1E, that enables cycle counting on PMCs 1-4. The
> +     * Linux kernel makes extensive use of 0x1E, so let's also support
> +     * it.
> +     */
> +    switch (event) {
> +    case 0xF0:
> +        if (sprn == SPR_POWER_PMC1) {
> +            update_PMC_PM_CYC(env, sprn, time_delta);
> +        }
> +        break;
> +    case 0x1E:
> +        update_PMC_PM_CYC(env, sprn, time_delta);
> +        break;
> +    default:
> +        return;
> +    }
> +}
> +
>  static void update_cycles_PMCs(CPUPPCState *env)
>  {
>      uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>      uint64_t time_delta = now - env->pmu_base_time;
> +    int sprn;
> +
> +    for (sprn = SPR_POWER_PMC1; sprn < SPR_POWER_PMC5; sprn++) {
> +        update_programmable_PMC_reg(env, sprn, time_delta);
> +    }
>  
>      update_PMC_PM_CYC(env, SPR_POWER_PMC6, time_delta);
>  }
diff mbox series

Patch

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 6878d950de..e5df644a3c 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -350,6 +350,14 @@  typedef struct ppc_v3_pate_t {
 #define MMCR0_FCECE PPC_BIT(38)         /* FC on Enabled Cond or Event */
 #define MMCR0_PMCC  PPC_BITMASK(44, 45) /* PMC Control */
 
+#define MMCR1_PMC1SEL_SHIFT (63 - 39)
+#define MMCR1_PMC1SEL PPC_BITMASK(32, 39)
+#define MMCR1_PMC2SEL_SHIFT (63 - 47)
+#define MMCR1_PMC2SEL PPC_BITMASK(40, 47)
+#define MMCR1_PMC3SEL_SHIFT (63 - 55)
+#define MMCR1_PMC3SEL PPC_BITMASK(48, 55)
+#define MMCR1_PMC4SEL PPC_BITMASK(56, 63)
+
 /* LPCR bits */
 #define LPCR_VPM0         PPC_BIT(0)
 #define LPCR_VPM1         PPC_BIT(1)
diff --git a/target/ppc/power8_pmu.c b/target/ppc/power8_pmu.c
index 47de38a99e..007007824d 100644
--- a/target/ppc/power8_pmu.c
+++ b/target/ppc/power8_pmu.c
@@ -31,10 +31,63 @@  static void update_PMC_PM_CYC(CPUPPCState *env, int sprn,
     env->spr[sprn] += time_delta;
 }
 
+static void update_programmable_PMC_reg(CPUPPCState *env, int sprn,
+                                        uint64_t time_delta)
+{
+    uint8_t event;
+
+    switch (sprn) {
+    case SPR_POWER_PMC1:
+        event = MMCR1_PMC1SEL & env->spr[SPR_POWER_MMCR1];
+        event = event >> MMCR1_PMC1SEL_SHIFT;
+        break;
+    case SPR_POWER_PMC2:
+        event = MMCR1_PMC2SEL & env->spr[SPR_POWER_MMCR1];
+        event = event >> MMCR1_PMC2SEL_SHIFT;
+        break;
+    case SPR_POWER_PMC3:
+        event = MMCR1_PMC3SEL & env->spr[SPR_POWER_MMCR1];
+        event = event >> MMCR1_PMC3SEL_SHIFT;
+        break;
+    case SPR_POWER_PMC4:
+        event = MMCR1_PMC4SEL & env->spr[SPR_POWER_MMCR1];
+        break;
+    default:
+        return;
+    }
+
+    /*
+     * MMCR0_PMC1SEL = 0xF0 is the architected PowerISA v3.1 event
+     * that counts cycles using PMC1.
+     *
+     * IBM POWER chips also has support for an implementation dependent
+     * event, 0x1E, that enables cycle counting on PMCs 1-4. The
+     * Linux kernel makes extensive use of 0x1E, so let's also support
+     * it.
+     */
+    switch (event) {
+    case 0xF0:
+        if (sprn == SPR_POWER_PMC1) {
+            update_PMC_PM_CYC(env, sprn, time_delta);
+        }
+        break;
+    case 0x1E:
+        update_PMC_PM_CYC(env, sprn, time_delta);
+        break;
+    default:
+        return;
+    }
+}
+
 static void update_cycles_PMCs(CPUPPCState *env)
 {
     uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     uint64_t time_delta = now - env->pmu_base_time;
+    int sprn;
+
+    for (sprn = SPR_POWER_PMC1; sprn < SPR_POWER_PMC5; sprn++) {
+        update_programmable_PMC_reg(env, sprn, time_delta);
+    }
 
     update_PMC_PM_CYC(env, SPR_POWER_PMC6, time_delta);
 }