sched/power: allocate power for cooling devices
authorLukasz Luba <l.luba@partner.samsung.com>
Thu, 8 Nov 2018 16:04:30 +0000 (17:04 +0100)
committerLukasz Luba <l.luba@partner.samsung.com>
Fri, 17 May 2019 07:15:48 +0000 (09:15 +0200)
Simple implementation of throttling the CPUs.

Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
kernel/sched/power.c
kernel/sched/power.h

index 3ae497d2ba34bb11bf7385e3330c27993f9a6952..f74c59e83d4f0a73f8497b5db4ab5ee370b18b0f 100644 (file)
@@ -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 @@ handle_cooling_instance:
                        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 @@ handle_cooling_instance:
 
        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) {
index 7fafaa9f6609f3d6db7977179afe939d05693c79..77516a6f5809c64545565da70c2181e0c77a11dc 100644 (file)
@@ -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;