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/cpu.h>
  15#include <linux/sysfs.h>
  16#include <linux/cpufreq.h>
  17#include <linux/module.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                        cur_time - stat->last_time;
  65        stat->last_time = cur_time;
  66        spin_unlock(&cpufreq_stats_lock);
  67        return 0;
  68}
  69
  70static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
  71{
  72        struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
  73        if (!stat)
  74                return 0;
  75        return sprintf(buf, "%d\n",
  76                        per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
  77}
  78
  79static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
  80{
  81        ssize_t len = 0;
  82        int i;
  83        struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
  84        if (!stat)
  85                return 0;
  86        cpufreq_stats_update(stat->cpu);
  87        for (i = 0; i < stat->state_num; i++) {
  88                len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
  89                        (unsigned long long)
  90                        cputime64_to_clock_t(stat->time_in_state[i]));
  91        }
  92        return len;
  93}
  94
  95#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
  96static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
  97{
  98        ssize_t len = 0;
  99        int i, j;
 100
 101        struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
 102        if (!stat)
 103                return 0;
 104        cpufreq_stats_update(stat->cpu);
 105        len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
 106        len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
 107        for (i = 0; i < stat->state_num; i++) {
 108                if (len >= PAGE_SIZE)
 109                        break;
 110                len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
 111                                stat->freq_table[i]);
 112        }
 113        if (len >= PAGE_SIZE)
 114                return PAGE_SIZE;
 115
 116        len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 117
 118        for (i = 0; i < stat->state_num; i++) {
 119                if (len >= PAGE_SIZE)
 120                        break;
 121
 122                len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
 123                                stat->freq_table[i]);
 124
 125                for (j = 0; j < stat->state_num; j++)   {
 126                        if (len >= PAGE_SIZE)
 127                                break;
 128                        len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
 129                                        stat->trans_table[i*stat->max_state+j]);
 130                }
 131                if (len >= PAGE_SIZE)
 132                        break;
 133                len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 134        }
 135        if (len >= PAGE_SIZE)
 136                return PAGE_SIZE;
 137        return len;
 138}
 139CPUFREQ_STATDEVICE_ATTR(trans_table, 0444, show_trans_table);
 140#endif
 141
 142CPUFREQ_STATDEVICE_ATTR(total_trans, 0444, show_total_trans);
 143CPUFREQ_STATDEVICE_ATTR(time_in_state, 0444, show_time_in_state);
 144
 145static struct attribute *default_attrs[] = {
 146        &_attr_total_trans.attr,
 147        &_attr_time_in_state.attr,
 148#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 149        &_attr_trans_table.attr,
 150#endif
 151        NULL
 152};
 153static struct attribute_group stats_attr_group = {
 154        .attrs = default_attrs,
 155        .name = "stats"
 156};
 157
 158static int freq_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
 167/* should be called late in the CPU removal sequence so that the stats
 168 * memory is still available in case someone tries to use it.
 169 */
 170static void cpufreq_stats_free_table(unsigned int cpu)
 171{
 172        struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
 173        if (stat) {
 174                kfree(stat->time_in_state);
 175                kfree(stat);
 176        }
 177        per_cpu(cpufreq_stats_table, cpu) = NULL;
 178}
 179
 180/* must be called early in the CPU removal sequence (before
 181 * cpufreq_remove_dev) so that policy is still valid.
 182 */
 183static void cpufreq_stats_free_sysfs(unsigned int cpu)
 184{
 185        struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
 186        if (policy && policy->cpu == cpu)
 187                sysfs_remove_group(&policy->kobj, &stats_attr_group);
 188        if (policy)
 189                cpufreq_cpu_put(policy);
 190}
 191
 192static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
 193                struct cpufreq_frequency_table *table)
 194{
 195        unsigned int i, j, count = 0, ret = 0;
 196        struct cpufreq_stats *stat;
 197        struct cpufreq_policy *data;
 198        unsigned int alloc_size;
 199        unsigned int cpu = policy->cpu;
 200        if (per_cpu(cpufreq_stats_table, cpu))
 201                return -EBUSY;
 202        stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
 203        if ((stat) == NULL)
 204                return -ENOMEM;
 205
 206        data = cpufreq_cpu_get(cpu);
 207        if (data == NULL) {
 208                ret = -EINVAL;
 209                goto error_get_fail;
 210        }
 211
 212        ret = sysfs_create_group(&data->kobj, &stats_attr_group);
 213        if (ret)
 214                goto error_out;
 215
 216        stat->cpu = cpu;
 217        per_cpu(cpufreq_stats_table, cpu) = stat;
 218
 219        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 220                unsigned int freq = table[i].frequency;
 221                if (freq == CPUFREQ_ENTRY_INVALID)
 222                        continue;
 223                count++;
 224        }
 225
 226        alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
 227
 228#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 229        alloc_size += count * count * sizeof(int);
 230#endif
 231        stat->max_state = count;
 232        stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
 233        if (!stat->time_in_state) {
 234                ret = -ENOMEM;
 235                goto error_out;
 236        }
 237        stat->freq_table = (unsigned int *)(stat->time_in_state + count);
 238
 239#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 240        stat->trans_table = stat->freq_table + count;
 241#endif
 242        j = 0;
 243        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 244                unsigned int freq = table[i].frequency;
 245                if (freq == CPUFREQ_ENTRY_INVALID)
 246                        continue;
 247                if (freq_table_get_index(stat, freq) == -1)
 248                        stat->freq_table[j++] = freq;
 249        }
 250        stat->state_num = j;
 251        spin_lock(&cpufreq_stats_lock);
 252        stat->last_time = get_jiffies_64();
 253        stat->last_index = freq_table_get_index(stat, policy->cur);
 254        spin_unlock(&cpufreq_stats_lock);
 255        cpufreq_cpu_put(data);
 256        return 0;
 257error_out:
 258        cpufreq_cpu_put(data);
 259error_get_fail:
 260        kfree(stat);
 261        per_cpu(cpufreq_stats_table, cpu) = NULL;
 262        return ret;
 263}
 264
 265static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
 266                unsigned long val, void *data)
 267{
 268        int ret;
 269        struct cpufreq_policy *policy = data;
 270        struct cpufreq_frequency_table *table;
 271        unsigned int cpu = policy->cpu;
 272        if (val != CPUFREQ_NOTIFY)
 273                return 0;
 274        table = cpufreq_frequency_get_table(cpu);
 275        if (!table)
 276                return 0;
 277        ret = cpufreq_stats_create_table(policy, table);
 278        if (ret)
 279                return ret;
 280        return 0;
 281}
 282
 283static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
 284                unsigned long val, void *data)
 285{
 286        struct cpufreq_freqs *freq = data;
 287        struct cpufreq_stats *stat;
 288        int old_index, new_index;
 289
 290        if (val != CPUFREQ_POSTCHANGE)
 291                return 0;
 292
 293        stat = per_cpu(cpufreq_stats_table, freq->cpu);
 294        if (!stat)
 295                return 0;
 296
 297        old_index = stat->last_index;
 298        new_index = freq_table_get_index(stat, freq->new);
 299
 300        /* We can't do stat->time_in_state[-1]= .. */
 301        if (old_index == -1 || new_index == -1)
 302                return 0;
 303
 304        cpufreq_stats_update(freq->cpu);
 305
 306        if (old_index == new_index)
 307                return 0;
 308
 309        spin_lock(&cpufreq_stats_lock);
 310        stat->last_index = new_index;
 311#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
 312        stat->trans_table[old_index * stat->max_state + new_index]++;
 313#endif
 314        stat->total_trans++;
 315        spin_unlock(&cpufreq_stats_lock);
 316        return 0;
 317}
 318
 319static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
 320                                               unsigned long action,
 321                                               void *hcpu)
 322{
 323        unsigned int cpu = (unsigned long)hcpu;
 324
 325        switch (action) {
 326        case CPU_ONLINE:
 327        case CPU_ONLINE_FROZEN:
 328                cpufreq_update_policy(cpu);
 329                break;
 330        case CPU_DOWN_PREPARE:
 331                cpufreq_stats_free_sysfs(cpu);
 332                break;
 333        case CPU_DEAD:
 334        case CPU_DEAD_FROZEN:
 335                cpufreq_stats_free_table(cpu);
 336                break;
 337        }
 338        return NOTIFY_OK;
 339}
 340
 341/* priority=1 so this will get called before cpufreq_remove_dev */
 342static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
 343        .notifier_call = cpufreq_stat_cpu_callback,
 344        .priority = 1,
 345};
 346
 347static struct notifier_block notifier_policy_block = {
 348        .notifier_call = cpufreq_stat_notifier_policy
 349};
 350
 351static struct notifier_block notifier_trans_block = {
 352        .notifier_call = cpufreq_stat_notifier_trans
 353};
 354
 355static int __init cpufreq_stats_init(void)
 356{
 357        int ret;
 358        unsigned int cpu;
 359
 360        spin_lock_init(&cpufreq_stats_lock);
 361        ret = cpufreq_register_notifier(&notifier_policy_block,
 362                                CPUFREQ_POLICY_NOTIFIER);
 363        if (ret)
 364                return ret;
 365
 366        ret = cpufreq_register_notifier(&notifier_trans_block,
 367                                CPUFREQ_TRANSITION_NOTIFIER);
 368        if (ret) {
 369                cpufreq_unregister_notifier(&notifier_policy_block,
 370                                CPUFREQ_POLICY_NOTIFIER);
 371                return ret;
 372        }
 373
 374        register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
 375        for_each_online_cpu(cpu) {
 376                cpufreq_update_policy(cpu);
 377        }
 378        return 0;
 379}
 380static void __exit cpufreq_stats_exit(void)
 381{
 382        unsigned int cpu;
 383
 384        cpufreq_unregister_notifier(&notifier_policy_block,
 385                        CPUFREQ_POLICY_NOTIFIER);
 386        cpufreq_unregister_notifier(&notifier_trans_block,
 387                        CPUFREQ_TRANSITION_NOTIFIER);
 388        unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
 389        for_each_online_cpu(cpu) {
 390                cpufreq_stats_free_table(cpu);
 391                cpufreq_stats_free_sysfs(cpu);
 392        }
 393}
 394
 395MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
 396MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
 397                                "through sysfs filesystem");
 398MODULE_LICENSE("GPL");
 399
 400module_init(cpufreq_stats_init);
 401module_exit(cpufreq_stats_exit);
 402
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.