Skip to content

Commit 40cf2f8

Browse files
author
Ruchi Kandoi
committed
cpufreq: Persist cpufreq time in state data across hotplug
Cpufreq time_in_state data for all CPUs is made persistent across hotplug and exposed to userspace via sysfs file /sys/devices/system/cpu/cpufreq/all_time_in_state Change-Id: I97cb5de24b6de16189bf8b5df9592d0a6e6ddf32 Signed-off-by: Ruchi Kandoi <[email protected]>
1 parent 4c7894c commit 40cf2f8

File tree

1 file changed

+223
-1
lines changed

1 file changed

+223
-1
lines changed

drivers/cpufreq/cpufreq_stats.c

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <linux/kobject.h>
2121
#include <linux/spinlock.h>
2222
#include <linux/notifier.h>
23+
#include <linux/sort.h>
24+
#include <linux/err.h>
2325
#include <asm/cputime.h>
2426

2527
static spinlock_t cpufreq_stats_lock;
@@ -38,6 +40,20 @@ struct cpufreq_stats {
3840
#endif
3941
};
4042

43+
struct all_cpufreq_stats {
44+
unsigned int state_num;
45+
cputime64_t *time_in_state;
46+
unsigned int *freq_table;
47+
};
48+
49+
struct all_freq_table {
50+
unsigned int *freq_table;
51+
unsigned int table_size;
52+
};
53+
54+
static struct all_freq_table *all_freq_table;
55+
56+
static DEFINE_PER_CPU(struct all_cpufreq_stats *, all_cpufreq_stats);
4157
static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
4258

