[S390] Add per-cpu idle time / idle count sysfs attributes.
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Mon, 22 Oct 2007 10:52:39 +0000 (12:52 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 22 Oct 2007 10:52:47 +0000 (12:52 +0200)
Add two new sysfs entries per cpu: idle_count and idle_time.
idle_count contains the number of times a cpu went into idle state.
idle_time contains the time a cpu spent in idle state in microseconds.
This can be used e.g. by powertop to tell how often idle state is
entered and left.

# cat /sys/devices/system/cpu/cpu0/idle_count
504
# cat /sys/devices/system/cpu/cpu0/idle_time
469734037 us

Cc: Arjan van de Ven <arjan@infradead.org>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/kernel/process.c
arch/s390/kernel/smp.c
include/asm-s390/cpu.h [new file with mode: 0644]

index 70c57378f426c3a8d7250479fd5e524ef68678fa..cc7c4ba0774d837e90fc10813f6a08eac50f64a9 100644 (file)
@@ -44,6 +44,7 @@
 #include <asm/processor.h>
 #include <asm/irq.h>
 #include <asm/timer.h>
+#include <asm/cpu.h>
 
 asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
 
@@ -91,6 +92,14 @@ EXPORT_SYMBOL(unregister_idle_notifier);
 
 void do_monitor_call(struct pt_regs *regs, long interruption_code)
 {
+       struct s390_idle_data *idle;
+
+       idle = &__get_cpu_var(s390_idle);
+       spin_lock(&idle->lock);
+       idle->idle_time += get_clock() - idle->idle_enter;
+       idle->in_idle = 0;
+       spin_unlock(&idle->lock);
+
        /* disable monitor call class 0 */
        __ctl_clear_bit(8, 15);
 
@@ -105,6 +114,7 @@ extern void s390_handle_mcck(void);
 static void default_idle(void)
 {
        int cpu, rc;
+       struct s390_idle_data *idle;
 
        /* CPU is going idle. */
        cpu = smp_processor_id();
@@ -142,6 +152,12 @@ static void default_idle(void)
                return;
        }
 
+       idle = &__get_cpu_var(s390_idle);
+       spin_lock(&idle->lock);
+       idle->idle_count++;
+       idle->in_idle = 1;
+       idle->idle_enter = get_clock();
+       spin_unlock(&idle->lock);
        trace_hardirqs_on();
        /* Wait for external, I/O or machine check interrupt. */
        __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT |
index 35edbef1d2228b021b8e677f22a9bcf7a66d0eeb..ba3fff06fad1b69c8d7ebe3220a1e5ee562cdbac 100644 (file)
@@ -42,6 +42,7 @@
 #include <asm/tlbflush.h>
 #include <asm/timer.h>
 #include <asm/lowcore.h>
+#include <asm/cpu.h>
 
 /*
  * An array with a pointer the lowcore of every CPU.
@@ -494,6 +495,8 @@ int __cpuinit start_secondary(void *cpuvoid)
        return 0;
 }
 
+DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
+
 static void __init smp_create_idle(unsigned int cpu)
 {
        struct task_struct *p;
@@ -506,6 +509,7 @@ static void __init smp_create_idle(unsigned int cpu)
        if (IS_ERR(p))
                panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
        current_set[cpu] = p;
+       spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock);
 }
 
 static int cpu_stopped(int cpu)
@@ -724,6 +728,7 @@ void __init smp_prepare_boot_cpu(void)
        cpu_set(0, cpu_online_map);
        S390_lowcore.percpu_offset = __per_cpu_offset[0];
        current_set[0] = current;
+       spin_lock_init(&(&__get_cpu_var(s390_idle))->lock);
 }
 
 void __init smp_cpus_done(unsigned int max_cpus)
@@ -756,22 +761,71 @@ static ssize_t show_capability(struct sys_device *dev, char *buf)
 }
 static SYSDEV_ATTR(capability, 0444, show_capability, NULL);
 
+static ssize_t show_idle_count(struct sys_device *dev, char *buf)
+{
+       struct s390_idle_data *idle;
+       unsigned long long idle_count;
+
+       idle = &per_cpu(s390_idle, dev->id);
+       spin_lock_irq(&idle->lock);
+       idle_count = idle->idle_count;
+       spin_unlock_irq(&idle->lock);
+       return sprintf(buf, "%llu\n", idle_count);
+}
+static SYSDEV_ATTR(idle_count, 0444, show_idle_count, NULL);
+
+static ssize_t show_idle_time(struct sys_device *dev, char *buf)
+{
+       struct s390_idle_data *idle;
+       unsigned long long new_time;
+
+       idle = &per_cpu(s390_idle, dev->id);
+       spin_lock_irq(&idle->lock);
+       if (idle->in_idle) {
+               new_time = get_clock();
+               idle->idle_time += new_time - idle->idle_enter;
+               idle->idle_enter = new_time;
+       }
+       new_time = idle->idle_time;
+       spin_unlock_irq(&idle->lock);
+       return sprintf(buf, "%llu us\n", new_time >> 12);
+}
+static SYSDEV_ATTR(idle_time, 0444, show_idle_time, NULL);
+
+static struct attribute *cpu_attrs[] = {
+       &attr_capability.attr,
+       &attr_idle_count.attr,
+       &attr_idle_time.attr,
+       NULL,
+};
+
+static struct attribute_group cpu_attr_group = {
+       .attrs = cpu_attrs,
+};
+
 static int __cpuinit smp_cpu_notify(struct notifier_block *self,
                                    unsigned long action, void *hcpu)
 {
        unsigned int cpu = (unsigned int)(long)hcpu;
        struct cpu *c = &per_cpu(cpu_devices, cpu);
        struct sys_device *s = &c->sysdev;
+       struct s390_idle_data *idle;
 
        switch (action) {
        case CPU_ONLINE:
        case CPU_ONLINE_FROZEN:
-               if (sysdev_create_file(s, &attr_capability))
+               idle = &per_cpu(s390_idle, cpu);
+               spin_lock_irq(&idle->lock);
+               idle->idle_enter = 0;
+               idle->idle_time = 0;
+               idle->idle_count = 0;
+               spin_unlock_irq(&idle->lock);
+               if (sysfs_create_group(&s->kobj, &cpu_attr_group))
                        return NOTIFY_BAD;
                break;
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
-               sysdev_remove_file(s, &attr_capability);
+               sysfs_remove_group(&s->kobj, &cpu_attr_group);
                break;
        }
        return NOTIFY_OK;
@@ -784,6 +838,7 @@ static struct notifier_block __cpuinitdata smp_cpu_nb = {
 static int __init topology_init(void)
 {
        int cpu;
+       int rc;
 
        register_cpu_notifier(&smp_cpu_nb);
 
@@ -796,7 +851,9 @@ static int __init topology_init(void)
                if (!cpu_online(cpu))
                        continue;
                s = &c->sysdev;
-               sysdev_create_file(s, &attr_capability);
+               rc = sysfs_create_group(&s->kobj, &cpu_attr_group);
+               if (rc)
+                       return rc;
        }
        return 0;
 }
diff --git a/include/asm-s390/cpu.h b/include/asm-s390/cpu.h
new file mode 100644 (file)
index 0000000..352dde1
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  include/asm-s390/cpu.h
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_CPU_H_
+#define _ASM_S390_CPU_H_
+
+#include <linux/types.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+
+struct s390_idle_data {
+       spinlock_t lock;
+       unsigned int in_idle;
+       unsigned long long idle_count;
+       unsigned long long idle_enter;
+       unsigned long long idle_time;
+};
+
+DECLARE_PER_CPU(struct s390_idle_data, s390_idle);
+
+#endif /* _ASM_S390_CPU_H_ */