oprofile, s390: Enhance OProfile to support System zs hardware sampling feature
authorHeinz Graalfs <graalfs@linux.vnet.ibm.com>
Fri, 21 Jan 2011 10:06:53 +0000 (10:06 +0000)
committerRobert Richter <robert.richter@amd.com>
Tue, 15 Feb 2011 10:08:50 +0000 (11:08 +0100)
OProfile is enhanced to export all files for controlling System z's
hardware sampling, and to invoke hwsampler exported functions to
initialize and use System z's hardware sampling.

The patch invokes hwsampler_setup() during oprofile init and exports
following hwsampler files under oprofilefs if hwsampler's setup
succeeded:

A new directory for hardware sampling based files

 /dev/oprofile/hwsampling/

The userland daemon must explicitly write to the following files
to disable (or enable) hardware based sampling

 /dev/oprofile/hwsampling/hwsampler

to modify the actual sampling rate

 /dev/oprofile/hwsampling/hw_interval

to modify the amount of sampling memory (measured in 4K pages)

 /dev/oprofile/hwsampling/hw_sdbt_blocks

The following files are read only and show
the possible minimum sampling rate

 /dev/oprofile/hwsampling/hw_min_interval

the possible maximum sampling rate

 /dev/oprofile/hwsampling/hw_max_interval

The patch splits the oprofile_timer_[init/exit] function so that it
can be also called through user context (oprofilefs) to avoid kernel
oops.

Applied with following changes:
* whitespace changes in Makefile and timer_int.c

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Maran Pakkirisamy <maranp@linux.vnet.ibm.com>
Signed-off-by: Heinz Graalfs <graalfs@linux.vnet.ibm.com>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Robert Richter <robert.richter@amd.com>
arch/s390/oprofile/Makefile
arch/s390/oprofile/hwsampler_files.c [new file with mode: 0644]
arch/s390/oprofile/init.c
drivers/oprofile/oprof.c
drivers/oprofile/oprof.h
drivers/oprofile/timer_int.c
include/linux/oprofile.h

index d698cddcfbdd9db4d60a39c1c84acf6989afd2ca..00d8fc8e4429065fe4a4d6a68e172248eccc7f16 100644 (file)
@@ -6,4 +6,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
                oprofilefs.o oprofile_stats.o  \
                timer_int.o )
 
-oprofile-y :=  $(DRIVER_OBJS) init.o backtrace.o hwsampler.o
+oprofile-y :=  $(DRIVER_OBJS) init.o backtrace.o hwsampler.o hwsampler_files.o
diff --git a/arch/s390/oprofile/hwsampler_files.c b/arch/s390/oprofile/hwsampler_files.c
new file mode 100644 (file)
index 0000000..493f7cc
--- /dev/null
@@ -0,0 +1,146 @@
+/**
+ * arch/s390/oprofile/hwsampler_files.c
+ *
+ * Copyright IBM Corp. 2010
+ * Author: Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com)
+ */
+#include <linux/oprofile.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include "hwsampler.h"
+
+#define DEFAULT_INTERVAL       4096
+
+#define DEFAULT_SDBT_BLOCKS    1
+#define DEFAULT_SDB_BLOCKS     511
+
+static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL;
+static unsigned long oprofile_min_interval;
+static unsigned long oprofile_max_interval;
+
+static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS;
+static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS;
+
+static unsigned long oprofile_hwsampler;
+
+static int oprofile_hwsampler_start(void)
+{
+       int retval;
+
+       retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
+       if (retval)
+               return retval;
+
+       retval = hwsampler_start_all(oprofile_hw_interval);
+       if (retval)
+               hwsampler_deallocate();
+
+       return retval;
+}
+
+static void oprofile_hwsampler_stop(void)
+{
+       hwsampler_stop_all();
+       hwsampler_deallocate();
+       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,
+               size_t count, loff_t *offset)
+{
+       return oprofilefs_ulong_to_user(oprofile_hwsampler, buf, count, offset);
+}
+
+static ssize_t hwsampler_write(struct file *file, char const __user *buf,
+               size_t count, loff_t *offset)
+{
+       unsigned long val;
+       int retval;
+
+       if (*offset)
+               return -EINVAL;
+
+       retval = oprofilefs_ulong_from_user(&val, buf, count);
+       if (retval)
+               return retval;
+
+       if (oprofile_hwsampler == val)
+               return -EINVAL;
+
+       retval = oprofile_set_hwsampler(val);
+
+       if (retval)
+               return retval;
+
+       oprofile_hwsampler = val;
+       return count;
+}
+
+static const struct file_operations hwsampler_fops = {
+       .read           = hwsampler_read,
+       .write          = hwsampler_write,
+};
+
+static int oprofile_create_hwsampling_files(struct super_block *sb,
+                                               struct dentry *root)
+{
+       struct dentry *hw_dir;
+
+       /* reinitialize default values */
+       oprofile_hwsampler = 1;
+
+       hw_dir = oprofilefs_mkdir(sb, root, "hwsampling");
+       if (!hw_dir)
+               return -EINVAL;
+
+       oprofilefs_create_file(sb, hw_dir, "hwsampler", &hwsampler_fops);
+       oprofilefs_create_ulong(sb, hw_dir, "hw_interval",
+                               &oprofile_hw_interval);
+       oprofilefs_create_ro_ulong(sb, hw_dir, "hw_min_interval",
+                               &oprofile_min_interval);
+       oprofilefs_create_ro_ulong(sb, hw_dir, "hw_max_interval",
+                               &oprofile_max_interval);
+       oprofilefs_create_ulong(sb, hw_dir, "hw_sdbt_blocks",
+                               &oprofile_sdbt_blocks);
+
+       return 0;
+}
+
+int oprofile_hwsampler_init(struct oprofile_operations* ops)
+{
+       if (hwsampler_setup())
+               return -ENODEV;
+
+       /*
+        * create hwsampler files only if hwsampler_setup() succeeds.
+        */
+       ops->create_files = oprofile_create_hwsampling_files;
+       oprofile_min_interval = hwsampler_query_min_interval();
+       if (oprofile_min_interval < 0) {
+               oprofile_min_interval = 0;
+               return -ENODEV;
+       }
+       oprofile_max_interval = hwsampler_query_max_interval();
+       if (oprofile_max_interval < 0) {
+               oprofile_max_interval = 0;
+               return -ENODEV;
+       }
+       oprofile_arch_set_hwsampler(ops);
+       return 0;
+}
+
+void oprofile_hwsampler_exit(void)
+{
+       hwsampler_shutdown();
+}
index 7a995113b91844cc47caa8faacf851723792ff7e..f6b3f724f590b1bc902c96814d6dd07f5cacef9d 100644 (file)
 #include <linux/oprofile.h>
 #include <linux/init.h>
 #include <linux/errno.h>
