diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq_stats.c')
-rw-r--r-- | drivers/cpufreq/cpufreq_stats.c | 224 |
1 files changed, 178 insertions, 46 deletions
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 1337d99f0c83..3811168bf28d 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -14,7 +14,8 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/sort.h> -#include <linux/err.h> +#include <linux/of.h> +#include <linux/sched.h> #include <asm/cputime.h> static spinlock_t cpufreq_stats_lock; @@ -39,6 +40,12 @@ struct all_cpufreq_stats { unsigned int *freq_table; }; +struct cpufreq_power_stats { + unsigned int state_num; + unsigned int *curr; + unsigned int *freq_table; +}; + struct all_freq_table { unsigned int *freq_table; unsigned int table_size; @@ -48,6 +55,7 @@ static struct all_freq_table *all_freq_table; static DEFINE_PER_CPU(struct all_cpufreq_stats *, all_cpufreq_stats); static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table); +static DEFINE_PER_CPU(struct cpufreq_power_stats *, cpufreq_power_stats); struct cpufreq_stats_attribute { struct attribute attr; @@ -118,6 +126,47 @@ static int get_index_all_cpufreq_stat(struct all_cpufreq_stats *all_stat, return -1; } +void acct_update_power(struct task_struct *task, cputime_t cputime) { + struct cpufreq_power_stats *powerstats; + struct cpufreq_stats *stats; + unsigned int cpu_num, curr; + + if (!task) + return; + cpu_num = task_cpu(task); + powerstats = per_cpu(cpufreq_power_stats, cpu_num); + stats = per_cpu(cpufreq_stats_table, cpu_num); + if (!powerstats || !stats) + return; + + curr = powerstats->curr[stats->last_index]; + task->cpu_power += curr * cputime_to_usecs(cputime); +} +EXPORT_SYMBOL_GPL(acct_update_power); + +static ssize_t show_current_in_state(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + ssize_t len = 0; + unsigned int i, cpu; + struct cpufreq_power_stats *powerstats; + + spin_lock(&cpufreq_stats_lock); + for_each_possible_cpu(cpu) { + powerstats = per_cpu(cpufreq_power_stats, cpu); + if (!powerstats) + continue; + len += scnprintf(buf + len, PAGE_SIZE - len, "CPU%d:", cpu); + for (i = 0; i < powerstats->state_num; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d=%d ", powerstats->freq_table[i], + powerstats->curr[i]); + len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); + } + spin_unlock(&cpufreq_stats_lock); + return len; +} + static ssize_t show_all_time_in_state(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -227,6 +276,9 @@ static struct attribute_group stats_attr_group = { static struct kobj_attribute _attr_all_time_in_state = __ATTR(all_time_in_state, 0444, show_all_time_in_state, NULL); +static struct kobj_attribute _attr_current_in_state = __ATTR(current_in_state, + 0444, show_current_in_state, NULL); + static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) { int index; @@ -288,10 +340,27 @@ static void cpufreq_allstats_free(void) } } +static void cpufreq_powerstats_free(void) +{ + int cpu; + struct cpufreq_power_stats *powerstats; + + sysfs_remove_file(cpufreq_global_kobject, &_attr_current_in_state.attr); + + for_each_possible_cpu(cpu) { + powerstats = per_cpu(cpufreq_power_stats, cpu); + if (!powerstats) + continue; + kfree(powerstats->curr); + kfree(powerstats); + per_cpu(cpufreq_power_stats, cpu) = NULL; + } +} + static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, - struct cpufreq_frequency_table *table) + struct cpufreq_frequency_table *table, int count) { - unsigned int i, j, count = 0, ret = 0; + unsigned int i, j, ret = 0; struct cpufreq_stats *stat; struct cpufreq_policy *current_policy; unsigned int alloc_size; @@ -315,12 +384,6 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, stat->cpu = cpu; per_cpu(cpufreq_stats_table, cpu) = stat; - for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned int freq = table[i].frequency; - if (freq == CPUFREQ_ENTRY_INVALID) - continue; - count++; - } alloc_size = count * sizeof(int) + count * sizeof(u64); @@ -361,26 +424,6 @@ error_get_fail: return ret; } -static void cpufreq_stats_create_table(unsigned int cpu) -{ - struct cpufreq_policy *policy; - struct cpufreq_frequency_table *table; - - /* - * "likely(!policy)" because normally cpufreq_stats will be registered - * before cpufreq driver - */ - policy = cpufreq_cpu_get(cpu); - if (likely(!policy)) - return; - - table = cpufreq_frequency_get_table(policy->cpu); - if (likely(table)) - __cpufreq_stats_create_table(policy, table); - - cpufreq_cpu_put(policy); -} - static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) { struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, @@ -394,6 +437,54 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) stat->cpu = policy->cpu; } +static void cpufreq_powerstats_create(unsigned int cpu, + struct cpufreq_frequency_table *table, int count) { + unsigned int alloc_size, i = 0, j = 0, ret = 0; + struct cpufreq_power_stats *powerstats; + struct device_node *cpu_node; + char device_path[16]; + + powerstats = kzalloc(sizeof(struct cpufreq_power_stats), + GFP_KERNEL); + if (!powerstats) + return; + + /* Allocate memory for freq table per cpu as well as clockticks per + * freq*/ + alloc_size = count * sizeof(unsigned int) + + count * sizeof(unsigned int); + powerstats->curr = kzalloc(alloc_size, GFP_KERNEL); + if (!powerstats->curr) { + kfree(powerstats); + return; + } + powerstats->freq_table = powerstats->curr + count; + + spin_lock(&cpufreq_stats_lock); + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END && j < count; i++) { + unsigned int freq = table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + powerstats->freq_table[j++] = freq; + } + powerstats->state_num = j; + + snprintf(device_path, sizeof(device_path), "/cpus/cpu@%d", cpu); + cpu_node = of_find_node_by_path(device_path); + if (cpu_node) { + ret = of_property_read_u32_array(cpu_node, "current", + powerstats->curr, count); + if (ret) { + kfree(powerstats->curr); + kfree(powerstats); + powerstats = NULL; + } + } + per_cpu(cpufreq_power_stats, cpu) = powerstats; + spin_unlock(&cpufreq_stats_lock); +} + static int compare_for_sort(const void *lhs_ptr, const void *rhs_ptr) { unsigned int lhs = *(const unsigned int *)(lhs_ptr); @@ -438,24 +529,14 @@ static void add_all_freq_table(unsigned int freq) all_freq_table->freq_table[all_freq_table->table_size++] = freq; } -static void cpufreq_allstats_create(unsigned int cpu) +static void cpufreq_allstats_create(unsigned int cpu, + struct cpufreq_frequency_table *table, int count) { int i , j = 0; - unsigned int alloc_size, count = 0; - struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(cpu); + unsigned int alloc_size; struct all_cpufreq_stats *all_stat; bool sort_needed = false; - if (!table) - return; - - for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned int freq = table[i].frequency; - if (freq == CPUFREQ_ENTRY_INVALID) - continue; - count++; - } - all_stat = kzalloc(sizeof(struct all_cpufreq_stats), GFP_KERNEL); if (!all_stat) { @@ -494,10 +575,44 @@ static void cpufreq_allstats_create(unsigned int cpu) spin_unlock(&cpufreq_stats_lock); } +static void cpufreq_stats_create_table(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *table; + int i, count = 0; + /* + * "likely(!policy)" because normally cpufreq_stats will be registered + * before cpufreq driver + */ + policy = cpufreq_cpu_get(cpu); + if (likely(!policy)) + return; + + table = cpufreq_frequency_get_table(policy->cpu); + if (likely(table)) { + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned int freq = table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + count++; + } + + if (!per_cpu(all_cpufreq_stats, cpu)) + cpufreq_allstats_create(cpu, table, count); + + if (!per_cpu(cpufreq_power_stats, cpu)) + cpufreq_powerstats_create(cpu, table, count); + + __cpufreq_stats_create_table(policy, table, count); + } + cpufreq_cpu_put(policy); +} + static int cpufreq_stat_notifier_policy(struct notifier_block *nb, unsigned long val, void *data) { - int ret = 0; + int ret = 0, count = 0, i; struct cpufreq_policy *policy = data; struct cpufreq_frequency_table *table; unsigned int cpu = policy->cpu; @@ -511,11 +626,22 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, if (!table) return 0; + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned int freq = table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + count++; + } + if (!per_cpu(all_cpufreq_stats, cpu)) - cpufreq_allstats_create(cpu); + cpufreq_allstats_create(cpu, table, count); + + if (!per_cpu(cpufreq_power_stats, cpu)) + cpufreq_powerstats_create(cpu, table, count); if (val == CPUFREQ_CREATE_POLICY) - ret = __cpufreq_stats_create_table(policy, table); + ret = __cpufreq_stats_create_table(policy, table, count); else if (val == CPUFREQ_REMOVE_POLICY) __cpufreq_stats_free_table(policy); @@ -595,7 +721,12 @@ static int __init cpufreq_stats_init(void) ret = sysfs_create_file(cpufreq_global_kobject, &_attr_all_time_in_state.attr); if (ret) - pr_warn("Error creating sysfs file for cpufreq stats\n"); + pr_warn("Cannot create sysfs file for cpufreq stats\n"); + + ret = sysfs_create_file(cpufreq_global_kobject, + &_attr_current_in_state.attr); + if (ret) + pr_warn("Cannot create sysfs file for cpufreq current stats\n"); return 0; } @@ -610,6 +741,7 @@ static void __exit cpufreq_stats_exit(void) for_each_online_cpu(cpu) cpufreq_stats_free_table(cpu); cpufreq_allstats_free(); + cpufreq_powerstats_free(); cpufreq_put_global_kobject(); } |