sched/power: add recalc power budget and trace events
authorLukasz Luba <l.luba@partner.samsung.com>
Wed, 14 Nov 2018 15:27:17 +0000 (16:27 +0100)
committerLukasz Luba <l.luba@partner.samsung.com>
Fri, 17 May 2019 07:15:54 +0000 (09:15 +0200)
When sharing power budget fails, try to recalculate and share again.

Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
include/trace/events/sched_power.h [new file with mode: 0644]
kernel/sched/power.c
kernel/sched/power.h

diff --git a/include/trace/events/sched_power.h b/include/trace/events/sched_power.h
new file mode 100644 (file)
index 0000000..ab29da8
--- /dev/null
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM sched_power
+
+#if !defined(_TRACE_SCHED_POWER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SCHED_POWER_H
+
+#include <linux/tracepoint.h>
+
+
+TRACE_EVENT(sched_power_set_power,
+       TP_PROTO(int id, int cid, u32 weight, u32 power, unsigned long target,
+                unsigned long state, int ret),
+       TP_ARGS(id, cid, weight, power, target, state, ret),
+       TP_STRUCT__entry(
+               __field(int, id       )
+               __field(int, cid      )
+               __field(u32, weight      )
+               __field(u32, power      )
+               __field(unsigned long, target      )
+               __field(unsigned long, state      )
+               __field(int, ret      )
+       ),
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->cid = cid;
+               __entry->weight = weight;
+               __entry->power = power;
+               __entry->target = target;
+               __entry->state = state;
+               __entry->ret = ret;
+       ),
+
+       TP_printk("zone_id=%d cool_id=%d weight=%u power=%u target=%lu state=%lu ret=%d",
+                 __entry->id, __entry->cid, __entry->weight,
+                 __entry->power, __entry->target, __entry->state, __entry->ret)
+);
+
+TRACE_EVENT(sched_power_calc_budget,
+       TP_PROTO(int id, int temp, s64 power),
+       TP_ARGS(id, temp, power),
+       TP_STRUCT__entry(
+               __field(int, id)
+               __field(int, temp)
+               __field(s32, power)
+       ),
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->temp = temp;
+               __entry->power = power;
+       ),
+
+       TP_printk("zone_id=%d temp=%d power=%d",
+                 __entry->id, __entry->temp, __entry->power)
+);
+
+#endif /* _TRACE_SCHED_POWER_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 923f689860d1d08652ab310df52b05831fa79c29..daee84b00b3705a245443d3fc899392945072b2f 100644 (file)
@@ -12,6 +12,9 @@
 #include <linux/thermal_core.h>
 #include <linux/idle_inject.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/sched_power.h>
+
 #include "power.h"
 
 #define THERMAL_REQUEST_KFIFO_SIZE     (64 * sizeof(struct power_request))
@@ -598,6 +601,9 @@ static u32 calc_power_budget(struct _thermal_zone *zone)
        decay = zone->trip_ctrl_alg.integral >> 3;
        zone->trip_ctrl_alg.integral -= decay;
 
+       trace_sched_power_calc_budget(zone->tz->id, tz->temperature,
+                                     power_budget);
+
        return power_budget;
 }
 
@@ -680,10 +686,10 @@ static int cooling_dev_set_state(struct _thermal_zone *zone,
 }
 
 static int _set_power(struct _cooling_instance *inst, struct _thermal_zone *zone,
-                    u32 power)
+                    u32 power, unsigned long *state)
 {
        int ret;
-       unsigned long state = 0, target;
+       unsigned long target;
        struct thermal_cooling_device *cdev;
        struct thermal_zone_device *tz = zone->tz;
 
@@ -691,14 +697,11 @@ static int _set_power(struct _cooling_instance *inst, struct _thermal_zone *zone
        ret = cdev->ops->power2state(cdev, tz, power, &target);
        if (!ret) {
                ret = cooling_dev_set_state(zone, inst->cooling, target,
-                                           &state);
-               if (!ret)
-                       pr_info("inst_weight=%u, set_power=%u, target=%lu, state=%lu, temp=%d\n",
-                               inst->weight, power, target, state,
-                               tz->temperature);
-               else
-                       pr_info("_set_power: ret=%d\n, target=%lu, state=%lu\n",
-                               ret, target, state);
+                                           state);
+
+               trace_sched_power_set_power(zone->tz->id, cdev->id,
+                                           inst->weight, power, target, *state,
+                                           ret);
        }
 
        return ret;
@@ -724,6 +727,17 @@ static int _get_max_power(struct _cooling_instance *inst,
        return cdev->ops->state2power(cdev, tz, MAX_POWER_STATE_ID, power);
 }
 
+static int _get_power_for_state(struct _cooling_instance *inst,
+                               struct _thermal_zone *zone, u32 *power,
+                               unsigned long state)
+{
+       struct thermal_cooling_device *cdev;
+       struct thermal_zone_device *tz = zone->tz;
+
+       cdev = inst->cooling->cdev;
+       return cdev->ops->state2power(cdev, tz, state, power);
+}
+
 static int share_power_budget(struct _thermal_zone *zone, u32 power_budget)
 {
        struct _cooling_instance *inst;
@@ -735,18 +749,24 @@ static int share_power_budget(struct _thermal_zone *zone, u32 power_budget)
        u64 left_power_budget = power_budget;
        int i = 0;
        int ret;
+       unsigned long state = 0;
+       int rebalance = 0;
 
        power = kzalloc(sizeof(struct power_info) * zone->num_cooling,
                        GFP_KERNEL);
        if (!power)
                return -ENOMEM;
 
+try_balance:
+       if (rebalance > MAX_REBALANCE_TRIES_POWER_BUDGET)
+               goto balanced;
        /* 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) {
                p = &power[i];
                ret = _get_requested_power(inst, zone, &p->requested);
-               ret = _get_max_power(inst, zone, &p->max_possible);
+               if (!rebalance)
+                       ret = _get_max_power(inst, zone, &p->max_possible);
                /* pr_info("req=%u\n", p->requested); */
                i++;
        }
@@ -807,16 +827,23 @@ static int share_power_budget(struct _thermal_zone *zone, u32 power_budget)
        i = 0;
        list_for_each_entry(inst, &zone->cooling_list, node) {
                p = &power[i];
-               ret = _set_power(inst, zone, p->requested);
+               ret = _set_power(inst, zone, p->requested, &state);
                if (ret) {
                        /* TODO: collect power which cannot be used for other
                         * devices */
+                       rebalance++;
                        extra_power += p->requested;
                        p->requested = 0;
+                       ret = _get_power_for_state(inst, zone, &p->max_possible,
+                                                  state);
                }
                i++;
        }
 
+       if (rebalance)
+               goto try_balance;
+
+balanced:
        kfree(power);
 
        return 0;
index 1a234a3ef92470c7eb2055ce00befb290cc544dd..3f30dc2b9374b9a8375301af4bac44face040c12 100644 (file)
@@ -24,6 +24,7 @@
 /* MAX_POWER_STATE_ID for current implementation in thermal framework
  * 'freq table' (DESC ordered) */
 #define MAX_POWER_STATE_ID 0
+#define MAX_REBALANCE_TRIES_POWER_BUDGET 100
 
 struct power_budget {
        s64 temp;