+++ /dev/null
-menu "CPUQUIET Framework"
-
-config CPUQUIET_FRAMEWORK
- bool "Cpuquiet framework"
- help
- Cpuquiet implements pluggable policies for forcing cpu cores into a
- quiescent state. Appropriate policies will save power without hurting
- performance.
-
-
-if CPUQUIET_FRAMEWORK
-
-config CPUQUIET_STATS
- bool "per CPU statistics"
- default n
- help
- Enable up/down count and total time plugged statistics per CPU. These
- depend on correct driver input for reliability
-
- If in doubt say N.
-
-config CPUQUIET_GOVERNOR_USERSPACE
- bool "userspace"
- default y
- help
- Manual control of the number of CPUs online.
- This governor allows userspace to control the number of online CPUs.
-
- If in doubt say Y.
-
-config CPUQUIET_GOVERNOR_BALANCED
- bool "balanced"
- default y
- depends on CPU_FREQ
- help
- Scale the number of CPUs online depending on the CPU load.
- This governor will scale the number of CPUs online depending on the
- CPU load and the number of runnable threads.
-
- If in doubt say Y.
-
-config CPUQUIET_GOVERNOR_RUNNABLE
- bool "runnable threads"
- default y
- help
- Scale the number of CPUs online depending on the number of runnable
- threads. This governor will scale the number of CPUs online depending
- on the number of runnable threads.
-
- If in doubt say Y.
-
-choice
- prompt "Default CPUQuiet governor"
- default CPUQUIET_DEFAULT_GOV_USERSPACE
- help
- This option sets which CPUQuiet governor shall be loaded at
- startup. If in doubt, select 'userspace'.
-
-config CPUQUIET_DEFAULT_GOV_USERSPACE
- bool "userspace"
- select CPUQUIET_GOVERNOR_USERSPACE
- help
- Use the CPUQuiet governor 'userspace' as default.
-
-config CPUQUIET_DEFAULT_GOV_BALANCED
- bool "balanced"
- select CPUQUIET_GOVERNOR_BALANCED
- depends on CPU_FREQ
- help
- Use the CPUQuiet governor 'balanced' as default.
-
-config CPUQUIET_DEFAULT_GOV_RUNNABLE
- bool "runnable threads"
- select CPUQUIET_GOVERNOR_RUNNABLE
- help
- Use the CPUQuiet governor 'runnable threads' as default.
-
-endchoice
-
-config ROCKCHIP_CPUQUIET
- bool "CPUQuiet driver for Rockchip CPUs"
- depends on ARCH_ROCKCHIP
- default y
- help
- This enables the CPUQuiet driver for Rockchip CPUs.
-
- If in doubt, say N.
-
-endif
-endmenu
+++ /dev/null
-GCOV_PROFILE := y
-
-obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o cpuquiet_attribute.o governor.o governors/
-obj-$(CONFIG_ROCKCHIP_CPUQUIET) += rockchip-cpuquiet.o
+++ /dev/null
-/*
- * 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/kernel.h>
-#include <linux/cpu.h>
-#include <linux/slab.h>
-#include <linux/cpuquiet.h>
-#include "cpuquiet.h"
-
-DEFINE_MUTEX(cpuquiet_lock);
-
-static int __init cpuquiet_init(void)
-{
- return cpuquiet_add_interface(cpu_subsys.dev_root);
-}
-
-core_initcall(cpuquiet_init);
+++ /dev/null
-/*
- * 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.
- *
- */
-
-#ifndef __DRIVER_CPUQUIET_H
-#define __DRIVER_CPUQUIET_H
-
-#include <linux/device.h>
-
-extern struct mutex cpuquiet_lock;
-extern struct cpuquiet_governor *cpuquiet_curr_governor;
-extern struct list_head cpuquiet_governors;
-int cpuquiet_add_interface(struct device *dev);
-struct cpuquiet_governor *cpuquiet_find_governor(const char *str);
-int cpuquiet_switch_governor(struct cpuquiet_governor *gov);
-struct cpuquiet_governor *cpuquiet_get_first_governor(void);
-struct cpuquiet_driver *cpuquiet_get_driver(void);
-void cpuquiet_add_dev(struct device *dev, unsigned int cpu);
-void cpuquiet_remove_dev(unsigned int cpu);
-int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
- char *name, int cpu);
-#endif
+++ /dev/null
-/*
- * 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/cpuquiet.h>
-
-ssize_t show_int_attribute(struct cpuquiet_attribute *cattr, char *buf)
-{
- return sprintf(buf, "%d\n", *((int *)cattr->param));
-}
-
-ssize_t store_int_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count)
-{
- int err, val;
-
- err = kstrtoint(buf, 0, &val);
- if (err < 0)
- return err;
-
- *((int *)(cattr->param)) = val;
-
- if (cattr->store_callback)
- cattr->store_callback(cattr);
-
- return count;
-}
-
-ssize_t show_bool_attribute(struct cpuquiet_attribute *cattr, char *buf)
-{
- return sprintf(buf, "%d\n", *((bool *)cattr->param));
-}
-
-ssize_t store_bool_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count)
-{
- int err, val;
-
- err = kstrtoint(buf, 0, &val);
- if (err < 0)
- return err;
-
- if (val < 0 || val > 1)
- return -EINVAL;
-
- *((bool *)(cattr->param)) = val;
-
- if (cattr->store_callback)
- cattr->store_callback(cattr);
-
- return count;
-}
-
-ssize_t show_uint_attribute(struct cpuquiet_attribute *cattr, char *buf)
-{
- return sprintf(buf, "%u\n", *((unsigned int *)cattr->param));
-}
-
-ssize_t store_uint_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count)
-{
- int err;
- unsigned int val;
-
- err = kstrtouint(buf, 0, &val);
- if (err < 0)
- return err;
-
- *((unsigned int *)(cattr->param)) = val;
-
- if (cattr->store_callback)
- cattr->store_callback(cattr);
-
- return count;
-}
-
-ssize_t store_ulong_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count)
-{
- int err;
- unsigned long val;
-
- err = kstrtoul(buf, 0, &val);
- if (err < 0)
- return err;
-
- *((unsigned long *)(cattr->param)) = val;
-
- if (cattr->store_callback)
- cattr->store_callback(cattr);
-
- return count;
-}
-
-ssize_t show_ulong_attribute(struct cpuquiet_attribute *cattr,
- char *buf)
-{
- return sprintf(buf, "%lu\n", *((unsigned long *)cattr->param));
-}
-
-ssize_t cpuquiet_auto_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- struct cpuquiet_attribute *cattr =
- container_of(attr, struct cpuquiet_attribute, attr);
-
- if (cattr->store)
- return cattr->store(cattr, buf, count);
-
- return -EINVAL;
-}
-
-ssize_t cpuquiet_auto_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct cpuquiet_attribute *cattr =
- container_of(attr, struct cpuquiet_attribute, attr);
-
- return cattr->show(cattr, buf);
-}
+++ /dev/null
-/*
- * Copyright (c) 2012-2013 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"
-
-static struct cpuquiet_driver *cpuquiet_curr_driver;
-
-#ifdef CONFIG_CPUQUIET_STATS
-struct cpuquiet_cpu_stat {
- cputime64_t time_up_total;
- u64 last_update;
- unsigned int up_down_count;
- struct kobject cpu_kobject;
-};
-
-struct cpuquiet_cpu_stat *stats;
-
-struct cpu_attribute {
- struct attribute attr;
- enum { up_down_count, time_up_total } type;
-};
-
-#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 += cur_jiffies - stat->last_update;
-
- if (was_up != up)
- stat->up_down_count++;
-
- stat->last_update = cur_jiffies;
-}
-
-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,
-};
-#endif
-
-int cpuquiet_quiesence_cpu(unsigned int cpunumber, bool sync)
-{
- int err = -EPERM;
-
- if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
- err = cpuquiet_curr_driver->quiesence_cpu(cpunumber, sync);
-
-#ifdef CONFIG_CPUQUIET_STATS
- if (!err)
- stats_update(stats + cpunumber, 0);
-#endif
-
- return err;
-}
-EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
-
-int cpuquiet_wake_cpu(unsigned int cpunumber, bool sync)
-{
- int err = -EPERM;
-
- if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
- err = cpuquiet_curr_driver->wake_cpu(cpunumber, sync);
-
-#ifdef CONFIG_CPUQUIET_STATS
- if (!err)
- stats_update(stats + cpunumber, 1);
-#endif
-
- return err;
-}
-EXPORT_SYMBOL(cpuquiet_wake_cpu);
-
-int cpuquiet_register_driver(struct cpuquiet_driver *drv)
-{
- int err = -EBUSY;
- unsigned int cpu;
- struct device *dev;
-
- if (!drv)
- return -EINVAL;
-
-#ifdef CONFIG_CPUQUIET_STATS
- stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL);
- if (!stats)
- return -ENOMEM;
-#endif
-
- for_each_possible_cpu(cpu) {
-#ifdef CONFIG_CPUQUIET_STATS
- u64 cur_jiffies = get_jiffies_64();
- stats[cpu].last_update = cur_jiffies;
- if (cpu_online(cpu))
- stats[cpu].up_down_count = 1;
-#endif
- dev = get_cpu_device(cpu);
- if (dev) {
- cpuquiet_add_dev(dev, cpu);
-#ifdef CONFIG_CPUQUIET_STATS
- cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject,
- &ktype_cpu_stats, "stats", cpu);
-#endif
- }
- }
-
- 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;
- }
-
- mutex_lock(&cpuquiet_lock);
-
- /* Stop current governor first */
- cpuquiet_switch_governor(NULL);
- cpuquiet_curr_driver = NULL;
-
- for_each_possible_cpu(cpu) {
-#ifdef CONFIG_CPUQUIET_STATS
- kobject_put(&stats[cpu].cpu_kobject);
-#endif
- cpuquiet_remove_dev(cpu);
- }
-
- mutex_unlock(&cpuquiet_lock);
-}
-EXPORT_SYMBOL(cpuquiet_unregister_driver);
+++ /dev/null
-/*
- * 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 "cpuquiet.h"
-
-LIST_HEAD(cpuquiet_governors);
-struct cpuquiet_governor *cpuquiet_curr_governor;
-
-struct cpuquiet_governor *cpuquiet_get_first_governor(void)
-{
- if (!list_empty(&cpuquiet_governors))
- return list_entry(cpuquiet_governors.next,
- struct cpuquiet_governor,
- governor_list);
- else
- return NULL;
-}
-
-struct cpuquiet_governor *cpuquiet_find_governor(const char *str)
-{
- struct cpuquiet_governor *gov;
-
- list_for_each_entry(gov, &cpuquiet_governors, governor_list)
- if (!strnicmp(str, gov->name, CPUQUIET_NAME_LEN))
- return gov;
-
- return NULL;
-}
-
-int cpuquiet_switch_governor(struct cpuquiet_governor *gov)
-{
- int err = 0;
-
- if (cpuquiet_curr_governor) {
- if (cpuquiet_curr_governor->stop)
- cpuquiet_curr_governor->stop();
- module_put(cpuquiet_curr_governor->owner);
- }
-
- cpuquiet_curr_governor = gov;
-
- if (gov) {
- if (!try_module_get(cpuquiet_curr_governor->owner))
- return -EINVAL;
- if (gov->start)
- err = gov->start();
- if (!err)
- cpuquiet_curr_governor = gov;
- }
-
- return err;
-}
-
-int cpuquiet_register_governor(struct cpuquiet_governor *gov)
-{
- int ret = -EEXIST;
-
- if (!gov)
- return -EINVAL;
-
- mutex_lock(&cpuquiet_lock);
- if (cpuquiet_find_governor(gov->name) == NULL) {
- ret = 0;
- list_add_tail(&gov->governor_list, &cpuquiet_governors);
- if (!cpuquiet_curr_governor && cpuquiet_get_driver())
- cpuquiet_switch_governor(gov);
- }
- mutex_unlock(&cpuquiet_lock);
-
- return ret;
-}
-
-void cpuquiet_unregister_governor(struct cpuquiet_governor *gov)
-{
- if (!gov)
- return;
-
- mutex_lock(&cpuquiet_lock);
- if (cpuquiet_curr_governor == gov)
- cpuquiet_switch_governor(NULL);
- list_del(&gov->governor_list);
- mutex_unlock(&cpuquiet_lock);
-}
-
-void cpuquiet_device_busy(void)
-{
- if (cpuquiet_curr_governor &&
- cpuquiet_curr_governor->device_busy_notification)
- cpuquiet_curr_governor->device_busy_notification();
-}
-
-void cpuquiet_device_free(void)
-{
- if (cpuquiet_curr_governor &&
- cpuquiet_curr_governor->device_free_notification)
- cpuquiet_curr_governor->device_free_notification();
-}
+++ /dev/null
-GCOV_PROFILE := y
-
-obj-$(CONFIG_CPUQUIET_GOVERNOR_USERSPACE) += userspace.o
-obj-$(CONFIG_CPUQUIET_GOVERNOR_BALANCED) += balanced.o
-obj-$(CONFIG_CPUQUIET_GOVERNOR_RUNNABLE) += runnable_threads.o
+++ /dev/null
-/*
- * 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/kernel.h>
-#include <linux/cpuquiet.h>
-#include <linux/cpumask.h>
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <linux/pm_qos.h>
-#include <linux/jiffies.h>
-#include <linux/slab.h>
-#include <linux/cpu.h>
-#include <linux/sched.h>
-#include <linux/tick.h>
-#include <asm/cputime.h>
-
-#define CPUNAMELEN 8
-
-typedef enum {
- CPU_SPEED_BALANCED,
- CPU_SPEED_BIASED,
- CPU_SPEED_SKEWED,
-} CPU_SPEED_BALANCE;
-
-typedef enum {
- IDLE,
- DOWN,
- UP,
-} BALANCED_STATE;
-
-struct idle_info {
- u64 idle_last;
- u64 last_timestamp;
- u64 idle_current;
- u64 timestamp;
-};
-
-static DEFINE_PER_CPU(struct idle_info, idleinfo);
-static DEFINE_PER_CPU(unsigned int, cpu_load);
-
-static struct timer_list load_timer;
-static bool load_timer_active;
-
-/* configurable parameters */
-static unsigned int balance_level = 60;
-static unsigned int idle_bottom_freq;
-static unsigned int idle_top_freq;
-static unsigned long up_delay;
-static unsigned long down_delay;
-static unsigned long last_change_time;
-static unsigned int load_sample_rate = 20; /* msec */
-static struct workqueue_struct *balanced_wq;
-static struct delayed_work balanced_work;
-static BALANCED_STATE balanced_state;
-static struct kobject *balanced_kobject;
-
-static void calculate_load_timer(unsigned long data)
-{
- int i;
- u64 idle_time, elapsed_time;
-
- if (!load_timer_active)
- return;
-
- for_each_online_cpu(i) {
- struct idle_info *iinfo = &per_cpu(idleinfo, i);
- unsigned int *load = &per_cpu(cpu_load, i);
-
- iinfo->idle_last = iinfo->idle_current;
- iinfo->last_timestamp = iinfo->timestamp;
- iinfo->idle_current =
- get_cpu_idle_time_us(i, &iinfo->timestamp);
- elapsed_time = iinfo->timestamp - iinfo->last_timestamp;
-
- idle_time = iinfo->idle_current - iinfo->idle_last;
- idle_time *= 100;
- do_div(idle_time, elapsed_time);
- *load = 100 - idle_time;
- }
- mod_timer(&load_timer, jiffies + msecs_to_jiffies(load_sample_rate));
-}
-
-static void start_load_timer(void)
-{
- int i;
-
- if (load_timer_active)
- return;
-
- load_timer_active = true;
-
- for_each_online_cpu(i) {
- struct idle_info *iinfo = &per_cpu(idleinfo, i);
-
- iinfo->idle_current =
- get_cpu_idle_time_us(i, &iinfo->timestamp);
- }
- mod_timer(&load_timer, jiffies + msecs_to_jiffies(100));
-}
-
-static void stop_load_timer(void)
-{
- if (!load_timer_active)
- return;
-
- load_timer_active = false;
- del_timer(&load_timer);
-}
-
-static unsigned int get_slowest_cpu_n(void)
-{
- unsigned int cpu = nr_cpu_ids;
- unsigned long minload = ULONG_MAX;
- int i;
-
- for_each_online_cpu(i) {
- unsigned int *load = &per_cpu(cpu_load, i);
-
- if ((i > 0) && (minload > *load)) {
- cpu = i;
- minload = *load;
- }
- }
-
- return cpu;
-}
-
-static unsigned int cpu_highest_speed(void)
-{
- unsigned int maxload = 0;
- int i;
-
- for_each_online_cpu(i) {
- unsigned int *load = &per_cpu(cpu_load, i);
-
- maxload = max(maxload, *load);
- }
-
- return maxload;
-}
-
-static unsigned int count_slow_cpus(unsigned int limit)
-{
- unsigned int cnt = 0;
- int i;
-
- for_each_online_cpu(i) {
- unsigned int *load = &per_cpu(cpu_load, i);
-
- if (*load <= limit)
- cnt++;
- }
-
- return cnt;
-}
-
-#define NR_FSHIFT 2
-
-static unsigned int rt_profile_sel;
-static unsigned int core_bias; //Dummy variable exposed to userspace
-
-static unsigned int rt_profile_default[] = {
-/* 1, 2, 3, 4 - on-line cpus target */
- 5, 9, 10, UINT_MAX
-};
-
-static unsigned int rt_profile_1[] = {
-/* 1, 2, 3, 4 - on-line cpus target */
- 8, 9, 10, UINT_MAX
-};
-
-static unsigned int rt_profile_2[] = {
-/* 1, 2, 3, 4 - on-line cpus target */
- 5, 13, 14, UINT_MAX
-};
-
-static unsigned int rt_profile_disable[] = {
-/* 1, 2, 3, 4 - on-line cpus target */
- 0, 0, 0, UINT_MAX
-};
-
-static unsigned int *rt_profiles[] = {
- rt_profile_default,
- rt_profile_1,
- rt_profile_2,
- rt_profile_disable
-};
-
-static unsigned int nr_run_hysteresis = 2; /* 0.5 thread */
-static unsigned int nr_run_last;
-
-struct runnables_avg_sample {
- u64 previous_integral;
- unsigned int avg;
- bool integral_sampled;
- u64 prev_timestamp;
-};
-
-static DEFINE_PER_CPU(struct runnables_avg_sample, avg_nr_sample);
-
-static unsigned int get_avg_nr_runnables(void)
-{
- unsigned int i, sum = 0;
- struct runnables_avg_sample *sample;
- u64 integral, old_integral, delta_integral, delta_time, cur_time;
-
- for_each_online_cpu(i) {
- sample = &per_cpu(avg_nr_sample, i);
- integral = nr_running_integral(i);
- old_integral = sample->previous_integral;
- sample->previous_integral = integral;
- cur_time = ktime_to_ns(ktime_get());
- delta_time = cur_time - sample->prev_timestamp;
- sample->prev_timestamp = cur_time;
-
- if (!sample->integral_sampled) {
- sample->integral_sampled = true;
- /* First sample to initialize prev_integral, skip
- * avg calculation
- */
- continue;
- }
-
- if (integral < old_integral) {
- /* Overflow */
- delta_integral = (ULLONG_MAX - old_integral) + integral;
- } else {
- delta_integral = integral - old_integral;
- }
-
- /* Calculate average for the previous sample window */
- do_div(delta_integral, delta_time);
- sample->avg = delta_integral;
- sum += sample->avg;
- }
-
- return sum;
-}
-
-static CPU_SPEED_BALANCE balanced_speed_balance(void)
-{
- unsigned long highest_speed = cpu_highest_speed();
- unsigned long balanced_speed = highest_speed * balance_level / 100;
- unsigned long skewed_speed = balanced_speed / 2;
- unsigned int nr_cpus = num_online_cpus();
- unsigned int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
- unsigned int avg_nr_run = get_avg_nr_runnables();
- unsigned int nr_run;
- unsigned int *current_profile = rt_profiles[rt_profile_sel];
-
- /* balanced: freq targets for all CPUs are above 50% of highest speed
- biased: freq target for at least one CPU is below 50% threshold
- skewed: freq targets for at least 2 CPUs are below 25% threshold */
- for (nr_run = 1; nr_run < ARRAY_SIZE(rt_profile_default); nr_run++) {
- unsigned int nr_threshold = current_profile[nr_run - 1];
- if (nr_run_last <= nr_run)
- nr_threshold += nr_run_hysteresis;
- if (avg_nr_run <= (nr_threshold << (FSHIFT - NR_FSHIFT)))
- break;
- }
- nr_run_last = nr_run;
-
- if (count_slow_cpus(skewed_speed) >= 2 || nr_cpus > max_cpus ||
- nr_run < nr_cpus)
- return CPU_SPEED_SKEWED;
-
- if (count_slow_cpus(balanced_speed) >= 1 || nr_cpus == max_cpus ||
- nr_run <= nr_cpus)
- return CPU_SPEED_BIASED;
-
- return CPU_SPEED_BALANCED;
-}
-
-static void balanced_work_func(struct work_struct *work)
-{
- bool up = false;
- unsigned int cpu = nr_cpu_ids;
- unsigned long now = jiffies;
-
- CPU_SPEED_BALANCE balance;
-
- switch (balanced_state) {
- case IDLE:
- break;
- case DOWN:
- cpu = get_slowest_cpu_n();
- if (cpu < nr_cpu_ids) {
- up = false;
- queue_delayed_work(balanced_wq,
- &balanced_work, up_delay);
- } else
- stop_load_timer();
- break;
- case UP:
- balance = balanced_speed_balance();
- switch (balance) {
-
- /* cpu speed is up and balanced - one more on-line */
- case CPU_SPEED_BALANCED:
- cpu = cpumask_next_zero(0, cpu_online_mask);
- if (cpu < nr_cpu_ids)
- up = true;
- break;
- /* cpu speed is up, but skewed - remove one core */
- case CPU_SPEED_SKEWED:
- cpu = get_slowest_cpu_n();
- if (cpu < nr_cpu_ids)
- up = false;
- break;
- /* cpu speed is up, but under-utilized - do nothing */
- case CPU_SPEED_BIASED:
- default:
- break;
- }
- queue_delayed_work(
- balanced_wq, &balanced_work, up_delay);
- break;
- default:
- pr_err("%s: invalid cpuquiet balanced governor state %d\n",
- __func__, balanced_state);
- }
-
- if (!up && ((now - last_change_time) < down_delay))
- cpu = nr_cpu_ids;
-
- if (cpu < nr_cpu_ids) {
- last_change_time = now;
- if (up)
- cpuquiet_wake_cpu(cpu, false);
- else
- cpuquiet_quiesence_cpu(cpu, false);
- }
-}
-
-static int balanced_cpufreq_transition(struct notifier_block *nb,
- unsigned long state, void *data)
-{
- struct cpufreq_freqs *freqs = data;
- unsigned long cpu_freq;
-
- if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) {
- cpu_freq = freqs->new;
-
- switch (balanced_state) {
- case IDLE:
- if (cpu_freq >= idle_top_freq) {
- balanced_state = UP;
- queue_delayed_work(
- balanced_wq, &balanced_work, up_delay);
- start_load_timer();
- } else if (cpu_freq <= idle_bottom_freq) {
- balanced_state = DOWN;
- queue_delayed_work(
- balanced_wq, &balanced_work,
- down_delay);
- start_load_timer();
- }
- break;
- case DOWN:
- if (cpu_freq >= idle_top_freq) {
- balanced_state = UP;
- queue_delayed_work(
- balanced_wq, &balanced_work, up_delay);
- start_load_timer();
- }
- break;
- case UP:
- if (cpu_freq <= idle_bottom_freq) {
- balanced_state = DOWN;
- queue_delayed_work(balanced_wq,
- &balanced_work, up_delay);
- start_load_timer();
- }
- break;
- default:
- pr_err("%s: invalid cpuquiet balanced governor "
- "state %d\n", __func__, balanced_state);
- }
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block balanced_cpufreq_nb = {
- .notifier_call = balanced_cpufreq_transition,
-};
-
-static void delay_callback(struct cpuquiet_attribute *attr)
-{
- unsigned long val;
-
- if (attr) {
- val = (*((unsigned long *)(attr->param)));
- (*((unsigned long *)(attr->param))) = msecs_to_jiffies(val);
- }
-}
-
-static void core_bias_callback (struct cpuquiet_attribute *attr)
-{
- unsigned long val;
- if (attr) {
- val = (*((unsigned int*)(attr->param)));
- if (val < ARRAY_SIZE(rt_profiles)) {
- rt_profile_sel = val;
- }
- else { //Revert the change due to invalid range
- core_bias = rt_profile_sel;
- }
- }
-}
-
-CPQ_BASIC_ATTRIBUTE(balance_level, 0644, uint);
-CPQ_BASIC_ATTRIBUTE(idle_bottom_freq, 0644, uint);
-CPQ_BASIC_ATTRIBUTE(idle_top_freq, 0644, uint);
-CPQ_BASIC_ATTRIBUTE(load_sample_rate, 0644, uint);
-CPQ_ATTRIBUTE(core_bias, 0644, uint, core_bias_callback);
-CPQ_ATTRIBUTE(up_delay, 0644, ulong, delay_callback);
-CPQ_ATTRIBUTE(down_delay, 0644, ulong, delay_callback);
-
-static struct attribute *balanced_attributes[] = {
- &balance_level_attr.attr,
- &idle_bottom_freq_attr.attr,
- &idle_top_freq_attr.attr,
- &up_delay_attr.attr,
- &down_delay_attr.attr,
- &load_sample_rate_attr.attr,
- &core_bias_attr.attr,
- NULL,
-};
-
-static const struct sysfs_ops balanced_sysfs_ops = {
- .show = cpuquiet_auto_sysfs_show,
- .store = cpuquiet_auto_sysfs_store,
-};
-
-static struct kobj_type ktype_balanced = {
- .sysfs_ops = &balanced_sysfs_ops,
- .default_attrs = balanced_attributes,
-};
-
-static int balanced_sysfs(void)
-{
- int err;
-
- balanced_kobject = kzalloc(sizeof(*balanced_kobject),
- GFP_KERNEL);
-
- if (!balanced_kobject)
- return -ENOMEM;
-
- err = cpuquiet_kobject_init(balanced_kobject, &ktype_balanced,
- "balanced");
-
- if (err)
- kfree(balanced_kobject);
-
- return err;
-}
-
-static void balanced_stop(void)
-{
- /*
- first unregister the notifiers. This ensures the governor state
- can't be modified by a cpufreq transition
- */
- cpufreq_unregister_notifier(&balanced_cpufreq_nb,
- CPUFREQ_TRANSITION_NOTIFIER);
-
- /* now we can force the governor to be idle */
- balanced_state = IDLE;
- cancel_delayed_work_sync(&balanced_work);
- destroy_workqueue(balanced_wq);
- del_timer(&load_timer);
-
- kobject_put(balanced_kobject);
-}
-
-static int balanced_start(void)
-{
- int err, count;
- struct cpufreq_frequency_table *table;
- struct cpufreq_freqs initial_freq;
-
- err = balanced_sysfs();
- if (err)
- return err;
-
- balanced_wq = alloc_workqueue("cpuquiet-balanced",
- WQ_UNBOUND | WQ_FREEZABLE, 1);
- if (!balanced_wq)
- return -ENOMEM;
-
- INIT_DELAYED_WORK(&balanced_work, balanced_work_func);
-
- up_delay = msecs_to_jiffies(100);
- down_delay = msecs_to_jiffies(2000);
-
- table = cpufreq_frequency_get_table(0);
- if (!table)
- return -EINVAL;
-
- for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++);
-
- if (count < 4)
- return -EINVAL;
-
- idle_top_freq = table[(count / 2) - 1].frequency;
- idle_bottom_freq = table[(count / 2) - 2].frequency;
-
- cpufreq_register_notifier(&balanced_cpufreq_nb,
- CPUFREQ_TRANSITION_NOTIFIER);
-
- init_timer(&load_timer);
- load_timer.function = calculate_load_timer;
-
- /*FIXME: Kick start the state machine by faking a freq notification*/
- initial_freq.new = cpufreq_get(0);
- if (initial_freq.new != 0)
- balanced_cpufreq_transition(NULL, CPUFREQ_RESUMECHANGE,
- &initial_freq);
- return 0;
-}
-
-struct cpuquiet_governor balanced_governor = {
- .name = "balanced",
- .start = balanced_start,
- .stop = balanced_stop,
- .owner = THIS_MODULE,
-};
-
-static int __init init_balanced(void)
-{
- return cpuquiet_register_governor(&balanced_governor);
-}
-
-static void __exit exit_balanced(void)
-{
- cpuquiet_unregister_governor(&balanced_governor);
-}
-
-MODULE_LICENSE("GPL");
-#ifdef CONFIG_CPUQUIET_DEFAULT_GOV_BALANCED
-fs_initcall(init_balanced);
-#else
-module_init(init_balanced);
-#endif
-module_exit(exit_balanced);
-
+++ /dev/null
-/*
- * Copyright (c) 2012-2014 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/kernel.h>
-#include <linux/cpuquiet.h>
-#include <linux/cpumask.h>
-#include <linux/module.h>
-#include <linux/pm_qos.h>
-#include <linux/jiffies.h>
-#include <linux/slab.h>
-#include <linux/cpu.h>
-#include <linux/sched.h>
-
-typedef enum {
- DISABLED,
- IDLE,
- RUNNING,
-} RUNNABLES_STATE;
-
-static struct work_struct runnables_work;
-static struct kobject *runnables_kobject;
-static struct timer_list runnables_timer;
-
-static RUNNABLES_STATE runnables_state;
-/* configurable parameters */
-static unsigned int sample_rate = 20; /* msec */
-
-#define NR_FSHIFT_EXP 3
-#define NR_FSHIFT (1 << NR_FSHIFT_EXP)
-/* avg run threads * 8 (e.g., 11 = 1.375 threads) */
-static unsigned int default_thresholds[] = {
- 10, 18, 20, UINT_MAX
-};
-
-static unsigned int nr_run_last;
-static unsigned int nr_run_hysteresis = 2; /* 1 / 2 thread */
-static unsigned int default_threshold_level = 4; /* 1 / 4 thread */
-static unsigned int nr_run_thresholds[NR_CPUS];
-
-DEFINE_MUTEX(runnables_lock);
-
-struct runnables_avg_sample {
- u64 previous_integral;
- unsigned int avg;
- bool integral_sampled;
- u64 prev_timestamp;
-};
-
-static DEFINE_PER_CPU(struct runnables_avg_sample, avg_nr_sample);
-
-/* EXP = alpha in the exponential moving average.
- * Alpha = e ^ (-sample_rate / window_size) * FIXED_1
- * Calculated for sample_rate of 20ms, window size of 100ms
- */
-#define EXP 1677
-
-static unsigned int get_avg_nr_runnables(void)
-{
- unsigned int i, sum = 0;
- static unsigned int avg;
- struct runnables_avg_sample *sample;
- u64 integral, old_integral, delta_integral, delta_time, cur_time;
-
- for_each_online_cpu(i) {
- sample = &per_cpu(avg_nr_sample, i);
- integral = nr_running_integral(i);
- old_integral = sample->previous_integral;
- sample->previous_integral = integral;
- cur_time = ktime_to_ns(ktime_get());
- delta_time = cur_time - sample->prev_timestamp;
- sample->prev_timestamp = cur_time;
-
- if (!sample->integral_sampled) {
- sample->integral_sampled = true;
- /* First sample to initialize prev_integral, skip
- * avg calculation
- */
- continue;
- }
-
- if (integral < old_integral) {
- /* Overflow */
- delta_integral = (ULLONG_MAX - old_integral) + integral;
- } else {
- delta_integral = integral - old_integral;
- }
-
- /* Calculate average for the previous sample window */
- do_div(delta_integral, delta_time);
- sample->avg = delta_integral;
- sum += sample->avg;
- }
-
- /* Exponential moving average
- * Avgn = Avgn-1 * alpha + new_avg * (1 - alpha)
- */
- avg *= EXP;
- avg += sum * (FIXED_1 - EXP);
- avg >>= FSHIFT;
-
- return avg;
-}
-
-static int get_action(unsigned int nr_run)
-{
- unsigned int nr_cpus = num_online_cpus();
- int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
- int min_cpus = pm_qos_request(PM_QOS_MIN_ONLINE_CPUS);
-
- if ((nr_cpus > max_cpus || nr_run < nr_cpus) && nr_cpus >= min_cpus)
- return -1;
-
- if (nr_cpus < min_cpus || nr_run > nr_cpus)
- return 1;
-
- return 0;
-}
-
-static void runnables_avg_sampler(unsigned long data)
-{
- unsigned int nr_run, avg_nr_run;
- int action;
-
- rmb();
- if (runnables_state != RUNNING)
- return;
-
- avg_nr_run = get_avg_nr_runnables();
- mod_timer(&runnables_timer, jiffies + msecs_to_jiffies(sample_rate));
-
- for (nr_run = 1; nr_run < ARRAY_SIZE(nr_run_thresholds); nr_run++) {
- unsigned int nr_threshold = nr_run_thresholds[nr_run - 1];
- if (nr_run_last <= nr_run)
- nr_threshold += NR_FSHIFT / nr_run_hysteresis;
- if (avg_nr_run <= (nr_threshold << (FSHIFT - NR_FSHIFT_EXP)))
- break;
- }
-
- nr_run_last = nr_run;
-
- action = get_action(nr_run);
- if (action != 0) {
- wmb();
- schedule_work(&runnables_work);
- }
-}
-
-static unsigned int get_lightest_loaded_cpu_n(void)
-{
- unsigned long min_avg_runnables = ULONG_MAX;
- unsigned int cpu = nr_cpu_ids;
- int i;
-
- for_each_online_cpu(i) {
- struct runnables_avg_sample *s = &per_cpu(avg_nr_sample, i);
- unsigned int nr_runnables = s->avg;
- if (i > 0 && min_avg_runnables > nr_runnables) {
- cpu = i;
- min_avg_runnables = nr_runnables;
- }
- }
-
- return cpu;
-}
-
-static void runnables_work_func(struct work_struct *work)
-{
- unsigned int cpu = nr_cpu_ids;
- int action;
-
- if (runnables_state != RUNNING)
- return;
-
- action = get_action(nr_run_last);
- if (action > 0) {
- cpu = cpumask_next_zero(0, cpu_online_mask);
- if (cpu < nr_cpu_ids)
- cpuquiet_wake_cpu(cpu, false);
- } else if (action < 0) {
- cpu = get_lightest_loaded_cpu_n();
- if (cpu < nr_cpu_ids)
- cpuquiet_quiesence_cpu(cpu, false);
- }
-}
-
-#define MAX_BYTES 100
-
-static ssize_t show_thresholds(struct cpuquiet_attribute *attr, char *buf)
-{
- char buffer[MAX_BYTES];
- unsigned int i;
- int size = 0;
- buffer[0] = 0;
- for_each_possible_cpu(i) {
- if (i == ARRAY_SIZE(nr_run_thresholds) - 1)
- break;
- if (size >= sizeof(buffer))
- break;
- size += snprintf(buffer + size, sizeof(buffer) - size,
- "%u->%u core threshold: %u\n",
- i + 1, i + 2, nr_run_thresholds[i]);
- }
- return snprintf(buf, sizeof(buffer), "%s", buffer);
-}
-
-static ssize_t store_thresholds(struct cpuquiet_attribute *attr,
- const char *buf, size_t count)
-{
- int ret, i = 0;
- char *val, *str, input[MAX_BYTES];
- unsigned int thresholds[NR_CPUS];
-
- if (!count || count >= MAX_BYTES)
- return -EINVAL;
- strncpy(input, buf, count);
- input[count] = '\0';
- str = input;
- memcpy(thresholds, nr_run_thresholds, sizeof(nr_run_thresholds));
- while ((val = strsep(&str, " ")) != NULL) {
- if (*val == '\0')
- continue;
- if (i == ARRAY_SIZE(nr_run_thresholds) - 1)
- break;
- ret = kstrtouint(val, 10, &thresholds[i]);
- if (ret)
- return -EINVAL;
- i++;
- }
-
- memcpy(nr_run_thresholds, thresholds, sizeof(thresholds));
- return count;
-}
-
-CPQ_BASIC_ATTRIBUTE(sample_rate, 0644, uint);
-CPQ_BASIC_ATTRIBUTE(nr_run_hysteresis, 0644, uint);
-CPQ_ATTRIBUTE_CUSTOM(nr_run_thresholds, 0644,
- show_thresholds, store_thresholds);
-
-static struct attribute *runnables_attributes[] = {
- &sample_rate_attr.attr,
- &nr_run_hysteresis_attr.attr,
- &nr_run_thresholds_attr.attr,
- NULL,
-};
-
-static const struct sysfs_ops runnables_sysfs_ops = {
- .show = cpuquiet_auto_sysfs_show,
- .store = cpuquiet_auto_sysfs_store,
-};
-
-static struct kobj_type ktype_runnables = {
- .sysfs_ops = &runnables_sysfs_ops,
- .default_attrs = runnables_attributes,
-};
-
-static int runnables_sysfs(void)
-{
- int err;
-
- runnables_kobject = kzalloc(sizeof(*runnables_kobject),
- GFP_KERNEL);
-
- if (!runnables_kobject)
- return -ENOMEM;
-
- err = cpuquiet_kobject_init(runnables_kobject, &ktype_runnables,
- "runnable_threads");
-
- if (err)
- kfree(runnables_kobject);
-
- return err;
-}
-
-static void runnables_device_busy(void)
-{
- mutex_lock(&runnables_lock);
- if (runnables_state == RUNNING) {
- runnables_state = IDLE;
- del_timer_sync(&runnables_timer);
- }
- mutex_unlock(&runnables_lock);
-}
-
-static void runnables_device_free(void)
-{
- mutex_lock(&runnables_lock);
- if (runnables_state == IDLE) {
- runnables_state = RUNNING;
- mod_timer(&runnables_timer, jiffies + 1);
- }
- mutex_unlock(&runnables_lock);
-}
-
-static void runnables_stop(void)
-{
- mutex_lock(&runnables_lock);
-
- runnables_state = DISABLED;
- del_timer_sync(&runnables_timer);
- kobject_put(runnables_kobject);
- kfree(runnables_kobject);
-
- mutex_unlock(&runnables_lock);
-}
-
-static int runnables_start(void)
-{
- int err, i;
-
- err = runnables_sysfs();
- if (err)
- return err;
-
- INIT_WORK(&runnables_work, runnables_work_func);
-
- init_timer(&runnables_timer);
- runnables_timer.function = runnables_avg_sampler;
-
- for(i = 0; i < ARRAY_SIZE(nr_run_thresholds); ++i) {
- if (i == (ARRAY_SIZE(nr_run_thresholds) - 1))
- nr_run_thresholds[i] = UINT_MAX;
- else if (i < ARRAY_SIZE(default_thresholds))
- nr_run_thresholds[i] = default_thresholds[i];
- else
- nr_run_thresholds[i] = i + 1 +
- NR_FSHIFT / default_threshold_level;
- }
-
- mutex_lock(&runnables_lock);
- runnables_state = RUNNING;
- mutex_unlock(&runnables_lock);
-
- runnables_avg_sampler(0);
-
- return 0;
-}
-
-struct cpuquiet_governor runnables_governor = {
- .name = "runnable",
- .start = runnables_start,
- .device_free_notification = runnables_device_free,
- .device_busy_notification = runnables_device_busy,
- .stop = runnables_stop,
- .owner = THIS_MODULE,
-};
-
-static int __init init_runnables(void)
-{
- return cpuquiet_register_governor(&runnables_governor);
-}
-
-static void __exit exit_runnables(void)
-{
- cpuquiet_unregister_governor(&runnables_governor);
-}
-
-MODULE_LICENSE("GPL");
-#ifdef CONFIG_CPUQUIET_DEFAULT_GOV_RUNNABLE
-fs_initcall(init_runnables);
-#else
-module_init(init_runnables);
-#endif
-module_exit(exit_runnables);
+++ /dev/null
-/*
- * 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/sysfs.h>
-
-static DEFINE_MUTEX(userspace_mutex);
-
-static int governor_set(unsigned int cpu, bool active)
-{
- int err;
-
- mutex_lock(&userspace_mutex);
- if (active)
- err = cpuquiet_wake_cpu(cpu, true);
- else
- err = cpuquiet_quiesence_cpu(cpu, true);
- mutex_unlock(&userspace_mutex);
-
- return err;
-}
-
-struct cpuquiet_governor userspace_governor = {
- .name = "userspace",
- .store_active = governor_set,
- .owner = THIS_MODULE,
-};
-
-static int __init init_usermode(void)
-{
- return cpuquiet_register_governor(&userspace_governor);
-}
-
-static void __exit exit_usermode(void)
-{
- cpuquiet_unregister_governor(&userspace_governor);
-}
-
-MODULE_LICENSE("GPL");
-#ifdef CONFIG_CPUQUIET_DEFAULT_GOV_USERSPACE
-fs_initcall(init_usermode);
-#else
-module_init(init_usermode);
-#endif
-module_exit(exit_usermode);
+++ /dev/null
-/*
- * Cpuquiet driver for Rockchip SoCs
- *
- * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
- *
- * 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.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/cpu.h>
-#include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/cpuquiet.h>
-#include <linux/pm_qos.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-
-#define INITIAL_STATE CPQ_DISABLED
-#define HOTPLUG_DELAY_MS 100
-
-static DEFINE_MUTEX(rockchip_cpuquiet_lock);
-static DEFINE_MUTEX(rockchip_cpq_lock_stats);
-
-static struct workqueue_struct *cpuquiet_wq;
-static struct work_struct cpuquiet_work;
-
-static wait_queue_head_t wait_enable;
-static wait_queue_head_t wait_cpu;
-
-static bool enable;
-static unsigned long hotplug_timeout_jiffies;
-
-static struct cpumask cpumask_online_requests;
-static struct cpumask cpumask_offline_requests;
-
-enum {
- CPQ_DISABLED = 0,
- CPQ_ENABLED,
- CPQ_IDLE,
-};
-
-static int cpq_target_state;
-
-static int cpq_state;
-
-static struct {
- cputime64_t time_up_total;
- u64 last_update;
- unsigned int up_down_count;
-} hp_stats[CONFIG_NR_CPUS];
-
-static void hp_init_stats(void)
-{
- int i;
- u64 cur_jiffies = get_jiffies_64();
-
- mutex_lock(&rockchip_cpq_lock_stats);
-
- for (i = 0; i < nr_cpu_ids; i++) {
- hp_stats[i].time_up_total = 0;
- hp_stats[i].last_update = cur_jiffies;
-
- hp_stats[i].up_down_count = 0;
- if (cpu_online(i))
- hp_stats[i].up_down_count = 1;
- }
-
- mutex_unlock(&rockchip_cpq_lock_stats);
-}
-
-/* must be called with rockchip_cpq_lock_stats held */
-static void __hp_stats_update(unsigned int cpu, bool up)
-{
- u64 cur_jiffies = get_jiffies_64();
- bool was_up;
-
- was_up = hp_stats[cpu].up_down_count & 0x1;
-
- if (was_up)
- hp_stats[cpu].time_up_total =
- hp_stats[cpu].time_up_total +
- (cur_jiffies - hp_stats[cpu].last_update);
-
- if (was_up != up) {
- hp_stats[cpu].up_down_count++;
- if ((hp_stats[cpu].up_down_count & 0x1) != up) {
- /* FIXME: sysfs user space CPU control breaks stats */
- pr_err("hotplug stats out of sync with CPU%d", cpu);
- hp_stats[cpu].up_down_count ^= 0x1;
- }
- }
- hp_stats[cpu].last_update = cur_jiffies;
-}
-
-static void hp_stats_update(unsigned int cpu, bool up)
-{
- mutex_lock(&rockchip_cpq_lock_stats);
-
- __hp_stats_update(cpu, up);
-
- mutex_unlock(&rockchip_cpq_lock_stats);
-}
-
-static int update_core_config(unsigned int cpunumber, bool up)
-{
- int ret = 0;
-
- mutex_lock(&rockchip_cpuquiet_lock);
-
- if (cpq_state == CPQ_DISABLED || cpunumber >= nr_cpu_ids) {
- mutex_unlock(&rockchip_cpuquiet_lock);
- return -EINVAL;
- }
-
- if (up) {
- cpumask_set_cpu(cpunumber, &cpumask_online_requests);
- cpumask_clear_cpu(cpunumber, &cpumask_offline_requests);
- queue_work(cpuquiet_wq, &cpuquiet_work);
- } else {
- cpumask_set_cpu(cpunumber, &cpumask_offline_requests);
- cpumask_clear_cpu(cpunumber, &cpumask_online_requests);
- queue_work(cpuquiet_wq, &cpuquiet_work);
- }
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- return ret;
-}
-
-static int rockchip_quiesence_cpu(unsigned int cpunumber, bool sync)
-{
- int err = 0;
-
- err = update_core_config(cpunumber, false);
- if (err || !sync)
- return err;
-
- err = wait_event_interruptible_timeout(wait_cpu,
- !cpu_online(cpunumber),
- hotplug_timeout_jiffies);
-
- if (err < 0)
- return err;
-
- if (err > 0)
- return 0;
- else
- return -ETIMEDOUT;
-}
-
-static int rockchip_wake_cpu(unsigned int cpunumber, bool sync)
-{
- int err = 0;
-
- err = update_core_config(cpunumber, true);
- if (err || !sync)
- return err;
-
- err = wait_event_interruptible_timeout(wait_cpu, cpu_online(cpunumber),
- hotplug_timeout_jiffies);
-
- if (err < 0)
- return err;
-
- if (err > 0)
- return 0;
- else
- return -ETIMEDOUT;
-}
-
-static struct cpuquiet_driver rockchip_cpuquiet_driver = {
- .name = "rockchip",
- .quiesence_cpu = rockchip_quiesence_cpu,
- .wake_cpu = rockchip_wake_cpu,
-};
-
-/* must be called from worker function */
-static void __cpuinit __apply_core_config(void)
-{
- int count = -1;
- unsigned int cpu;
- int nr_cpus;
- struct cpumask online, offline, cpu_online;
- int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS);
- int min_cpus = pm_qos_request(PM_QOS_MIN_ONLINE_CPUS);
-
- if (min_cpus > num_possible_cpus())
- min_cpus = 0;
- if (max_cpus <= 0)
- max_cpus = num_present_cpus();
-
- mutex_lock(&rockchip_cpuquiet_lock);
-
- online = cpumask_online_requests;
- offline = cpumask_offline_requests;
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- /* always keep CPU0 online */
- cpumask_set_cpu(0, &online);
- cpu_online = *cpu_online_mask;
-
- if (max_cpus < min_cpus)
- max_cpus = min_cpus;
-
- nr_cpus = cpumask_weight(&online);
- if (nr_cpus < min_cpus) {
- cpu = 0;
- count = min_cpus - nr_cpus;
- for (; count > 0; count--) {
- cpu = cpumask_next_zero(cpu, &online);
- cpumask_set_cpu(cpu, &online);
- cpumask_clear_cpu(cpu, &offline);
- }
- } else if (nr_cpus > max_cpus) {
- count = nr_cpus - max_cpus;
- cpu = 0;
- for (; count > 0; count--) {
- /* CPU0 should always be online */
- cpu = cpumask_next(cpu, &online);
- cpumask_set_cpu(cpu, &offline);
- cpumask_clear_cpu(cpu, &online);
- }
- }
-
- cpumask_andnot(&online, &online, &cpu_online);
- for_each_cpu(cpu, &online) {
- cpu_up(cpu);
- hp_stats_update(cpu, true);
- }
-
- cpumask_and(&offline, &offline, &cpu_online);
- for_each_cpu(cpu, &offline) {
- cpu_down(cpu);
- hp_stats_update(cpu, false);
- }
- wake_up_interruptible(&wait_cpu);
-}
-
-static void __cpuinit rockchip_cpuquiet_work_func(struct work_struct *work)
-{
- int action;
-
- mutex_lock(&rockchip_cpuquiet_lock);
-
- action = cpq_target_state;
-
- if (action == CPQ_ENABLED) {
- hp_init_stats();
- cpuquiet_device_free();
- pr_info("cpuquiet enabled\n");
- cpq_state = CPQ_ENABLED;
- cpq_target_state = CPQ_IDLE;
- wake_up_interruptible(&wait_enable);
- }
-
- if (cpq_state == CPQ_DISABLED) {
- mutex_unlock(&rockchip_cpuquiet_lock);
- return;
- }
-
- if (action == CPQ_DISABLED) {
- cpq_state = CPQ_DISABLED;
- mutex_unlock(&rockchip_cpuquiet_lock);
- cpuquiet_device_busy();
- pr_info("cpuquiet disabled\n");
- wake_up_interruptible(&wait_enable);
- return;
- }
-
- mutex_unlock(&rockchip_cpuquiet_lock);
- __apply_core_config();
-}
-
-static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
-{
- mutex_lock(&rockchip_cpuquiet_lock);
-
- if (cpq_state != CPQ_DISABLED)
- queue_work(cpuquiet_wq, &cpuquiet_work);
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- return NOTIFY_OK;
-}
-
-static int max_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
-{
- mutex_lock(&rockchip_cpuquiet_lock);
-
- if (cpq_state != CPQ_DISABLED)
- queue_work(cpuquiet_wq, &cpuquiet_work);
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- return NOTIFY_OK;
-}
-
-/* Must be called with rockchip_cpuquiet_lock held */
-static void __idle_stop_governor(void)
-{
- if (cpq_state == CPQ_DISABLED)
- return;
-
- if (num_online_cpus() == 1)
- cpuquiet_device_busy();
- else
- cpuquiet_device_free();
-}
-
-static int __cpuinit cpu_online_notify(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- switch (action) {
- case CPU_POST_DEAD:
- if (num_online_cpus() == 1) {
- mutex_lock(&rockchip_cpuquiet_lock);
- __idle_stop_governor();
- mutex_unlock(&rockchip_cpuquiet_lock);
- }
- break;
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- mutex_lock(&rockchip_cpuquiet_lock);
- __idle_stop_governor();
- mutex_unlock(&rockchip_cpuquiet_lock);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block cpu_online_notifier __cpuinitdata = {
- .notifier_call = cpu_online_notify,
-};
-
-static struct notifier_block min_cpus_notifier = {
- .notifier_call = min_cpus_notify,
-};
-
-static struct notifier_block max_cpus_notifier = {
- .notifier_call = max_cpus_notify,
-};
-
-static void delay_callback(struct cpuquiet_attribute *attr)
-{
- unsigned long val;
-
- if (attr) {
- val = (*((unsigned long *)(attr->param)));
- (*((unsigned long *)(attr->param))) = msecs_to_jiffies(val);
- }
-}
-
-static void enable_callback(struct cpuquiet_attribute *attr)
-{
- int target_state = enable ? CPQ_ENABLED : CPQ_DISABLED;
-
- mutex_lock(&rockchip_cpuquiet_lock);
-
- if (cpq_state != target_state) {
- cpq_target_state = target_state;
- queue_work(cpuquiet_wq, &cpuquiet_work);
- }
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- wait_event_interruptible(wait_enable, cpq_state == target_state);
-}
-
-CPQ_ATTRIBUTE(hotplug_timeout_jiffies, 0644, ulong, delay_callback);
-CPQ_ATTRIBUTE(enable, 0644, bool, enable_callback);
-
-static struct attribute *rockchip_cpuquiet_attributes[] = {
- &enable_attr.attr,
- &hotplug_timeout_jiffies_attr.attr,
- NULL,
-};
-
-static const struct sysfs_ops rockchip_cpuquiet_sysfs_ops = {
- .show = cpuquiet_auto_sysfs_show,
- .store = cpuquiet_auto_sysfs_store,
-};
-
-static struct kobj_type ktype_sysfs = {
- .sysfs_ops = &rockchip_cpuquiet_sysfs_ops,
- .default_attrs = rockchip_cpuquiet_attributes,
-};
-
-static int rockchip_cpuquiet_sysfs_init(void)
-{
- int err;
-
- struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
-
- if (!kobj)
- return -ENOMEM;
-
- err = cpuquiet_kobject_init(kobj, &ktype_sysfs, "rockchip_cpuquiet");
-
- if (err)
- kfree(kobj);
-
- return err;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int hp_stats_show(struct seq_file *s, void *data)
-{
- int i;
- u64 cur_jiffies = get_jiffies_64();
-
- mutex_lock(&rockchip_cpuquiet_lock);
-
- mutex_lock(&rockchip_cpq_lock_stats);
-
- if (cpq_state != CPQ_DISABLED) {
- for (i = 0; i < nr_cpu_ids; i++) {
- bool was_up;
-
- was_up = (hp_stats[i].up_down_count & 0x1);
- __hp_stats_update(i, was_up);
- }
- }
- mutex_unlock(&rockchip_cpq_lock_stats);
-
- mutex_unlock(&rockchip_cpuquiet_lock);
-
- seq_printf(s, "%-15s ", "cpu:");
- for (i = 0; i < nr_cpu_ids; i++)
- seq_printf(s, "G%-9d ", i);
-
- seq_printf(s, "%-15s ", "transitions:");
- for (i = 0; i < nr_cpu_ids; i++)
- seq_printf(s, "%-10u ", hp_stats[i].up_down_count);
- seq_puts(s, "\n");
-
- seq_printf(s, "%-15s ", "time plugged:");
- for (i = 0; i < nr_cpu_ids; i++) {
- seq_printf(s, "%-10llu ",
- cputime64_to_clock_t(hp_stats[i].time_up_total));
- }
- seq_puts(s, "\n");
-
- seq_printf(s, "%-15s %llu\n", "time-stamp:",
- cputime64_to_clock_t(cur_jiffies));
-
- return 0;
-}
-
-static int hp_stats_open(struct inode *inode, struct file *file)
-{
- return single_open(file, hp_stats_show, inode->i_private);
-}
-
-static const struct file_operations hp_stats_fops = {
- .open = hp_stats_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int __init rockchip_cpuquiet_debug_init(void)
-{
- struct dentry *dir;
-
- dir = debugfs_create_dir("rockchip_cpuquiet", NULL);
- if (!dir)
- return -ENOMEM;
-
- if (!debugfs_create_file("stats", S_IRUGO, dir, NULL, &hp_stats_fops))
- goto err_out;
-
- return 0;
-
-err_out:
- debugfs_remove_recursive(dir);
- return -ENOMEM;
-}
-
-late_initcall(rockchip_cpuquiet_debug_init);
-#endif /* CONFIG_DEBUG_FS */
-
-static int __init rockchip_cpuquiet_init(void)
-{
- int err;
-
- init_waitqueue_head(&wait_enable);
- init_waitqueue_head(&wait_cpu);
-
- /*
- * Not bound to the issuer CPU (=> high-priority), has rescue worker
- * task, single-threaded, freezable.
- */
- cpuquiet_wq = alloc_workqueue(
- "cpuquiet", WQ_NON_REENTRANT | WQ_FREEZABLE, 1);
-
- if (!cpuquiet_wq)
- return -ENOMEM;
-
- INIT_WORK(&cpuquiet_work, rockchip_cpuquiet_work_func);
- hotplug_timeout_jiffies = msecs_to_jiffies(HOTPLUG_DELAY_MS);
- cpumask_clear(&cpumask_online_requests);
- cpumask_clear(&cpumask_offline_requests);
-
- cpq_state = INITIAL_STATE;
- enable = cpq_state == CPQ_DISABLED ? false : true;
- hp_init_stats();
-
- pr_info("cpuquiet initialized: %s\n",
- (cpq_state == CPQ_DISABLED) ? "disabled" : "enabled");
-
- if (pm_qos_add_notifier(PM_QOS_MIN_ONLINE_CPUS, &min_cpus_notifier))
- pr_err("Failed to register min cpus PM QoS notifier\n");
- if (pm_qos_add_notifier(PM_QOS_MAX_ONLINE_CPUS, &max_cpus_notifier))
- pr_err("Failed to register max cpus PM QoS notifier\n");
-
- register_hotcpu_notifier(&cpu_online_notifier);
-
- err = cpuquiet_register_driver(&rockchip_cpuquiet_driver);
- if (err) {
- destroy_workqueue(cpuquiet_wq);
- return err;
- }
-
- err = rockchip_cpuquiet_sysfs_init();
- if (err) {
- cpuquiet_unregister_driver(&rockchip_cpuquiet_driver);
- destroy_workqueue(cpuquiet_wq);
- }
-
- return err;
-}
-device_initcall(rockchip_cpuquiet_init);
+++ /dev/null
-/*
- * Copyright (c) 2012-2013 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/kernel.h>
-#include <linux/sysfs.h>
-#include <linux/slab.h>
-#include <linux/cpuquiet.h>
-
-#include "cpuquiet.h"
-
-struct cpuquiet_dev {
- unsigned int cpu;
- struct kobject kobj;
-};
-
-struct cpuquiet_sysfs_attr {
- struct attribute attr;
- ssize_t (*show)(char *);
- ssize_t (*store)(const char *, size_t count);
-};
-
-static struct kobject *cpuquiet_global_kobject;
-struct cpuquiet_dev *cpuquiet_cpu_devices[CONFIG_NR_CPUS];
-
-static ssize_t show_current_governor(char *buf)
-{
- ssize_t ret;
-
- mutex_lock(&cpuquiet_lock);
-
- if (cpuquiet_curr_governor)
- ret = sprintf(buf, "%s\n", cpuquiet_curr_governor->name);
- else
- ret = sprintf(buf, "none\n");
-
- mutex_unlock(&cpuquiet_lock);
-
- return ret;
-
-}
-
-static ssize_t store_current_governor(const char *buf, size_t count)
-{
- char name[CPUQUIET_NAME_LEN];
- struct cpuquiet_governor *gov;
- int len = count, ret = -EINVAL;
-
- if (!len || len >= sizeof(name))
- return -EINVAL;
-
- memcpy(name, buf, count);
- name[len] = '\0';
- if (name[len - 1] == '\n')
- name[--len] = '\0';
-
- mutex_lock(&cpuquiet_lock);
- gov = cpuquiet_find_governor(name);
-
- if (gov)
- ret = cpuquiet_switch_governor(gov);
- mutex_unlock(&cpuquiet_lock);
-
- if (ret)
- return ret;
- else
- return count;
-}
-
-static ssize_t available_governors_show(char *buf)
-{
- ssize_t ret = 0, len;
- struct cpuquiet_governor *gov;
-
- mutex_lock(&cpuquiet_lock);
- if (!list_empty(&cpuquiet_governors)) {
- list_for_each_entry(gov, &cpuquiet_governors, governor_list) {
- len = sprintf(buf, "%s ", gov->name);
- buf += len;
- ret += len;
- }
- buf--;
- *buf = '\n';
- } else
- ret = sprintf(buf, "none\n");
-
- mutex_unlock(&cpuquiet_lock);
-
- return ret;
-}
-
-struct cpuquiet_sysfs_attr attr_current_governor = __ATTR(current_governor,
- 0644, show_current_governor, store_current_governor);
-struct cpuquiet_sysfs_attr attr_governors = __ATTR_RO(available_governors);
-
-
-static struct attribute *cpuquiet_default_attrs[] = {
- &attr_current_governor.attr,
- &attr_governors.attr,
- NULL
-};
-
-static ssize_t cpuquiet_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct cpuquiet_sysfs_attr *cattr =
- container_of(attr, struct cpuquiet_sysfs_attr, attr);
-
- return cattr->show(buf);
-}
-
-static ssize_t cpuquiet_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- struct cpuquiet_sysfs_attr *cattr =
- container_of(attr, struct cpuquiet_sysfs_attr, attr);
-
- if (cattr->store)
- return cattr->store(buf, count);
-
- return -EINVAL;
-}
-
-static const struct sysfs_ops cpuquiet_sysfs_ops = {
- .show = cpuquiet_sysfs_show,
- .store = cpuquiet_sysfs_store,
-};
-
-static struct kobj_type ktype_cpuquiet_sysfs = {
- .sysfs_ops = &cpuquiet_sysfs_ops,
- .default_attrs = cpuquiet_default_attrs,
-};
-
-int cpuquiet_add_group(struct attribute_group *attrs)
-{
- return sysfs_create_group(cpuquiet_global_kobject, attrs);
-}
-
-void cpuquiet_remove_group(struct attribute_group *attrs)
-{
- sysfs_remove_group(cpuquiet_global_kobject, attrs);
-}
-
-int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
- char *name)
-{
- int err;
-
- err = kobject_init_and_add(kobj, type, cpuquiet_global_kobject, name);
- if (!err)
- kobject_uevent(kobj, KOBJ_ADD);
-
- return err;
-}
-
-int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
- char *name, int cpu)
-{
- int err;
-
- err = kobject_init_and_add(kobj, type, &cpuquiet_cpu_devices[cpu]->kobj,
- name);
- if (!err)
- kobject_uevent(kobj, KOBJ_ADD);
-
- return err;
-}
-
-int cpuquiet_add_interface(struct device *dev)
-{
- int err;
-
- cpuquiet_global_kobject = kzalloc(sizeof(*cpuquiet_global_kobject),
- GFP_KERNEL);
- if (!cpuquiet_global_kobject)
- return -ENOMEM;
-
- err = kobject_init_and_add(cpuquiet_global_kobject,
- &ktype_cpuquiet_sysfs, &dev->kobj, "cpuquiet");
- if (!err)
- kobject_uevent(cpuquiet_global_kobject, KOBJ_ADD);
-
- return err;
-}
-
-
-struct cpuquiet_attr {
- struct attribute attr;
- ssize_t (*show)(unsigned int, char *);
- ssize_t (*store)(unsigned int, const char *, size_t count);
-};
-
-
-static ssize_t cpuquiet_state_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct cpuquiet_attr *cattr = container_of(attr,
- struct cpuquiet_attr, attr);
- struct cpuquiet_dev *dev = container_of(kobj,
- struct cpuquiet_dev, kobj);
-
- return cattr->show(dev->cpu, buf);
-}
-
-static ssize_t cpuquiet_state_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- struct cpuquiet_attr *cattr = container_of(attr,
- struct cpuquiet_attr, attr);
- struct cpuquiet_dev *dev = container_of(kobj,
- struct cpuquiet_dev, kobj);
-
- if (cattr->store)
- return cattr->store(dev->cpu, buf, count);
-
- return -EINVAL;
-}
-
-static ssize_t show_active(unsigned int cpu, char *buf)
-{
- return sprintf(buf, "%u\n", cpu_online(cpu));
-}
-
-static ssize_t store_active(unsigned int cpu, const char *value, size_t count)
-{
- unsigned int active;
- int ret;
-
- if (!cpuquiet_curr_governor->store_active)
- return -EINVAL;
-
- ret = sscanf(value, "%u", &active);
- if (ret != 1)
- return -EINVAL;
-
- cpuquiet_curr_governor->store_active(cpu, active);
-
- return count;
-}
-
-struct cpuquiet_attr attr_active = __ATTR(active, 0644, show_active,
- store_active);
-
-static struct attribute *cpuquiet_default_cpu_attrs[] = {
- &attr_active.attr,
- NULL
-};
-
-static const struct sysfs_ops cpuquiet_cpu_sysfs_ops = {
- .show = cpuquiet_state_show,
- .store = cpuquiet_state_store,
-};
-
-static struct kobj_type ktype_cpuquiet = {
- .sysfs_ops = &cpuquiet_cpu_sysfs_ops,
- .default_attrs = cpuquiet_default_cpu_attrs,
-};
-
-void cpuquiet_add_dev(struct device *device, unsigned int cpu)
-{
- struct cpuquiet_dev *dev;
- int err;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- dev->cpu = cpu;
- cpuquiet_cpu_devices[cpu] = dev;
- err = kobject_init_and_add(&dev->kobj, &ktype_cpuquiet,
- &device->kobj, "cpuquiet");
- if (!err)
- kobject_uevent(&dev->kobj, KOBJ_ADD);
-}
-
-void cpuquiet_remove_dev(unsigned int cpu)
-{
- if (cpu < CONFIG_NR_CPUS && cpuquiet_cpu_devices[cpu])
- kobject_put(&cpuquiet_cpu_devices[cpu]->kobj);
-}
+++ /dev/null
-/*
- * 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.
- *
- */
-
-#ifndef _LINUX_CPUONLINE_H
-#define _LINUX_CPUONLINE_H
-
-#include <linux/sysfs.h>
-#include <linux/kobject.h>
-
-#define CPUQUIET_NAME_LEN 16
-
-struct cpuquiet_governor {
- char name[CPUQUIET_NAME_LEN];
- struct list_head governor_list;
- int (*start) (void);
- void (*stop) (void);
- int (*store_active) (unsigned int cpu, bool active);
- void (*device_free_notification) (void);
- void (*device_busy_notification) (void);
- struct module *owner;
-};
-
-struct cpuquiet_driver {
- char name[CPUQUIET_NAME_LEN];
- int (*quiesence_cpu) (unsigned int cpunumber, bool sync);
- int (*wake_cpu) (unsigned int cpunumber, bool sync);
-};
-
-extern int cpuquiet_register_governor(struct cpuquiet_governor *gov);
-extern void cpuquiet_unregister_governor(struct cpuquiet_governor *gov);
-extern int cpuquiet_quiesence_cpu(unsigned int cpunumber, bool sync);
-extern int cpuquiet_wake_cpu(unsigned int cpunumber, bool sync);
-extern int cpuquiet_register_driver(struct cpuquiet_driver *drv);
-extern void cpuquiet_unregister_driver(struct cpuquiet_driver *drv);
-extern int cpuquiet_add_group(struct attribute_group *attrs);
-extern void cpuquiet_remove_group(struct attribute_group *attrs);
-extern void cpuquiet_device_busy(void);
-extern void cpuquiet_device_free(void);
-int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
- char *name);
-extern unsigned int nr_cluster_ids;
-
-/* Sysfs support */
-struct cpuquiet_attribute {
- struct attribute attr;
- ssize_t (*show)(struct cpuquiet_attribute *attr, char *buf);
- ssize_t (*store)(struct cpuquiet_attribute *attr, const char *buf,
- size_t count);
- /* Optional. Called after store is called */
- void (*store_callback)(struct cpuquiet_attribute *attr);
- void *param;
-};
-
-#define CPQ_ATTRIBUTE(_name, _mode, _type, _callback) \
- static struct cpuquiet_attribute _name ## _attr = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .show = show_ ## _type ## _attribute, \
- .store = store_ ## _type ## _attribute, \
- .store_callback = _callback, \
- .param = &_name, \
-}
-
-#define CPQ_BASIC_ATTRIBUTE(_name, _mode, _type) \
- CPQ_ATTRIBUTE(_name, _mode, _type, NULL)
-
-#define CPQ_ATTRIBUTE_CUSTOM(_name, _mode, _show, _store) \
- static struct cpuquiet_attribute _name ## _attr = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .show = _show, \
- .store = _store, \
- .store_callback = NULL, \
- .param = &_name, \
-}
-
-
-extern ssize_t show_int_attribute(struct cpuquiet_attribute *cattr, char *buf);
-extern ssize_t store_int_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count);
-extern ssize_t show_bool_attribute(struct cpuquiet_attribute *cattr, char *buf);
-extern ssize_t store_bool_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count);
-extern ssize_t store_uint_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count);
-extern ssize_t show_uint_attribute(struct cpuquiet_attribute *cattr, char *buf);
-extern ssize_t store_ulong_attribute(struct cpuquiet_attribute *cattr,
- const char *buf, size_t count);
-extern ssize_t show_ulong_attribute(struct cpuquiet_attribute *cattr,
- char *buf);
-extern ssize_t cpuquiet_auto_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf);
-extern ssize_t cpuquiet_auto_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf,
- size_t count);
-#endif