2 * drivers/cpufreq/cpufreq_stats.c
4 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
5 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
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.
12 #include <linux/cpu.h>
13 #include <linux/cpufreq.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/cputime.h>
18 static spinlock_t cpufreq_stats_lock
;
20 struct cpufreq_stats
{
22 unsigned int total_trans
;
23 unsigned long long last_time
;
24 unsigned int max_state
;
25 unsigned int state_num
;
26 unsigned int last_index
;
28 unsigned int *freq_table
;
29 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
30 unsigned int *trans_table
;
34 static int cpufreq_stats_update(struct cpufreq_stats
*stat
)
36 unsigned long long cur_time
= get_jiffies_64();
38 spin_lock(&cpufreq_stats_lock
);
39 if (stat
->time_in_state
)
40 stat
->time_in_state
[stat
->last_index
] +=
41 cur_time
- stat
->last_time
;
42 stat
->last_time
= cur_time
;
43 spin_unlock(&cpufreq_stats_lock
);
47 static ssize_t
show_total_trans(struct cpufreq_policy
*policy
, char *buf
)
49 return sprintf(buf
, "%d\n", policy
->stats
->total_trans
);
52 static ssize_t
show_time_in_state(struct cpufreq_policy
*policy
, char *buf
)
54 struct cpufreq_stats
*stat
= policy
->stats
;
58 cpufreq_stats_update(stat
);
59 for (i
= 0; i
< stat
->state_num
; i
++) {
60 len
+= sprintf(buf
+ len
, "%u %llu\n", stat
->freq_table
[i
],
62 jiffies_64_to_clock_t(stat
->time_in_state
[i
]));
67 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
68 static ssize_t
show_trans_table(struct cpufreq_policy
*policy
, char *buf
)
70 struct cpufreq_stats
*stat
= policy
->stats
;
74 cpufreq_stats_update(stat
);
75 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " From : To\n");
76 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " : ");
77 for (i
= 0; i
< stat
->state_num
; i
++) {
80 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
86 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
88 for (i
= 0; i
< stat
->state_num
; i
++) {
92 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u: ",
95 for (j
= 0; j
< stat
->state_num
; j
++) {
98 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
99 stat
->trans_table
[i
*stat
->max_state
+j
]);
101 if (len
>= PAGE_SIZE
)
103 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
105 if (len
>= PAGE_SIZE
)
109 cpufreq_freq_attr_ro(trans_table
);
112 cpufreq_freq_attr_ro(total_trans
);
113 cpufreq_freq_attr_ro(time_in_state
);
115 static struct attribute
*default_attrs
[] = {
118 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
123 static struct attribute_group stats_attr_group
= {
124 .attrs
= default_attrs
,
128 static int freq_table_get_index(struct cpufreq_stats
*stat
, unsigned int freq
)
131 for (index
= 0; index
< stat
->max_state
; index
++)
132 if (stat
->freq_table
[index
] == freq
)
137 static void __cpufreq_stats_free_table(struct cpufreq_policy
*policy
)
139 struct cpufreq_stats
*stat
= policy
->stats
;
145 pr_debug("%s: Free stat table\n", __func__
);
147 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
148 kfree(stat
->time_in_state
);
150 policy
->stats
= NULL
;
153 static void cpufreq_stats_free_table(unsigned int cpu
)
155 struct cpufreq_policy
*policy
;
157 policy
= cpufreq_cpu_get(cpu
);
161 __cpufreq_stats_free_table(policy
);
163 cpufreq_cpu_put(policy
);
166 static int __cpufreq_stats_create_table(struct cpufreq_policy
*policy
)
168 unsigned int i
, count
= 0, ret
= 0;
169 struct cpufreq_stats
*stat
;
170 unsigned int alloc_size
;
171 unsigned int cpu
= policy
->cpu
;
172 struct cpufreq_frequency_table
*pos
, *table
;
174 table
= cpufreq_frequency_get_table(cpu
);
175 if (unlikely(!table
))
178 /* stats already initialized */
182 stat
= kzalloc(sizeof(*stat
), GFP_KERNEL
);
186 ret
= sysfs_create_group(&policy
->kobj
, &stats_attr_group
);
191 policy
->stats
= stat
;
193 cpufreq_for_each_valid_entry(pos
, table
)
196 alloc_size
= count
* sizeof(int) + count
* sizeof(u64
);
198 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
199 alloc_size
+= count
* count
* sizeof(int);
201 stat
->max_state
= count
;
202 stat
->time_in_state
= kzalloc(alloc_size
, GFP_KERNEL
);
203 if (!stat
->time_in_state
) {
207 stat
->freq_table
= (unsigned int *)(stat
->time_in_state
+ count
);
209 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
210 stat
->trans_table
= stat
->freq_table
+ count
;
213 cpufreq_for_each_valid_entry(pos
, table
)
214 if (freq_table_get_index(stat
, pos
->frequency
) == -1)
215 stat
->freq_table
[i
++] = pos
->frequency
;
217 spin_lock(&cpufreq_stats_lock
);
218 stat
->last_time
= get_jiffies_64();
219 stat
->last_index
= freq_table_get_index(stat
, policy
->cur
);
220 spin_unlock(&cpufreq_stats_lock
);
223 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
226 policy
->stats
= NULL
;
230 static void cpufreq_stats_create_table(unsigned int cpu
)
232 struct cpufreq_policy
*policy
;
235 * "likely(!policy)" because normally cpufreq_stats will be registered
236 * before cpufreq driver
238 policy
= cpufreq_cpu_get(cpu
);
242 __cpufreq_stats_create_table(policy
);
244 cpufreq_cpu_put(policy
);
247 static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy
*policy
)
249 policy
->stats
->cpu
= policy
->cpu
;
252 static int cpufreq_stat_notifier_policy(struct notifier_block
*nb
,
253 unsigned long val
, void *data
)
256 struct cpufreq_policy
*policy
= data
;
258 if (val
== CPUFREQ_UPDATE_POLICY_CPU
) {
259 cpufreq_stats_update_policy_cpu(policy
);
263 if (val
== CPUFREQ_CREATE_POLICY
)
264 ret
= __cpufreq_stats_create_table(policy
);
265 else if (val
== CPUFREQ_REMOVE_POLICY
)
266 __cpufreq_stats_free_table(policy
);
271 static int cpufreq_stat_notifier_trans(struct notifier_block
*nb
,
272 unsigned long val
, void *data
)
274 struct cpufreq_freqs
*freq
= data
;
275 struct cpufreq_policy
*policy
= cpufreq_cpu_get(freq
->cpu
);
276 struct cpufreq_stats
*stat
;
277 int old_index
, new_index
;
280 pr_err("%s: No policy found\n", __func__
);
284 if (val
!= CPUFREQ_POSTCHANGE
)
287 if (!policy
->stats
) {
288 pr_debug("%s: No stats found\n", __func__
);
292 stat
= policy
->stats
;
294 old_index
= stat
->last_index
;
295 new_index
= freq_table_get_index(stat
, freq
->new);
297 /* We can't do stat->time_in_state[-1]= .. */
298 if (old_index
== -1 || new_index
== -1)
301 cpufreq_stats_update(stat
);
303 if (old_index
== new_index
)
306 spin_lock(&cpufreq_stats_lock
);
307 stat
->last_index
= new_index
;
308 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
309 stat
->trans_table
[old_index
* stat
->max_state
+ new_index
]++;
312 spin_unlock(&cpufreq_stats_lock
);
315 cpufreq_cpu_put(policy
);
319 static struct notifier_block notifier_policy_block
= {
320 .notifier_call
= cpufreq_stat_notifier_policy
323 static struct notifier_block notifier_trans_block
= {
324 .notifier_call
= cpufreq_stat_notifier_trans
327 static int __init
cpufreq_stats_init(void)
332 spin_lock_init(&cpufreq_stats_lock
);
333 ret
= cpufreq_register_notifier(¬ifier_policy_block
,
334 CPUFREQ_POLICY_NOTIFIER
);
338 for_each_online_cpu(cpu
)
339 cpufreq_stats_create_table(cpu
);
341 ret
= cpufreq_register_notifier(¬ifier_trans_block
,
342 CPUFREQ_TRANSITION_NOTIFIER
);
344 cpufreq_unregister_notifier(¬ifier_policy_block
,
345 CPUFREQ_POLICY_NOTIFIER
);
346 for_each_online_cpu(cpu
)
347 cpufreq_stats_free_table(cpu
);
353 static void __exit
cpufreq_stats_exit(void)
357 cpufreq_unregister_notifier(¬ifier_policy_block
,
358 CPUFREQ_POLICY_NOTIFIER
);
359 cpufreq_unregister_notifier(¬ifier_trans_block
,
360 CPUFREQ_TRANSITION_NOTIFIER
);
361 for_each_online_cpu(cpu
)
362 cpufreq_stats_free_table(cpu
);
365 MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
366 MODULE_DESCRIPTION("Export cpufreq stats via sysfs");
367 MODULE_LICENSE("GPL");
369 module_init(cpufreq_stats_init
);
370 module_exit(cpufreq_stats_exit
);