cpufreq: stats: Fix concurrency issues while resetting stats
[linux-2.6-block.git] / drivers / cpufreq / cpufreq_stats.c
CommitLineData
1da177e4
LT
1/*
2 * drivers/cpufreq/cpufreq_stats.c
3 *
4 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
0a829c5a 5 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
1da177e4
LT
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
1da177e4 12#include <linux/cpu.h>
1da177e4 13#include <linux/cpufreq.h>
5c720d37 14#include <linux/module.h>
5ff0a268 15#include <linux/slab.h>
1da177e4 16
1aefc75b 17static DEFINE_SPINLOCK(cpufreq_stats_lock);
1da177e4 18
1da177e4 19struct cpufreq_stats {
1da177e4 20 unsigned int total_trans;
bb176f7d 21 unsigned long long last_time;
1da177e4
LT
22 unsigned int max_state;
23 unsigned int state_num;
24 unsigned int last_index;
1e7586a1 25 u64 *time_in_state;
1da177e4 26 unsigned int *freq_table;
1da177e4 27 unsigned int *trans_table;
1da177e4
LT
28};
29
d476ec4f 30static void cpufreq_stats_update(struct cpufreq_stats *stats)
1da177e4 31{
9531347c 32 unsigned long long cur_time = get_jiffies_64();
58f1df25 33
c960f9b2 34 stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
50941607 35 stats->last_time = cur_time;
1da177e4
LT
36}
37
ee7930ee
MM
38static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
39{
40 unsigned int count = stats->max_state;
41
9795607d 42 spin_lock(&cpufreq_stats_lock);
ee7930ee 43 memset(stats->time_in_state, 0, count * sizeof(u64));
ee7930ee 44 memset(stats->trans_table, 0, count * count * sizeof(int));
ee7930ee
MM
45 stats->last_time = get_jiffies_64();
46 stats->total_trans = 0;
9795607d 47 spin_unlock(&cpufreq_stats_lock);
ee7930ee
MM
48}
49
0a829c5a 50static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
1da177e4 51{
a9aaf291 52 return sprintf(buf, "%d\n", policy->stats->total_trans);
1da177e4 53}
10b81821 54cpufreq_freq_attr_ro(total_trans);
1da177e4 55
0a829c5a 56static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
1da177e4 57{
50941607 58 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
59 ssize_t len = 0;
60 int i;
a9aaf291 61
1aefc75b
RW
62 if (policy->fast_switch_enabled)
63 return 0;
64
9795607d 65 spin_lock(&cpufreq_stats_lock);
50941607 66 cpufreq_stats_update(stats);
9795607d
VK
67 spin_unlock(&cpufreq_stats_lock);
68
50941607
VK
69 for (i = 0; i < stats->state_num; i++) {
70 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
0a829c5a 71 (unsigned long long)
50941607 72 jiffies_64_to_clock_t(stats->time_in_state[i]));
1da177e4
LT
73 }
74 return len;
75}
10b81821 76cpufreq_freq_attr_ro(time_in_state);
1da177e4 77
ee7930ee
MM
78static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
79 size_t count)
80{
81 /* We don't care what is written to the attribute. */
82 cpufreq_stats_clear_table(policy->stats);
83 return count;
84}
10b81821 85cpufreq_freq_attr_wo(reset);
ee7930ee 86
0a829c5a 87static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
1da177e4 88{
50941607 89 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
90 ssize_t len = 0;
91 int i, j;
92
1aefc75b
RW
93 if (policy->fast_switch_enabled)
94 return 0;
95
58f1df25
VP
96 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
97 len += snprintf(buf + len, PAGE_SIZE - len, " : ");
50941607 98 for (i = 0; i < stats->state_num; i++) {
58f1df25
VP
99 if (len >= PAGE_SIZE)
100 break;
101 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
50941607 102 stats->freq_table[i]);
58f1df25
VP
103 }
104 if (len >= PAGE_SIZE)
25aca347 105 return PAGE_SIZE;
58f1df25
VP
106
107 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
108
50941607 109 for (i = 0; i < stats->state_num; i++) {
1da177e4
LT
110 if (len >= PAGE_SIZE)
111 break;
58f1df25
VP
112
113 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
50941607 114 stats->freq_table[i]);
1da177e4 115
50941607 116 for (j = 0; j < stats->state_num; j++) {
1da177e4
LT
117 if (len >= PAGE_SIZE)
118 break;
58f1df25 119 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
50941607 120 stats->trans_table[i*stats->max_state+j]);
1da177e4 121 }
25aca347
CEB
122 if (len >= PAGE_SIZE)
123 break;
1da177e4
LT
124 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
125 }
f7bc9b20
GS
126
127 if (len >= PAGE_SIZE) {
128 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
129 return -EFBIG;
130 }
1da177e4
LT
131 return len;
132}
df18e504 133cpufreq_freq_attr_ro(trans_table);
1da177e4 134
1da177e4 135static struct attribute *default_attrs[] = {
df18e504
VK
136 &total_trans.attr,
137 &time_in_state.attr,
ee7930ee 138 &reset.attr,
df18e504 139 &trans_table.attr,
1da177e4
LT
140 NULL
141};
402202e8 142static const struct attribute_group stats_attr_group = {
1da177e4
LT
143 .attrs = default_attrs,
144 .name = "stats"
145};
146
50941607 147static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
1da177e4
LT
148{
149 int index;
50941607
VK
150 for (index = 0; index < stats->max_state; index++)
151 if (stats->freq_table[index] == freq)
1da177e4
LT
152 return index;
153 return -1;
154}
155
1aefc75b 156void cpufreq_stats_free_table(struct cpufreq_policy *policy)
1da177e4 157{
50941607 158 struct cpufreq_stats *stats = policy->stats;
b8eed8af 159
a9aaf291 160 /* Already freed */
50941607 161 if (!stats)
2d13594d
VK
162 return;
163
50941607 164 pr_debug("%s: Free stats table\n", __func__);
2d13594d
VK
165
166 sysfs_remove_group(&policy->kobj, &stats_attr_group);
50941607
VK
167 kfree(stats->time_in_state);
168 kfree(stats);
a9aaf291 169 policy->stats = NULL;
98586ed8 170}
171
1aefc75b 172void cpufreq_stats_create_table(struct cpufreq_policy *policy)
1da177e4 173{
a685c6d0 174 unsigned int i = 0, count = 0, ret = -ENOMEM;
50941607 175 struct cpufreq_stats *stats;
1da177e4 176 unsigned int alloc_size;
55d85293 177 struct cpufreq_frequency_table *pos;
ad4c2302 178
55d85293
VK
179 count = cpufreq_table_count_valid_entries(policy);
180 if (!count)
1aefc75b 181 return;
ad4c2302 182
b8c67448 183 /* stats already initialized */
a9aaf291 184 if (policy->stats)
1aefc75b 185 return;
b8c67448 186
50941607 187 stats = kzalloc(sizeof(*stats), GFP_KERNEL);
a685c6d0 188 if (!stats)
1aefc75b 189 return;
1da177e4 190
1e7586a1 191 alloc_size = count * sizeof(int) + count * sizeof(u64);
1da177e4 192
1da177e4 193 alloc_size += count * count * sizeof(int);
a685c6d0
VK
194
195 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
50941607 196 stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
a685c6d0
VK
197 if (!stats->time_in_state)
198 goto free_stat;
199
50941607 200 stats->freq_table = (unsigned int *)(stats->time_in_state + count);
1da177e4 201
50941607 202 stats->trans_table = stats->freq_table + count;
a685c6d0
VK
203
204 stats->max_state = count;
205
206 /* Find valid-unique entries */
55d85293 207 cpufreq_for_each_valid_entry(pos, policy->freq_table)
50941607
VK
208 if (freq_table_get_index(stats, pos->frequency) == -1)
209 stats->freq_table[i++] = pos->frequency;
a685c6d0 210
490285c6 211 stats->state_num = i;
50941607
VK
212 stats->last_time = get_jiffies_64();
213 stats->last_index = freq_table_get_index(stats, policy->cur);
a685c6d0
VK
214
215 policy->stats = stats;
216 ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
217 if (!ret)
1aefc75b 218 return;
a685c6d0
VK
219
220 /* We failed, release resources */
a9aaf291 221 policy->stats = NULL;
a685c6d0
VK
222 kfree(stats->time_in_state);
223free_stat:
224 kfree(stats);
b3f9ff88
VK
225}
226
1aefc75b
RW
227void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
228 unsigned int new_freq)
1da177e4 229{
1aefc75b 230 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
231 int old_index, new_index;
232
1aefc75b 233 if (!stats) {
a9aaf291 234 pr_debug("%s: No stats found\n", __func__);
1aefc75b 235 return;
a9aaf291
VK
236 }
237
50941607 238 old_index = stats->last_index;
1aefc75b 239 new_index = freq_table_get_index(stats, new_freq);
1da177e4 240
50941607 241 /* We can't do stats->time_in_state[-1]= .. */
1aefc75b
RW
242 if (old_index == -1 || new_index == -1 || old_index == new_index)
243 return;
8edc59d9 244
9795607d 245 spin_lock(&cpufreq_stats_lock);
e7347694
VK
246 cpufreq_stats_update(stats);
247
50941607 248 stats->last_index = new_index;
50941607 249 stats->trans_table[old_index * stats->max_state + new_index]++;
50941607 250 stats->total_trans++;
9795607d 251 spin_unlock(&cpufreq_stats_lock);
1da177e4 252}