oprofile, s390: Rework hwsampler implementation

This patch is a rework of the hwsampler oprofile implementation that
has been applied recently. Now there are less non-architectural
changes. The only changes are:

* introduction of oprofile_add_ext_hw_sample(), and
* removal of section attributes of oprofile_timer_init/_exit().

To setup hwsampler for oprofile we need to modify start()/stop()
callbacks and additional hwsampler control files in oprofilefs. We do
not reinitialize the timer or hwsampler mode by restarting calling
init/exit() anymore, instead hwsampler_running is used to switch the
mode directly in oprofile_hwsampler_start/_stop(). For locking reasons
there is also hwsampler_file that reflects the value in oprofilefs.

The overall diffstat of the oprofile s390 hwsampler implemenation
shows the low impact to non-architectural code:

 arch/Kconfig                         |    3 +
 arch/s390/Kconfig                    |    1 +
 arch/s390/oprofile/Makefile          |    2 +-
 arch/s390/oprofile/hwsampler.c       | 1256 ++++++++++++++++++++++++++++++++++
 arch/s390/oprofile/hwsampler.h       |  113 +++
 arch/s390/oprofile/hwsampler_files.c |  162 +++++
 arch/s390/oprofile/init.c            |    6 +-
 drivers/oprofile/cpu_buffer.c        |   24 +-
 drivers/oprofile/timer_int.c         |    4 +-
 include/linux/oprofile.h             |    7 +
 10 files changed, 1567 insertions(+), 11 deletions(-)

Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Robert Richter <robert.richter@amd.com>
This commit is contained in:
Robert Richter 2011-02-11 17:31:44 +01:00
parent 997dbb4967
commit a0d76247e0
6 changed files with 41 additions and 90 deletions

View File

