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
{
21 unsigned int total_trans
;
22 unsigned long long last_time
;
23 unsigned int max_state
;
24 unsigned int state_num
;
25 unsigned int last_index
;
27 unsigned int *freq_table
;
28 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
29 unsigned int *trans_table
;
33 static int cpufreq_stats_update(struct cpufreq_stats
*stats
)
35 unsigned long long cur_time
= get_jiffies_64();
37 spin_lock(&cpufreq_stats_lock
);
38 stats
->time_in_state
[stats
->last_index
] += cur_time
- stats
->last_time
;
39 stats
->last_time
= cur_time
;
40 spin_unlock(&cpufreq_stats_lock
);
44 static ssize_t
show_total_trans(struct cpufreq_policy
*policy
, char *buf
)
46 return sprintf(buf
, "%d\n", policy
->stats
->total_trans
);
49 static ssize_t
show_time_in_state(struct cpufreq_policy
*policy
, char *buf
)
51 struct cpufreq_stats
*stats
= policy
->stats
;
55 cpufreq_stats_update(stats
);
56 for (i
= 0; i
< stats
->state_num
; i
++) {
57 len
+= sprintf(buf
+ len
, "%u %llu\n", stats
->freq_table
[i
],
59 jiffies_64_to_clock_t(stats
->time_in_state
[i
]));
64 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
65 static ssize_t
show_trans_table(struct cpufreq_policy
*policy
, char *buf
)
67 struct cpufreq_stats
*stats
= policy
->stats
;
71 cpufreq_stats_update(stats
);
72 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " From : To\n");
73 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " : ");
74 for (i
= 0; i
< stats
->state_num
; i
++) {
77 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
78 stats
->freq_table
[i
]);
83 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
85 for (i
= 0; i
< stats
->state_num
; i
++) {
89 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u: ",
90 stats
->freq_table
[i
]);
92 for (j
= 0; j
< stats
->state_num
; j
++) {
95 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
96 stats
->trans_table
[i
*stats
->max_state
+j
]);
100 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
102 if (len
>= PAGE_SIZE
)
106 cpufreq_freq_attr_ro(trans_table
);
109 cpufreq_freq_attr_ro(total_trans
);
110 cpufreq_freq_attr_ro(time_in_state
);
112 static struct attribute
*default_attrs
[] = {
115 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
120 static struct attribute_group stats_attr_group
= {
121 .attrs
= default_attrs
,
125 static int freq_table_get_index(struct cpufreq_stats
*stats
, unsigned int freq
)
128 for (index
= 0; index
< stats
->max_state
; index
++)
129 if (stats
->freq_table
[index
] == freq
)
134 static void __cpufreq_stats_free_table(struct cpufreq_policy
*policy
)
136 struct cpufreq_stats
*stats
= policy
->stats
;
142 pr_debug("%s: Free stats table\n", __func__
);
144 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
145 kfree(stats
->time_in_state
);
147 policy
->stats
= NULL
;
150 static void cpufreq_stats_free_table(unsigned int cpu
)
152 struct cpufreq_policy
*policy
;
154 policy
= cpufreq_cpu_get(cpu
);
158 __cpufreq_stats_free_table(policy
);
160 cpufreq_cpu_put(policy
);
163 static int __cpufreq_stats_create_table(struct cpufreq_policy
*policy
)
165 unsigned int i
= 0, count
= 0, ret
= -ENOMEM
;
166 struct cpufreq_stats
*stats
;
167 unsigned int alloc_size
;
168 unsigned int cpu
= policy
->cpu
;
169 struct cpufreq_frequency_table
*pos
, *table
;
171 /* We need cpufreq table for creating stats table */
172 table
= cpufreq_frequency_get_table(cpu
);
173 if (unlikely(!table
))
176 /* stats already initialized */
180 stats
= kzalloc(sizeof(*stats
), GFP_KERNEL
);
184 /* Find total allocation size */
185 cpufreq_for_each_valid_entry(pos
, table
)
188 alloc_size
= count
* sizeof(int) + count
* sizeof(u64
);
190 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
191 alloc_size
+= count
* count
* sizeof(int);
194 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
195 stats
->time_in_state
= kzalloc(alloc_size
, GFP_KERNEL
);
196 if (!stats
->time_in_state
)
199 stats
->freq_table
= (unsigned int *)(stats
->time_in_state
+ count
);
201 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
202 stats
->trans_table
= stats
->freq_table
+ count
;
205 stats
->max_state
= count
;
207 /* Find valid-unique entries */
208 cpufreq_for_each_valid_entry(pos
, table
)
209 if (freq_table_get_index(stats
, pos
->frequency
) == -1)
210 stats
->freq_table
[i
++] = pos
->frequency
;
211 stats
->state_num
= i
;
213 spin_lock(&cpufreq_stats_lock
);
214 stats
->last_time
= get_jiffies_64();
215 stats
->last_index
= freq_table_get_index(stats
, policy
->cur
);
216 spin_unlock(&cpufreq_stats_lock
);
218 policy
->stats
= stats
;
219 ret
= sysfs_create_group(&policy
->kobj
, &stats_attr_group
);
223 /* We failed, release resources */
224 policy
->stats
= NULL
;
225 kfree(stats
->time_in_state
);
232 static void cpufreq_stats_create_table(unsigned int cpu
)
234 struct cpufreq_policy
*policy
;
237 * "likely(!policy)" because normally cpufreq_stats will be registered
238 * before cpufreq driver
240 policy
= cpufreq_cpu_get(cpu
);
244 __cpufreq_stats_create_table(policy
);
246 cpufreq_cpu_put(policy
);
249 static int cpufreq_stat_notifier_policy(struct notifier_block
*nb
,
250 unsigned long val
, void *data
)
253 struct cpufreq_policy
*policy
= data
;
255 if (val
== CPUFREQ_CREATE_POLICY
)
256 ret
= __cpufreq_stats_create_table(policy
);
257 else if (val
== CPUFREQ_REMOVE_POLICY
)
258 __cpufreq_stats_free_table(policy
);
263 static int cpufreq_stat_notifier_trans(struct notifier_block
*nb
,
264 unsigned long val
, void *data
)
266 struct cpufreq_freqs
*freq
= data
;
267 struct cpufreq_policy
*policy
= cpufreq_cpu_get(freq
->cpu
);
268 struct cpufreq_stats
*stats
;
269 int old_index
, new_index
;
272 pr_err("%s: No policy found\n", __func__
);
276 if (val
!= CPUFREQ_POSTCHANGE
)
279 if (!policy
->stats
) {
280 pr_debug("%s: No stats found\n", __func__
);
284 stats
= policy
->stats
;
286 old_index
= stats
->last_index
;
287 new_index
= freq_table_get_index(stats
, freq
->new);
289 /* We can't do stats->time_in_state[-1]= .. */
290 if (old_index
== -1 || new_index
== -1)
293 cpufreq_stats_update(stats
);
295 if (old_index
== new_index
)
298 spin_lock(&cpufreq_stats_lock
);
299 stats
->last_index
= new_index
;
300 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
301 stats
->trans_table
[old_index
* stats
->max_state
+ new_index
]++;
303 stats
->total_trans
++;
304 spin_unlock(&cpufreq_stats_lock
);
307 cpufreq_cpu_put(policy
);
311 static struct notifier_block notifier_policy_block
= {
312 .notifier_call
= cpufreq_stat_notifier_policy
315 static struct notifier_block notifier_trans_block
= {
316 .notifier_call
= cpufreq_stat_notifier_trans
319 static int __init
cpufreq_stats_init(void)
324 spin_lock_init(&cpufreq_stats_lock
);
325 ret
= cpufreq_register_notifier(¬ifier_policy_block
,
326 CPUFREQ_POLICY_NOTIFIER
);
330 for_each_online_cpu(cpu
)
331 cpufreq_stats_create_table(cpu
);
333 ret
= cpufreq_register_notifier(¬ifier_trans_block
,
334 CPUFREQ_TRANSITION_NOTIFIER
);
336 cpufreq_unregister_notifier(¬ifier_policy_block
,
337 CPUFREQ_POLICY_NOTIFIER
);
338 for_each_online_cpu(cpu
)
339 cpufreq_stats_free_table(cpu
);
345 static void __exit
cpufreq_stats_exit(void)
349 cpufreq_unregister_notifier(¬ifier_policy_block
,
350 CPUFREQ_POLICY_NOTIFIER
);
351 cpufreq_unregister_notifier(¬ifier_trans_block
,
352 CPUFREQ_TRANSITION_NOTIFIER
);
353 for_each_online_cpu(cpu
)
354 cpufreq_stats_free_table(cpu
);
357 MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
358 MODULE_DESCRIPTION("Export cpufreq stats via sysfs");
359 MODULE_LICENSE("GPL");
361 module_init(cpufreq_stats_init
);
362 module_exit(cpufreq_stats_exit
);