From: Chanwoo Choi <cw00.choi@samsung.com>
To: Chanwoo Choi <cw00.choi@samsung.com>
Cc: rjw@sisk.pl, viresh.kumar@linaro.org,
linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
cpufreq@vger.kernel.org, kyungmin.park@samsung.com,
myungjoo.ham@samsung.com
Subject: Re: [PATCH v3] cpufreq: stats: Add 'load_table' debugfs file to show accumulated data of CPUs
Date: Wed, 26 Jun 2013 09:10:15 +0900 [thread overview]
Message-ID: <51CA3167.1020606@samsung.com> (raw)
In-Reply-To: <1372064549-5872-1-git-send-email-cw00.choi@samsung.com>
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@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
> ---
> 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
> };
>
>
>
next prev parent reply other threads:[~2013-06-26 0:10 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-06-24 9:02 [PATCH v3] cpufreq: stats: Add 'load_table' debugfs file to show accumulated data of CPUs Chanwoo Choi
2013-06-26 0:10 ` Chanwoo Choi [this message]
2013-06-26 8:04 ` Viresh Kumar
2013-06-27 10:14 ` Chanwoo Choi
2013-06-27 10:23 ` Viresh Kumar
2013-06-27 10:32 ` Chanwoo Choi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=51CA3167.1020606@samsung.com \
--to=cw00.choi@samsung.com \
--cc=cpufreq@vger.kernel.org \
--cc=kyungmin.park@samsung.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=myungjoo.ham@samsung.com \
--cc=rjw@sisk.pl \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.