From: Ruchi Kandoi <kandoiruchi@google.com>
To: kandoiruchi@google.com, "Rafael J. Wysocki" <rjw@rjwysocki.net>,
Viresh Kumar <viresh.kumar@linaro.org>,
Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Andrew Morton <akpm@linux-foundation.org>,
Oleg Nesterov <oleg@redhat.com>,
"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
Vladimir Davydov <vdavydov@parallels.com>,
Heinrich Schuchardt <xypron.glpk@gmx.de>,
Thomas Gleixner <tglx@linutronix.de>,
Kees Cook <keescook@chromium.org>,
Konstantin Khlebnikov <khlebnikov@yandex-team.ru>,
Davidlohr Bueso <dave@stgolabs.net>,
linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 1/2] cpufreq_stats: Adds sysfs file /sys/devices/system/cpu/cpufreq/current_in_state
Date: Thu, 14 May 2015 17:12:49 -0700 [thread overview]
Message-ID: <1431648770-7404-2-git-send-email-kandoiruchi@google.com> (raw)
In-Reply-To: <1431648770-7404-1-git-send-email-kandoiruchi@google.com>
Adds the sysfs file for userspace to initialize the active current
values for all the cores at each of the frequencies.
The format for storing the values is as follows:
echo "CPU<cpu#>:<freq1>=<current in uA> <freq2>=<current>,CPU<cpu#>:
..." > /sys/devices/system/cpu/cpufreq/current_in_state
Signed-off-by: Ruchi Kandoi <kandoiruchi@google.com>
---
drivers/cpufreq/cpufreq_stats.c | 163 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 161 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 5e370a3..6f0b562 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -30,6 +30,14 @@ struct cpufreq_stats {
#endif
};
+struct cpufreq_power_stats {
+ unsigned int state_num;
+ unsigned int *curr;
+ unsigned int *freq_table;
+};
+
+static DEFINE_PER_CPU(struct cpufreq_power_stats *, cpufreq_power_stats);
+
static int cpufreq_stats_update(struct cpufreq_stats *stats)
{
unsigned long long cur_time = get_jiffies_64();
@@ -61,6 +69,87 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
return len;
}
+static void store_current_value(struct cpufreq_power_stats *powerstats,
+ int freq, int curr)
+{
+ int i;
+
+ /* freq_table doesn't contain any CPU_FREQ_INVALID */
+ for (i = 0; i < powerstats->state_num; i++) {
+ if (powerstats->freq_table[i] == freq) {
+ powerstats->curr[i] = curr;
+ break;
+ }
+ }
+}
+
+static ssize_t store_current_in_state(struct cpufreq_policy *policy,
+ const char *buf, size_t len)
+{
+ char *cp, *cp2, *start, *buffer;
+ unsigned int cpu_num, ret, curr, freq;
+ struct cpufreq_power_stats *powerstats;
+
+ if (!buf || len < 0)
+ return len;
+
+ buffer = kzalloc(len + 1, GFP_KERNEL);
+ if (!buffer)
+ return len;
+
+ strncpy(buffer, buf, len);
+ buffer[len] = '\0';
+ cp = buffer;
+ spin_lock(&cpufreq_stats_lock);
+ while ((start = strsep(&cp, ","))) {
+ ret = sscanf(start, "CPU%u:", &cpu_num);
+ if (ret != 1 || cpu_num > (num_possible_cpus() - 1)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ powerstats = per_cpu(cpufreq_power_stats, cpu_num);
+ if (!powerstats)
+ continue;
+
+ /* sscanf makes sure that strchr doesn't return a NULL */
+ cp2 = strchr(start, ':') + 1;
+ while ((start = strsep(&cp2, " "))) {
+ if (sscanf(start, "%u=%u", &freq, &curr) != 2) {
+ ret = -EINVAL;
+ goto error;
+ }
+ store_current_value(powerstats, freq, curr);
+ }
+ }
+ ret = len;
+error:
+ spin_unlock(&cpufreq_stats_lock);
+ kfree(buffer);
+ return ret;
+}
+
+static ssize_t show_current_in_state(struct cpufreq_policy *policy, 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;
+}
+
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
{
@@ -107,6 +196,7 @@ cpufreq_freq_attr_ro(trans_table);
cpufreq_freq_attr_ro(total_trans);
cpufreq_freq_attr_ro(time_in_state);
+cpufreq_freq_attr_rw(current_in_state);
static struct attribute *default_attrs[] = {
&total_trans.attr,
@@ -159,6 +249,67 @@ static void cpufreq_stats_free_table(unsigned int cpu)
cpufreq_cpu_put(policy);
}
+static void cpufreq_powerstats_free(void)
+{
+ int cpu;
+ struct cpufreq_power_stats *powerstats;
+
+ sysfs_remove_file(cpufreq_global_kobject, ¤t_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 void cpufreq_powerstats_create(unsigned int cpu)
+{
+ unsigned int alloc_size, i = 0, j = 0, count = 0;
+ struct cpufreq_power_stats *powerstats;
+ struct cpufreq_frequency_table *pos, *table;
+
+ /* We need cpufreq table for creating power stats table */
+ table = cpufreq_frequency_get_table(cpu);
+ if (unlikely(!table))
+ return;
+
+ powerstats = kzalloc(sizeof(struct cpufreq_power_stats),
+ GFP_KERNEL);
+ if (!powerstats)
+ return;
+
+ /* Find total allocation size */
+ cpufreq_for_each_valid_entry(pos, table)
+ count++;
+
+ /* 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;
+ per_cpu(cpufreq_power_stats, cpu) = powerstats;
+ spin_unlock(&cpufreq_stats_lock);
+}
+
static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
{
unsigned int i = 0, count = 0, ret = -ENOMEM;
@@ -249,9 +400,11 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
int ret = 0;
struct cpufreq_policy *policy = data;
- if (val == CPUFREQ_CREATE_POLICY)
+ if (val == CPUFREQ_CREATE_POLICY) {
ret = __cpufreq_stats_create_table(policy);
- else if (val == CPUFREQ_REMOVE_POLICY)
+ if (!per_cpu(cpufreq_power_stats, policy->cpu))
+ cpufreq_powerstats_create(policy->cpu);
+ } else if (val == CPUFREQ_REMOVE_POLICY)
__cpufreq_stats_free_table(policy);
return ret;
@@ -335,8 +488,13 @@ static int __init cpufreq_stats_init(void)
return ret;
}
+ ret = sysfs_create_file(cpufreq_global_kobject, ¤t_in_state.attr);
+ if (ret)
+ pr_warn("Cannot create sysfs file for cpufreq current stats\n");
+
return 0;
}
+
static void __exit cpufreq_stats_exit(void)
{
unsigned int cpu;
@@ -347,6 +505,7 @@ static void __exit cpufreq_stats_exit(void)
CPUFREQ_TRANSITION_NOTIFIER);
for_each_online_cpu(cpu)
cpufreq_stats_free_table(cpu);
+ cpufreq_powerstats_free();
}
MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
--
2.2.0.rc0.207.ga3a616c
next prev parent reply other threads:[~2015-05-15 0:13 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-05-15 0:12 [PATCH v2 0/2] Adds cpu power accounting per-pid basis Ruchi Kandoi
2015-05-15 0:12 ` Ruchi Kandoi [this message]
2015-05-15 2:48 ` [PATCH v2 1/2] cpufreq_stats: Adds sysfs file /sys/devices/system/cpu/cpufreq/current_in_state Viresh Kumar
2015-05-16 0:55 ` Ruchi Kandoi
2015-05-16 2:15 ` Viresh Kumar
2015-05-15 0:12 ` [PATCH v2 2/2] sched: cpufreq: Adds a field cpu_power in the task_struct Ruchi Kandoi
2015-05-15 6:34 ` [PATCH v2 0/2] Adds cpu power accounting per-pid basis Heinrich Schuchardt
2015-05-18 21:00 ` Ruchi Kandoi
2015-05-21 14:34 ` Daniel Lezcano
2015-05-28 19:37 ` Ruchi Kandoi
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=1431648770-7404-2-git-send-email-kandoiruchi@google.com \
--to=kandoiruchi@google.com \
--cc=akpm@linux-foundation.org \
--cc=dave@stgolabs.net \
--cc=keescook@chromium.org \
--cc=khlebnikov@yandex-team.ru \
--cc=kirill.shutemov@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=oleg@redhat.com \
--cc=peterz@infradead.org \
--cc=rjw@rjwysocki.net \
--cc=tglx@linutronix.de \
--cc=vdavydov@parallels.com \
--cc=viresh.kumar@linaro.org \
--cc=xypron.glpk@gmx.de \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®