Kevin,
On Tue, Jul 10, 2007 at 08:44:31AM -0500, Kevin Corry wrote:
> Base PowerPC changes to support the new Cell PMU module. Most notably, this
> adds a table of function pointers to the pfm_arch_pmu_info structure, which
I was tempted to do this as well for x86 to try and cleanup the arch/perfmon.c
file, esepcially with regards to features such as PEBS.
Following the Itanium model, I would expect perfmon.c to contain only
architected PMU code and then hooks for model specific features which would
then be burried into the PMU description modules. That would make it for a much
more flexible environment. The difficulties is where you draw the line
between perfmon.c and the description module.
> are then implemented in each of the PMU-specific modules. This allows us to
> move all the PPC32 and POWER5 code out of the PowerPC core code. This also
> adds the base PMU interrupt handler, which allows us to properly support
> the 64-bit counter virtualization provided by Perfmon.
>
> Signed-off-by: Kevin Corry <[EMAIL PROTECTED]>
> Signed-off-by: Carl Love <[EMAIL PROTECTED]>
>
> Index: linux-2.6.22-arnd1-perfmon1/arch/powerpc/perfmon/perfmon.c
> ===================================================================
> --- linux-2.6.22-arnd1-perfmon1.orig/arch/powerpc/perfmon/perfmon.c
> +++ linux-2.6.22-arnd1-perfmon1/arch/powerpc/perfmon/perfmon.c
> @@ -25,61 +25,19 @@
> #include <linux/interrupt.h>
> #include <linux/perfmon.h>
>
> -/*
> - * collect pending overflowed PMDs. Called from pfm_ctxsw()
> - * and from PMU interrupt handler. Must fill in set->povfl_pmds[]
> - * and set->npend_ovfls. Interrupts are masked
> - */
> -static void __pfm_get_ovfl_pmds(struct pfm_context *ctx,
> - struct pfm_event_set *set)
> -{
> - u64 new_val, wmask;
> - u64 *used_mask, *cnt_pmds;
> - u64 mask[PFM_PMD_BV];
> - unsigned int i, max;
> -
> - max = pfm_pmu_conf->regs.max_cnt_pmd;
> - used_mask = set->used_pmds;
> - cnt_pmds = pfm_pmu_conf->regs.cnt_pmds;
> - wmask = 1ULL << pfm_pmu_conf->counter_width;
> - bitmap_and(cast_ulp(mask),
> - cast_ulp(cnt_pmds),
> - cast_ulp(used_mask), max);
> -
> - for (i = 0; i < max; i++) {
> - /* assume all PMD are counters */
> - if (test_bit(i, mask)) {
> - new_val = pfm_arch_read_pmd(ctx, i);
> -
> - PFM_DBG_ovfl("pmd%u new_val=0x%llx bit=%d",
> - i, (unsigned long long)new_val,
> - (new_val & wmask) ? 1 : 0);
> -
> - if (!(new_val & wmask)) {
> - __set_bit(i, set->povfl_pmds);
> - set->npend_ovfls++;
> - }
> - }
> - }
> -}
> -
> static void pfm_stop_active(struct task_struct *task,
> struct pfm_context *ctx, struct pfm_event_set *set)
> {
> - unsigned int i, max;
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
>
> - max = pfm_pmu_conf->regs.max_pmc;
> - /*
> - * clear enable bits
> - */
> - for (i = 0; i < max; i++)
> - if (test_bit(i, set->used_pmcs))
> - pfm_arch_write_pmc(ctx, i,0);
> + BUG_ON(!arch_info->disable_counters || !arch_info->get_ovfl_pmds);
> +
> + arch_info->disable_counters(ctx, set);
>
> if (set->npend_ovfls)
> return;
>
> - __pfm_get_ovfl_pmds(ctx, set);
> + arch_info->get_ovfl_pmds(ctx, set);
> }
>
> /*
> @@ -96,6 +54,8 @@ static void pfm_stop_active(struct task_
> int pfm_arch_ctxswout_thread(struct task_struct *task,
> struct pfm_context *ctx, struct pfm_event_set *set)
> {
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> +
> /*
> * disable lazy restore of PMC registers.
> */
> @@ -103,10 +63,32 @@ int pfm_arch_ctxswout_thread(struct task
>
> pfm_stop_active(task, ctx, set);
>
> + if (arch_info->ctxswout_thread) {
> + arch_info->ctxswout_thread(task, ctx, set);
> + }
> +
> return 1;
> }
>
> /*
> + * Called from pfm_ctxsw
> + */
> +void pfm_arch_ctxswin_thread(struct task_struct *task,
> + struct pfm_context *ctx, struct pfm_event_set *set)
> +{
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> +
> + if (ctx->state != PFM_CTX_MASKED && ctx->flags.started == 1) {
> + BUG_ON(!arch_info->enable_counters);
> + arch_info->enable_counters(ctx, set);
> + }
> +
> + if (arch_info->ctxswin_thread) {
> + arch_info->ctxswin_thread(task, ctx, set);
> + }
> +}
> +
> +/*
> * Called from pfm_stop() and idle notifier
> *
> * Interrupts are masked. Context is locked. Set is the active set.
> @@ -157,16 +139,14 @@ void pfm_arch_stop(struct task_struct *t
> void pfm_arch_start(struct task_struct *task,
> struct pfm_context *ctx, struct pfm_event_set *set)
> {
> - unsigned int i, max_pmc;
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
>
> if (task != current)
> return;
>
> - max_pmc = pfm_pmu_conf->regs.max_pmc;
> + BUG_ON(!arch_info->enable_counters);
>
> - for (i = 0; i < max_pmc; i++)
> - if (test_bit(i,set->used_pmcs))
> - pfm_arch_write_pmc(ctx, i, set->pmcs[i]);
> + arch_info->enable_counters(ctx, set);
> }
>
> /*
> @@ -179,34 +159,24 @@ void pfm_arch_start(struct task_struct *
> */
> void pfm_arch_restore_pmds(struct pfm_context *ctx, struct pfm_event_set
> *set)
> {
> - u64 ovfl_mask, val;
> - u64 *impl_pmds;
> - unsigned int i;
> - unsigned int max_pmd;
> -
> - max_pmd = pfm_pmu_conf->regs.max_pmd;
> - ovfl_mask = pfm_pmu_conf->ovfl_mask;
> - impl_pmds = pfm_pmu_conf->regs.pmds;
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> + u64 *used_pmds;
> + unsigned int i, max_pmd;
>
> - /*
> - * must restore all pmds to avoid leaking
> - * information to user.
> + /* The model-specific module can override the default
> + * restore-PMD method.
> */
> - for (i = 0; i < max_pmd; i++) {
> -
> - if (!test_bit(i, impl_pmds))
> - continue;
> -
> - val = set->pmds[i].value;
> + if (arch_info->restore_pmds) {
> + return arch_info->restore_pmds(set);
> + }
>
> - /*
> - * set upper bits for counter to ensure
> - * overflow will trigger
> - */
> - val &= ovfl_mask;
> + max_pmd = pfm_pmu_conf->regs.max_pmd;
> + used_pmds = set->used_pmds;
>
> - pfm_arch_write_pmd(ctx, i, val);
> - }
> + for (i = 0; i < max_pmd; i++)
> + if (test_bit(i, used_pmds) &&
> + !(pfm_pmu_conf->pmd_desc[i].type & PFM_REG_RO))
> + pfm_arch_write_pmd(ctx, i, set->pmds[i].value);
> }
>
> /*
> @@ -219,20 +189,28 @@ void pfm_arch_restore_pmds(struct pfm_co
> */
> void pfm_arch_restore_pmcs(struct pfm_context *ctx, struct pfm_event_set
> *set)
> {
> + struct pfm_arch_pmu_info *arch_info;
> u64 *impl_pmcs;
> unsigned int i, max_pmc;
>
> - max_pmc = pfm_pmu_conf->regs.max_pmc;
> - impl_pmcs = pfm_pmu_conf->regs.pmcs;
> + /* The model-specific module can override the default
> + * restore-PMC method.
> + */
> + arch_info = pfm_pmu_conf->arch_info;
> + if (arch_info->restore_pmcs) {
> + return arch_info->restore_pmcs(set);
> + }
>
> - /*
> - * - by default no PMCS measures anything
> - * - on ctxswout, all used PMCs are disabled (cccr enable bit cleared)
> - * hence when masked we do not need to restore anything
> + /* The "common" powerpc model's enable the counters simply by writing
> + * all the control registers. Therefore, if we're masked or stopped we
> + * don't need to bother restoring the PMCs now.
> */
> if (ctx->state == PFM_CTX_MASKED || ctx->flags.started == 0)
> return;
>
> + max_pmc = pfm_pmu_conf->regs.max_pmc;
> + impl_pmcs = pfm_pmu_conf->regs.pmcs;
> +
> /*
> * restore all pmcs
> */
> @@ -284,3 +262,21 @@ void pfm_arch_init_percpu(void)
> ppc64_enable_pmcs();
> #endif
> }
> +
> +/**
> + * powerpc_irq_handler
> + *
> + * Get the perfmon context that belongs to the current CPU, and call the
> + * model-specific interrupt handler.
> + **/
> +void powerpc_irq_handler(struct pt_regs *regs)
> +{
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> + struct pfm_context *ctx;
> +
> + if (arch_info->irq_handler) {
> + ctx = __get_cpu_var(pmu_ctx);
> + if (likely(ctx))
> + arch_info->irq_handler(regs, ctx);
> + }
> +}
> Index: linux-2.6.22-arnd1-perfmon1/include/asm-powerpc/cell-pmu.h
> ===================================================================
> --- linux-2.6.22-arnd1-perfmon1.orig/include/asm-powerpc/cell-pmu.h
> +++ linux-2.6.22-arnd1-perfmon1/include/asm-powerpc/cell-pmu.h
> @@ -61,6 +61,7 @@
>
> /* Macros for the pm_status register. */
> #define CBE_PM_CTR_OVERFLOW_INTR(ctr) (1 << (31 - ((ctr) & 7)))
> +#define CBE_PM_OVERFLOW_CTRS(pm_status) (((pm_status) >> 24) & 0xff)
>
> enum pm_reg_name {
> group_control,
> Index: linux-2.6.22-arnd1-perfmon1/include/asm-powerpc/perfmon.h
> ===================================================================
> --- linux-2.6.22-arnd1-perfmon1.orig/include/asm-powerpc/perfmon.h
> +++ linux-2.6.22-arnd1-perfmon1/include/asm-powerpc/perfmon.h
> @@ -39,10 +39,41 @@ enum powerpc_pmu_type {
> PFM_POWERPC_PMU_7400,
> PFM_POWERPC_PMU_7450,
> PFM_POWERPC_PMU_POWER5,
> + PFM_POWERPC_PMU_CELL,
> };
>
> struct pfm_arch_pmu_info {
> enum powerpc_pmu_type pmu_style;
> +
> + void (*write_pmc)(unsigned int cnum, u64 value);
> + void (*write_pmd)(unsigned int cnum, u64 value);
> +
> + u64 (*read_pmd)(unsigned int cnum);
> +
> + void (*enable_counters)(struct pfm_context *ctx,
> + struct pfm_event_set *set);
> + void (*disable_counters)(struct pfm_context *ctx,
> + struct pfm_event_set *set);
> +
> + void (*irq_handler)(struct pt_regs *regs, struct pfm_context *ctx);
> + void (*get_ovfl_pmds)(struct pfm_context *ctx,
> + struct pfm_event_set *set);
> +
> + /* The following routines are optional. */
> + void (*restore_pmcs)(struct pfm_event_set *set);
> + void (*restore_pmds)(struct pfm_event_set *set);
> +
> + int (*ctxswout_thread)(struct task_struct *task,
> + struct pfm_context *ctx,
> + struct pfm_event_set *set);
> + void (*ctxswin_thread)(struct task_struct *task,
> + struct pfm_context *ctx,
> + struct pfm_event_set *set);
> + int (*load_context)(struct pfm_context *ctx,
> + struct pfm_event_set *set,
> + struct task_struct *task);
> + int (*unload_context)(struct pfm_context *ctx,
> + struct task_struct *task);
> };
>
> #ifdef CONFIG_PPC32
> @@ -63,114 +94,39 @@ static inline void pfm_arch_write_pmc(st
> unsigned int cnum,
> u64 value)
> {
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> +
> /*
> * we only write to the actual register when monitoring is
> * active (pfm_start was issued)
> */
> - if (ctx && (ctx->flags.started == 0))
> + if (ctx && ctx->flags.started == 0)
> return;
>
> - switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
> - case SPRN_MMCR0:
> - mtspr(SPRN_MMCR0, value);
> - break;
> - case SPRN_MMCR1:
> - mtspr(SPRN_MMCR1, value);
> - break;
> -#ifdef CONFIG_PPC64
> - case SPRN_MMCRA:
> - mtspr(SPRN_MMCRA, value);
> - break;
> -#else
> - case SPRN_MMCR2:
> - mtspr(SPRN_MMCR2, value);
> - break;
> -#endif
> - default:
> - BUG();
> - }
> -}
> + BUG_ON(!arch_info->write_pmc);
>
> -static inline u64 pfm_arch_read_pmc(struct pfm_context *ctx, unsigned int
> cnum)
> -{
> - switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
> - case SPRN_MMCR0:
> - return mfspr(SPRN_MMCR0);
> - case SPRN_MMCR1:
> - return mfspr(SPRN_MMCR1);
> -#ifdef CONFIG_PPC64
> - case SPRN_MMCRA:
> - return mfspr(SPRN_MMCRA);
> -#else
> - case SPRN_MMCR2:
> - return mfspr(SPRN_MMCR2);
> -#endif
> - default:
> - BUG();
> - }
> + arch_info->write_pmc(cnum, value);
> }
>
> static inline void pfm_arch_write_pmd(struct pfm_context *ctx,
> unsigned int cnum, u64 value)
> {
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> +
> value &= pfm_pmu_conf->ovfl_mask;
>
> - switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
> - case SPRN_PMC1:
> - mtspr(SPRN_PMC1, value);
> - break;
> - case SPRN_PMC2:
> - mtspr(SPRN_PMC2, value);
> - break;
> - case SPRN_PMC3:
> - mtspr(SPRN_PMC3, value);
> - break;
> - case SPRN_PMC4:
> - mtspr(SPRN_PMC4, value);
> - break;
> - case SPRN_PMC5:
> - mtspr(SPRN_PMC5, value);
> - break;
> - case SPRN_PMC6:
> - mtspr(SPRN_PMC6, value);
> - break;
> -#ifdef CONFIG_PPC64
> - case SPRN_PMC7:
> - mtspr(SPRN_PMC7, value);
> - break;
> - case SPRN_PMC8:
> - mtspr(SPRN_PMC8, value);
> - break;
> -#endif
> - default:
> - BUG();
> - }
> + BUG_ON(!arch_info->write_pmd);
> +
> + arch_info->write_pmd(cnum, value);
> }
>
> static inline u64 pfm_arch_read_pmd(struct pfm_context *ctx, unsigned int
> cnum)
> {
> - switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
> - case SPRN_PMC1:
> - return mfspr(SPRN_PMC1);
> - case SPRN_PMC2:
> - return mfspr(SPRN_PMC2);
> - case SPRN_PMC3:
> - return mfspr(SPRN_PMC3);
> - case SPRN_PMC4:
> - return mfspr(SPRN_PMC4);
> - case SPRN_PMC5:
> - return mfspr(SPRN_PMC5);
> - case SPRN_PMC6:
> - return mfspr(SPRN_PMC6);
> -#ifdef CONFIG_PPC64
> - case SPRN_PMC7:
> - return mfspr(SPRN_PMC7);
> - case SPRN_PMC8:
> - return mfspr(SPRN_PMC8);
> -#endif
> - default:
> - BUG();
> - }
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> +
> + BUG_ON(!arch_info->read_pmd);
> +
> + return arch_info->read_pmd(cnum);
> }
>
> /*
> @@ -211,15 +167,12 @@ static inline void pfm_arch_ctxswin_sys(
> struct pfm_event_set *set)
> {}
>
> -static inline void pfm_arch_ctxswin_thread(struct task_struct *task,
> - struct pfm_context *ctx,
> - struct pfm_event_set *set)
> -{}
> -
> void pfm_arch_init_percpu(void);
> int pfm_arch_is_monitoring_active(struct pfm_context *ctx);
> int pfm_arch_ctxswout_thread(struct task_struct *task, struct pfm_context
> *ctx,
> struct pfm_event_set *set);
> +void pfm_arch_ctxswin_thread(struct task_struct *task, struct pfm_context
> *ctx,
> + struct pfm_event_set *set);
> void pfm_arch_stop(struct task_struct *task, struct pfm_context *ctx,
> struct pfm_event_set *set);
> void pfm_arch_start(struct task_struct *task, struct pfm_context *ctx,
> @@ -243,12 +196,16 @@ static inline void pfm_arch_intr_freeze_
> pfm_arch_stop(current, ctx, set);
> }
>
> +void powerpc_irq_handler(struct pt_regs *regs);
> +
> /*
> * unfreeze PMU from pfm_do_interrupt_handler()
> * ctx may be NULL for spurious
> */
> static inline void pfm_arch_intr_unfreeze_pmu(struct pfm_context *ctx)
> {
> + struct pfm_arch_pmu_info *arch_info;
> +
> if (!ctx)
> return;
>
> @@ -259,7 +216,9 @@ static inline void pfm_arch_intr_unfreez
> if (ctx->state == PFM_CTX_MASKED)
> return;
>
> - pfm_arch_restore_pmcs(ctx, ctx->active_set);
> + arch_info = pfm_pmu_conf->arch_info;
> + BUG_ON(!arch_info->enable_counters);
> + arch_info->enable_counters(ctx->active_set);
> }
>
> /*
> @@ -311,12 +270,6 @@ static inline int pfm_arch_context_creat
> static inline void pfm_arch_context_free(struct pfm_context *ctx)
> {}
>
> -static inline int pfm_arch_unload_context(struct pfm_context *ctx,
> - struct task_struct *task)
> -{
> - return 0;
> -}
> -
> /*
> * function called from pfm_setfl_sane(). Context is locked
> * and interrupts are masked.
> @@ -342,7 +295,27 @@ static inline int pfm_arch_load_context(
> struct pfm_event_set *set,
> struct task_struct *task)
> {
> - return 0;
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> + int rc = 0;
> +
> + if (arch_info->load_context) {
> + rc = arch_info->load_context(ctx, set, task);
> + }
> +
> + return rc;
> +}
> +
> +static inline int pfm_arch_unload_context(struct pfm_context *ctx,
> + struct task_struct *task)
> +{
> + struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
> + int rc = 0;
> +
> + if (arch_info->unload_context) {
> + rc = arch_info->unload_context(ctx, task);
> + }
> +
> + return rc;
> }
>
> /*
> @@ -356,14 +329,23 @@ static inline int pfm_smpl_buffer_alloc_
>
> static inline int pfm_arch_pmu_acquire(void)
> {
> - return 0;
> + return reserve_pmc_hardware(powerpc_irq_handler);
> }
>
> static inline void pfm_arch_pmu_release(void)
> -{}
> +{
> + release_pmc_hardware();
> +}
>
> struct pfm_arch_context {
> - /* empty */
> + /* Cell: Most recent value of the pm_status
> + * register read by the interrupt handler.
> + *
> + * Interrupt handler sets last_read_updated if it
> + * just read and updated last_read_pm_status
> + */
> + u32 last_read_pm_status;
> + u32 last_read_updated;
> };
>
> #define PFM_ARCH_CTX_SIZE sizeof(struct pfm_arch_context)
> _______________________________________________
> perfmon mailing list
> [email protected]
> http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/
--
-Stephane
_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/