@ -8,6 +8,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "../../../drivers/oprofile/oprof.h"
#include "hwsampler.h" #include "hwsampler.h"
#define DEFAULT_INTERVAL 4096 #define DEFAULT_INTERVAL 4096
@ -22,12 +23,20 @@ static unsigned long oprofile_max_interval;
static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS;
static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS;
static unsigned long oprofile_hwsampler; static int hwsampler_file;
static int hwsampler_running; /* start_mutex must be held to change */
static struct oprofile_operations timer_ops;
static int oprofile_hwsampler_start(void) static int oprofile_hwsampler_start(void)
{ {
int retval; int retval;
hwsampler_running = hwsampler_file;
if (!hwsampler_running)
return timer_ops.start();
retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
if (retval) if (retval)
return retval; return retval;
@ -41,25 +50,20 @@ static int oprofile_hwsampler_start(void)
static void oprofile_hwsampler_stop(void) static void oprofile_hwsampler_stop(void)
{ {
if (!hwsampler_running) {
timer_ops.stop();
return;
}
hwsampler_stop_all(); hwsampler_stop_all();
hwsampler_deallocate(); hwsampler_deallocate();
return; return;
} }
int oprofile_arch_set_hwsampler(struct oprofile_operations *ops)
{
printk(KERN_INFO "oprofile: using hardware sampling\n");
ops->start = oprofile_hwsampler_start;
ops->stop = oprofile_hwsampler_stop;
ops->cpu_type = "timer";
return 0;
}
static ssize_t hwsampler_read(struct file *file, char __user *buf, static ssize_t hwsampler_read(struct file *file, char __user *buf,
size_t count, loff_t *offset) size_t count, loff_t *offset)
{ {
return oprofilefs_ulong_to_user(oprofile_hwsampler, buf, count, offset); return oprofilefs_ulong_to_user(hwsampler_file, buf, count, offset);
} }
static ssize_t hwsampler_write(struct file *file, char const __user *buf, static ssize_t hwsampler_write(struct file *file, char const __user *buf,
@ -75,15 +79,16 @@ static ssize_t hwsampler_write(struct file *file, char const __user *buf,
if (retval) if (retval)
return retval; return retval;
if (oprofile_hwsampler == val) if (oprofile_started)
return -EINVAL; /*
* save to do without locking as we set
* hwsampler_running in start() when start_mutex is
* held
*/
return -EBUSY;
retval = oprofile_set_hwsampler(val); hwsampler_file = val;
if (retval)
return retval;
oprofile_hwsampler = val;
return count; return count;
} }
@ -98,7 +103,7 @@ static int oprofile_create_hwsampling_files(struct super_block *sb,
struct dentry *hw_dir; struct dentry *hw_dir;
/* reinitialize default values */ /* reinitialize default values */
oprofile_hwsampler = 1; hwsampler_file = 1;
hw_dir = oprofilefs_mkdir(sb, root, "hwsampling"); hw_dir = oprofilefs_mkdir(sb, root, "hwsampling");
if (!hw_dir) if (!hw_dir)
@ -125,7 +130,6 @@ int oprofile_hwsampler_init(struct oprofile_operations* ops)
/* /*
* create hwsampler files only if hwsampler_setup() succeeds. * create hwsampler files only if hwsampler_setup() succeeds.
*/ */
ops->create_files = oprofile_create_hwsampling_files;
oprofile_min_interval = hwsampler_query_min_interval(); oprofile_min_interval = hwsampler_query_min_interval();
if (oprofile_min_interval < 0) { if (oprofile_min_interval < 0) {
oprofile_min_interval = 0; oprofile_min_interval = 0;
@ -136,11 +140,23 @@ int oprofile_hwsampler_init(struct oprofile_operations* ops)
oprofile_max_interval = 0; oprofile_max_interval = 0;
return -ENODEV; return -ENODEV;
} }
oprofile_arch_set_hwsampler(ops);
if (oprofile_timer_init(ops))
return -ENODEV;
printk(KERN_INFO "oprofile: using hardware sampling\n");
memcpy(&timer_ops, ops, sizeof(timer_ops));
ops->start = oprofile_hwsampler_start;
ops->stop = oprofile_hwsampler_stop;
ops->create_files = oprofile_create_hwsampling_files;
return 0; return 0;
} }
void oprofile_hwsampler_exit(void) void oprofile_hwsampler_exit(void)
{ {
oprofile_timer_exit();
hwsampler_shutdown(); hwsampler_shutdown();
} }

View File

@ -11,7 +11,6 @@
#include <linux/oprofile.h> #include <linux/oprofile.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h>
extern int oprofile_hwsampler_init(struct oprofile_operations* ops); extern int oprofile_hwsampler_init(struct oprofile_operations* ops);
extern void oprofile_hwsampler_exit(void); extern void oprofile_hwsampler_exit(void);

View File

@ -239,38 +239,6 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val)
return err; return err;
} }
#ifdef CONFIG_HAVE_HWSAMPLER
int oprofile_set_hwsampler(unsigned long val)
{
int err = 0;
mutex_lock(&start_mutex);
if (oprofile_started) {
err = -EBUSY;
goto out;
}
switch (val) {
case 1:
/* Switch to hardware sampling. */
__oprofile_timer_exit();
err = oprofile_arch_set_hwsampler(&oprofile_ops);
break;
case 0:
printk(KERN_INFO "oprofile: using timer interrupt.\n");
err = __oprofile_timer_init(&oprofile_ops);
break;
default:
err = -EINVAL;
}
out:
mutex_unlock(&start_mutex);
return err;
}
#endif /* CONFIG_HAVE_HWSAMPLER */
static int __init oprofile_init(void) static int __init oprofile_init(void)
{ {
int err; int err;

View File

@ -35,9 +35,7 @@ struct dentry;
void oprofile_create_files(struct super_block *sb, struct dentry *root); void oprofile_create_files(struct super_block *sb, struct dentry *root);
int oprofile_timer_init(struct oprofile_operations *ops); int oprofile_timer_init(struct oprofile_operations *ops);
int __oprofile_timer_init(struct oprofile_operations *ops);
void oprofile_timer_exit(void); void oprofile_timer_exit(void);
void __oprofile_timer_exit(void);
int oprofile_set_ulong(unsigned long *addr, unsigned long val); int oprofile_set_ulong(unsigned long *addr, unsigned long val);
int oprofile_set_timeout(unsigned long time); int oprofile_set_timeout(unsigned long time);

View File

@ -97,13 +97,14 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {
.notifier_call = oprofile_cpu_notify, .notifier_call = oprofile_cpu_notify,
}; };
int __oprofile_timer_init(struct oprofile_operations *ops) int oprofile_timer_init(struct oprofile_operations *ops)
{ {
int rc; int rc;
rc = register_hotcpu_notifier(&oprofile_cpu_notifier); rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
if (rc) if (rc)
return rc; return rc;
ops->create_files = NULL;
ops->setup = NULL; ops->setup = NULL;
ops->shutdown = NULL; ops->shutdown = NULL;
ops->start = oprofile_hrtimer_start; ops->start = oprofile_hrtimer_start;
@ -112,17 +113,7 @@ int __oprofile_timer_init(struct oprofile_operations *ops)
return 0; return 0;
} }
int __init oprofile_timer_init(struct oprofile_operations *ops) void oprofile_timer_exit(void)
{
return __oprofile_timer_init(ops);
}
void __oprofile_timer_exit(void)
{ {
unregister_hotcpu_notifier(&oprofile_cpu_notifier); unregister_hotcpu_notifier(&oprofile_cpu_notifier);
} }
void __exit oprofile_timer_exit(void)
{
__oprofile_timer_exit();
}

View File

@ -91,27 +91,6 @@ int oprofile_arch_init(struct oprofile_operations * ops);
*/ */
void oprofile_arch_exit(void); void oprofile_arch_exit(void);
#ifdef CONFIG_HAVE_HWSAMPLER
/**
* setup hardware sampler for oprofiling.
*/
int oprofile_set_hwsampler(unsigned long);
/**
* hardware sampler module initialization for the s390 arch
*/
int oprofile_arch_set_hwsampler(struct oprofile_operations *ops);
/**
* Add an s390 hardware sample.
*/
void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs,
unsigned long event, int is_kernel,
struct task_struct *task);
#endif /* CONFIG_HAVE_HWSAMPLER */
/** /**
* Add a sample. This may be called from any context. * Add a sample. This may be called from any context.
*/ */