lkml.org 
[lkml]   [2004]   [May]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH][6/7] perfctr-2.7.2 for 2.6.6-mm2: global-mode counters
perfctr-2.7.2 for 2.6.6-mm2, part 6/7:

- driver for global-mode (system-wide) performance counters

global.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global.h | 16 ++++
2 files changed, 254 insertions(+)
diff -ruN linux-2.6.6-mm2/drivers/perfctr/global.c linux-2.6.6-mm2.perfctr-2.7.2.global/drivers/perfctr/global.c
--- linux-2.6.6-mm2/drivers/perfctr/global.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-mm2.perfctr-2.7.2.global/drivers/perfctr/global.c 2004-05-14 14:35:08.600164909 +0200
@@ -0,0 +1,238 @@
+/* $Id: global.c,v 1.40 2004/05/13 23:32:49 mikpe Exp $
+ * Global-mode performance-monitoring counters.
+ *
+ * Copyright (C) 2000-2004 Mikael Pettersson
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/perfctr.h>
+
+#include <asm/uaccess.h>
+
+#include "cpumask.h"
+#include "global.h"
+#include "marshal.h"
+
+static const char this_service[] = __FILE__;
+static int hardware_is_ours = 0;
+static struct timer_list sampling_timer;
+static DECLARE_MUTEX(control_mutex);
+static unsigned int nr_active_cpus = 0;
+
+struct gperfctr {
+ struct perfctr_cpu_state cpu_state;
+ spinlock_t lock;
+} ____cacheline_aligned;
+
+static struct gperfctr per_cpu_gperfctr[NR_CPUS] __cacheline_aligned;
+
+static int reserve_hardware(void)
+{
+ const char *other;
+
+ if( hardware_is_ours )
+ return 0;
+ other = perfctr_cpu_reserve(this_service);
+ if( other ) {
+ printk(KERN_ERR __FILE__ ":%s: failed because hardware is taken by '%s'\n",
+ __FUNCTION__, other);
+ return -EBUSY;
+ }
+ hardware_is_ours = 1;
+ return 0;
+}
+
+static void release_hardware(void)
+{
+ int i;
+
+ nr_active_cpus = 0;
+ if( hardware_is_ours ) {
+ hardware_is_ours = 0;
+ del_timer(&sampling_timer);
+ sampling_timer.data = 0;
+ perfctr_cpu_release(this_service);
+ for(i = 0; i < NR_CPUS; ++i)
+ per_cpu_gperfctr[i].cpu_state.cstatus = 0;
+ }
+}
+
+static void sample_this_cpu(void *unused)
+{
+ /* PREEMPT note: when called via smp_call_function(),
+ this is in IRQ context with preemption disabled. */
+ struct gperfctr *perfctr;
+
+ perfctr = &per_cpu_gperfctr[smp_processor_id()];
+ if( !perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) )
+ return;
+ spin_lock(&perfctr->lock);
+ perfctr_cpu_sample(&perfctr->cpu_state);
+ spin_unlock(&perfctr->lock);
+}
+
+static void sample_all_cpus(void)
+{
+ on_each_cpu(sample_this_cpu, NULL, 1, 1);
+}
+
+static void sampling_timer_function(unsigned long interval)
+{
+ sample_all_cpus();
+ sampling_timer.expires = jiffies + interval;
+ add_timer(&sampling_timer);
+}
+
+static unsigned long usectojiffies(unsigned long usec)
+{
+ usec += 1000000 / HZ - 1;
+ usec /= 1000000 / HZ;
+ return usec;
+}
+
+static void start_sampling_timer(unsigned long interval_usec)
+{
+ if( interval_usec > 0 ) {
+ unsigned long interval = usectojiffies(interval_usec);
+ init_timer(&sampling_timer);
+ sampling_timer.function = sampling_timer_function;
+ sampling_timer.data = interval;
+ sampling_timer.expires = jiffies + interval;
+ add_timer(&sampling_timer);
+ }
+}
+
+static void start_this_cpu(void *unused)
+{
+ /* PREEMPT note: when called via smp_call_function(),
+ this is in IRQ context with preemption disabled. */
+ struct gperfctr *perfctr;
+
+ perfctr = &per_cpu_gperfctr[smp_processor_id()];
+ if( perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) )
+ perfctr_cpu_resume(&perfctr->cpu_state);
+}
+
+static void start_all_cpus(void)
+{
+ on_each_cpu(start_this_cpu, NULL, 1, 1);
+}
+
+static int gperfctr_control(struct perfctr_struct_buf *argp)
+{
+ int ret;
+ struct gperfctr *perfctr;
+ struct gperfctr_cpu_control cpu_control;
+
+ ret = perfctr_copy_from_user(&cpu_control, argp, &gperfctr_cpu_control_sdesc);
+ if( ret )
+ return ret;
+ if( cpu_control.cpu >= NR_CPUS ||
+ !cpu_online(cpu_control.cpu) ||
+ perfctr_cpu_is_forbidden(cpu_control.cpu) )
+ return -EINVAL;
+ /* we don't permit i-mode counters */
+ if( cpu_control.cpu_control.nrictrs != 0 )
+ return -EPERM;
+ down(&control_mutex);
+ ret = -EBUSY;
+ if( hardware_is_ours )
+ goto out_up; /* you have to stop them first */
+ perfctr = &per_cpu_gperfctr[cpu_control.cpu];
+ spin_lock(&perfctr->lock);
+ perfctr->cpu_state.tsc_start = 0;
+ perfctr->cpu_state.tsc_sum = 0;
+ memset(&perfctr->cpu_state.pmc, 0, sizeof perfctr->cpu_state.pmc);
+ perfctr->cpu_state.control = cpu_control.cpu_control;
+ ret = perfctr_cpu_update_control(&perfctr->cpu_state, 1);
+ spin_unlock(&perfctr->lock);
+ if( ret < 0 )
+ goto out_up;
+ if( perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) )
+ ++nr_active_cpus;
+ ret = nr_active_cpus;
+ out_up:
+ up(&control_mutex);
+ return ret;
+}
+
+static int gperfctr_start(unsigned int interval_usec)
+{
+ int ret;
+
+ if( interval_usec < 10000 )
+ return -EINVAL;
+ down(&control_mutex);
+ ret = nr_active_cpus;
+ if( ret > 0 ) {
+ if( reserve_hardware() < 0 ) {
+ ret = -EBUSY;
+ } else {
+ start_all_cpus();
+ start_sampling_timer(interval_usec);
+ }
+ }
+ up(&control_mutex);
+ return ret;
+}
+
+static int gperfctr_stop(void)
+{
+ down(&control_mutex);
+ release_hardware();
+ up(&control_mutex);
+ return 0;
+}
+
+static int gperfctr_read(struct perfctr_struct_buf *argp)
+{
+ struct gperfctr *perfctr;
+ struct gperfctr_cpu_state state;
+ int err;
+
+ // XXX: sample_all_cpus() ???
+ err = perfctr_copy_from_user(&state, argp, &gperfctr_cpu_state_only_cpu_sdesc);
+ if( err )
+ return err;
+ if( state.cpu >= NR_CPUS || !cpu_online(state.cpu) )
+ return -EINVAL;
+ perfctr = &per_cpu_gperfctr[state.cpu];
+ spin_lock(&perfctr->lock);
+ state.cpu_control = perfctr->cpu_state.control;
+ //state.sum = perfctr->cpu_state.sum;
+ {
+ int j;
+ state.sum.tsc = perfctr->cpu_state.tsc_sum;
+ for(j = 0; j < ARRAY_SIZE(state.sum.pmc); ++j)
+ state.sum.pmc[j] = perfctr->cpu_state.pmc[j].sum;
+ }
+ spin_unlock(&perfctr->lock);
+ return perfctr_copy_to_user(argp, &state, &gperfctr_cpu_state_sdesc);
+}
+
+int sys_gperfctr(unsigned int cmd, unsigned long arg)
+{
+ if( !capable(CAP_SYS_PTRACE) )
+ return -EPERM;
+ switch( cmd ) {
+ case GPERFCTR_CONTROL:
+ return gperfctr_control((struct perfctr_struct_buf*)arg);
+ case GPERFCTR_READ:
+ return gperfctr_read((struct perfctr_struct_buf*)arg);
+ case GPERFCTR_STOP:
+ return gperfctr_stop();
+ case GPERFCTR_START:
+ return gperfctr_start(arg);
+ }
+ return -EINVAL;
+}
+
+void __init gperfctr_init(void)
+{
+ int i;
+
+ for(i = 0; i < NR_CPUS; ++i)
+ per_cpu_gperfctr[i].lock = SPIN_LOCK_UNLOCKED;
+}
diff -ruN linux-2.6.6-mm2/drivers/perfctr/global.h linux-2.6.6-mm2.perfctr-2.7.2.global/drivers/perfctr/global.h
--- linux-2.6.6-mm2/drivers/perfctr/global.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.6-mm2.perfctr-2.7.2.global/drivers/perfctr/global.h 2004-05-14 14:35:08.600164909 +0200
@@ -0,0 +1,16 @@
+/* $Id: global.h,v 1.8 2004/05/13 23:32:49 mikpe Exp $
+ * Global-mode performance-monitoring counters.
+ *
+ * Copyright (C) 2000-2004 Mikael Pettersson
+ */
+
+#ifdef CONFIG_PERFCTR_GLOBAL
+extern int sys_gperfctr(unsigned int cmd, unsigned long arg);
+extern void gperfctr_init(void);
+#else
+extern int sys_gperfctr(unsigned int cmd, unsigned long arg)
+{
+ return -EINVAL;
+}
+static inline void gperfctr_init(void) { }
+#endif
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 14:03    [from the cache]
©2003-2011 Jasper Spaans