sched/power: improve share power algorithm
authorLukasz Luba <l.luba@partner.samsung.com>
Fri, 9 Nov 2018 14:15:22 +0000 (15:15 +0100)
committerLukasz Luba <l.luba@partner.samsung.com>
Fri, 17 May 2019 07:15:52 +0000 (09:15 +0200)
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
kernel/sched/power.h

index fded1ba78b5e2fb86978ac9aee31a3fd0c7301c2..b0e103695fa27440074f9d523c718b50499315b8 100644 (file)
@@ -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++;
        }
 
index c7e92295cab19d5cb2fde8e60b5f8294f82ee64f..1a234a3ef92470c7eb2055ce00befb290cc544dd 100644 (file)
@@ -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 {