Commit e7becb96 authored by David Gibson's avatar David Gibson Committed by Linus Torvalds

[PATCH] ppc64: functions to reserve performance monitor hardware

The PPC64 interrupt code includes a hook to call when an exception
from the performance monitor unit occurs.  However, there's no way of
reserving the hook properly, so if more than one bit of code tries to
use it things will get ugly.  Currently oprofile is the only user, but
there are likely to be more in future e.g. perfctr, if and when it
reaches a fit state for merging.

This patch creates functions to reserve and release the performance
monitor hardware (including its interrupt), and makes oprofile use
them.  It also creates a new arch/ppc64/kernel/pmc.c, in which we can
put any future helper functions for handling the performance monitor
counters.
Signed-off-by: default avatarDavid Gibson <dwg@au1.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2fe0c005
......@@ -11,7 +11,7 @@ obj-y := setup.o entry.o traps.o irq.o idle.o dma.o \
udbg.o binfmt_elf32.o sys_ppc32.o ioctl32.o \
ptrace32.o signal32.o rtc.o init_task.o \
lmb.o cputable.o cpu_setup_power4.o idle_power4.o \
iommu.o sysfs.o vdso.o
iommu.o sysfs.o vdso.o pmc.o
obj-y += vdso32/ vdso64/
obj-$(CONFIG_PPC_OF) += of_device.o
......
/*
* linux/arch/ppc64/kernel/pmc.c
*
* Copyright (C) 2004 David Gibson, IBM Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <asm/processor.h>
#include <asm/pmc.h>
/* Ensure exceptions are disabled */
static void dummy_perf(struct pt_regs *regs)
{
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
mmcr0 &= ~(MMCR0_PMXE|MMCR0_PMAO);
mtspr(SPRN_MMCR0, mmcr0);
}
static spinlock_t pmc_owner_lock = SPIN_LOCK_UNLOCKED;
static void *pmc_owner_caller; /* mostly for debugging */
perf_irq_t perf_irq = dummy_perf;
int reserve_pmc_hardware(perf_irq_t new_perf_irq)
{
int err = 0;
spin_lock(&pmc_owner_lock);
if (pmc_owner_caller) {
printk(KERN_WARNING "reserve_pmc_hardware: "
"PMC hardware busy (reserved by caller %p)\n",
pmc_owner_caller);
err = -EBUSY;
goto out;
}
pmc_owner_caller = __builtin_return_address(0);
perf_irq = new_perf_irq ? : dummy_perf;
out:
spin_unlock(&pmc_owner_lock);
return err;
}
void release_pmc_hardware(void)
{
spin_lock(&pmc_owner_lock);
WARN_ON(! pmc_owner_caller);
pmc_owner_caller = NULL;
perf_irq = dummy_perf;
spin_unlock(&pmc_owner_lock);
}
......@@ -41,6 +41,7 @@
#include <asm/rtas.h>
#include <asm/systemcfg.h>
#include <asm/machdep.h>
#include <asm/pmc.h>
#ifdef CONFIG_DEBUGGER
int (*__debugger)(struct pt_regs *regs);
......@@ -450,18 +451,7 @@ void altivec_unavailable_exception(struct pt_regs *regs)
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
}
/* Ensure exceptions are disabled */
static void dummy_perf(struct pt_regs *regs)
{
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
mmcr0 &= ~(MMCR0_PMXE|MMCR0_PMAO);
mtspr(SPRN_MMCR0, mmcr0);
}
void (*perf_irq)(struct pt_regs *) = dummy_perf;
EXPORT_SYMBOL(perf_irq);
extern perf_irq_t perf_irq;
void performance_monitor_exception(struct pt_regs *regs)
{
......
......@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/pmc.h>
#include "op_impl.h"
......@@ -22,9 +23,6 @@ extern struct op_ppc64_model op_model_rs64;
extern struct op_ppc64_model op_model_power4;
static struct op_ppc64_model *model;
extern void (*perf_irq)(struct pt_regs *);
static void (*save_perf_irq)(struct pt_regs *);
static struct op_counter_config ctr[OP_MAX_COUNTER];
static struct op_system_config sys;
......@@ -35,11 +33,12 @@ static void op_handle_interrupt(struct pt_regs *regs)
static int op_ppc64_setup(void)
{
/* Install our interrupt handler into the existing hook. */
save_perf_irq = perf_irq;
perf_irq = op_handle_interrupt;
int err;
mb();
/* Grab the hardware */
err = reserve_pmc_hardware(op_handle_interrupt);
if (err)
return err;
/* Pre-compute the values to stuff in the hardware registers. */
model->reg_setup(ctr, &sys, model->num_counters);
......@@ -52,10 +51,7 @@ static int op_ppc64_setup(void)
static void op_ppc64_shutdown(void)
{
mb();
/* Remove our interrupt handler. We may be removing this module. */
perf_irq = save_perf_irq;
release_pmc_hardware();
}
static void op_ppc64_cpu_start(void *dummy)
......
/*
* pmc.h
* Copyright (C) 2004 David Gibson, IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PPC64_PMC_H
#define _PPC64_PMC_H
#include <asm/ptrace.h>
typedef void (*perf_irq_t)(struct pt_regs *);
int reserve_pmc_hardware(perf_irq_t new_perf_irq);
void release_pmc_hardware(void);
#endif /* _PPC64_PMC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment