Simple implementation of throttling the CPUs.
Signed-off-by: Lukasz Luba l.luba@partner.samsung.com --- kernel/sched/power.c | 136 +++++++++++++++++++++++++++++++++++++------ kernel/sched/power.h | 3 + 2 files changed, 120 insertions(+), 19 deletions(-)
diff --git a/kernel/sched/power.c b/kernel/sched/power.c index 3ae497d2ba34..f74c59e83d4f 100644 --- a/kernel/sched/power.c +++ b/kernel/sched/power.c @@ -480,6 +480,7 @@ struct trip_ctrl_alg { struct _thermal_zone { struct thermal_zone_device *tz; bool single_cooling_dev; + int num_cooling; struct list_head node; struct list_head cooling_list; struct trip_ctrl_alg trip_ctrl_alg; @@ -499,6 +500,7 @@ struct _cooling_dev { struct _cooling_instance { struct list_head node; struct _cooling_dev *cooling; + u32 weight; };
static LIST_HEAD(tz_list); @@ -526,14 +528,15 @@ static u64 estimate_total_min_power(struct thermal_zone_device *tz, int trip) return total_min_power; }
-static u32 calc_power_budget(struct _thermal_zone *zone, int control_temp) +static u32 calc_power_budget(struct _thermal_zone *zone) { s64 temp_diff; + int desired_temp = zone->trip_ctrl_alg.desired_temp; s64 power_budget; struct thermal_zone_device *tz = zone->tz;
/* temperature is represented in milidegress */ - temp_diff = control_temp - tz->temperature; + temp_diff = desired_temp - tz->temperature;
power_budget = temp_diff;
@@ -576,14 +579,111 @@ static int throttle_single_cdev(struct _thermal_zone *zone) return 0; }
+static int cooling_dev_set_state(struct _thermal_zone *zone, + struct _cooling_dev *cooling, + unsigned long target) +{ + struct thermal_cooling_device *cdev = cooling->cdev; + unsigned long curr_state; + + cdev->ops->get_cur_state(cdev, &curr_state); + + if (curr_state == target) + return 0; + + /* check if we can it go with higher freq for zone with a few devices*/ + if (!zone->single_cooling_dev && cooling->max_single_state > target) + return -EINVAL; + + if (zone->single_cooling_dev && cooling->max_single_state > target) + cooling->max_single_state = target; + + mutex_lock(&cdev->lock); + if (!cdev->ops->set_cur_state(cdev, target)) + thermal_cooling_device_stats_update(cdev, target); + + cdev->updated = true; + mutex_unlock(&cdev->lock); + + return 0; +} + +static int set_power(struct _cooling_instance *inst, struct _thermal_zone *zone, + u32 power) +{ + int ret; + unsigned long state; + struct thermal_cooling_device *cdev; + struct thermal_zone_device *tz = zone->tz; + + cdev = inst->cooling->cdev; + ret = cdev->ops->power2state(cdev, tz, power, &state); + + pr_info("set_power=%u, state=%lu, temp=%d\n", power, state, + tz->temperature); + if (!ret) { + ret = cooling_dev_set_state(zone, inst->cooling, state); + } + + return ret; +} + +static int get_requested_power(struct _cooling_instance *inst, + struct _thermal_zone *zone, u32 *power) +{ + struct thermal_cooling_device *cdev; + struct thermal_zone_device *tz = zone->tz; + + cdev = inst->cooling->cdev; + return cdev->ops->get_requested_power(cdev, tz, power); +} + +static int share_power_budget(struct _thermal_zone *zone, u32 power_budget) +{ + struct _cooling_instance *inst; + u32 *power; + u64 sum_power = 0; + int i = 0; + + power = kzalloc(sizeof(u32) * zone->num_cooling, GFP_KERNEL); + if (!power) + return -ENOMEM; + + /* estimate cooling dev's power and total power */ + list_for_each_entry(inst, &zone->cooling_list, node) { + get_requested_power(inst, zone, &power[i]); + + power[i] = (inst->weight * power[i]) >> 10; + sum_power += power[i]; + i++; + } + + if (sum_power <= 0) + goto cleanup; + + /* split power budget to cooling devices */ + i = 0; + list_for_each_entry(inst, &zone->cooling_list, node) { + power[i] = (power[i] * power_budget) / sum_power; + i++; + } + + /* set the new state for cooling device based on its granted power */ + i = 0; + list_for_each_entry(inst, &zone->cooling_list, node) { + set_power(inst, zone, power[i]); + i++; + } + +cleanup: + kfree(power); + + return 0; +}
static int sched_power_gov_throttle(struct thermal_zone_device *tz, int trip) { - struct thermal_cooling_device *cdev = NULL; - struct thermal_instance *inst = NULL; - u32 dev_power; u32 power_budget; - int control_temp = 0; struct _thermal_zone *zone; int ret;
@@ -591,19 +691,16 @@ static int sched_power_gov_throttle(struct thermal_zone_device *tz, int trip) if (!zone) return -EINVAL;
+ if (zone->trip_ctrl_alg.desired_id < 0) + return -EINVAL;
- if (zone->single_cooling_dev) { - power_budget = calc_power_budget(zone, control_temp); - /* only deadline tasks can ask for over-speed with idlers */ - ret = inject_more_idle(zone, power_budget); - if (ret) { - throttle_single_cdev(zone); - power_actor_set_power(cdev, inst, dev_power); - } - } else { + /* skip calls from other trip points */ + if (trip != zone->trip_ctrl_alg.desired_id) + return 0;
- } + power_budget = calc_power_budget(zone);
+ ret = share_power_budget(zone, power_budget);
return 0; } @@ -784,7 +881,9 @@ static int sched_power_gov_bind(struct thermal_zone_device *tz) goto cleanup;
_inst->cooling = cooling; + _inst->weight = DEFAULT_WEIGHT; list_add(&_inst->node, &zone->cooling_list); + zone->num_cooling++;
pr_info("pinned cooling device into zone\n"); } @@ -795,15 +894,14 @@ static int sched_power_gov_bind(struct thermal_zone_device *tz)
ret = sched_power_setup_trips(zone); if (ret < 0) { - pr_info("lack of temp settings in DT allowing to control \ - the algorithm\n"); + pr_warn("lack of temp settings needed in control algorithm\n"); }
mutex_lock(&tz_list_lock); list_add(&zone->node, &tz_list); mutex_unlock(&tz_list_lock);
- return 0; + return ret;
cleanup: if (prev_cooling) { diff --git a/kernel/sched/power.h b/kernel/sched/power.h index 7fafaa9f6609..77516a6f5809 100644 --- a/kernel/sched/power.h +++ b/kernel/sched/power.h @@ -17,6 +17,9 @@ // void (*func)(struct update_sched_power *, int, unsigned int, int); // };
+#define MAX_WEIGHT 1024 +#define DEFAULT_WEIGHT MAX_WEIGHT + struct power_budget { s64 temp; s64 temp_limit;