diff options
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/Kconfig | 13 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 4 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq_stats.c | 154 |
3 files changed, 29 insertions, 142 deletions
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index b7445b6ae5a4..c822d72629d5 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig | |||
@@ -31,23 +31,18 @@ config CPU_FREQ_BOOST_SW | |||
31 | depends on THERMAL | 31 | depends on THERMAL |
32 | 32 | ||
33 | config CPU_FREQ_STAT | 33 | config CPU_FREQ_STAT |
34 | tristate "CPU frequency translation statistics" | 34 | bool "CPU frequency transition statistics" |
35 | default y | 35 | default y |
36 | help | 36 | help |
37 | This driver exports CPU frequency statistics information through sysfs | 37 | Export CPU frequency statistics information through sysfs. |
38 | file system. | ||
39 | |||
40 | To compile this driver as a module, choose M here: the | ||
41 | module will be called cpufreq_stats. | ||
42 | 38 | ||
43 | If in doubt, say N. | 39 | If in doubt, say N. |
44 | 40 | ||
45 | config CPU_FREQ_STAT_DETAILS | 41 | config CPU_FREQ_STAT_DETAILS |
46 | bool "CPU frequency translation statistics details" | 42 | bool "CPU frequency transition statistics details" |
47 | depends on CPU_FREQ_STAT | 43 | depends on CPU_FREQ_STAT |
48 | help | 44 | help |
49 | This will show detail CPU frequency translation table in sysfs file | 45 | Show detailed CPU frequency transition table in sysfs. |
50 | system. | ||
51 | 46 | ||
52 | If in doubt, say N. | 47 | If in doubt, say N. |
53 | 48 | ||
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 198416bc22de..c6a14ba239a2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -347,6 +347,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, | |||
347 | pr_debug("FREQ: %lu - CPU: %lu\n", | 347 | pr_debug("FREQ: %lu - CPU: %lu\n", |
348 | (unsigned long)freqs->new, (unsigned long)freqs->cpu); | 348 | (unsigned long)freqs->new, (unsigned long)freqs->cpu); |
349 | trace_cpu_frequency(freqs->new, freqs->cpu); | 349 | trace_cpu_frequency(freqs->new, freqs->cpu); |
350 | cpufreq_stats_record_transition(policy, freqs->new); | ||
350 | srcu_notifier_call_chain(&cpufreq_transition_notifier_list, | 351 | srcu_notifier_call_chain(&cpufreq_transition_notifier_list, |
351 | CPUFREQ_POSTCHANGE, freqs); | 352 | CPUFREQ_POSTCHANGE, freqs); |
352 | if (likely(policy) && likely(policy->cpu == freqs->cpu)) | 353 | if (likely(policy) && likely(policy->cpu == freqs->cpu)) |
@@ -1108,6 +1109,7 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify) | |||
1108 | CPUFREQ_REMOVE_POLICY, policy); | 1109 | CPUFREQ_REMOVE_POLICY, policy); |
1109 | 1110 | ||
1110 | down_write(&policy->rwsem); | 1111 | down_write(&policy->rwsem); |
1112 | cpufreq_stats_free_table(policy); | ||
1111 | cpufreq_remove_dev_symlink(policy); | 1113 | cpufreq_remove_dev_symlink(policy); |
1112 | kobj = &policy->kobj; | 1114 | kobj = &policy->kobj; |
1113 | cmp = &policy->kobj_unregister; | 1115 | cmp = &policy->kobj_unregister; |
@@ -1262,6 +1264,8 @@ static int cpufreq_online(unsigned int cpu) | |||
1262 | ret = cpufreq_add_dev_interface(policy); | 1264 | ret = cpufreq_add_dev_interface(policy); |
1263 | if (ret) | 1265 | if (ret) |
1264 | goto out_exit_policy; | 1266 | goto out_exit_policy; |
1267 | |||
1268 | cpufreq_stats_create_table(policy); | ||
1265 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, | 1269 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, |
1266 | CPUFREQ_CREATE_POLICY, policy); | 1270 | CPUFREQ_CREATE_POLICY, policy); |
1267 | 1271 | ||
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 5e370a30a964..c6e7f81a0397 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c | |||
@@ -15,7 +15,7 @@ | |||
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/cputime.h> | 16 | #include <linux/cputime.h> |
17 | 17 | ||
18 | static spinlock_t cpufreq_stats_lock; | 18 | static DEFINE_SPINLOCK(cpufreq_stats_lock); |
19 | 19 | ||
20 | struct cpufreq_stats { | 20 | struct cpufreq_stats { |
21 | unsigned int total_trans; | 21 | unsigned int total_trans; |
@@ -52,6 +52,9 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) | |||
52 | ssize_t len = 0; | 52 | ssize_t len = 0; |
53 | int i; | 53 | int i; |
54 | 54 | ||
55 | if (policy->fast_switch_enabled) | ||
56 | return 0; | ||
57 | |||
55 | cpufreq_stats_update(stats); | 58 | cpufreq_stats_update(stats); |
56 | for (i = 0; i < stats->state_num; i++) { | 59 | for (i = 0; i < stats->state_num; i++) { |
57 | len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], | 60 | len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], |
@@ -68,6 +71,9 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) | |||
68 | ssize_t len = 0; | 71 | ssize_t len = 0; |
69 | int i, j; | 72 | int i, j; |
70 | 73 | ||
74 | if (policy->fast_switch_enabled) | ||
75 | return 0; | ||
76 | |||
71 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); | 77 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); |
72 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); | 78 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); |
73 | for (i = 0; i < stats->state_num; i++) { | 79 | for (i = 0; i < stats->state_num; i++) { |
@@ -130,7 +136,7 @@ static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq) | |||
130 | return -1; | 136 | return -1; |
131 | } | 137 | } |
132 | 138 | ||
133 | static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) | 139 | void cpufreq_stats_free_table(struct cpufreq_policy *policy) |
134 | { | 140 | { |
135 | struct cpufreq_stats *stats = policy->stats; | 141 | struct cpufreq_stats *stats = policy->stats; |
136 | 142 | ||
@@ -146,20 +152,7 @@ static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) | |||
146 | policy->stats = NULL; | 152 | policy->stats = NULL; |
147 | } | 153 | } |
148 | 154 | ||
149 | static void cpufreq_stats_free_table(unsigned int cpu) | 155 | void cpufreq_stats_create_table(struct cpufreq_policy *policy) |
150 | { | ||
151 | struct cpufreq_policy *policy; | ||
152 | |||
153 | policy = cpufreq_cpu_get(cpu); | ||
154 | if (!policy) | ||
155 | return; | ||
156 | |||
157 | __cpufreq_stats_free_table(policy); | ||
158 | |||
159 | cpufreq_cpu_put(policy); | ||
160 | } | ||
161 | |||
162 | static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) | ||
163 | { | 156 | { |
164 | unsigned int i = 0, count = 0, ret = -ENOMEM; | 157 | unsigned int i = 0, count = 0, ret = -ENOMEM; |
165 | struct cpufreq_stats *stats; | 158 | struct cpufreq_stats *stats; |
@@ -170,15 +163,15 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) | |||
170 | /* We need cpufreq table for creating stats table */ | 163 | /* We need cpufreq table for creating stats table */ |
171 | table = cpufreq_frequency_get_table(cpu); | 164 | table = cpufreq_frequency_get_table(cpu); |
172 | if (unlikely(!table)) | 165 | if (unlikely(!table)) |
173 | return 0; | 166 | return; |
174 | 167 | ||
175 | /* stats already initialized */ | 168 | /* stats already initialized */ |
176 | if (policy->stats) | 169 | if (policy->stats) |
177 | return -EEXIST; | 170 | return; |
178 | 171 | ||
179 | stats = kzalloc(sizeof(*stats), GFP_KERNEL); | 172 | stats = kzalloc(sizeof(*stats), GFP_KERNEL); |
180 | if (!stats) | 173 | if (!stats) |
181 | return -ENOMEM; | 174 | return; |
182 | 175 | ||
183 | /* Find total allocation size */ | 176 | /* Find total allocation size */ |
184 | cpufreq_for_each_valid_entry(pos, table) | 177 | cpufreq_for_each_valid_entry(pos, table) |
@@ -215,80 +208,32 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) | |||
215 | policy->stats = stats; | 208 | policy->stats = stats; |
216 | ret = sysfs_create_group(&policy->kobj, &stats_attr_group); | 209 | ret = sysfs_create_group(&policy->kobj, &stats_attr_group); |
217 | if (!ret) | 210 | if (!ret) |
218 | return 0; | 211 | return; |
219 | 212 | ||
220 | /* We failed, release resources */ | 213 | /* We failed, release resources */ |
221 | policy->stats = NULL; | 214 | policy->stats = NULL; |
222 | kfree(stats->time_in_state); | 215 | kfree(stats->time_in_state); |
223 | free_stat: | 216 | free_stat: |
224 | kfree(stats); | 217 | kfree(stats); |
225 | |||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | static void cpufreq_stats_create_table(unsigned int cpu) | ||
230 | { | ||
231 | struct cpufreq_policy *policy; | ||
232 | |||
233 | /* | ||
234 | * "likely(!policy)" because normally cpufreq_stats will be registered | ||
235 | * before cpufreq driver | ||
236 | */ | ||
237 | policy = cpufreq_cpu_get(cpu); | ||
238 | if (likely(!policy)) | ||
239 | return; | ||
240 | |||
241 | __cpufreq_stats_create_table(policy); | ||
242 | |||
243 | cpufreq_cpu_put(policy); | ||
244 | } | 218 | } |
245 | 219 | ||
246 | static int cpufreq_stat_notifier_policy(struct notifier_block *nb, | 220 | void cpufreq_stats_record_transition(struct cpufreq_policy *policy, |
247 | unsigned long val, void *data) | 221 | unsigned int new_freq) |
248 | { | 222 | { |
249 | int ret = 0; | 223 | struct cpufreq_stats *stats = policy->stats; |
250 | struct cpufreq_policy *policy = data; | ||
251 | |||
252 | if (val == CPUFREQ_CREATE_POLICY) | ||
253 | ret = __cpufreq_stats_create_table(policy); | ||
254 | else if (val == CPUFREQ_REMOVE_POLICY) | ||
255 | __cpufreq_stats_free_table(policy); | ||
256 | |||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | static int cpufreq_stat_notifier_trans(struct notifier_block *nb, | ||
261 | unsigned long val, void *data) | ||
262 | { | ||
263 | struct cpufreq_freqs *freq = data; | ||
264 | struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu); | ||
265 | struct cpufreq_stats *stats; | ||
266 | int old_index, new_index; | 224 | int old_index, new_index; |
267 | 225 | ||
268 | if (!policy) { | 226 | if (!stats) { |
269 | pr_err("%s: No policy found\n", __func__); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | if (val != CPUFREQ_POSTCHANGE) | ||
274 | goto put_policy; | ||
275 | |||
276 | if (!policy->stats) { | ||
277 | pr_debug("%s: No stats found\n", __func__); | 227 | pr_debug("%s: No stats found\n", __func__); |
278 | goto put_policy; | 228 | return; |
279 | } | 229 | } |
280 | 230 | ||
281 | stats = policy->stats; | ||
282 | |||
283 | old_index = stats->last_index; | 231 | old_index = stats->last_index; |
284 | new_index = freq_table_get_index(stats, freq->new); | 232 | new_index = freq_table_get_index(stats, new_freq); |
285 | 233 | ||
286 | /* We can't do stats->time_in_state[-1]= .. */ | 234 | /* We can't do stats->time_in_state[-1]= .. */ |
287 | if (old_index == -1 || new_index == -1) | 235 | if (old_index == -1 || new_index == -1 || old_index == new_index) |
288 | goto put_policy; | 236 | return; |
289 | |||
290 | if (old_index == new_index) | ||
291 | goto put_policy; | ||
292 | 237 | ||
293 | cpufreq_stats_update(stats); | 238 | cpufreq_stats_update(stats); |
294 | 239 | ||
@@ -297,61 +242,4 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, | |||
297 | stats->trans_table[old_index * stats->max_state + new_index]++; | 242 | stats->trans_table[old_index * stats->max_state + new_index]++; |
298 | #endif | 243 | #endif |
299 | stats->total_trans++; | 244 | stats->total_trans++; |
300 | |||
301 | put_policy: | ||
302 | cpufreq_cpu_put(policy); | ||
303 | return 0; | ||
304 | } | 245 | } |
305 | |||
306 | static struct notifier_block notifier_policy_block = { | ||
307 | .notifier_call = cpufreq_stat_notifier_policy | ||
308 | }; | ||
309 | |||
310 | static struct notifier_block notifier_trans_block = { | ||
311 | .notifier_call = cpufreq_stat_notifier_trans | ||
312 | }; | ||
313 | |||
314 | static int __init cpufreq_stats_init(void) | ||
315 | { | ||
316 | int ret; | ||
317 | unsigned int cpu; | ||
318 | |||
319 | spin_lock_init(&cpufreq_stats_lock); | ||
320 | ret = cpufreq_register_notifier(¬ifier_policy_block, | ||
321 | CPUFREQ_POLICY_NOTIFIER); | ||
322 | if (ret) | ||
323 | return ret; | ||
324 | |||
325 | for_each_online_cpu(cpu) | ||
326 | cpufreq_stats_create_table(cpu); | ||
327 | |||
328 | ret = cpufreq_register_notifier(¬ifier_trans_block, | ||
329 | CPUFREQ_TRANSITION_NOTIFIER); | ||
330 | if (ret) { | ||
331 | cpufreq_unregister_notifier(¬ifier_policy_block, | ||
332 | CPUFREQ_POLICY_NOTIFIER); | ||
333 | for_each_online_cpu(cpu) | ||
334 | cpufreq_stats_free_table(cpu); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | static void __exit cpufreq_stats_exit(void) | ||
341 | { | ||
342 | unsigned int cpu; | ||
343 | |||
344 | cpufreq_unregister_notifier(¬ifier_policy_block, | ||
345 | CPUFREQ_POLICY_NOTIFIER); | ||
346 | cpufreq_unregister_notifier(¬ifier_trans_block, | ||
347 | CPUFREQ_TRANSITION_NOTIFIER); | ||
348 | for_each_online_cpu(cpu) | ||
349 | cpufreq_stats_free_table(cpu); | ||
350 | } | ||
351 | |||
352 | MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); | ||
353 | MODULE_DESCRIPTION("Export cpufreq stats via sysfs"); | ||
354 | MODULE_LICENSE("GPL"); | ||
355 | |||
356 | module_init(cpufreq_stats_init); | ||
357 | module_exit(cpufreq_stats_exit); | ||