Commit cd7fa3e1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull thermal control updates from Rafael Wysocki:
 "These are thermal core changes, including the addition of support for
  temperature thresholds that can be set from user space, fixes related
  to thermal zone initialization, suspend/resume and exit, locking
  rework and rearrangement of the code handling thermal zone temperature
  updates.

  Specifics:

   - Add support for thermal thresholds that can be added and removed
     from user space via netlink along with a related library update
     (Daniel Lezcano)

   - Fix thermal zone initialization, suspend/resume and exit
     synchronization issues (Rafael Wysocki)

   - Rearrange locking in the thermal core to use guards (Rafael
     Wysocki)

   - Make the code handling thermal zone temperature updates use sorted
     lists of trip points to reduce the number of trip points table
     walks in the thermal core (Rafael Wysocki)

   - Fix and clean up the thermal testing facility code (Rafael Wysocki)

   - Fix a Power Allocator thermal governor issue (ZhengShaobo)"

* tag 'thermal-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (45 commits)
  thermal: testing: Initialize some variables annoteded with _free()
  thermal: testing: Use DEFINE_FREE() and __free() to simplify code
  thermal: testing: Simplify tt_get_tt_zone()
  thermal: gov_power_allocator: Granted power set to max when nobody request power
  thermal: core: Relocate thermal zone initialization routine
  thermal: core: Use trip lists for trip crossing detection
  thermal: core: Eliminate thermal_zone_trip_down()
  thermal: core: Relocate functions that update trip points
  thermal: core: Move some trip processing to thermal_trip_crossed()
  thermal: core: Pass trip descriptor to thermal_trip_crossed()
  thermal: core: Rearrange __thermal_zone_device_update()
  thermal: core: Prepare for moving trips between sorted lists
  thermal: core: Rename trip list node in struct thermal_trip_desc
  thermal: core: Build sorted lists instead of sorting them later
  thermal/lib: Fix memory leak on error in thermal_genl_auto()
  thermal: thresholds: Fix thermal lock annotation issue
  tools/thermal/thermal-engine: Take into account the thresholds API
  tools/lib/thermal: Add the threshold netlink ABI
  tools/lib/thermal: Make more generic the command encoding function
  thermal: netlink: Add the commands and the events for the thresholds
  ...
parents ad52c55e 0104dcda
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src)
obj-$(CONFIG_THERMAL)		+= thermal_sys.o
thermal_sys-y			+= thermal_core.o thermal_sysfs.o
thermal_sys-y			+= thermal_trip.o thermal_helpers.o
thermal_sys-y			+= thermal_thresholds.o

# netlink interface to manage the thermal framework
thermal_sys-$(CONFIG_THERMAL_NETLINK)		+= thermal_netlink.o
+6 −9
Original line number Diff line number Diff line
@@ -30,9 +30,7 @@ static void bang_bang_set_instance_target(struct thermal_instance *instance,

	dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target);

	mutex_lock(&instance->cdev->lock);
	__thermal_cdev_update(instance->cdev);
	mutex_unlock(&instance->cdev->lock);
	thermal_cdev_update_nocheck(instance->cdev);
}

/**
@@ -67,6 +65,7 @@ static void bang_bang_control(struct thermal_zone_device *tz,
			      const struct thermal_trip *trip,
			      bool crossed_up)
{
	const struct thermal_trip_desc *td = trip_to_trip_desc(trip);
	struct thermal_instance *instance;

	lockdep_assert_held(&tz->lock);
@@ -75,11 +74,9 @@ static void bang_bang_control(struct thermal_zone_device *tz,
		thermal_zone_trip_id(tz, trip), trip->temperature,
		tz->temperature, trip->hysteresis);

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (instance->trip == trip)
	list_for_each_entry(instance, &td->thermal_instances, trip_node)
		bang_bang_set_instance_target(instance, crossed_up);
}
}

static void bang_bang_manage(struct thermal_zone_device *tz)
{
@@ -104,8 +101,8 @@ static void bang_bang_manage(struct thermal_zone_device *tz)
		 * to the thermal zone temperature and the trip point threshold.
		 */
		turn_on = tz->temperature >= td->threshold;
		list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
			if (!instance->initialized && instance->trip == trip)
		list_for_each_entry(instance, &td->thermal_instances, trip_node) {
			if (!instance->initialized)
				bang_bang_set_instance_target(instance, turn_on);
		}
	}
