Commit f9e58669 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: initial oprofile support

parent 74bb2dba
...@@ -503,6 +503,7 @@ config VIOPATH ...@@ -503,6 +503,7 @@ config VIOPATH
depends on PPC_ISERIES depends on PPC_ISERIES
default y default y
source "arch/ppc64/oprofile/Kconfig"
menu "Kernel hacking" menu "Kernel hacking"
......
...@@ -30,6 +30,9 @@ HEAD := arch/ppc64/kernel/head.o ...@@ -30,6 +30,9 @@ HEAD := arch/ppc64/kernel/head.o
core-y += arch/ppc64/kernel/ arch/ppc64/mm/ arch/ppc64/lib/ core-y += arch/ppc64/kernel/ arch/ppc64/mm/ arch/ppc64/lib/
core-$(CONFIG_XMON) += arch/ppc64/xmon/ core-$(CONFIG_XMON) += arch/ppc64/xmon/
# FIXME: is drivers- right ?
drivers-$(CONFIG_OPROFILE) += arch/ppc64/oprofile/
makeboot = $(call descend,arch/ppc64/boot,$(1)) makeboot = $(call descend,arch/ppc64/boot,$(1))
ifdef CONFIG_PPC_PSERIES ifdef CONFIG_PPC_PSERIES
......
...@@ -35,6 +35,8 @@ obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o ...@@ -35,6 +35,8 @@ obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PROFILING) += profile.o
obj-y += prom.o lmb.o rtas.o rtas-proc.o chrp_setup.o i8259.o obj-y += prom.o lmb.o rtas.o rtas-proc.o chrp_setup.o i8259.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
......
...@@ -743,7 +743,7 @@ _GLOBAL(sys_call_table32) ...@@ -743,7 +743,7 @@ _GLOBAL(sys_call_table32)
.llong .sys_ni_syscall /* reserved for alloc_hugepages */ .llong .sys_ni_syscall /* reserved for alloc_hugepages */
.llong .sys_ni_syscall /* reserved for free_hugepages */ .llong .sys_ni_syscall /* reserved for free_hugepages */
.llong .sys_exit_group .llong .sys_exit_group
.llong .sys_lookup_dcookie /* 245 */ .llong .sys32_lookup_dcookie /* 245 */
.llong .sys_epoll_create .llong .sys_epoll_create
.llong .sys_epoll_ctl .llong .sys_epoll_ctl
.llong .sys_epoll_wait .llong .sys_epoll_wait
......
...@@ -245,3 +245,6 @@ EXPORT_SYMBOL(debugger_fault_handler); ...@@ -245,3 +245,6 @@ EXPORT_SYMBOL(debugger_fault_handler);
#endif #endif
EXPORT_SYMBOL(tb_ticks_per_usec); EXPORT_SYMBOL(tb_ticks_per_usec);
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
/*
* linux/arch/i386/kernel/profile.c
*
* (C) 2002 John Levon <levon@movementarian.org>
*
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/irq.h>
#include <asm/hw_irq.h>
static struct notifier_block * profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void ppc64_profile_hook(struct pt_regs * regs)
{
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -4522,3 +4522,12 @@ unsigned long sys32_mmap2(unsigned long addr, size_t len, ...@@ -4522,3 +4522,12 @@ unsigned long sys32_mmap2(unsigned long addr, size_t len,
/* This should remain 12 even if PAGE_SIZE changes */ /* This should remain 12 even if PAGE_SIZE changes */
return sys_mmap(addr, len, prot, flags, fd, pgoff << 12); return sys_mmap(addr, len, prot, flags, fd, pgoff << 12);
} }
extern int sys_lookup_dcookie(u64 cookie64, char *buf, size_t len);
long sys32_lookup_dcookie(u32 cookie_high, u32 cookie_low, char *buf,
size_t len)
{
return sys_lookup_dcookie((u64)cookie_high << 32 | cookie_low,
buf, len);
}
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/profile.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -95,21 +96,35 @@ extern unsigned long wall_jiffies; ...@@ -95,21 +96,35 @@ extern unsigned long wall_jiffies;
extern unsigned long lpEvent_count; extern unsigned long lpEvent_count;
extern int smp_tb_synchronized; extern int smp_tb_synchronized;
extern unsigned long prof_cpu_mask;
extern unsigned int * prof_buffer;
extern unsigned long prof_len;
extern unsigned long prof_shift;
extern char _stext;
void ppc_adjtimex(void); void ppc_adjtimex(void);
static unsigned adjusting_time = 0; static unsigned adjusting_time = 0;
static inline void ppc_do_profile (unsigned long nip) /*
* The profiling function is SMP safe. (nothing can mess
* around with "current", and the profiling counters are
* updated with atomic operations). This is especially
* useful with a profiling multiplier != 1
*/
static inline void ppc64_do_profile(struct pt_regs *regs)
{ {
unsigned long nip;
extern unsigned long prof_cpu_mask;
extern char _stext;
#ifdef CONFIG_PROFILING
extern void ppc64_profile_hook(struct pt_regs *);
ppc64_profile_hook(regs);
#endif
if (user_mode(regs))
return;
if (!prof_buffer) if (!prof_buffer)
return; return;
nip = instruction_pointer(regs);
/* /*
* Only measure the CPUs specified by /proc/irq/prof_cpu_mask. * Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
* (default is all CPUs.) * (default is all CPUs.)
...@@ -129,7 +144,6 @@ static inline void ppc_do_profile (unsigned long nip) ...@@ -129,7 +144,6 @@ static inline void ppc_do_profile (unsigned long nip)
atomic_inc((atomic_t *)&prof_buffer[nip]); atomic_inc((atomic_t *)&prof_buffer[nip]);
} }
static __inline__ void timer_check_rtc(void) static __inline__ void timer_check_rtc(void)
{ {
/* /*
...@@ -259,8 +273,7 @@ int timer_interrupt(struct pt_regs * regs) ...@@ -259,8 +273,7 @@ int timer_interrupt(struct pt_regs * regs)
irq_enter(); irq_enter();
#ifndef CONFIG_PPC_ISERIES #ifndef CONFIG_PPC_ISERIES
if (!user_mode(regs)) ppc64_do_profile(regs);
ppc_do_profile(instruction_pointer(regs));
#endif #endif
lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 0; lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 0;
......
menu "Profiling support"
depends on EXPERIMENTAL
config PROFILING
bool "Profiling support (EXPERIMENTAL)"
help
Say Y here to enable the extended profiling support mechanisms used
by profilers such as OProfile.
config OPROFILE
tristate "OProfile system profiling (EXPERIMENTAL)"
depends on PROFILING
help
OProfile is a profiling system capable of profiling the
whole system, include the kernel, kernel modules, libraries,
and applications.
If unsure, say N.
endmenu
obj-$(CONFIG_OPROFILE) += oprofile.o
DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprof.o cpu_buffer.o buffer_sync.o \
event_buffer.o oprofile_files.o \
oprofilefs.o oprofile_stats.o )
oprofile-y := $(DRIVER_OBJS) init.o timer_int.o
/**
* @file init.c
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
*/
#include <linux/kernel.h>
#include <linux/oprofile.h>
#include <linux/init.h>
extern void timer_init(struct oprofile_operations ** ops, enum oprofile_cpu * cpu);
int __init oprofile_arch_init(struct oprofile_operations ** ops, enum oprofile_cpu * cpu)
{
timer_init(ops, cpu);
return 0;
}
/**
* @file timer_int.c
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/smp.h>
#include <linux/irq.h>
#include <linux/oprofile.h>
#include <asm/ptrace.h>
static int timer_notify(struct notifier_block * self, unsigned long val, void * data)
{
struct pt_regs * regs = (struct pt_regs *)data;
int cpu = smp_processor_id();
oprofile_add_sample(instruction_pointer(regs), 0, cpu);
return 0;
}
static struct notifier_block timer_notifier = {
.notifier_call = timer_notify,
};
static int timer_start(void)
{
return register_profile_notifier(&timer_notifier);
}
static void timer_stop(void)
{
unregister_profile_notifier(&timer_notifier);
}
static struct oprofile_operations timer_ops = {
.start = timer_start,
.stop = timer_stop
};
void __init timer_init(struct oprofile_operations ** ops, enum oprofile_cpu * cpu)
{
*ops = &timer_ops;
*cpu = OPROFILE_CPU_TIMER;
printk(KERN_INFO "oprofile: using timer interrupt.\n");
}
...@@ -80,5 +80,26 @@ static inline void __do_save_and_cli(unsigned long *flags) ...@@ -80,5 +80,26 @@ static inline void __do_save_and_cli(unsigned long *flags)
struct hw_interrupt_type; struct hw_interrupt_type;
static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {}
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block * nb);
int unregister_profile_notifier(struct notifier_block * nb);
#else
static inline int register_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
#endif /* CONFIG_PROFILING */
#endif /* _PPC64_HW_IRQ_H */ #endif /* _PPC64_HW_IRQ_H */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
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