Messages in this thread | | | Date | Wed, 7 Jun 2017 23:50:28 +0200 | From | Daniel Lezcano <> | Subject | Re: [PATCH RFC 1/2] thermal/cpu idle cooling: Introduce cpu idle cooling driver |
| |
On Tue, Jun 06, 2017 at 09:11:35AM +0530, viresh kumar wrote: > + Daniel
Hi Viresh,
thanks for the head up.
Before going deeply in the review, I have a dumb question:
Why isn't this mechanism implemented at the scheduler level?
> On 05-06-17, 17:07, Tao Wang wrote: > > cpu idle cooling driver performs synchronized idle injection across > > all cpu in same cluster, offers a new method to cooling down cpu, > > that is similar to intel_power_clamp driver, but is basically > > designed for ARM platform. > > Each cluster has its own idle cooling device, each core has its own > > idle injection thread, idle injection thread use play_idle to enter > > idle. In order to reach deepest idle state, all cores are aligned by > > jiffies. the injected idle ratio can be controlled through cooling > > device interface. > > > > Signed-off-by: Tao Wang <kevin.wangtao@hisilicon.com> > > --- > > drivers/thermal/Kconfig | 13 + > > drivers/thermal/Makefile | 3 + > > drivers/thermal/cpu_idle_cooling.c | 648 ++++++++++++++++++++++++++++++++++++ > > 3 files changed, 664 insertions(+) > > create mode 100644 drivers/thermal/cpu_idle_cooling.c > > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > > index b5b5fac..f78e85c 100644 > > --- a/drivers/thermal/Kconfig > > +++ b/drivers/thermal/Kconfig > > @@ -154,6 +154,19 @@ config CPU_THERMAL > > > > If you want this support, you should say Y here. > > > > +config CPU_IDLE_THERMAL > > + tristate "generic cpu idle cooling support" > > + depends on CPU_FREQ > > + help > > + This implements the generic cpu cooling mechanism through idle > > + injection. > > + > > + This will throttle cpu by injecting specified idle time in > > + a fixed cycle. All cpu in same cluster will enter idle synchronously > > + to reach deepest idle state when injecting idle. > > + > > + If you want this support, you should say Y here. > > + > > config CLOCK_THERMAL > > bool "Generic clock cooling support" > > depends on COMMON_CLK > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile > > index 094d703..a4db66e 100644 > > --- a/drivers/thermal/Makefile > > +++ b/drivers/thermal/Makefile > > @@ -26,6 +26,9 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o > > # devfreq cooling > > thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o > > > > +# cpu idle cooling > > +obj-$(CONFIG_CPU_IDLE_THERMAL) += cpu_idle_cooling.o > > + > > # platform thermal drivers > > obj-y += broadcom/ > > obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o > > diff --git a/drivers/thermal/cpu_idle_cooling.c b/drivers/thermal/cpu_idle_cooling.c > > new file mode 100644 > > index 0000000..89a15c5 > > --- /dev/null > > +++ b/drivers/thermal/cpu_idle_cooling.c > > @@ -0,0 +1,648 @@ > > +/* > > + * linux/drivers/thermal/cpu_idle_cooling.c > > + * > > + * Copyright (C) 2017 Tao Wang <kevin.wangtao@hisilicon.com> > > + * > > + * 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, see <http://www.gnu.org/licenses/>. > > + */ > > + > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/kernel_stat.h> > > +#include <linux/delay.h> > > +#include <linux/kthread.h> > > +#include <linux/freezer.h> > > +#include <linux/cpu.h> > > +#include <linux/topology.h> > > +#include <linux/cpufreq.h> > > +#include <linux/cpumask.h> > > +#include <linux/cpuidle.h> > > +#include <linux/thermal.h> > > +#include <linux/sched.h> > > +#include <uapi/linux/sched/types.h> > > +#include <linux/slab.h> > > +#include <linux/tick.h> > > +#include <linux/wait.h> > > +#include <linux/sched/rt.h> > > + > > +#define MAX_TARGET_RATIO (50U) > > + > > +#define DEFAULT_WINDOW_SIZE (1) > > +#define DEFAULT_DURATION_JIFFIES (20) > > + > > +struct cpu_idle_cooling_device { > > + int id; > > + struct thermal_cooling_device *cooling_dev; > > + wait_queue_head_t wait_queue; > > + > > + /* The cpu assigned to collect stat and update > > + * control parameters. default to BSP but BSP > > + * can be offlined. > > + */ > > + unsigned long control_cpu; > > + > > + unsigned int set_target_ratio; > > + unsigned int current_ratio; > > + unsigned int control_ratio; > > + unsigned int duration; > > + unsigned int window_size; > > + > > + cpumask_var_t related_cpus; > > + cpumask_var_t injected_cpus; > > + struct list_head node; > > + bool should_skip; > > + bool clamping; > > +}; > > + > > +static LIST_HEAD(cpu_idle_cooling_dev_list); > > +static DEFINE_PER_CPU(struct task_struct *, idle_injection_thread_ptr); > > +static DEFINE_MUTEX(cpu_idle_cooling_lock); > > + > > +unsigned long idle_time[NR_CPUS] = {0}; > > +unsigned long time_stamp[NR_CPUS] = {0}; > > +static enum cpuhp_state hp_state; > > + > > +#define STORE_PARAM(param, min, max) \ > > +static ssize_t store_##param(struct device *dev, \ > > + struct device_attribute *attr, \ > > + const char *buf, size_t count) \ > > +{ \ > > + unsigned int new_value; \ > > + struct thermal_cooling_device *cdev; \ > > + struct cpu_idle_cooling_device *idle_cooling_dev; \ > > + \ > > + if (dev == NULL || attr == NULL) \ > > + return 0; \ > > + \ > > + if (kstrtouint(buf, 10, &new_value)) \ > > + return -EINVAL; \ > > + \ > > + if (new_value > max || new_value < min) { \ > > + pr_err("Out of range %u, between %d-%d\n", \ > > + new_value, min, max); \ > > + return -EINVAL; \ > > + } \ > > + \ > > + cdev = container_of(dev, struct thermal_cooling_device, device);\ > > + idle_cooling_dev = cdev->devdata; \ > > + idle_cooling_dev->param = new_value; \ > > + \ > > + /* make new value visible to other cpus */ \ > > + smp_mb(); \ > > + \ > > + return count; \ > > +} > > + > > +STORE_PARAM(duration, 10, 500); > > +STORE_PARAM(window_size, 1, 10); > > + > > +#define SHOW_PARAM(param) \ > > +static ssize_t show_##param(struct device *dev, \ > > + struct device_attribute *attr, char *buf) \ > > +{ \ > > + struct thermal_cooling_device *cdev; \ > > + struct cpu_idle_cooling_device *idle_cooling_dev; \ > > + \ > > + if (dev == NULL || attr == NULL) \ > > + return 0; \ > > + \ > > + cdev = container_of(dev, struct thermal_cooling_device, device);\ > > + idle_cooling_dev = cdev->devdata; \ > > + \ > > + return snprintf(buf, 12UL, "%d\n", \ > > + idle_cooling_dev->param); \ > > +} > > + > > +SHOW_PARAM(duration); > > +SHOW_PARAM(window_size); > > + > > +static DEVICE_ATTR(duration, 0644, show_duration, store_duration); > > +static DEVICE_ATTR(window_size, 0644, show_window_size, store_window_size); > > + > > +static struct cpu_idle_cooling_device * > > +get_cpu_idle_cooling_dev(unsigned long cpu) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + list_for_each_entry(idle_cooling_dev, > > + &cpu_idle_cooling_dev_list, node) { > > + if (cpumask_test_cpu(cpu, idle_cooling_dev->related_cpus)) > > + return idle_cooling_dev; > > + } > > + > > + return NULL; > > +} > > + > > +#define K_P 10 > > +#define MAX_COMP 10 > > +static unsigned int get_compensation(unsigned int current_ratio, > > + unsigned int target_ratio, unsigned int control_ratio) > > +{ > > + unsigned int comp; > > + > > + comp = abs(current_ratio - target_ratio) * K_P / 10; > > + if (comp > MAX_COMP) > > + comp = MAX_COMP; > > + > > + if (current_ratio > target_ratio) { > > + if (control_ratio > comp) > > + comp = control_ratio - comp; > > + else > > + comp = 1; > > + } else { > > + if (control_ratio + comp < MAX_TARGET_RATIO) > > + comp = control_ratio + comp; > > + else > > + comp = MAX_TARGET_RATIO; > > + > > + if (comp > (target_ratio * 6 / 5)) > > + comp = target_ratio * 6 / 5; > > + } > > + > > + return comp; > > +} > > + > > +static void update_stats(struct cpu_idle_cooling_device *idle_cooling_dev) > > +{ > > + unsigned long cpu; > > + u64 now, now_idle, delta_time, delta_idle; > > + u64 min_idle_ratio = 100; > > + u64 idle_ratio = 0; > > + > > + for_each_cpu(cpu, idle_cooling_dev->related_cpus) { > > + now_idle = get_cpu_idle_time(cpu, &now, 0); > > + delta_idle = now_idle - idle_time[cpu]; > > + delta_time = now - time_stamp[cpu]; > > + idle_time[cpu] = now_idle; > > + time_stamp[cpu] = now; > > + > > + if (delta_idle >= delta_time || !cpu_online(cpu)) > > + now_idle = 100; > > + else if (delta_time) > > + now_idle = div64_u64(100 * delta_idle, delta_time); > > + else > > + return; > > + > > + if (now_idle < min_idle_ratio) > > + min_idle_ratio = now_idle; > > + > > + idle_ratio += now_idle; > > + } > > + > > + idle_ratio /= cpumask_weight(idle_cooling_dev->related_cpus); > > + if (idle_ratio > MAX_TARGET_RATIO) > > + idle_ratio = min_idle_ratio; > > + > > + if (idle_cooling_dev->should_skip) > > + idle_ratio = (idle_cooling_dev->current_ratio + idle_ratio) / 2; > > + > > + idle_cooling_dev->current_ratio = (unsigned int)idle_ratio; > > + idle_cooling_dev->control_ratio = get_compensation(idle_ratio, > > + idle_cooling_dev->set_target_ratio, > > + idle_cooling_dev->control_ratio); > > + idle_cooling_dev->should_skip = > > + (idle_ratio > (2 * idle_cooling_dev->set_target_ratio)); > > + /* make new control_ratio and should skip flag visible to other cpus */ > > + smp_mb(); > > +} > > + > > +static void inject_idle_fn(struct cpu_idle_cooling_device *idle_cooling_dev) > > +{ > > + long sleeptime, guard; > > + unsigned int interval_ms; /* jiffies to sleep for each attempt */ > > + unsigned long target_jiffies; > > + unsigned int duration_ms = idle_cooling_dev->duration; > > + unsigned long duration_jiffies = msecs_to_jiffies(duration_ms); > > + > > + guard = DIV_ROUND_UP(duration_jiffies * (90 - MAX_TARGET_RATIO), 100); > > + > > + /* align idle time */ > > + target_jiffies = roundup(jiffies, duration_jiffies); > > + sleeptime = target_jiffies - jiffies; > > + if (sleeptime < guard) > > + sleeptime += duration_jiffies; > > + > > + if (sleeptime > 0) > > + schedule_timeout_interruptible(sleeptime); > > + > > + interval_ms = duration_ms * idle_cooling_dev->control_ratio / 100; > > + > > + if (idle_cooling_dev->should_skip) > > + return; > > + > > + if (interval_ms) > > + play_idle(interval_ms); > > +} > > + > > +static int idle_injection_thread(void *arg) > > +{ > > + unsigned long cpunr = (unsigned long)arg; > > + struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2 }; > > + unsigned int count = 0; > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + set_freezable(); > > + > > + sched_setscheduler(current, SCHED_FIFO, ¶m); > > + > > + mutex_lock(&cpu_idle_cooling_lock); > > + idle_cooling_dev = get_cpu_idle_cooling_dev(cpunr); > > + mutex_unlock(&cpu_idle_cooling_lock); > > + > > + while (!kthread_should_stop()) { > > + wait_event_interruptible(idle_cooling_dev->wait_queue, > > + (idle_cooling_dev->clamping && cpu_online(cpunr)) || > > + kthread_should_stop()); > > + > > + if (kthread_should_stop()) > > + break; > > + > > + /* rebind thread to cpu */ > > + if (set_cpus_allowed_ptr(current, cpumask_of(cpunr))) > > + continue; > > + > > + try_to_freeze(); > > + > > + while (idle_cooling_dev->clamping && > > + cpu_online(cpunr)) { > > + try_to_freeze(); > > + > > + count++; > > + /* > > + * only elected controlling cpu can collect stats > > + * and update control parameters. > > + */ > > + if (cpunr == idle_cooling_dev->control_cpu > > + && !(count % idle_cooling_dev->window_size)) > > + update_stats(idle_cooling_dev); > > + > > + inject_idle_fn(idle_cooling_dev); > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int create_idle_thread(struct cpu_idle_cooling_device *idle_cooling_dev) > > +{ > > + unsigned long cpu; > > + struct task_struct *thread; > > + > > + init_waitqueue_head(&idle_cooling_dev->wait_queue); > > + > > + /* start one thread per online cpu */ > > + for_each_cpu(cpu, idle_cooling_dev->related_cpus) { > > + thread = kthread_create_on_node(idle_injection_thread, > > + (void *) cpu, > > + cpu_to_node(cpu), > > + "kidle_inject/%lu", cpu); > > + /* bind to cpu here */ > > + if (likely(!IS_ERR(thread))) { > > + cpumask_set_cpu(cpu, idle_cooling_dev->injected_cpus); > > + kthread_bind(thread, cpu); > > + wake_up_process(thread); > > + per_cpu(idle_injection_thread_ptr, cpu) = thread; > > + } else { > > + return -ENOMEM; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static void stop_idle_thread(struct cpu_idle_cooling_device *idle_cooling_dev) > > +{ > > + unsigned long cpu; > > + struct task_struct **percpu_thread; > > + > > + idle_cooling_dev->clamping = false; > > + /* > > + * make clamping visible to other cpus and give per cpu threads > > + * sometime to exit, or gets killed later. > > + */ > > + smp_mb(); > > + msleep(idle_cooling_dev->duration); > > + for_each_cpu(cpu, idle_cooling_dev->injected_cpus) { > > + pr_debug("idle inject thread for cpu %lu alive, kill\n", cpu); > > + percpu_thread = per_cpu_ptr(&idle_injection_thread_ptr, cpu); > > + if (!IS_ERR_OR_NULL(*percpu_thread)) { > > + kthread_stop(*percpu_thread); > > + *percpu_thread = NULL; > > + } > > + cpumask_clear_cpu(cpu, idle_cooling_dev->injected_cpus); > > + } > > +} > > + > > +static int idle_injection_cpu_online(unsigned int cpu) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + idle_cooling_dev = get_cpu_idle_cooling_dev(cpu); > > + if (idle_cooling_dev) { > > + /* prefer BSP as controlling CPU */ > > + if (cpu == cpumask_first(idle_cooling_dev->injected_cpus) > > + || !cpu_online(idle_cooling_dev->control_cpu)) { > > + idle_cooling_dev->control_cpu = cpu; > > + /* make new control_cpu visible to other cpus */ > > + smp_mb(); > > + } > > + wake_up_interruptible(&idle_cooling_dev->wait_queue); > > + } > > + > > + return 0; > > +} > > + > > +static int idle_injection_cpu_predown(unsigned int cpu) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + idle_cooling_dev = get_cpu_idle_cooling_dev(cpu); > > + if (idle_cooling_dev) { > > + if (cpu == idle_cooling_dev->control_cpu) { > > + cpu = cpumask_next_and(-1, > > + idle_cooling_dev->injected_cpus, > > + cpu_online_mask); > > + > > + if (cpu < nr_cpu_ids) > > + idle_cooling_dev->control_cpu = cpu; > > + /* make new control_cpu visible to other cpus */ > > + smp_mb(); > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int idle_get_max_state(struct thermal_cooling_device *cdev, > > + unsigned long *state) > > +{ > > + *state = MAX_TARGET_RATIO; > > + > > + return 0; > > +} > > + > > +static int idle_get_cur_state(struct thermal_cooling_device *cdev, > > + unsigned long *state) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev = cdev->devdata; > > + > > + if (true == idle_cooling_dev->clamping) > > + *state = (unsigned long)idle_cooling_dev->current_ratio; > > + else > > + *state = 0; /* indicates invalid state */ > > + > > + return 0; > > +} > > + > > +static int idle_set_cur_state(struct thermal_cooling_device *cdev, > > + unsigned long new_target_ratio) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev = cdev->devdata; > > + int ret = 0; > > + > > + mutex_lock(&cdev->lock); > > + > > + new_target_ratio = clamp(new_target_ratio, 0UL, > > + (unsigned long) MAX_TARGET_RATIO); > > + if (idle_cooling_dev->set_target_ratio == 0 > > + && new_target_ratio > 0) { > > + idle_cooling_dev->set_target_ratio = > > + (unsigned int) new_target_ratio; > > + idle_cooling_dev->control_ratio = > > + idle_cooling_dev->set_target_ratio; > > + idle_cooling_dev->current_ratio = > > + idle_cooling_dev->set_target_ratio; > > + idle_cooling_dev->clamping = true; > > + wake_up_interruptible(&idle_cooling_dev->wait_queue); > > + } else if (idle_cooling_dev->set_target_ratio > 0) { > > + if (new_target_ratio == 0) { > > + idle_cooling_dev->set_target_ratio = 0; > > + idle_cooling_dev->clamping = false; > > + /* make clamping visible to other cpus */ > > + smp_mb(); > > + } else /* adjust currently running */ { > > + idle_cooling_dev->set_target_ratio = > > + (unsigned int) new_target_ratio; > > + /* make new set_target_ratio visible to other cpus */ > > + smp_mb(); > > + } > > + } > > + > > + mutex_unlock(&cdev->lock); > > + > > + return ret; > > +} > > + > > +static struct thermal_cooling_device_ops cpu_idle_injection_cooling_ops = { > > + .get_max_state = idle_get_max_state, > > + .get_cur_state = idle_get_cur_state, > > + .set_cur_state = idle_set_cur_state, > > +}; > > + > > +unsigned long get_max_idle_state(const struct cpumask *clip_cpus) > > +{ > > + return MAX_TARGET_RATIO; > > +} > > +EXPORT_SYMBOL_GPL(get_max_idle_state); > > + > > +void set_idle_state(const struct cpumask *clip_cpus, unsigned long idle_ratio) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + mutex_lock(&cpu_idle_cooling_lock); > > + list_for_each_entry(idle_cooling_dev, > > + &cpu_idle_cooling_dev_list, node) { > > + if (cpumask_subset(idle_cooling_dev->related_cpus, clip_cpus)) > > + idle_set_cur_state(idle_cooling_dev->cooling_dev, > > + idle_ratio); > > + } > > + mutex_unlock(&cpu_idle_cooling_lock); > > +} > > +EXPORT_SYMBOL_GPL(set_idle_state); > > + > > +struct thermal_cooling_device * __init > > +cpu_idle_cooling_register(const struct cpumask *clip_cpus) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + struct thermal_cooling_device *ret; > > + unsigned long cpu; > > + char dev_name[THERMAL_NAME_LENGTH]; > > + > > + if (cpumask_empty(clip_cpus)) > > + return ERR_PTR(-ENOMEM); > > + > > + mutex_lock(&cpu_idle_cooling_lock); > > + get_online_cpus(); > > + list_for_each_entry(idle_cooling_dev, > > + &cpu_idle_cooling_dev_list, node) { > > + if (cpumask_intersects(idle_cooling_dev->related_cpus, > > + clip_cpus)) { > > + ret = ERR_PTR(-EINVAL); > > + goto exit_unlock; > > + } > > + } > > + > > + idle_cooling_dev = kzalloc(sizeof(*idle_cooling_dev), GFP_KERNEL); > > + if (!idle_cooling_dev) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_unlock; > > + } > > + > > + if (!zalloc_cpumask_var(&idle_cooling_dev->related_cpus, GFP_KERNEL)) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_free_dev; > > + } > > + > > + if (!zalloc_cpumask_var(&idle_cooling_dev->injected_cpus, GFP_KERNEL)) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_free_related_cpus; > > + } > > + > > + cpumask_copy(idle_cooling_dev->related_cpus, clip_cpus); > > + cpu = cpumask_first(clip_cpus); > > + idle_cooling_dev->control_cpu = cpu; > > + idle_cooling_dev->id = topology_physical_package_id(cpu); > > + idle_cooling_dev->window_size = DEFAULT_WINDOW_SIZE; > > + idle_cooling_dev->duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES); > > + > > + if (create_idle_thread(idle_cooling_dev)) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_free_injected_cpus; > > + } > > + > > + snprintf(dev_name, sizeof(dev_name), "thermal-cpuidle-%d", > > + idle_cooling_dev->id); > > + ret = thermal_cooling_device_register(dev_name, > > + idle_cooling_dev, > > + &cpu_idle_injection_cooling_ops); > > + if (IS_ERR(ret)) > > + goto exit_stop_thread; > > + > > + idle_cooling_dev->cooling_dev = ret; > > + > > + if (device_create_file(&idle_cooling_dev->cooling_dev->device, > > + &dev_attr_duration)) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_unregister_cdev; > > + } > > + > > + if (device_create_file(&idle_cooling_dev->cooling_dev->device, > > + &dev_attr_window_size)) { > > + ret = ERR_PTR(-ENOMEM); > > + goto exit_remove_duration_attr; > > + } > > + > > + list_add(&idle_cooling_dev->node, &cpu_idle_cooling_dev_list); > > + > > + goto exit_unlock; > > + > > +exit_remove_duration_attr: > > + device_remove_file(&idle_cooling_dev->cooling_dev->device, > > + &dev_attr_duration); > > +exit_unregister_cdev: > > + thermal_cooling_device_unregister(idle_cooling_dev->cooling_dev); > > +exit_stop_thread: > > + stop_idle_thread(idle_cooling_dev); > > +exit_free_injected_cpus: > > + free_cpumask_var(idle_cooling_dev->injected_cpus); > > +exit_free_related_cpus: > > + free_cpumask_var(idle_cooling_dev->related_cpus); > > +exit_free_dev: > > + kfree(idle_cooling_dev); > > +exit_unlock: > > + put_online_cpus(); > > + mutex_unlock(&cpu_idle_cooling_lock); > > + return ret; > > +} > > + > > +void cpu_idle_cooling_unregister(struct thermal_cooling_device *cdev) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + if (IS_ERR_OR_NULL(cdev)) > > + return; > > + > > + idle_cooling_dev = cdev->devdata; > > + > > + mutex_lock(&cpu_idle_cooling_lock); > > + get_online_cpus(); > > + list_del(&idle_cooling_dev->node); > > + put_online_cpus(); > > + mutex_unlock(&cpu_idle_cooling_lock); > > + > > + device_remove_file(&cdev->device, &dev_attr_window_size); > > + device_remove_file(&cdev->device, &dev_attr_duration); > > + thermal_cooling_device_unregister(idle_cooling_dev->cooling_dev); > > + > > + stop_idle_thread(idle_cooling_dev); > > + free_cpumask_var(idle_cooling_dev->injected_cpus); > > + free_cpumask_var(idle_cooling_dev->related_cpus); > > + kfree(idle_cooling_dev); > > +} > > + > > +static void __cpu_idle_cooling_exit(void) > > +{ > > + struct cpu_idle_cooling_device *idle_cooling_dev; > > + > > + while (!list_empty(&cpu_idle_cooling_dev_list)) { > > + idle_cooling_dev = list_first_entry(&cpu_idle_cooling_dev_list, > > + struct cpu_idle_cooling_device, node); > > + cpu_idle_cooling_unregister(idle_cooling_dev->cooling_dev); > > + } > > + > > + if (hp_state > 0) > > + cpuhp_remove_state_nocalls(hp_state); > > +} > > + > > +static int __init cpu_idle_cooling_init(void) > > +{ > > + struct thermal_cooling_device *ret; > > + cpumask_t rest_cpu_mask = CPU_MASK_ALL; > > + const struct cpumask *register_cpu_mask; > > + > > + hp_state = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, > > + "thermal/cpu_idle_cooling:online", > > + idle_injection_cpu_online, > > + idle_injection_cpu_predown); > > + if (hp_state < 0) > > + return hp_state; > > + > > + do { > > + register_cpu_mask = > > + topology_core_cpumask(cpumask_first(&rest_cpu_mask)); > > + > > + if (cpumask_empty(register_cpu_mask)) > > + break; > > + > > + ret = cpu_idle_cooling_register(register_cpu_mask); > > + if (IS_ERR(ret)) { > > + __cpu_idle_cooling_exit(); > > + return -ENOMEM; > > + } > > + } while (cpumask_andnot(&rest_cpu_mask, > > + &rest_cpu_mask, > > + register_cpu_mask)); > > + > > + return 0; > > +} > > +module_init(cpu_idle_cooling_init); > > + > > +static void __exit cpu_idle_cooling_exit(void) > > +{ > > + __cpu_idle_cooling_exit(); > > +} > > +module_exit(cpu_idle_cooling_exit); > > + > > +MODULE_LICENSE("GPL v2"); > > +MODULE_AUTHOR("Tao Wang <kevin.wangtao@hisilicon.com>"); > > +MODULE_DESCRIPTION("CPU Idle Cooling Driver for ARM Platform"); > > -- > > 1.7.9.5 > > -- > viresh
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
| |