From 39dc9f09a97fec0c346babc22d381e501e732da4 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Tue, 6 Nov 2018 17:47:54 +0100 Subject: [PATCH] sched/power: add thermal governor cooling devices and zones The patch populated cooling devices for clusters and CPUs. Signed-off-by: Lukasz Luba --- kernel/sched/power.c | 158 +++++++++++++++++++++++++++++++++++++++++++ kernel/sched/power.h | 4 ++ 2 files changed, 162 insertions(+) diff --git a/kernel/sched/power.c b/kernel/sched/power.c index 1976e5db8efc..22d5758b89ef 100644 --- a/kernel/sched/power.c +++ b/kernel/sched/power.c @@ -5,6 +5,7 @@ * Copyright (C) 2018 Samsung */ +#define pr_fmt(fmt) "SCHED_POWER: " fmt #include #include @@ -480,6 +481,28 @@ fs_initcall(sched_power_init); /////////////////thermal governor//////////////////////// +struct _thermal_zone { + struct thermal_zone_device *tz; + bool single_cooling_dev; + struct list_head node; +}; + +struct _cooling_dev { + struct thermal_cooling_device *cdev; + struct list_head node; + int max_single_state; /* max state (0-highest) which means freqency */ + int min_sum_single_idle; /* minimum sum of idle calculated for 'single' + zone. Other zones should not go bellow this + value for the same 'state' (frequency) */ + bool cpu_dev; + unsigned long int cpus[0]; +}; + +static LIST_HEAD(tz_list); +static LIST_HEAD(cdev_list); +static DEFINE_MUTEX(tz_list_lock); +static DEFINE_MUTEX(cdev_list_lock); + static int cdev_get_min_power(struct thermal_cooling_device *cdev, u64 *min_power) { @@ -521,22 +544,157 @@ static int controller_prepare_coefficents(struct thermal_zone_device *tz, return 0; } +static struct _thermal_zone *find_zone(struct thermal_zone_device *tz) +{ + struct _thermal_zone *zone; + + mutex_lock(&tz_list_lock); + list_for_each_entry(zone, &tz_list, node) { + if (zone->tz == tz) { + mutex_unlock(&tz_list_lock); + return zone; + } + } + mutex_unlock(&tz_list_lock); + + return NULL; +} +static int inject_more_idle(struct _thermal_zone *zone) +{ + + return 0; +} + +static int throttle_single_cdev(struct _thermal_zone *zone) +{ + + + return 0; +} + static int sched_power_gov_throttle(struct thermal_zone_device *tz, int trip) { + struct thermal_cooling_device *cdev; + struct thermal_instance *inst; + u32 dev_power; + struct _thermal_zone *zone; + int ret; + + zone = find_zone(tz); + if (!zone) + return -EINVAL; + + if (zone->single_cooling_dev) { + /* ret = calc_power_budget(tz); */ + ret = inject_more_idle(zone); + if (ret) { + throttle_single_cdev(zone); + power_actor_set_power(cdev, inst, dev_power); + } + } else { + + } + return 0; } +static inline bool is_cpufreq_cooling(struct thermal_cooling_device *cdev) +{ + const char dev_name[] = "thermal-cpufreq-"; + + return strncmp(dev_name, cdev->type, strlen(dev_name)) ? + false : true; +} + +static void cleanup_percpu_cooling_dev(struct _cooling_dev *cooling) +{ + int i; + struct cpumask *cpumask; + struct cpu_power *cpower; + + cpumask = to_cpumask(cooling->cpus); + for_each_cpu(i, cpumask) { + cpower = (&per_cpu(cpu_power, i)); + raw_spin_lock(&cpower->update_lock); + cpower->cooling = NULL; + raw_spin_unlock(&cpower->update_lock); + } + kfree(cooling); +} + static int sched_power_gov_bind(struct thermal_zone_device *tz) { struct thermal_instance *inst; + struct _thermal_zone *zone; + struct _cooling_dev *cooling, *prev_cooling; + struct thermal_cooling_device *cdev; + int i = 0; + int cpu; + int cpus_size; + struct cpumask *cpumask; + struct cpu_power *cpower; + + zone = kzalloc(sizeof(*zone), GFP_KERNEL); + if (!zone) + return -ENOMEM; + mutex_lock(&cdev_list_lock); list_for_each_entry(inst, &tz->thermal_instances, tz_node) { + cdev = inst->cdev; + if (is_cpufreq_cooling(cdev)) + cpus_size = cpumask_size(); + else + cpus_size = 0; + + cooling = kzalloc(sizeof(*cooling) + cpus_size, GFP_KERNEL); + if (!cooling) + goto cleanup; + + cooling->cpu_dev = !!cpus_size; + cooling->cdev = cdev; + if (cooling->cpu_dev) { + cpumask = to_cpumask(cooling->cpus); + + cpufreq_cooling_copy_cpumask(cdev, cpumask); + + for_each_cpu(cpu, cpumask) { + cpower = (&per_cpu(cpu_power, cpu)); + raw_spin_lock(&cpower->update_lock); + cpower->cooling = cooling; + raw_spin_unlock(&cpower->update_lock); + } + } + list_add(&cooling->node, &cdev_list); + pr_info("registred new cooling device for CPUs\n"); + prev_cooling = cooling; + i++; } + mutex_unlock(&cdev_list_lock); + + zone->single_cooling_dev = (i == 1 ? true : false); + zone->tz = tz; + + mutex_lock(&tz_list_lock); + list_add(&zone->node, &tz_list); + mutex_unlock(&tz_list_lock); return 0; + +cleanup: + list_for_each_entry_reverse(cooling, &prev_cooling->node, node) { + if (i-- == 0) + break; + list_del(&prev_cooling->node); + cleanup_percpu_cooling_dev(prev_cooling); + prev_cooling = cooling; + } + mutex_unlock(&cdev_list_lock); + kfree(zone); + + return -ENOMEM; } static void sched_power_gov_unbind(struct thermal_zone_device *tz) diff --git a/kernel/sched/power.h b/kernel/sched/power.h index 8ce1409ea538..da969b9bc30f 100644 --- a/kernel/sched/power.h +++ b/kernel/sched/power.h @@ -9,6 +9,7 @@ #define __SCHED_POWER_H__ #include +#include #include "sched.h" @@ -38,6 +39,8 @@ struct power_request { int flags }; +struct _cooling_dev; + struct cpu_power { struct update_sched_power update_power; unsigned int max_capacity; @@ -51,6 +54,7 @@ struct cpu_power { struct sched_power *sched_power; struct power_request req; bool operating; + struct _cooling_dev *cooling; /* lock shared with thermal framework and/or cpufreq */ raw_spinlock_t update_lock; struct idle_inject_device *ii_dev; -- 2.34.1