Improve the algorithm of sharing the power budget to cooling devices.
Signed-off-by: Lukasz Luba l.luba@partner.samsung.com --- kernel/sched/power.c | 69 +++++++++++++++++++++++++++++++++++++------- kernel/sched/power.h | 8 +++++ 2 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/kernel/sched/power.c b/kernel/sched/power.c index fded1ba78b5e..b0e103695fa2 100644 --- a/kernel/sched/power.c +++ b/kernel/sched/power.c @@ -665,7 +665,7 @@ static int cooling_dev_set_state(struct _thermal_zone *zone, return 0; }
-static int set_power(struct _cooling_instance *inst, struct _thermal_zone *zone, +static int _set_power(struct _cooling_instance *inst, struct _thermal_zone *zone, u32 power) { int ret; @@ -685,7 +685,7 @@ static int set_power(struct _cooling_instance *inst, struct _thermal_zone *zone, return ret; }
-static int get_requested_power(struct _cooling_instance *inst, +static int _get_requested_power(struct _cooling_instance *inst, struct _thermal_zone *zone, u32 *power) { struct thermal_cooling_device *cdev; @@ -695,21 +695,40 @@ static int get_requested_power(struct _cooling_instance *inst, return cdev->ops->get_requested_power(cdev, tz, power); }
+static int _get_max_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->state2power(cdev, tz, MAX_POWER_STATE_ID, power); +} + static int share_power_budget(struct _thermal_zone *zone, u32 power_budget) { struct _cooling_instance *inst; - u32 *power; + struct power_info *power, *p; u64 sum_power = 0; + u64 extra_power = 0; + u64 wish_power = 0; + u64 total_weight = 0; + u64 left_power_budget = power_budget; int i = 0; + int ret;
- power = kzalloc(sizeof(u32) * zone->num_cooling, GFP_KERNEL); + power = kzalloc(sizeof(struct power_info) * zone->num_cooling, + GFP_KERNEL); if (!power) return -ENOMEM;
/* The calculation two-loops split is needed due to taking zone->lock only for protecting 'weights'. */ list_for_each_entry(inst, &zone->cooling_list, node) { - get_requested_power(inst, zone, &power[i]); + p = &power[i]; + ret = _get_requested_power(inst, zone, &p->requested); + ret = _get_max_power(inst, zone, &p->max_possible); + /* pr_info("req=%u\n", p->requested); */ i++; }
@@ -718,8 +737,11 @@ static int share_power_budget(struct _thermal_zone *zone, u32 power_budget) /* estimate cooling dev's power and total power */ i = 0; list_for_each_entry(inst, &zone->cooling_list, node) { - power[i] = (inst->weight * power[i]) >> 10; - sum_power += power[i]; + p = &power[i]; + p->requested = (inst->weight * p->requested) >> 10; + sum_power += p->requested; + total_weight += inst->weight; + /* pr_info("req=%u\n", p->requested); */ i++; } mutex_unlock(&zone->lock); @@ -730,16 +752,43 @@ static int share_power_budget(struct _thermal_zone *zone, u32 power_budget) /* 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; + p = &power[i]; + p->requested = (p->requested * power_budget) / sum_power; + /* pr_info("req=%u max=%u\n", p->requested, p->max_possible); */ + if (p->requested > p->max_possible) { + extra_power += p->requested - p->max_possible; + p->requested = p->max_possible; + } + wish_power += p->max_possible - p->requested; + left_power_budget -= p->requested; i++; }
- /* clamp max possible power for devices and re-share the rest */ + /* re-share the rest extra power to devices according to their wish and + * weight*/ + if ((extra_power || left_power_budget) && wish_power) { + u32 headroom; + extra_power = max(left_power_budget, extra_power); + extra_power = min(wish_power, extra_power); + + i = 0; + list_for_each_entry(inst, &zone->cooling_list, node) { + p = &power[i]; + headroom = p->max_possible - p->requested; + /* headroom *= inst->weight; */ + /* headroom /= total_weight; */ + p->requested += (headroom * extra_power) / wish_power; + /* pr_info("req=%u\n", p->requested); */ + 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]); + p = &power[i]; + _set_power(inst, zone, p->requested); i++; }
diff --git a/kernel/sched/power.h b/kernel/sched/power.h index c7e92295cab1..1a234a3ef924 100644 --- a/kernel/sched/power.h +++ b/kernel/sched/power.h @@ -21,6 +21,9 @@ #define DEFAULT_WEIGHT MAX_WEIGHT #define DEFAULT_K_P 500 #define DEFAULT_K_I 50 +/* MAX_POWER_STATE_ID for current implementation in thermal framework + * 'freq table' (DESC ordered) */ +#define MAX_POWER_STATE_ID 0
struct power_budget { s64 temp; @@ -44,6 +47,11 @@ struct power_request { int flags; };
+struct power_info { + u32 max_possible; + u32 requested; +}; + struct _cooling_dev;
struct cpu_power {