linux/drivers/cpufreq/cpufreq_stats.c
<<
>>
Prefs
   1/*
   2 *  drivers/cpufreq/cpufreq_stats.c
   3 *
   4 *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
   5 *            (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/sysdev.h>
  14#include <linux/cpu.h>
  15#include <linux/sysfs.h>
  16#include <linux/cpufreq.h>
  17#include <linux/jiffies.h>
  18#include <linux/percpu.h>
  19#include <linux/kobject.h>
  20#include <linux/spinlock.h>
  21#include <linux/notifier.h>
  22#include <asm/cputime.h>
  23
  24static spinlock_t cpufreq_stats_lock;
  25
  26#define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
  27static struct freq_attr _attr_##_name = {\
  28        .attr = {.name = __stringify(_name), .mode = _mode, }, \
  29        .show = _show,\
  30};
  31
  32struct cpufreq_stats {
  33        unsigned int cpu;
  34        unsigned int total_trans;
  35        unsigned long long  last_time;
  36        unsigned int max_state;
  37        unsigned int state_num;
  38        unsigned int last_index;
  39        cputime64_t *time_in_state;
  40        unsigned int *freq_table;
  41#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
  42        unsigned int *trans_table;
  43#endif
  44};
  45
  46static struct cpufreq_stats *cpufreq_stats_table[NR_CPUS];
  47
  48struct cpufreq_stats_attribute {
  49        struct attribute attr;
  50        ssize_t(*show) (struct cpufreq_stats *, char *);
  51};
  52
  53static int
  54cpufreq_stats_update (unsigned int cpu)
  55{
  56        struct cpufreq_stats *stat;
  57        unsigned long long cur_time;
  58
  59        cur_time = get_jiffies_64();
  60        spin_lock(&cpufreq_stats_lock);
  61        stat = cpufreq_stats_table[cpu];
  62        if (stat->time_in_state)
  63                stat->time_in_state[stat->last_index] =
  64                        cputime64_add(stat->time_in_state[stat->last_index],
  65                                      cputime_sub(cur_time, stat->last_time));
  66        stat->last_time = cur_time;
  67        spin_unlock(&cpufreq_stats_lock);
  68        return 0;
  69}
  70
  71static ssize_t
  72show_total_trans(struct cpufreq_policy *policy, char *buf)
  73{
  74        struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
  75        if (!stat)
  76                return 0;
  77        return sprintf(buf, "%d\n",
  78                        cpufreq_stats_table[stat->cpu]->total_trans);
  79}
  80
  81static ssize_t
  82show_time_in_state(struct cpufreq_policy *policy, char *buf)
  83{
  84        ssize_t len = 0;
  85        int i;
  86        struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
  87        if (!stat)
  88                return 0;
  89        cpufreq_stats_update(stat->cpu);
  90        for (i = 0; i < stat->state_num; i++) {
  91                len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
  92                        (unsigned long long)cputime64_to_clock_t(stat->time_in_state[i]));
  93        }
  94        return len;
  95}
  96
  97#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
  98static ssize_t
  99show_trans_table(struct cpufreq_policy *policy, char *buf)
 100{
 101        ssize_t len = 0;
 102        int i, j;
 103
 104        struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
 105        if (!stat)
 106                return 0;
 107        cpufreq_stats_update(stat->cpu);
 108        len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
 109        len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
 110        for (i = 0; i < stat->state_num; i++) {
 111                if (len >= PAGE_SIZE)
 112                        break;
 113                len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
 114                                stat->freq_table[i]);
 115        }
 116        if (len >= PAGE_SIZE)
 117                return len;
 118
 119        len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 120
 121        for (i = 0; i < stat->state_num; i++) {
 122                if (len >= PAGE_SIZE)
 123                        break;
 124
 125                len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
 126                                stat->freq_table[i]);
 127
 128                for (j = 0; j < stat->state_num; j++)   {
 129                        if (len >= PAGE_SIZE)
 130                                break;
 131                        len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
 132                                        stat->trans_table[i*stat->max_state+j]);
 133                }
 134                len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 135        }
 136        return len;
 137}
 138CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
 139#endif
 140
 141CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
 142CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
 143
 144static struct attribute *default_attrs[] = {
 145        &_attr_total_trans.attr,
 146        &_attr_time_in_state.attr,
 147#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 148        &_attr_trans_table.attr,
 149#endif
 150        NULL
 151};
 152static struct attribute_group stats_attr_group = {
 153        .attrs = default_attrs,
 154        .name = "stats"
 155};
 156
 157static int
 158freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
 159{
 160        int index;
 161        for (index = 0; index < stat->max_state; index++)
 162                if (stat->freq_table[index] == freq)
 163                        return index;
 164        return -1;
 165}
 166
 167static void cpufreq_stats_free_table(unsigned int cpu)
 168{
 169        struct cpufreq_stats *stat = cpufreq_stats_table[cpu];
 170        struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
 171        if (policy && policy->cpu == cpu)
 172                sysfs_remove_group(&policy->kobj, &stats_attr_group);
 173        if (stat) {
 174                kfree(stat->time_in_state);
 175                kfree(stat);
 176        }
 177        cpufreq_stats_table[cpu] = NULL;
 178        if (policy)
 179                cpufreq_cpu_put(policy);
 180}
 181
 182static int
 183cpufreq_stats_create_table (struct cpufreq_policy *policy,
 184                struct cpufreq_frequency_table *table)
 185{
 186        unsigned int i, j, count = 0, ret = 0;
 187        struct cpufreq_stats *stat;
 188        struct cpufreq_policy *data;
 189        unsigned int alloc_size;
 190        unsigned int cpu = policy->cpu;
 191        if (cpufreq_stats_table[cpu])
 192                return -EBUSY;
 193        if ((stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
 194                return -ENOMEM;
 195
 196        data = cpufreq_cpu_get(cpu);
 197        if (data == NULL) {
 198                ret = -EINVAL;
 199                goto error_get_fail;
 200        }
 201
 202        if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
 203                goto error_out;
 204
 205        stat->cpu = cpu;
 206        cpufreq_stats_table[cpu] = stat;
 207
 208        for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 209                unsigned int freq = table[i].frequency;
 210                if (freq == CPUFREQ_ENTRY_INVALID)
 211                        continue;
 212                count++;
 213        }
 214
 215        alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
 216
 217#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 218        alloc_size += count * count * sizeof(int);
 219#endif
 220        stat->max_state = count;
 221        stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
 222        if (!stat->time_in_state) {
 223                ret = -ENOMEM;
 224                goto error_out;
 225        }
 226        stat->freq_table = (unsigned int *)(stat->time_in_state + count);
 227
 228#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 229        stat->trans_table = stat->freq_table + count;
 230#endif
 231        j = 0;
 232        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 233                unsigned int freq = table[i].frequency;
 234                if (freq == CPUFREQ_ENTRY_INVALID)
 235                        continue;
 236                if (freq_table_get_index(stat, freq) == -1)
 237                        stat->freq_table[j++] = freq;
 238        }
 239        stat->state_num = j;
 240        spin_lock(&cpufreq_stats_lock);
 241        stat->last_time = get_jiffies_64();
 242        stat->last_index = freq_table_get_index(stat, policy->cur);
 243        spin_unlock(&cpufreq_stats_lock);
 244        cpufreq_cpu_put(data);
 245        return 0;
 246error_out:
 247        cpufreq_cpu_put(data);
 248error_get_fail:
 249        kfree(stat);
 250        cpufreq_stats_table[cpu] = NULL;
 251        return ret;
 252}
 253
 254static int
 255cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
 256                void *data)
 257{
 258        int ret;
 259        struct cpufreq_policy *policy = data;
 260        struct cpufreq_frequency_table *table;
 261        unsigned int cpu = policy->cpu;
 262        if (val != CPUFREQ_NOTIFY)
 263                return 0;
 264        table = cpufreq_frequency_get_table(cpu);
 265        if (!table)
 266                return 0;
 267        if ((ret = cpufreq_stats_create_table(policy, table)))
 268                return ret;
 269        return 0;
 270}
 271
 272static int
 273cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
 274                void *data)
 275{
 276        struct cpufreq_freqs *freq = data;
 277        struct cpufreq_stats *stat;
 278        int old_index, new_index;
 279
 280        if (val != CPUFREQ_POSTCHANGE)
 281                return 0;
 282
 283        stat = cpufreq_stats_table[freq->cpu];
 284        if (!stat)
 285                return 0;
 286
 287        old_index = freq_table_get_index(stat, freq->old);
 288        new_index = freq_table_get_index(stat, freq->new);
 289
 290        cpufreq_stats_update(freq->cpu);
 291        if (old_index == new_index)
 292                return 0;
 293
 294        if (old_index == -1 || new_index == -1)
 295                return 0;
 296
 297        spin_lock(&cpufreq_stats_lock);
 298        stat->last_index = new_index;
 299#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 300        stat->trans_table[old_index * stat->max_state + new_index]++;
 301#endif
 302        stat->total_trans++;
 303        spin_unlock(&cpufreq_stats_lock);
 304        return 0;
 305}
 306
 307static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
 308                                               unsigned long action,
 309                                               void *hcpu)
 310{
 311        unsigned int cpu = (unsigned long)hcpu;
 312
 313        switch (action) {
 314        case CPU_ONLINE:
 315        case CPU_ONLINE_FROZEN:
 316                cpufreq_update_policy(cpu);
 317                break;
 318        case CPU_DEAD:
 319        case CPU_DEAD_FROZEN:
 320                cpufreq_stats_free_table(cpu);
 321                break;
 322        }
 323        return NOTIFY_OK;
 324}
 325
 326static struct notifier_block cpufreq_stat_cpu_notifier __cpuinitdata =
 327{
 328        .notifier_call = cpufreq_stat_cpu_callback,
 329};
 330
 331static struct notifier_block notifier_policy_block = {
 332        .notifier_call = cpufreq_stat_notifier_policy
 333};
 334
 335static struct notifier_block notifier_trans_block = {
 336        .notifier_call = cpufreq_stat_notifier_trans
 337};
 338
 339static int
 340__init cpufreq_stats_init(void)
 341{
 342        int ret;
 343        unsigned int cpu;
 344
 345        spin_lock_init(&cpufreq_stats_lock);
 346        if ((ret = cpufreq_register_notifier(&notifier_policy_block,
 347                                CPUFREQ_POLICY_NOTIFIER)))
 348                return ret;
 349
 350        if ((ret = cpufreq_register_notifier(&notifier_trans_block,
 351                                CPUFREQ_TRANSITION_NOTIFIER))) {
 352                cpufreq_unregister_notifier(&notifier_policy_block,
 353                                CPUFREQ_POLICY_NOTIFIER);
 354                return ret;
 355        }
 356
 357        register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
 358        for_each_online_cpu(cpu) {
 359                cpufreq_update_policy(cpu);
 360        }
 361        return 0;
 362}
 363static void
 364__exit cpufreq_stats_exit(void)
 365{
 366        unsigned int cpu;
 367
 368        cpufreq_unregister_notifier(&notifier_policy_block,
 369                        CPUFREQ_POLICY_NOTIFIER);
 370        cpufreq_unregister_notifier(&notifier_trans_block,
 371                        CPUFREQ_TRANSITION_NOTIFIER);
 372        unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
 373        for_each_online_cpu(cpu) {
 374                cpufreq_stats_free_table(cpu);
 375        }
 376}
 377
 378MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
 379MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats "
 380                                "through sysfs filesystem");
 381MODULE_LICENSE ("GPL");
 382
 383module_init(cpufreq_stats_init);
 384module_exit(cpufreq_stats_exit);
 385