diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq_stats.c')
-rw-r--r-- | drivers/cpufreq/cpufreq_stats.c | 219 |
1 files changed, 97 insertions, 122 deletions
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 0cd9b4dcef99..5e370a30a964 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c | |||
@@ -18,7 +18,6 @@ | |||
18 | static spinlock_t cpufreq_stats_lock; | 18 | static spinlock_t cpufreq_stats_lock; |
19 | 19 | ||
20 | struct cpufreq_stats { | 20 | struct cpufreq_stats { |
21 | unsigned int cpu; | ||
22 | unsigned int total_trans; | 21 | unsigned int total_trans; |
23 | unsigned long long last_time; | 22 | unsigned long long last_time; |
24 | unsigned int max_state; | 23 | unsigned int max_state; |
@@ -31,50 +30,33 @@ struct cpufreq_stats { | |||
31 | #endif | 30 | #endif |
32 | }; | 31 | }; |
33 | 32 | ||
34 | static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table); | 33 | static int cpufreq_stats_update(struct cpufreq_stats *stats) |
35 | |||
36 | struct cpufreq_stats_attribute { | ||
37 | struct attribute attr; | ||
38 | ssize_t(*show) (struct cpufreq_stats *, char *); | ||
39 | }; | ||
40 | |||
41 | static int cpufreq_stats_update(unsigned int cpu) | ||
42 | { | 34 | { |
43 | struct cpufreq_stats *stat; | 35 | unsigned long long cur_time = get_jiffies_64(); |
44 | unsigned long long cur_time; | ||
45 | 36 | ||
46 | cur_time = get_jiffies_64(); | ||
47 | spin_lock(&cpufreq_stats_lock); | 37 | spin_lock(&cpufreq_stats_lock); |
48 | stat = per_cpu(cpufreq_stats_table, cpu); | 38 | stats->time_in_state[stats->last_index] += cur_time - stats->last_time; |
49 | if (stat->time_in_state) | 39 | stats->last_time = cur_time; |
50 | stat->time_in_state[stat->last_index] += | ||
51 | cur_time - stat->last_time; | ||
52 | stat->last_time = cur_time; | ||
53 | spin_unlock(&cpufreq_stats_lock); | 40 | spin_unlock(&cpufreq_stats_lock); |
54 | return 0; | 41 | return 0; |
55 | } | 42 | } |
56 | 43 | ||
57 | static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) | 44 | static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) |
58 | { | 45 | { |
59 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); | 46 | return sprintf(buf, "%d\n", policy->stats->total_trans); |
60 | if (!stat) | ||
61 | return 0; | ||
62 | return sprintf(buf, "%d\n", | ||
63 | per_cpu(cpufreq_stats_table, stat->cpu)->total_trans); | ||
64 | } | 47 | } |
65 | 48 | ||
66 | static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) | 49 | static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) |
67 | { | 50 | { |
51 | struct cpufreq_stats *stats = policy->stats; | ||
68 | ssize_t len = 0; | 52 | ssize_t len = 0; |
69 | int i; | 53 | int i; |
70 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); | 54 | |
71 | if (!stat) | 55 | cpufreq_stats_update(stats); |
72 | return 0; | 56 | for (i = 0; i < stats->state_num; i++) { |
73 | cpufreq_stats_update(stat->cpu); | 57 | len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], |
74 | for (i = 0; i < stat->state_num; i++) { | ||
75 | len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i], | ||
76 | (unsigned long long) | 58 | (unsigned long long) |
77 | jiffies_64_to_clock_t(stat->time_in_state[i])); | 59 | jiffies_64_to_clock_t(stats->time_in_state[i])); |
78 | } | 60 | } |
79 | return len; | 61 | return len; |
80 | } | 62 | } |
@@ -82,38 +64,35 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) | |||
82 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | 64 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
83 | static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) | 65 | static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) |
84 | { | 66 | { |
67 | struct cpufreq_stats *stats = policy->stats; | ||
85 | ssize_t len = 0; | 68 | ssize_t len = 0; |
86 | int i, j; | 69 | int i, j; |
87 | 70 | ||
88 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); | ||
89 | if (!stat) | ||
90 | return 0; | ||
91 | cpufreq_stats_update(stat->cpu); | ||
92 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); | 71 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); |
93 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); | 72 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); |
94 | for (i = 0; i < stat->state_num; i++) { | 73 | for (i = 0; i < stats->state_num; i++) { |
95 | if (len >= PAGE_SIZE) | 74 | if (len >= PAGE_SIZE) |
96 | break; | 75 | break; |
97 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", | 76 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", |
98 | stat->freq_table[i]); | 77 | stats->freq_table[i]); |
99 | } | 78 | } |
100 | if (len >= PAGE_SIZE) | 79 | if (len >= PAGE_SIZE) |
101 | return PAGE_SIZE; | 80 | return PAGE_SIZE; |
102 | 81 | ||
103 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); | 82 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
104 | 83 | ||
105 | for (i = 0; i < stat->state_num; i++) { | 84 | for (i = 0; i < stats->state_num; i++) { |
106 | if (len >= PAGE_SIZE) | 85 | if (len >= PAGE_SIZE) |
107 | break; | 86 | break; |
108 | 87 | ||
109 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", | 88 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", |
110 | stat->freq_table[i]); | 89 | stats->freq_table[i]); |
111 | 90 | ||
112 | for (j = 0; j < stat->state_num; j++) { | 91 | for (j = 0; j < stats->state_num; j++) { |
113 | if (len >= PAGE_SIZE) | 92 | if (len >= PAGE_SIZE) |
114 | break; | 93 | break; |
115 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", | 94 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", |
116 | stat->trans_table[i*stat->max_state+j]); | 95 | stats->trans_table[i*stats->max_state+j]); |
117 | } | 96 | } |
118 | if (len >= PAGE_SIZE) | 97 | if (len >= PAGE_SIZE) |
119 | break; | 98 | break; |
@@ -142,28 +121,29 @@ static struct attribute_group stats_attr_group = { | |||
142 | .name = "stats" | 121 | .name = "stats" |
143 | }; | 122 | }; |
144 | 123 | ||
145 | static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) | 124 | static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq) |
146 | { | 125 | { |
147 | int index; | 126 | int index; |
148 | for (index = 0; index < stat->max_state; index++) | 127 | for (index = 0; index < stats->max_state; index++) |
149 | if (stat->freq_table[index] == freq) | 128 | if (stats->freq_table[index] == freq) |
150 | return index; | 129 | return index; |
151 | return -1; | 130 | return -1; |
152 | } | 131 | } |
153 | 132 | ||
154 | static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) | 133 | static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) |
155 | { | 134 | { |
156 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); | 135 | struct cpufreq_stats *stats = policy->stats; |
157 | 136 | ||
158 | if (!stat) | 137 | /* Already freed */ |
138 | if (!stats) | ||
159 | return; | 139 | return; |
160 | 140 | ||
161 | pr_debug("%s: Free stat table\n", __func__); | 141 | pr_debug("%s: Free stats table\n", __func__); |
162 | 142 | ||
163 | sysfs_remove_group(&policy->kobj, &stats_attr_group); | 143 | sysfs_remove_group(&policy->kobj, &stats_attr_group); |
164 | kfree(stat->time_in_state); | 144 | kfree(stats->time_in_state); |
165 | kfree(stat); | 145 | kfree(stats); |
166 | per_cpu(cpufreq_stats_table, policy->cpu) = NULL; | 146 | policy->stats = NULL; |
167 | } | 147 | } |
168 | 148 | ||
169 | static void cpufreq_stats_free_table(unsigned int cpu) | 149 | static void cpufreq_stats_free_table(unsigned int cpu) |
@@ -174,37 +154,33 @@ static void cpufreq_stats_free_table(unsigned int cpu) | |||
174 | if (!policy) | 154 | if (!policy) |
175 | return; | 155 | return; |
176 | 156 | ||
177 | if (cpufreq_frequency_get_table(policy->cpu)) | 157 | __cpufreq_stats_free_table(policy); |
178 | __cpufreq_stats_free_table(policy); | ||
179 | 158 | ||
180 | cpufreq_cpu_put(policy); | 159 | cpufreq_cpu_put(policy); |
181 | } | 160 | } |
182 | 161 | ||
183 | static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) | 162 | static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) |
184 | { | 163 | { |
185 | unsigned int i, count = 0, ret = 0; | 164 | unsigned int i = 0, count = 0, ret = -ENOMEM; |
186 | struct cpufreq_stats *stat; | 165 | struct cpufreq_stats *stats; |
187 | unsigned int alloc_size; | 166 | unsigned int alloc_size; |
188 | unsigned int cpu = policy->cpu; | 167 | unsigned int cpu = policy->cpu; |
189 | struct cpufreq_frequency_table *pos, *table; | 168 | struct cpufreq_frequency_table *pos, *table; |
190 | 169 | ||
170 | /* We need cpufreq table for creating stats table */ | ||
191 | table = cpufreq_frequency_get_table(cpu); | 171 | table = cpufreq_frequency_get_table(cpu); |
192 | if (unlikely(!table)) | 172 | if (unlikely(!table)) |
193 | return 0; | 173 | return 0; |
194 | 174 | ||
195 | if (per_cpu(cpufreq_stats_table, cpu)) | 175 | /* stats already initialized */ |
196 | return -EBUSY; | 176 | if (policy->stats) |
197 | stat = kzalloc(sizeof(*stat), GFP_KERNEL); | 177 | return -EEXIST; |
198 | if ((stat) == NULL) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | ret = sysfs_create_group(&policy->kobj, &stats_attr_group); | ||
202 | if (ret) | ||
203 | goto error_out; | ||
204 | 178 | ||
205 | stat->cpu = cpu; | 179 | stats = kzalloc(sizeof(*stats), GFP_KERNEL); |
206 | per_cpu(cpufreq_stats_table, cpu) = stat; | 180 | if (!stats) |
181 | return -ENOMEM; | ||
207 | 182 | ||
183 | /* Find total allocation size */ | ||
208 | cpufreq_for_each_valid_entry(pos, table) | 184 | cpufreq_for_each_valid_entry(pos, table) |
209 | count++; | 185 | count++; |
210 | 186 | ||
@@ -213,32 +189,40 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) | |||
213 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | 189 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
214 | alloc_size += count * count * sizeof(int); | 190 | alloc_size += count * count * sizeof(int); |
215 | #endif | 191 | #endif |
216 | stat->max_state = count; | 192 | |
217 | stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL); | 193 | /* Allocate memory for time_in_state/freq_table/trans_table in one go */ |
218 | if (!stat->time_in_state) { | 194 | stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL); |
219 | ret = -ENOMEM; | 195 | if (!stats->time_in_state) |
220 | goto error_alloc; | 196 | goto free_stat; |
221 | } | 197 | |
222 | stat->freq_table = (unsigned int *)(stat->time_in_state + count); | 198 | stats->freq_table = (unsigned int *)(stats->time_in_state + count); |
223 | 199 | ||
224 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | 200 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
225 | stat->trans_table = stat->freq_table + count; | 201 | stats->trans_table = stats->freq_table + count; |
226 | #endif | 202 | #endif |
227 | i = 0; | 203 | |
204 | stats->max_state = count; | ||
205 | |||
206 | /* Find valid-unique entries */ | ||
228 | cpufreq_for_each_valid_entry(pos, table) | 207 | cpufreq_for_each_valid_entry(pos, table) |
229 | if (freq_table_get_index(stat, pos->frequency) == -1) | 208 | if (freq_table_get_index(stats, pos->frequency) == -1) |
230 | stat->freq_table[i++] = pos->frequency; | 209 | stats->freq_table[i++] = pos->frequency; |
231 | stat->state_num = i; | 210 | |
232 | spin_lock(&cpufreq_stats_lock); | 211 | stats->state_num = i; |
233 | stat->last_time = get_jiffies_64(); | 212 | stats->last_time = get_jiffies_64(); |
234 | stat->last_index = freq_table_get_index(stat, policy->cur); | 213 | stats->last_index = freq_table_get_index(stats, policy->cur); |
235 | spin_unlock(&cpufreq_stats_lock); | 214 | |
236 | return 0; | 215 | policy->stats = stats; |
237 | error_alloc: | 216 | ret = sysfs_create_group(&policy->kobj, &stats_attr_group); |
238 | sysfs_remove_group(&policy->kobj, &stats_attr_group); | 217 | if (!ret) |
239 | error_out: | 218 | return 0; |
240 | kfree(stat); | 219 | |
241 | per_cpu(cpufreq_stats_table, cpu) = NULL; | 220 | /* We failed, release resources */ |
221 | policy->stats = NULL; | ||
222 | kfree(stats->time_in_state); | ||
223 | free_stat: | ||
224 | kfree(stats); | ||
225 | |||
242 | return ret; | 226 | return ret; |
243 | } | 227 | } |
244 | 228 | ||
@@ -259,30 +243,12 @@ static void cpufreq_stats_create_table(unsigned int cpu) | |||
259 | cpufreq_cpu_put(policy); | 243 | cpufreq_cpu_put(policy); |
260 | } | 244 | } |
261 | 245 | ||
262 | static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) | ||
263 | { | ||
264 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, | ||
265 | policy->last_cpu); | ||
266 | |||
267 | pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n", | ||
268 | policy->cpu, policy->last_cpu); | ||
269 | per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table, | ||
270 | policy->last_cpu); | ||
271 | per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL; | ||
272 | stat->cpu = policy->cpu; | ||
273 | } | ||
274 | |||
275 | static int cpufreq_stat_notifier_policy(struct notifier_block *nb, | 246 | static int cpufreq_stat_notifier_policy(struct notifier_block *nb, |
276 | unsigned long val, void *data) | 247 | unsigned long val, void *data) |
277 | { | 248 | { |
278 | int ret = 0; | 249 | int ret = 0; |
279 | struct cpufreq_policy *policy = data; | 250 | struct cpufreq_policy *policy = data; |
280 | 251 | ||
281 | if (val == CPUFREQ_UPDATE_POLICY_CPU) { | ||
282 | cpufreq_stats_update_policy_cpu(policy); | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | if (val == CPUFREQ_CREATE_POLICY) | 252 | if (val == CPUFREQ_CREATE_POLICY) |
287 | ret = __cpufreq_stats_create_table(policy); | 253 | ret = __cpufreq_stats_create_table(policy); |
288 | else if (val == CPUFREQ_REMOVE_POLICY) | 254 | else if (val == CPUFREQ_REMOVE_POLICY) |
@@ -295,35 +261,45 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, | |||
295 | unsigned long val, void *data) | 261 | unsigned long val, void *data) |
296 | { | 262 | { |
297 | struct cpufreq_freqs *freq = data; | 263 | struct cpufreq_freqs *freq = data; |
298 | struct cpufreq_stats *stat; | 264 | struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu); |
265 | struct cpufreq_stats *stats; | ||
299 | int old_index, new_index; | 266 | int old_index, new_index; |
300 | 267 | ||
301 | if (val != CPUFREQ_POSTCHANGE) | 268 | if (!policy) { |
269 | pr_err("%s: No policy found\n", __func__); | ||
302 | return 0; | 270 | return 0; |
271 | } | ||
303 | 272 | ||
304 | stat = per_cpu(cpufreq_stats_table, freq->cpu); | 273 | if (val != CPUFREQ_POSTCHANGE) |
305 | if (!stat) | 274 | goto put_policy; |
306 | return 0; | ||
307 | 275 | ||
308 | old_index = stat->last_index; | 276 | if (!policy->stats) { |
309 | new_index = freq_table_get_index(stat, freq->new); | 277 | pr_debug("%s: No stats found\n", __func__); |
278 | goto put_policy; | ||
279 | } | ||
310 | 280 | ||
311 | /* We can't do stat->time_in_state[-1]= .. */ | 281 | stats = policy->stats; |
312 | if (old_index == -1 || new_index == -1) | 282 | |
313 | return 0; | 283 | old_index = stats->last_index; |
284 | new_index = freq_table_get_index(stats, freq->new); | ||
314 | 285 | ||
315 | cpufreq_stats_update(freq->cpu); | 286 | /* We can't do stats->time_in_state[-1]= .. */ |
287 | if (old_index == -1 || new_index == -1) | ||
288 | goto put_policy; | ||
316 | 289 | ||
317 | if (old_index == new_index) | 290 | if (old_index == new_index) |
318 | return 0; | 291 | goto put_policy; |
319 | 292 | ||
320 | spin_lock(&cpufreq_stats_lock); | 293 | cpufreq_stats_update(stats); |
321 | stat->last_index = new_index; | 294 | |
295 | stats->last_index = new_index; | ||
322 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | 296 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
323 | stat->trans_table[old_index * stat->max_state + new_index]++; | 297 | stats->trans_table[old_index * stats->max_state + new_index]++; |
324 | #endif | 298 | #endif |
325 | stat->total_trans++; | 299 | stats->total_trans++; |
326 | spin_unlock(&cpufreq_stats_lock); | 300 | |
301 | put_policy: | ||
302 | cpufreq_cpu_put(policy); | ||
327 | return 0; | 303 | return 0; |
328 | } | 304 | } |
329 | 305 | ||
@@ -374,8 +350,7 @@ static void __exit cpufreq_stats_exit(void) | |||
374 | } | 350 | } |
375 | 351 | ||
376 | MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); | 352 | MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); |
377 | MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats " | 353 | MODULE_DESCRIPTION("Export cpufreq stats via sysfs"); |
378 | "through sysfs filesystem"); | ||
379 | MODULE_LICENSE("GPL"); | 354 | MODULE_LICENSE("GPL"); |
380 | 355 | ||
381 | module_init(cpufreq_stats_init); | 356 | module_init(cpufreq_stats_init); |