+6 −14
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
/**
 * fair_share_throttle - throttles devices associated with the given zone
 * @tz: thermal_zone_device
 * @trip: trip point
 * @td: trip point descriptor
 * @trip_level: number of trips crossed by the zone temperature
 *
 * Throttling Logic: This uses three parameters to calculate the new
@@ -61,29 +61,23 @@ static int get_trip_level(struct thermal_zone_device *tz)
 * new_state of cooling device = P3 * P2 * P1
 */
static void fair_share_throttle(struct thermal_zone_device *tz,
				const struct thermal_trip *trip,
				const struct thermal_trip_desc *td,
				int trip_level)
{
	struct thermal_instance *instance;
	int total_weight = 0;
	int nr_instances = 0;

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (instance->trip != trip)
			continue;

	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		total_weight += instance->weight;
		nr_instances++;
	}

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		struct thermal_cooling_device *cdev = instance->cdev;
		u64 dividend;
		u32 divisor;

		if (instance->trip != trip)
			continue;

		dividend = trip_level;
		dividend *= cdev->max_state;
		divisor = tz->num_trips;
@@ -95,9 +89,7 @@ static void fair_share_throttle(struct thermal_zone_device *tz,
		}
		instance->target = div_u64(dividend, divisor);

		mutex_lock(&cdev->lock);
		__thermal_cdev_update(cdev);
		mutex_unlock(&cdev->lock);
		thermal_cdev_update_nocheck(cdev);
	}
}

@@ -116,7 +108,7 @@ static void fair_share_manage(struct thermal_zone_device *tz)
		    trip->type == THERMAL_TRIP_HOT)
			continue;

		fair_share_throttle(tz, trip, trip_level);
		fair_share_throttle(tz, td, trip_level);
	}
}

+46 −40
Original line number Diff line number Diff line
@@ -97,11 +97,9 @@ struct power_allocator_params {
	struct power_actor *power;
};

static bool power_actor_is_valid(struct power_allocator_params *params,
				 struct thermal_instance *instance)
static bool power_actor_is_valid(struct thermal_instance *instance)
{
	return (instance->trip == params->trip_max &&
		 cdev_is_power_actor(instance->cdev));
	return cdev_is_power_actor(instance->cdev);
}

/**
@@ -118,13 +116,14 @@ static bool power_actor_is_valid(struct power_allocator_params *params,
static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
{
	struct power_allocator_params *params = tz->governor_data;
	const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
	struct thermal_cooling_device *cdev;
	struct thermal_instance *instance;
	u32 sustainable_power = 0;
	u32 min_power;

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (!power_actor_is_valid(params, instance))
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		if (!power_actor_is_valid(instance))
			continue;

		cdev = instance->cdev;
@@ -323,9 +322,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev,
		return ret;

	instance->target = clamp_val(state, instance->lower, instance->upper);
	mutex_lock(&cdev->lock);
	__thermal_cdev_update(cdev);
	mutex_unlock(&cdev->lock);

	thermal_cdev_update_nocheck(cdev);

	return 0;
}
@@ -356,11 +354,19 @@ static void divvy_up_power(struct power_actor *power, int num_actors,
	u32 extra_power = 0;
	int i;

	if (!total_req_power) {
		/*
	 * Prevent division by 0 if none of the actors request power.
		 * Nobody requested anything, just give everybody
		 * the maximum power
		 */
	if (!total_req_power)
		total_req_power = 1;
		for (i = 0; i < num_actors; i++) {
			struct power_actor *pa = &power[i];

			pa->granted_power = pa->max_power;
		}

		return;
	}

	for (i = 0; i < num_actors; i++) {
		struct power_actor *pa = &power[i];
@@ -400,6 +406,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors,
static void allocate_power(struct thermal_zone_device *tz, int control_temp)
{
	struct power_allocator_params *params = tz->governor_data;
	const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
	unsigned int num_actors = params->num_actors;
	struct power_actor *power = params->power;
	struct thermal_cooling_device *cdev;
@@ -417,10 +424,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp)
	/* Clean all buffers for new power estimations */
	memset(power, 0, params->buffer_size);

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		struct power_actor *pa = &power[i];

		if (!power_actor_is_valid(params, instance))
		if (!power_actor_is_valid(instance))
			continue;

		cdev = instance->cdev;
@@ -454,10 +461,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp)
		       power_range);

	i = 0;
	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		struct power_actor *pa = &power[i];

		if (!power_actor_is_valid(params, instance))
		if (!power_actor_is_valid(instance))
			continue;

		power_actor_set_power(instance->cdev, instance,
@@ -538,29 +545,29 @@ static void reset_pid_controller(struct power_allocator_params *params)
static void allow_maximum_power(struct thermal_zone_device *tz)
{
	struct power_allocator_params *params = tz->governor_data;
	const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
	struct thermal_cooling_device *cdev;
	struct thermal_instance *instance;
	u32 req_power;

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (!power_actor_is_valid(params, instance))
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		if (!power_actor_is_valid(instance))
			continue;

		cdev = instance->cdev;

		instance->target = 0;
		mutex_lock(&cdev->lock);
		scoped_guard(cooling_dev, cdev) {
			/*
		 * Call for updating the cooling devices local stats and avoid
		 * periods of dozen of seconds when those have not been
		 * maintained.
			 * Call for updating the cooling devices local stats and
			 * avoid periods of dozen of seconds when those have not
			 * been maintained.
			 */
			cdev->ops->get_requested_power(cdev, &req_power);

			if (params->update_cdevs)
				__thermal_cdev_update(cdev);

		mutex_unlock(&cdev->lock);
		}
	}
}

