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