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 | 60 ++++++++++++++++++++++++++++++ kernel/sched/power.c | 51 +++++++++++++++++++------ kernel/sched/power.h | 1 + 3 files changed, 100 insertions(+), 12 deletions(-) create mode 100644 include/trace/events/sched_power.h
diff --git a/include/trace/events/sched_power.h b/include/trace/events/sched_power.h new file mode 100644 index 000000000000..ab29da831335 --- /dev/null +++ b/include/trace/events/sched_power.h @@ -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> diff --git a/kernel/sched/power.c b/kernel/sched/power.c index 923f689860d1..daee84b00b37 100644 --- a/kernel/sched/power.c +++ b/kernel/sched/power.c @@ -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; diff --git a/kernel/sched/power.h b/kernel/sched/power.h index 1a234a3ef924..3f30dc2b9374 100644 --- a/kernel/sched/power.h +++ b/kernel/sched/power.h @@ -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;