cpufreq: stats: rename 'struct cpufreq_stats' objects as 'stats'
[deliverable/linux.git] / 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/cpu.h>
13 #include <linux/cpufreq.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/cputime.h>
17
18 static spinlock_t cpufreq_stats_lock;
19
20 struct cpufreq_stats {
21 unsigned int cpu;
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;
27 u64 *time_in_state;
28 unsigned int *freq_table;
29 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
30 unsigned int *trans_table;
31 #endif
32 };
33
34 static int cpufreq_stats_update(struct cpufreq_stats *stats)
35 {
36 unsigned long long cur_time = get_jiffies_64();
37
38 spin_lock(&cpufreq_stats_lock);
39 if (stats->time_in_state)
40 stats->time_in_state[stats->last_index] +=
41 cur_time - stats->last_time;
42 stats->last_time = cur_time;
43 spin_unlock(&cpufreq_stats_lock);
44 return 0;
45 }
46
47 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
48 {
49 return sprintf(buf, "%d\n", policy->stats->total_trans);
50 }
51
52 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
53 {
54 struct cpufreq_stats *stats = policy->stats;
55 ssize_t len = 0;
56 int i;
57
58 cpufreq_stats_update(stats);
59 for (i = 0; i < stats->state_num; i++) {
60 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
61 (unsigned long long)
62 jiffies_64_to_clock_t(stats->time_in_state[i]));
63 }
64 return len;
65 }
66
67 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
68 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
69 {
70 struct cpufreq_stats *stats = policy->stats;
71 ssize_t len = 0;
72 int i, j;
73
74 cpufreq_stats_update(stats);
75 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
76 len += snprintf(buf + len, PAGE_SIZE - len, " : ");
77 for (i = 0; i < stats->state_num; i++) {
78 if (len >= PAGE_SIZE)
79 break;
80 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
81 stats->freq_table[i]);
82 }
83 if (len >= PAGE_SIZE)
84 return PAGE_SIZE;
85
86 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
87
88 for (i = 0; i < stats->state_num; i++) {
89 if (len >= PAGE_SIZE)
90 break;
91
92 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
93 stats->freq_table[i]);
94
95 for (j = 0; j < stats->state_num; j++) {
96 if (len >= PAGE_SIZE)
97 break;
98 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
99 stats->trans_table[i*stats->max_state+j]);
100 }
101 if (len >= PAGE_SIZE)
102 break;
103 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
104 }
105 if (len >= PAGE_SIZE)
106 return PAGE_SIZE;
107 return len;
108 }
109 cpufreq_freq_attr_ro(trans_table);
110 #endif
111
112 cpufreq_freq_attr_ro(total_trans);
113 cpufreq_freq_attr_ro(time_in_state);
114
115 static struct attribute *default_attrs[] = {
116 &total_trans.attr,
117 &time_in_state.attr,
118 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
119 &trans_table.attr,
120 #endif
121 NULL
122 };
123 static struct attribute_group stats_attr_group = {
124 .attrs = default_attrs,
125 .name = "stats"
126 };
127
128 static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
129 {
130 int index;
131 for (index = 0; index < stats->max_state; index++)
132 if (stats->freq_table[index] == freq)
133 return index;
134 return -1;
135 }
136
137 static void __cpufreq_stats_free_table(struct cpufreq_policy *policy)
138 {
139 struct cpufreq_stats *stats = policy->stats;
140
141 /* Already freed */
142 if (!stats)
143 return;
144
145 pr_debug("%s: Free stats table\n", __func__);
146
147 sysfs_remove_group(&policy->kobj, &stats_attr_group);
148 kfree(stats->time_in_state);
149 kfree(stats);
150 policy->stats = NULL;
151 }
152
153 static void cpufreq_stats_free_table(unsigned int cpu)
154 {
155 struct cpufreq_policy *policy;
156
157 policy = cpufreq_cpu_get(cpu);
158 if (!policy)
159 return;
160
161 __cpufreq_stats_free_table(policy);
162
163 cpufreq_cpu_put(policy);
164 }
165
166 static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
167 {
168 unsigned int i, count = 0, ret = 0;
169 struct cpufreq_stats *stats;
170 unsigned int alloc_size;
171 unsigned int cpu = policy->cpu;
172 struct cpufreq_frequency_table *pos, *table;
173
174 table = cpufreq_frequency_get_table(cpu);
175 if (unlikely(!table))
176 return 0;
177
178 /* stats already initialized */
179 if (policy->stats)
180 return -EEXIST;
181
182 stats = kzalloc(sizeof(*stats), GFP_KERNEL);
183 if ((stats) == NULL)
184 return -ENOMEM;
185
186 ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
187 if (ret)
188 goto error_out;
189
190 stats->cpu = cpu;
191 policy->stats = stats;
192
193 cpufreq_for_each_valid_entry(pos, table)
194 count++;
195
196 alloc_size = count * sizeof(int) + count * sizeof(u64);
197
198 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
199 alloc_size += count * count * sizeof(int);
200 #endif
201 stats->max_state = count;
202 stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
203 if (!stats->time_in_state) {
204 ret = -ENOMEM;
205 goto error_alloc;
206 }
207 stats->freq_table = (unsigned int *)(stats->time_in_state + count);
208
209 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
210 stats->trans_table = stats->freq_table + count;
211 #endif
212 i = 0;
213 cpufreq_for_each_valid_entry(pos, table)
214 if (freq_table_get_index(stats, pos->frequency) == -1)
215 stats->freq_table[i++] = pos->frequency;
216 stats->state_num = i;
217 spin_lock(&cpufreq_stats_lock);
218 stats->last_time = get_jiffies_64();
219 stats->last_index = freq_table_get_index(stats, policy->cur);
220 spin_unlock(&cpufreq_stats_lock);
221 return 0;
222 error_alloc:
223 sysfs_remove_group(&policy->kobj, &stats_attr_group);
224 error_out:
225 kfree(stats);
226 policy->stats = NULL;
227 return ret;
228 }
229
230 static void cpufreq_stats_create_table(unsigned int cpu)
231 {
232 struct cpufreq_policy *policy;
233
234 /*
235 * "likely(!policy)" because normally cpufreq_stats will be registered
236 * before cpufreq driver
237 */
238 policy = cpufreq_cpu_get(cpu);
239 if (likely(!policy))
240 return;
241
242 __cpufreq_stats_create_table(policy);
243
244 cpufreq_cpu_put(policy);
245 }
246
247 static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
248 {
249 policy->stats->cpu = policy->cpu;
250 }
251
252 static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
253 unsigned long val, void *data)
254 {
255 int ret = 0;
256 struct cpufreq_policy *policy = data;
257
258 if (val == CPUFREQ_UPDATE_POLICY_CPU) {
259 cpufreq_stats_update_policy_cpu(policy);
260 return 0;
261 }
262
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);
267
268 return ret;
269 }
270
271 static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
272 unsigned long val, void *data)
273 {
274 struct cpufreq_freqs *freq = data;
275 struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu);
276 struct cpufreq_stats *stats;
277 int old_index, new_index;
278
279 if (!policy) {
280 pr_err("%s: No policy found\n", __func__);
281 return 0;
282 }
283
284 if (val != CPUFREQ_POSTCHANGE)
285 goto put_policy;
286
287 if (!policy->stats) {
288 pr_debug("%s: No stats found\n", __func__);
289 goto put_policy;
290 }
291
292 stats = policy->stats;
293
294 old_index = stats->last_index;
295 new_index = freq_table_get_index(stats, freq->new);
296
297 /* We can't do stats->time_in_state[-1]= .. */
298 if (old_index == -1 || new_index == -1)
299 goto put_policy;
300
301 cpufreq_stats_update(stats);
302
303 if (old_index == new_index)
304 goto put_policy;
305
306 spin_lock(&cpufreq_stats_lock);
307 stats->last_index = new_index;
308 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
309 stats->trans_table[old_index * stats->max_state + new_index]++;
310 #endif
311 stats->total_trans++;
312 spin_unlock(&cpufreq_stats_lock);
313
314 put_policy:
315 cpufreq_cpu_put(policy);
316 return 0;
317 }
318
319 static struct notifier_block notifier_policy_block = {
320 .notifier_call = cpufreq_stat_notifier_policy
321 };
322
323 static struct notifier_block notifier_trans_block = {
324 .notifier_call = cpufreq_stat_notifier_trans
325 };
326
327 static int __init cpufreq_stats_init(void)
328 {
329 int ret;
330 unsigned int cpu;
331
332 spin_lock_init(&cpufreq_stats_lock);
333 ret = cpufreq_register_notifier(&notifier_policy_block,
334 CPUFREQ_POLICY_NOTIFIER);
335 if (ret)
336 return ret;
337
338 for_each_online_cpu(cpu)
339 cpufreq_stats_create_table(cpu);
340
341 ret = cpufreq_register_notifier(&notifier_trans_block,
342 CPUFREQ_TRANSITION_NOTIFIER);
343 if (ret) {
344 cpufreq_unregister_notifier(&notifier_policy_block,
345 CPUFREQ_POLICY_NOTIFIER);
346 for_each_online_cpu(cpu)
347 cpufreq_stats_free_table(cpu);
348 return ret;
349 }
350
351 return 0;
352 }
353 static void __exit cpufreq_stats_exit(void)
354 {
355 unsigned int cpu;
356
357 cpufreq_unregister_notifier(&notifier_policy_block,
358 CPUFREQ_POLICY_NOTIFIER);
359 cpufreq_unregister_notifier(&notifier_trans_block,
360 CPUFREQ_TRANSITION_NOTIFIER);
361 for_each_online_cpu(cpu)
362 cpufreq_stats_free_table(cpu);
363 }
364
365 MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
366 MODULE_DESCRIPTION("Export cpufreq stats via sysfs");
367 MODULE_LICENSE("GPL");
368
369 module_init(cpufreq_stats_init);
370 module_exit(cpufreq_stats_exit);
This page took 0.039764 seconds and 5 git commands to generate.