mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
The current behavior of the Step-wise thermal governor is to increase the cooling level one step at a time after trip point threshold passing by thermal zone temperature until the temperature stops to rise. Then, nothing is done until the temperature decreases below the (possibly updated) trip point threshold, at which point the cooling level is reduced straight to the applicable minimum. While this generally works, it is not in agreement with the throttling logic description comment in step_wise_manage() any more after some relatively recent changes, and in the case of passive cooling, it may lead to undesirable performance oscillations between high and low levels. For this reason, modify the governor's cooling device state selection function, get_target_state(), to reduce cooling by one level even if the temperature is still above the thermal zone threshold, but the temperature has started to fall down. However, ensure that the cooling level will remain above the applicable minimum in that case to pull the zone temperature further down, possibly until it falls below the trip threshold (which may now be equal to the low temperature of the trip). Doing so should help higher performance to be restored earlier in some cases which is desirable especially for passive trip points with relatively high hysteresis values. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Lukasz Luba <lukasz.luba@arm.com> Link: https://patch.msgid.link/1947735.tdWV9SEqCh@rafael.j.wysocki [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
153 lines
4.5 KiB
C
153 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* step_wise.c - A step-by-step Thermal throttling governor
|
|
*
|
|
* Copyright (C) 2012 Intel Corp
|
|
* Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*/
|
|
|
|
#include <linux/thermal.h>
|
|
#include <linux/minmax.h>
|
|
#include "thermal_trace.h"
|
|
|
|
#include "thermal_core.h"
|
|
|
|
/*
|
|
* If the temperature is higher than a trip point,
|
|
* a. if the trend is THERMAL_TREND_RAISING, use higher cooling
|
|
* state for this trip point
|
|
* b. if the trend is THERMAL_TREND_DROPPING, use a lower cooling state
|
|
* for this trip point, but keep the cooling state above the applicable
|
|
* minimum
|
|
* If the temperature is lower than a trip point,
|
|
* a. if the trend is THERMAL_TREND_RAISING, do nothing
|
|
* b. if the trend is THERMAL_TREND_DROPPING, use the minimum applicable
|
|
* cooling state for this trip point, or if the cooling state already
|
|
* equals lower limit, deactivate the thermal instance
|
|
*/
|
|
static unsigned long get_target_state(struct thermal_instance *instance,
|
|
enum thermal_trend trend, bool throttle)
|
|
{
|
|
struct thermal_cooling_device *cdev = instance->cdev;
|
|
unsigned long cur_state;
|
|
|
|
/*
|
|
* We keep this instance the way it is by default.
|
|
* Otherwise, we use the current state of the
|
|
* cdev in use to determine the next_target.
|
|
*/
|
|
cdev->ops->get_cur_state(cdev, &cur_state);
|
|
dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
|
|
|
|
if (!instance->initialized) {
|
|
if (throttle)
|
|
return clamp(cur_state + 1, instance->lower, instance->upper);
|
|
|
|
return THERMAL_NO_TARGET;
|
|
}
|
|
|
|
if (throttle) {
|
|
if (trend == THERMAL_TREND_RAISING)
|
|
return clamp(cur_state + 1, instance->lower, instance->upper);
|
|
|
|
/*
|
|
* If the zone temperature is falling, the cooling level can
|
|
* be reduced, but it should still be above the lower state of
|
|
* the given thermal instance to pull the temperature further
|
|
* down.
|
|
*/
|
|
if (trend == THERMAL_TREND_DROPPING)
|
|
return clamp(cur_state - 1,
|
|
min(instance->lower + 1, instance->upper),
|
|
instance->upper);
|
|
} else if (trend == THERMAL_TREND_DROPPING) {
|
|
if (cur_state <= instance->lower)
|
|
return THERMAL_NO_TARGET;
|
|
|
|
/*
|
|
* If 'throttle' is false, no mitigation is necessary, so
|
|
* request the lower state for this instance.
|
|
*/
|
|
return instance->lower;
|
|
}
|
|
|
|
return instance->target;
|
|
}
|
|
|
|
static void thermal_zone_trip_update(struct thermal_zone_device *tz,
|
|
const struct thermal_trip_desc *td,
|
|
int trip_threshold)
|
|
{
|
|
bool throttle = tz->temperature >= trip_threshold;
|
|
const struct thermal_trip *trip = &td->trip;
|
|
enum thermal_trend trend = get_tz_trend(tz, trip);
|
|
int trip_id = thermal_zone_trip_id(tz, trip);
|
|
struct thermal_instance *instance;
|
|
|
|
if (throttle)
|
|
trace_thermal_zone_trip(tz, trip_id, trip->type);
|
|
|
|
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
|
|
trip_id, trip->type, trip_threshold, trend, throttle);
|
|
|
|
list_for_each_entry(instance, &td->thermal_instances, trip_node) {
|
|
int old_target;
|
|
|
|
old_target = instance->target;
|
|
instance->target = get_target_state(instance, trend, throttle);
|
|
|
|
dev_dbg(&instance->cdev->device, "old_target=%d, target=%ld\n",
|
|
old_target, instance->target);
|
|
|
|
if (instance->initialized && old_target == instance->target)
|
|
continue;
|
|
|
|
instance->initialized = true;
|
|
|
|
scoped_guard(cooling_dev, instance->cdev) {
|
|
instance->cdev->updated = false; /* cdev needs update */
|
|
}
|
|
}
|
|
}
|
|
|
|
static void step_wise_manage(struct thermal_zone_device *tz)
|
|
{
|
|
const struct thermal_trip_desc *td;
|
|
struct thermal_instance *instance;
|
|
|
|
lockdep_assert_held(&tz->lock);
|
|
|
|
/*
|
|
* Throttling Logic: Use the trend of the thermal zone to throttle.
|
|
* If the thermal zone is 'heating up', throttle all of the cooling
|
|
* devices associated with each trip point by one step. If the zone
|
|
* is 'cooling down', it brings back the performance of the devices
|
|
* by one step.
|
|
*/
|
|
for_each_trip_desc(tz, td) {
|
|
const struct thermal_trip *trip = &td->trip;
|
|
|
|
if (trip->temperature == THERMAL_TEMP_INVALID ||
|
|
trip->type == THERMAL_TRIP_CRITICAL ||
|
|
trip->type == THERMAL_TRIP_HOT)
|
|
continue;
|
|
|
|
thermal_zone_trip_update(tz, td, td->threshold);
|
|
}
|
|
|
|
for_each_trip_desc(tz, td) {
|
|
list_for_each_entry(instance, &td->thermal_instances, trip_node)
|
|
thermal_cdev_update(instance->cdev);
|
|
}
|
|
}
|
|
|
|
static struct thermal_governor thermal_gov_step_wise = {
|
|
.name = "step_wise",
|
|
.manage = step_wise_manage,
|
|
};
|
|
THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
|