+#include <linux/fs.h>
 
+extern int oprofile_hwsampler_init(struct oprofile_operations* ops);
+extern void oprofile_hwsampler_exit(void);
 
 extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth);
 
 int __init oprofile_arch_init(struct oprofile_operations* ops)
 {
        ops->backtrace = s390_backtrace;
-       return -ENODEV;
+
+       return oprofile_hwsampler_init(ops);
 }
 
 void oprofile_arch_exit(void)
 {
+       oprofile_hwsampler_exit();
 }
index f9bda64fcd1b62771880d5d823b53f797cb9c7f0..43b01daa91e132bf8a86f1dc6e61355635f54999 100644 (file)
@@ -239,6 +239,38 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val)
        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)
 {
        int err;
index 177b73de5e5f158cbb31fe27f8e31b9363c3a255..5a6ceb1954a2d7ea227aa7d9bc484e588f98df8c 100644 (file)
@@ -35,7 +35,9 @@ struct dentry;
 
 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);
 void oprofile_timer_exit(void);
+void __oprofile_timer_exit(void);
 
 int oprofile_set_ulong(unsigned long *addr, unsigned long val);
 int oprofile_set_timeout(unsigned long time);
index 010725117dbb0c81d042c7257a94724554425491..0099a458fd3774b58ff73b7ef74547f6747b5ee9 100644 (file)
@@ -97,14 +97,13 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {
        .notifier_call = oprofile_cpu_notify,
 };
 
-int __init oprofile_timer_init(struct oprofile_operations *ops)
+int  __oprofile_timer_init(struct oprofile_operations *ops)
 {
        int rc;
 
        rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
        if (rc)
                return rc;
-       ops->create_files = NULL;
        ops->setup = NULL;
        ops->shutdown = NULL;
        ops->start = oprofile_hrtimer_start;
@@ -113,7 +112,17 @@ int __init oprofile_timer_init(struct oprofile_operations *ops)
        return 0;
 }
 
-void __exit oprofile_timer_exit(void)
+int __init oprofile_timer_init(struct oprofile_operations *ops)
+{
+       return __oprofile_timer_init(ops);
+}
+
+void __oprofile_timer_exit(void)
 {
        unregister_hotcpu_notifier(&oprofile_cpu_notifier);
 }
+
+void __exit oprofile_timer_exit(void)
+{
+       __oprofile_timer_exit();
+}
index 7f5cfd3b37dd4d9dfe11ebdb1db92fb840c61dd4..b517d869e1ad90fc50e2a5285fef144befb893e6 100644 (file)
@@ -91,6 +91,27 @@ int oprofile_arch_init(struct oprofile_operations * ops);
  */
 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.
  */
This page took 0.037712 seconds and 5 git commands to generate.