Hi Viresh, Ping! This patch implemented load_table debugfs file whilch show old frequncy/new frequency. Best Regards, Chanwoo Choi On 06/24/2013 06:02 PM, Chanwoo Choi wrote: > This patch add new 'load_table' debugfs file to show previous accumulated data > of CPUs load as following path and add CPUFREQ_LOADCHECK notification to > CPUFREQ_TRANSITION_NOTIFIER notifier chain. > - /sys/kernel/debug/cpufreq/cpuX/load_table > > When governor calculates CPUs load on dbs_check_cpu(), governor send > CPUFREQ_LOADCHECK notification with CPUs load, so that cpufreq_stats > accumulates calculated CPUs load on 'load_table' storage. > > This debugfs file is used to judge the correct system state or determine > suitable system resource according to current CPUs load on user-space. > > This debugfs file include following data: > - Measurement point of time > - CPU frequency > - Per-CPU load > > Signed-off-by: Chanwoo Choi <cw00.choi@xxxxxxxxxxx> > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > Signed-off-by: Myungjoo Ham <myungjoo.ham@xxxxxxxxxxx> > --- > Changes since v2: > - Code clean according to Viresh Kumar's comment > - Show both old frequency and new frequency on 'load_table' debugfs file > - Change debufs file patch as below > old: /sys/kernel/debugfs/cpufreq/load_table > new: /sys/kernel/debugfs/cpufreq/cpuX/load_table > > Changes since v1: > - Set maximum storage size to save CPUs load on Kconfig > - Use spinlock to synchronize read/write operation for CPUs load > - Use local variable instead of global variable(struct cpufreq_freqs *freqs) > - Use pointer of data structure to get correct size of data structure > in sizeof() macro instead of structure name > : sizeof(struct cpufreq_freqs) -> sizeof(*stat->load_table) > - Change time unit from nanosecond to microsecond > - Remove unnecessary memory copy > > Following Test result : > - Cpufreq governor : ondemand governor > - Test application : MP3 play + Picture Audo-slide application > - NR_CPU_LOAD_STORAGE is 50 > > Time Old Frequency New Frequency CPU0 CPU1 CPU2 CPU3 > 347000 1600000 1600000 12 33 86 8 > 347100 1600000 1600000 4 30 72 4 > 347200 1600000 1600000 20 18 15 80 > 347300 1600000 1500000 61 20 56 66 > 347400 1500000 1300000 64 5 58 52 > 347500 1300000 1100000 39 19 57 59 > 347600 1100000 1100000 73 2 77 49 > 347700 1100000 1600000 70 40 84 58 > 347800 1600000 1600000 98 95 45 25 > 347900 1600000 1200000 39 30 16 53 > 348000 1200000 600000 37 7 1 3 > 348100 600000 200000 6 0 18 5 > 348200 200000 200000 22 8 5 23 > 348300 200000 200000 43 3 75 13 > 348400 200000 1600000 43 12 100 16 > 348500 1600000 700000 19 7 17 30 > 348600 700000 400000 31 12 37 2 > 348705 400000 1600000 67 53 83 18 > 348800 1600000 600000 26 7 7 17 > 348900 600000 600000 16 3 0 3 > 349000 200000 200000 42 9 9 48 > 349100 200000 1600000 19 11 26 84 > 349200 1600000 400000 17 9 15 15 > 349300 400000 300000 38 27 6 7 > 349400 300000 300000 53 11 2 21 > 349500 300000 300000 56 19 16 38 > 349600 300000 300000 58 32 7 40 > 349700 300000 300000 54 39 12 21 > 349800 300000 1600000 91 70 85 67 > 349900 1600000 900000 42 2 42 23 > 350000 900000 400000 30 3 2 5 > 350100 400000 200000 15 15 3 8 > 350200 200000 200000 37 33 1 26 > 350300 200000 1600000 10 95 0 4 > 350400 1600000 500000 20 10 1 13 > 350500 500000 300000 31 8 1 25 > 350600 300000 200000 36 10 10 6 > 350700 200000 200000 65 9 23 45 > 350800 200000 200000 68 40 63 44 > 350900 200000 1600000 95 73 84 89 > 351000 1600000 1600000 35 81 5 1 > 351100 1600000 600000 26 3 7 7 > 351200 600000 200000 13 6 8 23 > 351300 200000 200000 36 9 2 16 > 351400 200000 200000 60 53 0 57 > 351500 200000 200000 36 21 11 31 > 351600 200000 200000 54 1 33 34 > 351700 200000 200000 60 1 30 59 > 351800 200000 200000 36 18 9 16 > 351900 200000 1600000 84 12 46 17 > > drivers/cpufreq/Kconfig | 6 + > drivers/cpufreq/cpufreq.c | 4 + > drivers/cpufreq/cpufreq_governor.c | 19 +++- > drivers/cpufreq/cpufreq_stats.c | 227 +++++++++++++++++++++++++++++++++---- > include/linux/cpufreq.h | 6 + > 5 files changed, 242 insertions(+), 20 deletions(-) > > diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig > index 534fcb8..8a429b3 100644 > --- a/drivers/cpufreq/Kconfig > +++ b/drivers/cpufreq/Kconfig > @@ -45,6 +45,12 @@ config CPU_FREQ_STAT_DETAILS > > If in doubt, say N. > > +config NR_CPU_LOAD_STORAGE > + int "Maximum storage size to save CPU load (10-100)" > + range 10 100 > + depends on CPU_FREQ_STAT_DETAILS > + default "10" > + > choice > prompt "Default CPUFreq governor" > default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ > diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c > index 2d53f47..cbaaff0 100644 > --- a/drivers/cpufreq/cpufreq.c > +++ b/drivers/cpufreq/cpufreq.c > @@ -292,6 +292,10 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy, > if (likely(policy) && likely(policy->cpu == freqs->cpu)) > policy->cur = freqs->new; > break; > + case CPUFREQ_LOADCHECK: > + srcu_notifier_call_chain(&cpufreq_transition_notifier_list, > + CPUFREQ_LOADCHECK, freqs); > + break; > } > } > /** > diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c > index dc9b72e..7f19394 100644 > --- a/drivers/cpufreq/cpufreq_governor.c > +++ b/drivers/cpufreq/cpufreq_governor.c > @@ -90,6 +90,9 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) > unsigned int max_load = 0; > unsigned int ignore_nice; > unsigned int j; > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + struct cpufreq_freqs freq; > +#endif > > if (dbs_data->cdata->governor == GOV_ONDEMAND) > ignore_nice = od_tuners->ignore_nice; > @@ -144,10 +147,17 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) > idle_time += jiffies_to_usecs(cur_nice_jiffies); > } > > - if (unlikely(!wall_time || wall_time < idle_time)) > + if (unlikely(!wall_time || wall_time < idle_time)) { > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + freq.load[j] = 0; > +#endif > continue; > + } > > load = 100 * (wall_time - idle_time) / wall_time; > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + freq.load[j] = load; > +#endif > > if (dbs_data->cdata->governor == GOV_ONDEMAND) { > int freq_avg = __cpufreq_driver_getavg(policy, j); > @@ -161,6 +171,13 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) > max_load = load; > } > > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + freq.time = ktime_to_ms(ktime_get()); > + freq.old = freq.new = policy->cur; > + > + cpufreq_notify_transition(policy, &freq, CPUFREQ_LOADCHECK); > +#endif > + > dbs_data->cdata->gov_check_cpu(cpu, max_load); > } > EXPORT_SYMBOL_GPL(dbs_check_cpu); > diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c > index fb65dec..1b74b03 100644 > --- a/drivers/cpufreq/cpufreq_stats.c > +++ b/drivers/cpufreq/cpufreq_stats.c > @@ -12,6 +12,7 @@ > #include <linux/kernel.h> > #include <linux/slab.h> > #include <linux/cpu.h> > +#include <linux/debugfs.h> > #include <linux/sysfs.h> > #include <linux/cpufreq.h> > #include <linux/module.h> > @@ -24,6 +25,10 @@ > > static spinlock_t cpufreq_stats_lock; > > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > +static struct dentry *debugfs_cpufreq; > +#endif > + > struct cpufreq_stats { > unsigned int cpu; > unsigned int total_trans; > @@ -35,6 +40,12 @@ struct cpufreq_stats { > unsigned int *freq_table; > #ifdef CONFIG_CPU_FREQ_STAT_DETAILS > unsigned int *trans_table; > + > + /* debugfs file for load_table */ > + struct cpufreq_freqs *load_table; > + unsigned int load_last_index; > + unsigned int load_max_index; > + struct dentry *debugfs_cpu; > #endif > }; > > @@ -149,6 +160,163 @@ static struct attribute_group stats_attr_group = { > .name = "stats" > }; > > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > +#define MAX_LINE_SIZE 255 > +static ssize_t load_table_read(struct file *file, char __user *user_buf, > + size_t count, loff_t *ppos) > +{ > + struct cpufreq_policy *policy = file->private_data; > + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); > + struct cpufreq_freqs *load_table = stat->load_table; > + ssize_t len = 0; > + char *buf; > + int i, cpu, ret; > + > + buf = kzalloc(MAX_LINE_SIZE * stat->load_max_index, GFP_KERNEL); > + if (!buf) > + return 0; > + > + spin_lock(&cpufreq_stats_lock); > + len += sprintf(buf + len, "%10s %13s %13s", "Time", "Old Frequency", > + "New Frequency"); > + for_each_present_cpu(cpu) > + len += sprintf(buf + len, " %3s%d", "CPU", cpu); > + len += sprintf(buf + len, "\n"); > + > + i = stat->load_last_index; > + do { > + len += sprintf(buf + len, "%10lld %13d %13d", > + load_table[i].time, > + load_table[i].old, > + load_table[i].new); > + > + for_each_present_cpu(cpu) > + len += sprintf(buf + len, " %4d", > + load_table[i].load[cpu]); > + len += sprintf(buf + len, "\n"); > + > + if (++i == stat->load_max_index) > + i = 0; > + } while (i != stat->load_last_index); > + spin_unlock(&cpufreq_stats_lock); > + > + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); > + kfree(buf); > + > + return ret; > +} > + > +static const struct file_operations load_table_fops = { > + .read = load_table_read, > + .open = simple_open, > + .llseek = no_llseek, > +}; > + > +static void cpufreq_stats_store_load_table(struct cpufreq_freqs *freq, > + unsigned long val) > +{ > + struct cpufreq_stats *stat; > + int cpu, last_idx; > + > + stat = per_cpu(cpufreq_stats_table, freq->cpu); > + if (!stat) > + return; > + > + spin_lock(&cpufreq_stats_lock); > + > + switch (val) { > + case CPUFREQ_POSTCHANGE: > + if (!stat->load_last_index) > + last_idx = stat->load_max_index; > + else > + last_idx = stat->load_last_index - 1; > + > + stat->load_table[last_idx].new = freq->new; > + break; > + case CPUFREQ_LOADCHECK: > + last_idx = stat->load_last_index; > + > + stat->load_table[last_idx].time = freq->time; > + stat->load_table[last_idx].old = freq->old; > + stat->load_table[last_idx].new = freq->old; > + for_each_present_cpu(cpu) > + stat->load_table[last_idx].load[cpu] = freq->load[cpu]; > + > + if (++stat->load_last_index == stat->load_max_index) > + stat->load_last_index = 0; > + break; > + } > + > + spin_unlock(&cpufreq_stats_lock); > +} > + > +static int cpufreq_stats_create_debugfs(struct cpufreq_policy *policy) > +{ > + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); > + char buf[10]; > + int size, ret = 0; > + > + if (!stat) > + return -EINVAL; > + > + stat->load_last_index = 0; > + stat->load_max_index = CONFIG_NR_CPU_LOAD_STORAGE; > + > + size = sizeof(*stat->load_table) * stat->load_max_index; > + stat->load_table = kzalloc(size, GFP_KERNEL); > + if (!stat->load_table) { > + ret = -ENOMEM; > + goto err_alloc; > + } > + > + /* Create debugfs root and file for cpufreq */ > + if (!debugfs_cpufreq) { > + debugfs_cpufreq = debugfs_create_dir("cpufreq", NULL); > + if (!debugfs_cpufreq) { > + ret = -EINVAL; > + goto err_alloc; > + } > + } > + > + sprintf(buf, "cpu%d", policy->cpu); > + > + stat->debugfs_cpu = debugfs_create_dir(buf, debugfs_cpufreq); > + if (!stat->debugfs_cpu) { > + ret = -EINVAL; > + goto err_alloc; > + } > + > + debugfs_create_file("load_table", S_IWUSR, stat->debugfs_cpu, > + (void *)policy, &load_table_fops); > + > + return 0; > + > +err_alloc: > + kfree(stat->load_table); > + > + return ret; > +} > + > +static void cpufreq_stats_free_debugfs(unsigned int cpu) > +{ > + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); > + > + if (!stat) > + return; > + > + pr_debug("%s: Free debugfs stat\n", __func__); > + debugfs_remove(debugfs_cpufreq); > +} > +#else > +static void cpufreq_stats_store_load_table(struct cpufreq_freqs *freq, > + unsigned long val) { } > +static int cpufreq_stats_create_debugfs(struct cpufreq_policy *policy) > +{ > + return 0; > +} > +static void cpufreq_stats_free_debugfs(unsigned int cpu) { } > +#endif > + > static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) > { > int index; > @@ -167,6 +335,9 @@ static void cpufreq_stats_free_table(unsigned int cpu) > > if (stat) { > pr_debug("%s: Free stat table\n", __func__); > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + kfree(stat->load_table); > +#endif > kfree(stat->time_in_state); > kfree(stat); > per_cpu(cpufreq_stats_table, cpu) = NULL; > @@ -257,6 +428,14 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, > spin_lock(&cpufreq_stats_lock); > stat->last_time = get_jiffies_64(); > stat->last_index = freq_table_get_index(stat, policy->cur); > + > + ret = cpufreq_stats_create_debugfs(data); > + if (ret) { > + spin_unlock(&cpufreq_stats_lock); > + ret = -EINVAL; > + goto error_out; > + } > + > spin_unlock(&cpufreq_stats_lock); > cpufreq_cpu_put(data); > return 0; > @@ -312,32 +491,40 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, > struct cpufreq_stats *stat; > int old_index, new_index; > > - if (val != CPUFREQ_POSTCHANGE) > - return 0; > - > - stat = per_cpu(cpufreq_stats_table, freq->cpu); > - if (!stat) > - return 0; > + switch (val) { > + case CPUFREQ_POSTCHANGE: > + stat = per_cpu(cpufreq_stats_table, freq->cpu); > + if (!stat) > + return 0; > > - old_index = stat->last_index; > - new_index = freq_table_get_index(stat, freq->new); > + old_index = stat->last_index; > + new_index = freq_table_get_index(stat, freq->new); > > - /* We can't do stat->time_in_state[-1]= .. */ > - if (old_index == -1 || new_index == -1) > - return 0; > + /* We can't do stat->time_in_state[-1]= .. */ > + if (old_index == -1 || new_index == -1) > + return 0; > > - cpufreq_stats_update(freq->cpu); > + cpufreq_stats_update(freq->cpu); > > - if (old_index == new_index) > - return 0; > + if (old_index == new_index) > + return 0; > > - spin_lock(&cpufreq_stats_lock); > - stat->last_index = new_index; > + spin_lock(&cpufreq_stats_lock); > + stat->last_index = new_index; > #ifdef CONFIG_CPU_FREQ_STAT_DETAILS > - stat->trans_table[old_index * stat->max_state + new_index]++; > + stat->trans_table[old_index * stat->max_state + new_index]++; > #endif > - stat->total_trans++; > - spin_unlock(&cpufreq_stats_lock); > + stat->total_trans++; > + spin_unlock(&cpufreq_stats_lock); > + > + cpufreq_stats_store_load_table(freq, CPUFREQ_POSTCHANGE); > + > + break; > + case CPUFREQ_LOADCHECK: > + cpufreq_stats_store_load_table(freq, CPUFREQ_LOADCHECK); > + break; > + } > + > return 0; > } > > @@ -352,12 +539,14 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, > cpufreq_update_policy(cpu); > break; > case CPU_DOWN_PREPARE: > + cpufreq_stats_free_debugfs(cpu); > cpufreq_stats_free_sysfs(cpu); > break; > case CPU_DEAD: > cpufreq_stats_free_table(cpu); > break; > case CPU_UP_CANCELED_FROZEN: > + cpufreq_stats_free_debugfs(cpu); > cpufreq_stats_free_sysfs(cpu); > cpufreq_stats_free_table(cpu); > break; > diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h > index 037d36a..f780645 100644 > --- a/include/linux/cpufreq.h > +++ b/include/linux/cpufreq.h > @@ -140,12 +140,18 @@ static inline bool policy_is_shared(struct cpufreq_policy *policy) > #define CPUFREQ_POSTCHANGE (1) > #define CPUFREQ_RESUMECHANGE (8) > #define CPUFREQ_SUSPENDCHANGE (9) > +#define CPUFREQ_LOADCHECK (10) > > struct cpufreq_freqs { > unsigned int cpu; /* cpu nr */ > unsigned int old; > unsigned int new; > u8 flags; /* flags of cpufreq_driver, see below. */ > + > +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS > + int64_t time; > + unsigned int load[NR_CPUS]; > +#endif > }; > > > -- To unsubscribe from this list: send the line "unsubscribe cpufreq" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html