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