@@ -581,13 +588,11 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
static int check_power_actors(struct thermal_zone_device *tz,
			      struct power_allocator_params *params)
{
	const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
	struct thermal_instance *instance;
	int ret = 0;

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (instance->trip != params->trip_max)
			continue;

	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		if (!cdev_is_power_actor(instance->cdev)) {
			dev_warn(&tz->device, "power_allocator: %s is not a power actor\n",
				 instance->cdev->type);
@@ -635,14 +640,15 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz,
				      enum thermal_notify_event reason)
{
	struct power_allocator_params *params = tz->governor_data;
	const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
	struct thermal_instance *instance;
	int num_actors = 0;

	switch (reason) {
	case THERMAL_TZ_BIND_CDEV:
	case THERMAL_TZ_UNBIND_CDEV:
		list_for_each_entry(instance, &tz->thermal_instances, tz_node)
			if (power_actor_is_valid(params, instance))
		list_for_each_entry(instance, &td->thermal_instances, trip_node)
			if (power_actor_is_valid(instance))
				num_actors++;

		if (num_actors == params->num_actors)
@@ -652,8 +658,8 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz,
		break;
	case THERMAL_INSTANCE_WEIGHT_CHANGED:
		params->total_weight = 0;
		list_for_each_entry(instance, &tz->thermal_instances, tz_node)
			if (power_actor_is_valid(params, instance))
		list_for_each_entry(instance, &td->thermal_instances, trip_node)
			if (power_actor_is_valid(instance))
				params->total_weight += instance->weight;
		break;
	default:
+11 −11
Original line number Diff line number Diff line
@@ -66,9 +66,10 @@ static unsigned long get_target_state(struct thermal_instance *instance,
}

static void thermal_zone_trip_update(struct thermal_zone_device *tz,
				     const struct thermal_trip *trip,
				     const struct thermal_trip_desc *td,
				     int 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;
@@ -82,12 +83,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,
	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, &tz->thermal_instances, tz_node) {
	list_for_each_entry(instance, &td->thermal_instances, trip_node) {
		int old_target;

		if (instance->trip != trip)
			continue;

		old_target = instance->target;
		instance->target = get_target_state(instance, trend, throttle);

@@ -99,9 +97,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,

		instance->initialized = true;

		mutex_lock(&instance->cdev->lock);
		scoped_guard(cooling_dev, instance->cdev) {
			instance->cdev->updated = false; /* cdev needs update */
		mutex_unlock(&instance->cdev->lock);
		}
	}
}

@@ -127,12 +125,14 @@ static void step_wise_manage(struct thermal_zone_device *tz)
		    trip->type == THERMAL_TRIP_HOT)
			continue;

		thermal_zone_trip_update(tz, trip, td->threshold);
		thermal_zone_trip_update(tz, td, td->threshold);
	}

	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
	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",
Loading