diff options
author | Peter De Schrijver <pdeschrijver@nvidia.com> | 2012-03-30 11:40:44 +0300 |
---|---|---|
committer | Varun Wadekar <vwadekar@nvidia.com> | 2012-06-07 16:47:49 +0530 |
commit | 73d4bb112ce28aa07e856aa3ee5dcab75c0545b7 (patch) | |
tree | 9285d708aca3e9340b4b952f6864f82e396eaa0c /drivers/cpuquiet | |
parent | 28a9c9b81a92ed58ca7577b0ae5c1100b3e3785b (diff) |
cpuquiet: driver support
Change-Id: I4f3f67d4459eeda519efdfd80e1283bef2d597e3
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: http://git-master/r/105266
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Tested-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Diffstat (limited to 'drivers/cpuquiet')
-rw-r--r-- | drivers/cpuquiet/driver.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/drivers/cpuquiet/driver.c b/drivers/cpuquiet/driver.c new file mode 100644 index 000000000000..f9dcdf018f58 --- /dev/null +++ b/drivers/cpuquiet/driver.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/cpuquiet.h> +#include <linux/cpu.h> +#include <linux/jiffies.h> +#include <linux/slab.h> +#include <asm/cputime.h> + +#include "cpuquiet.h" + +struct cpuquiet_cpu_stat { + cputime64_t time_up_total; + u64 last_update; + unsigned int up_down_count; + struct kobject cpu_kobject; +}; + +struct cpu_attribute { + struct attribute attr; + enum { up_down_count, time_up_total } type; +}; + +static struct cpuquiet_driver *cpuquiet_curr_driver; +struct cpuquiet_cpu_stat *stats; + +#define CPU_ATTRIBUTE(_name) \ + static struct cpu_attribute _name ## _attr = { \ + .attr = {.name = __stringify(_name), .mode = 0444 }, \ + .type = _name, \ +} + +CPU_ATTRIBUTE(up_down_count); +CPU_ATTRIBUTE(time_up_total); + +static struct attribute *cpu_attributes[] = { + &up_down_count_attr.attr, + &time_up_total_attr.attr, + NULL, +}; + +static void stats_update(struct cpuquiet_cpu_stat *stat, bool up) +{ + u64 cur_jiffies = get_jiffies_64(); + bool was_up = stat->up_down_count & 0x1; + + if (was_up) + stat->time_up_total = cputime64_add(stat->time_up_total, + cputime64_sub(cur_jiffies, stat->last_update)); + + if (was_up != up) + stat->up_down_count++; + + stat->last_update = cur_jiffies; +} + +int cpuquiet_quiesence_cpu(unsigned int cpunumber) +{ + int err = -EPERM; + + if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu) + err = cpuquiet_curr_driver->quiesence_cpu(cpunumber); + + stats_update(stats + cpunumber, 0); + + return err; +} +EXPORT_SYMBOL(cpuquiet_quiesence_cpu); + +int cpuquiet_wake_cpu(unsigned int cpunumber) +{ + int err = -EPERM; + + if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu) + err = cpuquiet_curr_driver->wake_cpu(cpunumber); + + stats_update(stats + cpunumber, 1); + + return err; +} +EXPORT_SYMBOL(cpuquiet_wake_cpu); + +static ssize_t stats_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpu_attribute *cattr = + container_of(attr, struct cpu_attribute, attr); + struct cpuquiet_cpu_stat *stat = + container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject); + ssize_t len = 0; + bool was_up = stat->up_down_count & 0x1; + + stats_update(stat, was_up); + + switch (cattr->type) { + case up_down_count: + len = sprintf(buf, "%u\n", stat->up_down_count); + break; + case time_up_total: + len = sprintf(buf, "%llu\n", stat->time_up_total); + break; + } + + return len; +} + +static const struct sysfs_ops stats_sysfs_ops = { + .show = stats_sysfs_show, +}; + +static struct kobj_type ktype_cpu_stats = { + .sysfs_ops = &stats_sysfs_ops, + .default_attrs = cpu_attributes, +}; + +int cpuquiet_register_driver(struct cpuquiet_driver *drv) +{ + int err = -EBUSY; + unsigned int cpu; + struct sys_device *sys_dev; + u64 cur_jiffies; + + if (!drv) + return -EINVAL; + + stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + cur_jiffies = get_jiffies_64(); + stats[cpu].last_update = cur_jiffies; + if (cpu_online(cpu)) + stats[cpu].up_down_count = 1; + sys_dev = get_cpu_sysdev(cpu); + if (sys_dev) { + cpuquiet_add_dev(sys_dev, cpu); + cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject, + &ktype_cpu_stats, "stats", cpu); + } + } + + mutex_lock(&cpuquiet_lock); + if (!cpuquiet_curr_driver) { + err = 0; + cpuquiet_curr_driver = drv; + cpuquiet_switch_governor(cpuquiet_get_first_governor()); + } + mutex_unlock(&cpuquiet_lock); + + return err; +} +EXPORT_SYMBOL(cpuquiet_register_driver); + +struct cpuquiet_driver *cpuquiet_get_driver(void) +{ + return cpuquiet_curr_driver; +} + +void cpuquiet_unregister_driver(struct cpuquiet_driver *drv) +{ + unsigned int cpu; + + if (drv != cpuquiet_curr_driver) { + WARN(1, "invalid cpuquiet_unregister_driver(%s)\n", + drv->name); + return; + } + + /* stop current governor first */ + cpuquiet_switch_governor(NULL); + + mutex_lock(&cpuquiet_lock); + cpuquiet_curr_driver = NULL; + + for_each_possible_cpu(cpu) { + kobject_put(&stats[cpu].cpu_kobject); + cpuquiet_remove_dev(cpu); + } + + mutex_unlock(&cpuquiet_lock); +} +EXPORT_SYMBOL(cpuquiet_unregister_driver); |