Root/drivers/cpufreq/cpufreq_stats.c

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 <linux/string.h>
24#include <asm/cputime.h>
25
26static spinlock_t cpufreq_stats_lock;
27
28#define CPUFREQ_STATDEVICE_ATTR(_name, _mode, _show) \
29static struct freq_attr _attr_##_name = {\
30    .attr = {.name = __stringify(_name), .mode = _mode, }, \
31    .show = _show,\
32};
33
34struct cpufreq_stats {
35    unsigned int cpu;
36    unsigned int total_trans;
37    unsigned long long last_time;
38    unsigned int max_state;
39    unsigned int state_num;
40    int last_index;
41    cputime64_t *time_in_state;
42    unsigned int *freq_table;
43#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
44    unsigned int *trans_table;
45#endif
46};
47
48static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
49
50struct cpufreq_stats_attribute {
51    struct attribute attr;
52    ssize_t(*show) (struct cpufreq_stats *, char *);
53};
54
55static int cpufreq_stats_update(unsigned int cpu)
56{
57    struct cpufreq_stats *stat;
58    unsigned long long cur_time;
59
60    cur_time = get_jiffies_64();
61    spin_lock(&cpufreq_stats_lock);
62    stat = per_cpu(cpufreq_stats_table, cpu);
63    if (stat->time_in_state && stat->last_index != -1)
64        stat->time_in_state[stat->last_index] +=
65            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 || !stat->time_in_state)
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 || !stat->trans_table)
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    if (stat->freq_table)
163        for (index = 0; index < stat->max_state; index++)
164            if (stat->freq_table[index] == freq)
165                return index;
166    return -1;
167}
168
169static void cpufreq_stats_free_table(unsigned int cpu)
170{
171    struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
172    struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
173    if (policy && policy->cpu == cpu)
174        sysfs_remove_group(&policy->kobj, &stats_attr_group);
175    if (stat) {
176        kfree(stat->time_in_state);
177        kfree(stat);
178    }
179    per_cpu(cpufreq_stats_table, cpu) = NULL;
180    if (policy)
181        cpufreq_cpu_put(policy);
182}
183
184static int cpufreq_stats_update_table(struct cpufreq_policy *policy,
185        struct cpufreq_frequency_table *table)
186{
187    unsigned int i, j, count = 0;
188    unsigned int alloc_size;
189    unsigned int cpu = policy->cpu;
190    struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
191
192    for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
193        unsigned int freq = table[i].frequency;
194        if (freq == CPUFREQ_ENTRY_INVALID)
195            continue;
196        count++;
197    }
198
199    if (stat->max_state != count) {
200        stat->max_state = count;
201        kfree(stat->time_in_state);
202        stat->time_in_state = NULL;
203    }
204    alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
205#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
206    alloc_size += count * count * sizeof(int);
207#endif
208    if (stat->time_in_state) {
209        memset(stat->time_in_state, 0, alloc_size);
210    } else {
211        stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
212        if (!stat->time_in_state)
213            return -ENOMEM;
214        stat->freq_table = (unsigned int *)(
215                stat->time_in_state + count);
216#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
217        stat->trans_table = stat->freq_table + count;
218#endif
219    }
220
221    j = 0;
222    if (stat->freq_table) {
223        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
224            unsigned int freq = table[i].frequency;
225            if (freq == CPUFREQ_ENTRY_INVALID)
226                continue;
227            if (freq_table_get_index(stat, freq) == -1)
228                stat->freq_table[j++] = freq;
229        }
230    }
231    stat->state_num = j;
232    spin_lock(&cpufreq_stats_lock);
233    stat->last_time = get_jiffies_64();
234    stat->last_index = freq_table_get_index(stat, policy->cur);
235    spin_unlock(&cpufreq_stats_lock);
236    return 0;
237}
238
239static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
240        struct cpufreq_frequency_table *table)
241{
242    unsigned int ret = 0;
243    struct cpufreq_stats *stat;
244    struct cpufreq_policy *data;
245    unsigned int cpu = policy->cpu;
246
247    stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
248    if ((stat) == NULL)
249        return -ENOMEM;
250
251    data = cpufreq_cpu_get(cpu);
252    if (data == NULL) {
253        ret = -EINVAL;
254        goto error_out;
255    }
256    ret = sysfs_create_group(&data->kobj, &stats_attr_group);
257    cpufreq_cpu_put(data);
258    if (ret)
259        goto error_out;
260
261    stat->cpu = cpu;
262    per_cpu(cpufreq_stats_table, cpu) = stat;
263
264    return 0;
265error_out:
266    kfree(stat);
267    per_cpu(cpufreq_stats_table, cpu) = NULL;
268    return ret;
269}
270
271static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
272        unsigned long val, void *data)
273{
274    int ret;
275    struct cpufreq_policy *policy = data;
276    struct cpufreq_frequency_table *table;
277    unsigned int cpu = policy->cpu;
278    if (val != CPUFREQ_NOTIFY)
279        return 0;
280    table = cpufreq_frequency_get_table(cpu);
281    if (!table)
282        return 0;
283    if (!per_cpu(cpufreq_stats_table, cpu)) {
284        ret = cpufreq_stats_create_table(policy, table);
285        if (ret)
286            return ret;
287    }
288    return cpufreq_stats_update_table(policy, table);
289}
290
291static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
292        unsigned long val, void *data)
293{
294    struct cpufreq_freqs *freq = data;
295    struct cpufreq_stats *stat;
296    int old_index, new_index;
297
298    if (val != CPUFREQ_POSTCHANGE)
299        return 0;
300
301    stat = per_cpu(cpufreq_stats_table, freq->cpu);
302    if (!stat)
303        return 0;
304
305    old_index = stat->last_index;
306    new_index = freq_table_get_index(stat, freq->new);
307
308    cpufreq_stats_update(freq->cpu);
309    if (old_index == new_index)
310        return 0;
311
312    if (new_index == -1)
313        return 0;
314
315    spin_lock(&cpufreq_stats_lock);
316    stat->last_index = new_index;
317    if (old_index != -1) {
318#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
319        if (stat->trans_table)
320            stat->trans_table[old_index * stat->max_state +
321                      new_index]++;
322#endif
323        stat->total_trans++;
324    }
325    spin_unlock(&cpufreq_stats_lock);
326    return 0;
327}
328
329static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
330                           unsigned long action,
331                           void *hcpu)
332{
333    unsigned int cpu = (unsigned long)hcpu;
334
335    switch (action) {
336    case CPU_ONLINE:
337    case CPU_ONLINE_FROZEN:
338        cpufreq_update_policy(cpu);
339        break;
340    case CPU_DEAD:
341    case CPU_DEAD_FROZEN:
342        cpufreq_stats_free_table(cpu);
343        break;
344    }
345    return NOTIFY_OK;
346}
347
348static struct notifier_block cpufreq_stat_cpu_notifier __refdata =
349{
350    .notifier_call = cpufreq_stat_cpu_callback,
351};
352
353static struct notifier_block notifier_policy_block = {
354    .notifier_call = cpufreq_stat_notifier_policy
355};
356
357static struct notifier_block notifier_trans_block = {
358    .notifier_call = cpufreq_stat_notifier_trans
359};
360
361static int __init cpufreq_stats_init(void)
362{
363    int ret;
364    unsigned int cpu;
365
366    spin_lock_init(&cpufreq_stats_lock);
367    ret = cpufreq_register_notifier(&notifier_policy_block,
368                CPUFREQ_POLICY_NOTIFIER);
369    if (ret)
370        return ret;
371
372    ret = cpufreq_register_notifier(&notifier_trans_block,
373                CPUFREQ_TRANSITION_NOTIFIER);
374    if (ret) {
375        cpufreq_unregister_notifier(&notifier_policy_block,
376                CPUFREQ_POLICY_NOTIFIER);
377        return ret;
378    }
379
380    register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
381    for_each_online_cpu(cpu) {
382        cpufreq_update_policy(cpu);
383    }
384    return 0;
385}
386static void __exit cpufreq_stats_exit(void)
387{
388    unsigned int cpu;
389
390    cpufreq_unregister_notifier(&notifier_policy_block,
391            CPUFREQ_POLICY_NOTIFIER);
392    cpufreq_unregister_notifier(&notifier_trans_block,
393            CPUFREQ_TRANSITION_NOTIFIER);
394    unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
395    for_each_online_cpu(cpu) {
396        cpufreq_stats_free_table(cpu);
397    }
398}
399
400MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
401MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
402                "through sysfs filesystem");
403MODULE_LICENSE("GPL");
404
405module_init(cpufreq_stats_init);
406module_exit(cpufreq_stats_exit);
407

Archive Download this file



interactive