From patchwork Fri May 15 00:12:49 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ruchi Kandoi X-Patchwork-Id: 6410541 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 0A7AE9F32B for ; Fri, 15 May 2015 00:13:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CE7F3203DA for ; Fri, 15 May 2015 00:13:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A7741204A7 for ; Fri, 15 May 2015 00:13:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1423272AbbEOANN (ORCPT ); Thu, 14 May 2015 20:13:13 -0400 Received: from mail-ig0-f171.google.com ([209.85.213.171]:37548 "EHLO mail-ig0-f171.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1423226AbbEOANH (ORCPT ); Thu, 14 May 2015 20:13:07 -0400 Received: by igbsb11 with SMTP id sb11so88376740igb.0 for ; Thu, 14 May 2015 17:13:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=Om4l8YEBwm79GlYmnAcwE8cclA1VWQvvVbxDCxdJdAk=; b=QuBqVFxvLUhM8I+3ro+fTQPaN1p5bqMigXtKda4fnO6ILX2W+iQMJRCPfJjBkvmppw ttSKgD/FgH4ZbwgyHwPv8bbDkw5QHb6aAsyz+98mza8u2V4FdMFufegAu1SPBIFPiwmK jmJ8ATalch6vF27N12f+TeOl7gSwFWEh4+RJvONbmpPkyWr2grrdRAyIso8EYaILtqX4 kw//JN5TpS8KjD3lcdKx5W6Bx3BniXu4djruns2X4+679cSleuMAi3rvDykDuIl5/1/f juFiZXgz7EqyZj6Dz8k14QztX6wImEeuV2nlO2srE+8bERiHvWEiuXqfiqLBs7IPKAVm Ds6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=Om4l8YEBwm79GlYmnAcwE8cclA1VWQvvVbxDCxdJdAk=; b=EObq31Y7gJI5OExpeZkvk65zLMVctpiefTqoFQ4QA7aYAtX/cV2uMJcfScYGf9Y0Ez oDXVerYKhO8hQQLjAB0+iqLvCWda2D+CjtkKdd9c9jBU+V2ig3XkTzRqBskuo+4JoGHb YviD3V+UH2mETQjJesYeoQztxYmGBRQrg+i0JgFt+9qiU3nvi2SnctT/P2CQ7IhNihY3 8Lc/G439CyNILGOuDn4o3G8GdJGi/pi9/PkHm1xa9EsndCPAFi8Lsj4M9TwzG0BDGAJm uMQPCPZpWbjl8YxyFO4njuJwnTCNsUlBhqZoQWlPx5DBiabCm7NnY/mg39s1cSWQr7oP rFFg== X-Gm-Message-State: ALoCoQmZ6DYlvuHzZPIzfjL0G0RWUdvE48uSHvcYn5YHimeT1SGFuZhufqEzZauXJcPYtXvYnlbt X-Received: by 10.107.161.139 with SMTP id k133mr9195038ioe.41.1431648787157; Thu, 14 May 2015 17:13:07 -0700 (PDT) Received: from kandoiruchi.mtv.corp.google.com ([172.22.121.97]) by mx.google.com with ESMTPSA id qt1sm211235igb.5.2015.05.14.17.13.05 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 May 2015 17:13:06 -0700 (PDT) From: Ruchi Kandoi To: kandoiruchi@google.com, "Rafael J. Wysocki" , Viresh Kumar , Ingo Molnar , Peter Zijlstra , Andrew Morton , Oleg Nesterov , "Kirill A. Shutemov" , Vladimir Davydov , Heinrich Schuchardt , Thomas Gleixner , Kees Cook , Konstantin Khlebnikov , Davidlohr Bueso , 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 Message-Id: <1431648770-7404-2-git-send-email-kandoiruchi@google.com> X-Mailer: git-send-email 2.2.0.rc0.207.ga3a616c In-Reply-To: <1431648770-7404-1-git-send-email-kandoiruchi@google.com> References: <1431648770-7404-1-git-send-email-kandoiruchi@google.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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: ..." > /sys/devices/system/cpu/cpufreq/current_in_state Signed-off-by: Ruchi Kandoi --- 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 ");