4359
struct cpufreq_stats_attribute {
@@ -48,14 +64,24 @@ struct cpufreq_stats_attribute {
4864
static int cpufreq_stats_update(unsigned int cpu)
4965
{
5066
struct cpufreq_stats *stat;
67+
struct all_cpufreq_stats *all_stat;
5168
unsigned long long cur_time;
5269

5370
cur_time = get_jiffies_64();
5471
spin_lock(&cpufreq_stats_lock);
5572
stat = per_cpu(cpufreq_stats_table, cpu);
56-
if (stat->time_in_state)
73+
all_stat = per_cpu(all_cpufreq_stats, cpu);
74+
if (!stat) {
75+
spin_unlock(&cpufreq_stats_lock);
76+
return 0;
77+
}
78+
if (stat->time_in_state) {
5779
stat->time_in_state[stat->last_index] +=
5880
cur_time - stat->last_time;
81+
if (all_stat)
82+
all_stat->time_in_state[stat->last_index] +=
83+
cur_time - stat->last_time;
84+
}
5985
stat->last_time = cur_time;
6086
spin_unlock(&cpufreq_stats_lock);
6187
return 0;
@@ -86,6 +112,62 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
86112
return len;
87113
}
88114

115+
static int get_index_all_cpufreq_stat(struct all_cpufreq_stats *all_stat,
116+
unsigned int freq)
117+
{
118+
int i;
119+
if (!all_stat)
120+
return -1;
121+
for (i = 0; i < all_stat->state_num; i++) {
122+
if (all_stat->freq_table[i] == freq)
123+
return i;
124+
}
125+
return -1;
126+
}
127+
128+
static ssize_t show_all_time_in_state(struct kobject *kobj,
129+
struct kobj_attribute *attr, char *buf)
130+
{
131+
ssize_t len = 0;
132+
unsigned int i, cpu, freq, index;
133+
struct all_cpufreq_stats *all_stat;
134+
struct cpufreq_policy *policy;
135+
136+
len += scnprintf(buf + len, PAGE_SIZE - len, "freq\t\t");
137+
for_each_possible_cpu(cpu) {
138+
len += scnprintf(buf + len, PAGE_SIZE - len, "cpu%d\t\t", cpu);
139+
if (cpu_online(cpu))
140+
cpufreq_stats_update(cpu);
141+
}
142+
143+
if (!all_freq_table)
144+
goto out;
145+
for (i = 0; i < all_freq_table->table_size; i++) {
146+
freq = all_freq_table->freq_table[i];
147+
len += scnprintf(buf + len, PAGE_SIZE - len, "\n%u\t\t", freq);
148+
for_each_possible_cpu(cpu) {
149+
policy = cpufreq_cpu_get(cpu);
150+
if (policy == NULL)
151+
continue;
152+
all_stat = per_cpu(all_cpufreq_stats, policy->cpu);
153+
index = get_index_all_cpufreq_stat(all_stat, freq);
154+
if (index != -1) {
155+
len += scnprintf(buf + len, PAGE_SIZE - len,
156+
"%llu\t\t", (unsigned long long)
157+
cputime64_to_clock_t(all_stat->time_in_state[index]));
158+
} else {
159+
len += scnprintf(buf + len, PAGE_SIZE - len,
160+
"N/A\t\t");
161+
}
162+
cpufreq_cpu_put(policy);
163+
}
164+
}
165+
166+
out:
167+
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
168+
return len;
169+
}
170+
89171
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
90172
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
91173
{
@@ -149,6 +231,9 @@ static struct attribute_group stats_attr_group = {
149231
.name = "stats"
150232
};
151233

234+
static struct kobj_attribute _attr_all_time_in_state = __ATTR(all_time_in_state,
235+
0444, show_all_time_in_state, NULL);
236+
152237
static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
153238
{
154239
int index;
@@ -195,6 +280,29 @@ static void cpufreq_stats_free_sysfs(unsigned int cpu)
195280
cpufreq_cpu_put(policy);
196281
}
197282

283+
static void cpufreq_allstats_free(void)
284+
{
285+
int i;
286+
struct all_cpufreq_stats *all_stat;
287+
288+
sysfs_remove_file(cpufreq_global_kobject,
289+
&_attr_all_time_in_state.attr);
290+
291+
for (i = 0; i < total_cpus; i++) {
292+
all_stat = per_cpu(all_cpufreq_stats, i);
293+
if (!all_stat)
294+
continue;
295+
kfree(all_stat->time_in_state);
296+
kfree(all_stat);
297+
per_cpu(all_cpufreq_stats, i) = NULL;
298+
}
299+
if (all_freq_table) {
300+
kfree(all_freq_table->freq_table);
301+
kfree(all_freq_table);
302+
all_freq_table = NULL;
303+
}
304+
}
305+
198306
static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
199307
struct cpufreq_frequency_table *table)
200308
{
@@ -281,6 +389,106 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
281389
stat->cpu = policy->cpu;
282390
}
283391

392+
static int compare_for_sort(const void *lhs_ptr, const void *rhs_ptr)
393+
{
394+
unsigned int lhs = *(const unsigned int *)(lhs_ptr);
395+
unsigned int rhs = *(const unsigned int *)(rhs_ptr);
396+
if (lhs < rhs)
397+
return -1;
398+
if (lhs > rhs)
399+
return 1;
400+
return 0;
401+
}
402+
403+
static bool check_all_freq_table(unsigned int freq)
404+
{
405+
int i;
406+
for (i = 0; i < all_freq_table->table_size; i++) {
407+
if (freq == all_freq_table->freq_table[i])
408+
return true;
409+
}
410+
return false;
411+
}
412+
413+
static void create_all_freq_table(void)
414+
{
415+
all_freq_table = kzalloc(sizeof(struct all_freq_table),
416+
GFP_KERNEL);
417+
if (!all_freq_table)
418+
pr_warn("could not allocate memory for all_freq_table\n");
419+
return;
420+
}
421+
422+
static void add_all_freq_table(unsigned int freq)
423+
{
424+
unsigned int size;
425+
size = sizeof(unsigned int) * (all_freq_table->table_size + 1);
426+
all_freq_table->freq_table = krealloc(all_freq_table->freq_table,
427+
size, GFP_KERNEL);
428+
if (IS_ERR(all_freq_table->freq_table)) {
429+
pr_warn("Could not reallocate memory for freq_table\n");
430+
all_freq_table->freq_table = NULL;
431+
return;
432+
}
433+
all_freq_table->freq_table[all_freq_table->table_size++] = freq;
434+
}
435+
436+
static void cpufreq_allstats_create(unsigned int cpu)
437+
{
438+
int i , j = 0;
439+
unsigned int alloc_size, count = 0;
440+
struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(cpu);
441+
struct all_cpufreq_stats *all_stat;
442+
bool sort_needed = false;
443+
444+
if (!table)
445+
return;
446+
447+
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
448+
unsigned int freq = table[i].frequency;
449+
if (freq == CPUFREQ_ENTRY_INVALID)
450+
continue;
451+
count++;
452+
}
453+
454+
all_stat = kzalloc(sizeof(struct all_cpufreq_stats),
455+
GFP_KERNEL);
456+
if (!all_stat) {
457+
pr_warn("Cannot allocate memory for cpufreq stats\n");
458+
return;
459+
}
460+
461+
/*Allocate memory for freq table per cpu as well as clockticks per freq*/
462+
alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
463+
all_stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
464+
if (!all_stat->time_in_state) {
465+
pr_warn("Cannot allocate memory for cpufreq time_in_state\n");
466+
kfree(all_stat);
467+
all_stat = NULL;
468+
return;
469+
}
470+
all_stat->freq_table = (unsigned int *)
471+
(all_stat->time_in_state + count);
472+
473+
spin_lock(&cpufreq_stats_lock);
474+
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
475+
unsigned int freq = table[i].frequency;
476+
if (freq == CPUFREQ_ENTRY_INVALID)
477+
continue;
478+
all_stat->freq_table[j++] = freq;
479+
if (all_freq_table && !check_all_freq_table(freq)) {
480+
add_all_freq_table(freq);
481+
sort_needed = true;
482+
}
483+
}
484+
if (sort_needed)
485+
sort(all_freq_table->freq_table, all_freq_table->table_size,
486+
sizeof(unsigned int), &compare_for_sort, NULL);
487+
all_stat->state_num = j;
488+
per_cpu(all_cpufreq_stats, cpu) = all_stat;
489+
spin_unlock(&cpufreq_stats_lock);
490+
}
491+
284492
static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
285493
unsigned long val, void *data)
286494
{
@@ -299,6 +507,10 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
299507
table = cpufreq_frequency_get_table(cpu);
300508
if (!table)
301509
return 0;
510+
511+
if (!per_cpu(all_cpufreq_stats, cpu))
512+
cpufreq_allstats_create(cpu);
513+
302514
ret = cpufreq_stats_create_table(policy, table);
303515
if (ret)
304516
return ret;
@@ -355,6 +567,9 @@ static int cpufreq_stats_create_table_cpu(unsigned int cpu)
355567
if (!table)
356568
goto out;
357569

570+
if (!per_cpu(all_cpufreq_stats, cpu))
571+
cpufreq_allstats_create(cpu);
572+
358573
ret = cpufreq_stats_create_table(policy, table);
359574

360575
out:
@@ -430,6 +645,12 @@ static int __init cpufreq_stats_init(void)
430645
return ret;
431646
}
432647

648+
create_all_freq_table();
649+
ret = sysfs_create_file(cpufreq_global_kobject,
650+
&_attr_all_time_in_state.attr);
651+
if (ret)
652+
pr_warn("Error creating sysfs file for cpufreq stats\n");
653+
433654
return 0;
434655
}
435656
static void __exit cpufreq_stats_exit(void)
@@ -445,6 +666,7 @@ static void __exit cpufreq_stats_exit(void)
445666
cpufreq_stats_free_table(cpu);
446667
cpufreq_stats_free_sysfs(cpu);
447668
}
669+
cpufreq_allstats_free();
448670
}
449671

450672
MODULE_AUTHOR("Zou Nan hai <[email protected]>");

0 commit comments

Comments
 (0)