mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
Thermal control updates for 6.13-rc1
- 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). -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmc3sPYSHHJqd0Byand5 c29ja2kubmV0AAoJEILEb/54YlRxk7oQAKW9vjEPQvQvdkm86cI/mUnfMPMop6eF hKKwXUO5XHx+HqW1B8pQ9YPjAA8bcj0p9Ey1jn1cqXXOkjECU8V6tZZwIvSqLZdu xg2Jxtq/Hma269oxxQ6dmcUFHSLFWnPExpXfX9kbXjAZUOaoZ0I/Q8jEENQhZb50 puewxt/T8zj6sZ0X9Skwd3nI2kVP4q95In+Ed/EPxnmJdrRTsyDg4Q38Et2KuxqO Trp++JMKEOJzOor65WelQylKVuIJM+88Gv4FMEPa8aX/mgCEZqn2RUguMLKWSmhq DUJUgWgxfF65vHM1yqbfwuYSgN7kSkYUgLuvwn7vSPS/NctwD5MPNL8sVJeieMOn IV6NZ1gWzjd0vK8ZQgUZi5NxrI5mMHK/9cgKesJ55jT9XwTNX922bF6dRUd4eT7j iKhQDajoyUZ/Oro4IN3OnwZ8dAPc9CREjaKFQ5duqiHFWa3I29zU2k/xgrIqGCNI Ko+uxY2PxxNvNQ2CR3iDck2pY24DvN2oALL9mLZekHaTKLLd7woo44D6p1pr0fbq E1y77uoFEAnbrW1v+9taSBYGyTRrNz0e4fPOwrqHcZ/O8wNBAQW+RKjzEOsk7Qj7 rrh7XvBzM5aJGW1fcXTa2P581BhitD72i2/EXpxV3tH3JMZbqQxW3xGMG3GuW7QU RP0hnSpbiIWn =Pb3D -----END PGP SIGNATURE----- Merge tag 'thermal-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm 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 ...
This commit is contained in:
commit
cd7fa3e1b0
@ -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
|
||||
|
@ -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,10 +74,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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,11 +125,13 @@ 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 = {
|
||||
|
@ -185,7 +185,7 @@ static void tt_add_tz_work_fn(struct work_struct *work)
|
||||
int tt_add_tz(void)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone __free(kfree);
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
struct tt_work *tt_work __free(kfree) = NULL;
|
||||
int ret;
|
||||
|
||||
tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL);
|
||||
@ -237,7 +237,7 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone)
|
||||
|
||||
int tt_del_tz(const char *arg)
|
||||
{
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
struct tt_work *tt_work __free(kfree) = NULL;
|
||||
struct tt_thermal_zone *tt_zone, *aux;
|
||||
int ret;
|
||||
int id;
|
||||
@ -288,19 +288,14 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg)
|
||||
|
||||
guard(mutex)(&tt_thermal_zones_lock);
|
||||
|
||||
ret = -EINVAL;
|
||||
list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) {
|
||||
if (tt_zone->id == id) {
|
||||
tt_zone->refcount++;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return tt_zone;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone)
|
||||
@ -310,6 +305,9 @@ static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone)
|
||||
tt_zone->refcount--;
|
||||
}
|
||||
|
||||
DEFINE_FREE(put_tt_zone, struct tt_thermal_zone *,
|
||||
if (!IS_ERR_OR_NULL(_T)) tt_put_tt_zone(_T))
|
||||
|
||||
static void tt_zone_add_trip_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct tt_work *tt_work = tt_work_of_work(work);
|
||||
@ -332,9 +330,9 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work)
|
||||
|
||||
int tt_zone_add_trip(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL;
|
||||
struct tt_trip *tt_trip __free(kfree) = NULL;
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
struct tt_trip *tt_trip __free(kfree);
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
int id;
|
||||
|
||||
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
|
||||
@ -350,10 +348,8 @@ int tt_zone_add_trip(const char *arg)
|
||||
return PTR_ERR(tt_zone);
|
||||
|
||||
id = ida_alloc(&tt_zone->ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
tt_put_tt_zone(tt_zone);
|
||||
if (id < 0)
|
||||
return id;
|
||||
}
|
||||
|
||||
tt_trip->trip.type = THERMAL_TRIP_ACTIVE;
|
||||
tt_trip->trip.temperature = THERMAL_TEMP_INVALID;
|
||||
@ -366,7 +362,7 @@ int tt_zone_add_trip(const char *arg)
|
||||
tt_zone->num_trips++;
|
||||
|
||||
INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn);
|
||||
tt_work->tt_zone = tt_zone;
|
||||
tt_work->tt_zone = no_free_ptr(tt_zone);
|
||||
tt_work->tt_trip = no_free_ptr(tt_trip);
|
||||
schedule_work(&(no_free_ptr(tt_work)->work));
|
||||
|
||||
@ -391,7 +387,7 @@ static struct thermal_zone_device_ops tt_zone_ops = {
|
||||
|
||||
static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
struct thermal_trip *trips __free(kfree);
|
||||
struct thermal_trip *trips __free(kfree) = NULL;
|
||||
struct thermal_zone_device *tz;
|
||||
struct tt_trip *tt_trip;
|
||||
int i;
|
||||
@ -425,23 +421,18 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
|
||||
|
||||
int tt_zone_reg(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
int ret;
|
||||
struct tt_thermal_zone *tt_zone __free(put_tt_zone);
|
||||
|
||||
tt_zone = tt_get_tt_zone(arg);
|
||||
if (IS_ERR(tt_zone))
|
||||
return PTR_ERR(tt_zone);
|
||||
|
||||
ret = tt_zone_register_tz(tt_zone);
|
||||
|
||||
tt_put_tt_zone(tt_zone);
|
||||
|
||||
return ret;
|
||||
return tt_zone_register_tz(tt_zone);
|
||||
}
|
||||
|
||||
int tt_zone_unreg(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
struct tt_thermal_zone *tt_zone __free(put_tt_zone);
|
||||
|
||||
tt_zone = tt_get_tt_zone(arg);
|
||||
if (IS_ERR(tt_zone))
|
||||
@ -449,8 +440,6 @@ int tt_zone_unreg(const char *arg)
|
||||
|
||||
tt_zone_unregister_tz(tt_zone);
|
||||
|
||||
tt_put_tt_zone(tt_zone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,10 +9,12 @@
|
||||
#ifndef __THERMAL_CORE_H__
|
||||
#define __THERMAL_CORE_H__
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_netlink.h"
|
||||
#include "thermal_thresholds.h"
|
||||
#include "thermal_debugfs.h"
|
||||
|
||||
struct thermal_attr {
|
||||
@ -29,8 +31,8 @@ struct thermal_trip_attrs {
|
||||
struct thermal_trip_desc {
|
||||
struct thermal_trip trip;
|
||||
struct thermal_trip_attrs trip_attrs;
|
||||
struct list_head notify_list_node;
|
||||
int notify_temp;
|
||||
struct list_head list_node;
|
||||
struct list_head thermal_instances;
|
||||
int threshold;
|
||||
};
|
||||
|
||||
@ -61,6 +63,13 @@ struct thermal_governor {
|
||||
struct list_head governor_list;
|
||||
};
|
||||
|
||||
#define TZ_STATE_FLAG_SUSPENDED BIT(0)
|
||||
#define TZ_STATE_FLAG_RESUMING BIT(1)
|
||||
#define TZ_STATE_FLAG_INIT BIT(2)
|
||||
#define TZ_STATE_FLAG_EXIT BIT(3)
|
||||
|
||||
#define TZ_STATE_READY 0
|
||||
|
||||
/**
|
||||
* struct thermal_zone_device - structure for a thermal zone
|
||||
* @id: unique id number for each thermal zone
|
||||
@ -68,6 +77,9 @@ struct thermal_governor {
|
||||
* @device: &struct device for this thermal zone
|
||||
* @removal: removal completion
|
||||
* @resume: resume completion
|
||||
* @trips_high: trips above the current zone temperature
|
||||
* @trips_reached: trips below or at the current zone temperature
|
||||
* @trips_invalid: trips with invalid temperature
|
||||
* @mode: current mode of this thermal zone
|
||||
* @devdata: private pointer for device private data
|
||||
* @num_trips: number of trip points the thermal zone supports
|
||||
@ -88,20 +100,17 @@ struct thermal_governor {
|
||||
trip point.
|
||||
* @prev_high_trip: the above current temperature if you've crossed a
|
||||
passive trip point.
|
||||
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
|
||||
* @ops: operations this &thermal_zone_device supports
|
||||
* @tzp: thermal zone parameters
|
||||
* @governor: pointer to the governor for this thermal zone
|
||||
* @governor_data: private pointer for governor data
|
||||
* @thermal_instances: list of &struct thermal_instance of this thermal zone
|
||||
* @ida: &struct ida to generate unique id for this zone's cooling
|
||||
* devices
|
||||
* @lock: lock to protect thermal_instances list
|
||||
* @node: node in thermal_tz_list (in thermal_core.c)
|
||||
* @poll_queue: delayed work for polling
|
||||
* @notify_event: Last notification event
|
||||
* @suspended: thermal zone suspend indicator
|
||||
* @resuming: indicates whether or not thermal zone resume is in progress
|
||||
* @state: current state of the thermal zone
|
||||
* @trips: array of struct thermal_trip objects
|
||||
*/
|
||||
struct thermal_zone_device {
|
||||
@ -111,6 +120,9 @@ struct thermal_zone_device {
|
||||
struct completion removal;
|
||||
struct completion resume;
|
||||
struct attribute_group trips_attribute_group;
|
||||
struct list_head trips_high;
|
||||
struct list_head trips_reached;
|
||||
struct list_head trips_invalid;
|
||||
enum thermal_device_mode mode;
|
||||
void *devdata;
|
||||
int num_trips;
|
||||
@ -123,25 +135,29 @@ struct thermal_zone_device {
|
||||
int passive;
|
||||
int prev_low_trip;
|
||||
int prev_high_trip;
|
||||
atomic_t need_update;
|
||||
struct thermal_zone_device_ops ops;
|
||||
struct thermal_zone_params *tzp;
|
||||
struct thermal_governor *governor;
|
||||
void *governor_data;
|
||||
struct list_head thermal_instances;
|
||||
struct ida ida;
|
||||
struct mutex lock;
|
||||
struct list_head node;
|
||||
struct delayed_work poll_queue;
|
||||
enum thermal_notify_event notify_event;
|
||||
bool suspended;
|
||||
bool resuming;
|
||||
u8 state;
|
||||
#ifdef CONFIG_THERMAL_DEBUGFS
|
||||
struct thermal_debugfs *debugfs;
|
||||
#endif
|
||||
struct list_head user_thresholds;
|
||||
struct thermal_trip_desc trips[] __counted_by(num_trips);
|
||||
};
|
||||
|
||||
DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock),
|
||||
mutex_unlock(&_T->lock))
|
||||
|
||||
DEFINE_GUARD(thermal_zone_reverse, struct thermal_zone_device *,
|
||||
mutex_unlock(&_T->lock), mutex_lock(&_T->lock))
|
||||
|
||||
/* Initial thermal zone temperature. */
|
||||
#define THERMAL_TEMP_INIT INT_MIN
|
||||
|
||||
@ -204,6 +220,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
|
||||
}
|
||||
|
||||
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||
void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev);
|
||||
void __thermal_cdev_update(struct thermal_cooling_device *cdev);
|
||||
|
||||
int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip);
|
||||
@ -226,7 +243,7 @@ struct thermal_instance {
|
||||
struct device_attribute attr;
|
||||
char weight_attr_name[THERMAL_NAME_LENGTH];
|
||||
struct device_attribute weight_attr;
|
||||
struct list_head tz_node; /* node in tz->thermal_instances */
|
||||
struct list_head trip_node; /* node in trip->thermal_instances */
|
||||
struct list_head cdev_node; /* node in cdev->thermal_instances */
|
||||
unsigned int weight; /* The weight of the cooling device */
|
||||
bool upper_no_limit;
|
||||
@ -261,8 +278,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high);
|
||||
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
|
||||
void thermal_zone_trip_down(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int hyst);
|
||||
|
||||
|
@ -516,6 +516,19 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state)
|
||||
cdev->debugfs = thermal_dbg;
|
||||
}
|
||||
|
||||
static struct thermal_debugfs *thermal_debug_cdev_clear(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg;
|
||||
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
thermal_dbg = cdev->debugfs;
|
||||
if (thermal_dbg)
|
||||
cdev->debugfs = NULL;
|
||||
|
||||
return thermal_dbg;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_debug_cdev_remove - Remove a cooling device debugfs entry
|
||||
*
|
||||
@ -527,17 +540,9 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
thermal_dbg = cdev->debugfs;
|
||||
if (!thermal_dbg) {
|
||||
mutex_unlock(&cdev->lock);
|
||||
thermal_dbg = thermal_debug_cdev_clear(cdev);
|
||||
if (!thermal_dbg)
|
||||
return;
|
||||
}
|
||||
|
||||
cdev->debugfs = NULL;
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
||||
mutex_lock(&thermal_dbg->lock);
|
||||
|
||||
@ -885,6 +890,19 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
|
||||
tz->debugfs = thermal_dbg;
|
||||
}
|
||||
|
||||
static struct thermal_debugfs *thermal_debug_tz_clear(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg;
|
||||
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
thermal_dbg = tz->debugfs;
|
||||
if (thermal_dbg)
|
||||
tz->debugfs = NULL;
|
||||
|
||||
return thermal_dbg;
|
||||
}
|
||||
|
||||
void thermal_debug_tz_remove(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_debugfs *thermal_dbg;
|
||||
@ -892,17 +910,9 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz)
|
||||
struct tz_debugfs *tz_dbg;
|
||||
int *trips_crossed;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
thermal_dbg = tz->debugfs;
|
||||
if (!thermal_dbg) {
|
||||
mutex_unlock(&tz->lock);
|
||||
thermal_dbg = thermal_debug_tz_clear(tz);
|
||||
if (!thermal_dbg)
|
||||
return;
|
||||
}
|
||||
|
||||
tz->debugfs = NULL;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
tz_dbg = &thermal_dbg->tz_dbg;
|
||||
|
||||
|
@ -43,10 +43,11 @@ static bool thermal_instance_present(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev,
|
||||
const struct thermal_trip *trip)
|
||||
{
|
||||
const struct thermal_trip_desc *td = trip_to_trip_desc(trip);
|
||||
struct thermal_instance *ti;
|
||||
|
||||
list_for_each_entry(ti, &tz->thermal_instances, tz_node) {
|
||||
if (ti->trip == trip && ti->cdev == cdev)
|
||||
list_for_each_entry(ti, &td->thermal_instances, trip_node) {
|
||||
if (ti->cdev == cdev)
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -57,17 +58,10 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
bool ret;
|
||||
guard(thermal_zone)(tz);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
ret = thermal_instance_present(tz, cdev, trip);
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
return thermal_instance_present(tz, cdev, trip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev);
|
||||
|
||||
@ -137,19 +131,14 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
if (IS_ERR_OR_NULL(tz))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
if (!tz->ops.get_temp) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
if (!tz->ops.get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
ret = __thermal_zone_get_temp(tz, temp);
|
||||
if (!ret && *temp <= THERMAL_TEMP_INVALID)
|
||||
ret = -ENODATA;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
return -ENODATA;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -201,12 +190,23 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
*/
|
||||
void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
if (!cdev->updated) {
|
||||
__thermal_cdev_update(cdev);
|
||||
cdev->updated = true;
|
||||
}
|
||||
mutex_unlock(&cdev->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_cdev_update_nocheck() - Unconditionally update cooling device state
|
||||
* @cdev: Target cooling device.
|
||||
*/
|
||||
void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
__thermal_cdev_update(cdev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,12 +78,9 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
int temperature;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
ret = tz->ops.get_crit_temp(tz, &temperature);
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/thermal.h>
|
||||
|
||||
@ -49,6 +50,11 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] =
|
||||
[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 },
|
||||
|
||||
/* Thresholds */
|
||||
[THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
struct param {
|
||||
@ -62,6 +68,8 @@ struct param {
|
||||
int trip_type;
|
||||
int trip_hyst;
|
||||
int temp;
|
||||
int prev_temp;
|
||||
int direction;
|
||||
int cdev_state;
|
||||
int cdev_max_state;
|
||||
struct thermal_genl_cpu_caps *cpu_capabilities;
|
||||
@ -234,6 +242,34 @@ out_cancel_nest:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_threshold_add(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_threshold_flush(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_threshold_up(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_PREV_TEMP, p->prev_temp) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_genl_event_tz_delete(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz")));
|
||||
|
||||
@ -246,6 +282,12 @@ int thermal_genl_event_tz_disable(struct param *p)
|
||||
int thermal_genl_event_tz_trip_down(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz_trip_up")));
|
||||
|
||||
int thermal_genl_event_threshold_delete(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_threshold_add")));
|
||||
|
||||
int thermal_genl_event_threshold_down(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_threshold_up")));
|
||||
|
||||
static cb_t event_cb[] = {
|
||||
[THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create,
|
||||
[THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete,
|
||||
@ -259,6 +301,11 @@ static cb_t event_cb[] = {
|
||||
[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update,
|
||||
[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change,
|
||||
[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
|
||||
[THERMAL_GENL_EVENT_THRESHOLD_ADD] = thermal_genl_event_threshold_add,
|
||||
[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = thermal_genl_event_threshold_delete,
|
||||
[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = thermal_genl_event_threshold_flush,
|
||||
[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = thermal_genl_event_threshold_down,
|
||||
[THERMAL_GENL_EVENT_THRESHOLD_UP] = thermal_genl_event_threshold_up,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -401,6 +448,43 @@ int thermal_genl_cpu_capability_event(int count,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
|
||||
|
||||
int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_threshold_flush(const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p);
|
||||
}
|
||||
|
||||
/*************************** Command encoding ********************************/
|
||||
|
||||
static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
|
||||
@ -459,7 +543,7 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
if (!start_trip)
|
||||
return -EMSGSIZE;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
for_each_trip_desc(tz, td) {
|
||||
const struct thermal_trip *trip = &td->trip;
|
||||
@ -469,19 +553,12 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis))
|
||||
goto out_cancel_nest;
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
nla_nest_end(msg, start_trip);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_nest:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_tz_get_temp(struct param *p)
|
||||
@ -512,7 +589,7 @@ static int thermal_genl_cmd_tz_get_temp(struct param *p)
|
||||
static int thermal_genl_cmd_tz_get_gov(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
int id, ret = 0;
|
||||
int id;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
@ -523,16 +600,14 @@ static int thermal_genl_cmd_tz_get_gov(struct param *p)
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
|
||||
nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
|
||||
tz->governor->name))
|
||||
ret = -EMSGSIZE;
|
||||
return -EMSGSIZE;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
|
||||
@ -572,12 +647,128 @@ out_cancel_nest:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
|
||||
{
|
||||
struct sk_buff *msg = arg;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, threshold->direction))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_threshold_get(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct nlattr *start_trip;
|
||||
int id, ret;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
|
||||
CLASS(thermal_zone_get_by_id, tz)(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
|
||||
if (!start_trip)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
|
||||
if (ret)
|
||||
return -EMSGSIZE;
|
||||
|
||||
nla_nest_end(msg, start_trip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_threshold_add(struct param *p)
|
||||
{
|
||||
int id, temp, direction;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
|
||||
!p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
|
||||
!p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
|
||||
direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]);
|
||||
|
||||
CLASS(thermal_zone_get_by_id, tz)(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
return thermal_thresholds_add(tz, temp, direction);
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_threshold_delete(struct param *p)
|
||||
{
|
||||
int id, temp, direction;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
|
||||
!p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
|
||||
!p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
|
||||
direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]);
|
||||
|
||||
CLASS(thermal_zone_get_by_id, tz)(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
return thermal_thresholds_delete(tz, temp, direction);
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_threshold_flush(struct param *p)
|
||||
{
|
||||
int id;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
|
||||
CLASS(thermal_zone_get_by_id, tz)(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
thermal_thresholds_flush(tz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cb_t cmd_cb[] = {
|
||||
[THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id,
|
||||
[THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip,
|
||||
[THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp,
|
||||
[THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov,
|
||||
[THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
|
||||
[THERMAL_GENL_CMD_THRESHOLD_GET] = thermal_genl_cmd_threshold_get,
|
||||
[THERMAL_GENL_CMD_THRESHOLD_ADD] = thermal_genl_cmd_threshold_add,
|
||||
[THERMAL_GENL_CMD_THRESHOLD_DELETE] = thermal_genl_cmd_threshold_delete,
|
||||
[THERMAL_GENL_CMD_THRESHOLD_FLUSH] = thermal_genl_cmd_threshold_flush,
|
||||
};
|
||||
|
||||
static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
|
||||
@ -688,6 +879,26 @@ static const struct genl_small_ops thermal_genl_ops[] = {
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.dumpit = thermal_genl_cmd_dumpit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_THRESHOLD_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_THRESHOLD_ADD,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family thermal_genl_family __ro_after_init = {
|
||||
@ -700,7 +911,7 @@ static struct genl_family thermal_genl_family __ro_after_init = {
|
||||
.unbind = thermal_genl_unbind,
|
||||
.small_ops = thermal_genl_ops,
|
||||
.n_small_ops = ARRAY_SIZE(thermal_genl_ops),
|
||||
.resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1,
|
||||
.resv_start_op = __THERMAL_GENL_CMD_MAX,
|
||||
.mcgrps = thermal_genl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
|
||||
};
|
||||
|
@ -53,6 +53,13 @@ int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz,
|
||||
int thermal_genl_sampling_temp(int id, int temp);
|
||||
int thermal_genl_cpu_capability_event(int count,
|
||||
struct thermal_genl_cpu_caps *caps);
|
||||
int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction);
|
||||
int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction);
|
||||
int thermal_notify_threshold_flush(const struct thermal_zone_device *tz);
|
||||
int thermal_notify_threshold_down(const struct thermal_zone_device *tz);
|
||||
int thermal_notify_threshold_up(const struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline int thermal_netlink_init(void)
|
||||
{
|
||||
@ -139,6 +146,33 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __init thermal_netlink_exit(void) {}
|
||||
|
||||
#endif /* CONFIG_THERMAL_NETLINK */
|
||||
|
@ -50,13 +50,13 @@ static ssize_t
|
||||
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int enabled;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
enabled = tz->mode == THERMAL_DEVICE_ENABLED;
|
||||
mutex_unlock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled");
|
||||
if (tz->mode == THERMAL_DEVICE_ENABLED)
|
||||
return sprintf(buf, "enabled\n");
|
||||
|
||||
return sprintf(buf, "disabled\n");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -103,38 +103,34 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int ret, temp;
|
||||
int temp;
|
||||
|
||||
ret = kstrtoint(buf, 10, &temp);
|
||||
if (ret)
|
||||
if (kstrtoint(buf, 10, &temp))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
if (temp == trip->temperature)
|
||||
goto unlock;
|
||||
return count;
|
||||
|
||||
/* Arrange the condition to avoid integer overflows. */
|
||||
if (temp != THERMAL_TEMP_INVALID &&
|
||||
temp <= trip->hysteresis + THERMAL_TEMP_INVALID) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
temp <= trip->hysteresis + THERMAL_TEMP_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
if (tz->ops.set_trip_temp) {
|
||||
int ret;
|
||||
|
||||
ret = tz->ops.set_trip_temp(tz, trip, temp);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
thermal_zone_set_trip_temp(tz, trip, temp);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -152,16 +148,15 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int ret, hyst;
|
||||
int hyst;
|
||||
|
||||
ret = kstrtoint(buf, 10, &hyst);
|
||||
if (ret || hyst < 0)
|
||||
if (kstrtoint(buf, 10, &hyst) || hyst < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
if (hyst == trip->hysteresis)
|
||||
goto unlock;
|
||||
return count;
|
||||
|
||||
/*
|
||||
* Allow the hysteresis to be updated when the temperature is invalid
|
||||
@ -171,22 +166,17 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
*/
|
||||
if (trip->temperature == THERMAL_TEMP_INVALID) {
|
||||
WRITE_ONCE(trip->hysteresis, hyst);
|
||||
goto unlock;
|
||||
return count;
|
||||
}
|
||||
|
||||
if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
if (trip->temperature - hyst <= THERMAL_TEMP_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
thermal_zone_set_trip_hyst(tz, trip, hyst);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -236,25 +226,26 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int ret = 0;
|
||||
int temperature;
|
||||
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
if (tz->ops.set_emul_temp) {
|
||||
int ret;
|
||||
|
||||
if (!tz->ops.set_emul_temp)
|
||||
tz->emul_temperature = temperature;
|
||||
else
|
||||
ret = tz->ops.set_emul_temp(tz, temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
tz->emul_temperature = temperature;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(emul_temp);
|
||||
#endif
|
||||
@ -553,14 +544,15 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
|
||||
if (state > cdev->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
result = cdev->ops->set_cur_state(cdev, state);
|
||||
if (!result)
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
thermal_cooling_device_stats_update(cdev, state);
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
return result ? result : count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute
|
||||
@ -634,21 +626,18 @@ static ssize_t total_trans_show(struct device *dev,
|
||||
{
|
||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||
struct cooling_dev_stats *stats;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
stats = cdev->stats;
|
||||
if (!stats)
|
||||
goto unlock;
|
||||
return 0;
|
||||
|
||||
spin_lock(&stats->lock);
|
||||
ret = sprintf(buf, "%u\n", stats->total_trans);
|
||||
spin_unlock(&stats->lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -661,11 +650,11 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
stats = cdev->stats;
|
||||
if (!stats)
|
||||
goto unlock;
|
||||
return 0;
|
||||
|
||||
spin_lock(&stats->lock);
|
||||
|
||||
@ -677,9 +666,6 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
spin_unlock(&stats->lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -691,11 +677,11 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
struct cooling_dev_stats *stats;
|
||||
int i, states;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
stats = cdev->stats;
|
||||
if (!stats)
|
||||
goto unlock;
|
||||
return count;
|
||||
|
||||
states = cdev->max_state + 1;
|
||||
|
||||
@ -711,9 +697,6 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
|
||||
spin_unlock(&stats->lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -725,13 +708,11 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
ssize_t len = 0;
|
||||
int i, j;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
stats = cdev->stats;
|
||||
if (!stats) {
|
||||
len = -ENODATA;
|
||||
goto unlock;
|
||||
}
|
||||
if (!stats)
|
||||
return -ENODATA;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
|
||||
@ -740,10 +721,8 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
|
||||
}
|
||||
if (len >= PAGE_SIZE) {
|
||||
len = PAGE_SIZE;
|
||||
goto unlock;
|
||||
}
|
||||
if (len >= PAGE_SIZE)
|
||||
return PAGE_SIZE;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
|
||||
@ -769,9 +748,6 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
len = -EFBIG;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -894,13 +870,11 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr,
|
||||
instance = container_of(attr, struct thermal_instance, weight_attr);
|
||||
|
||||
/* Don't race with governors using the 'weight' value */
|
||||
mutex_lock(&tz->lock);
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
instance->weight = weight;
|
||||
|
||||
thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED);
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
240
drivers/thermal/thermal_thresholds.c
Normal file
240
drivers/thermal/thermal_thresholds.c
Normal file
@ -0,0 +1,240 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2024 Linaro Limited
|
||||
*
|
||||
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
*
|
||||
* Thermal thresholds
|
||||
*/
|
||||
#include <linux/list.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_thresholds.h"
|
||||
|
||||
int thermal_thresholds_init(struct thermal_zone_device *tz)
|
||||
{
|
||||
INIT_LIST_HEAD(&tz->user_thresholds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __thermal_thresholds_flush(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct list_head *thresholds = &tz->user_thresholds;
|
||||
struct user_threshold *entry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, thresholds, list_node) {
|
||||
list_del(&entry->list_node);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void thermal_thresholds_flush(struct thermal_zone_device *tz)
|
||||
{
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
__thermal_thresholds_flush(tz);
|
||||
|
||||
thermal_notify_threshold_flush(tz);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
|
||||
}
|
||||
|
||||
void thermal_thresholds_exit(struct thermal_zone_device *tz)
|
||||
{
|
||||
__thermal_thresholds_flush(tz);
|
||||
}
|
||||
|
||||
static int __thermal_thresholds_cmp(void *data,
|
||||
const struct list_head *l1,
|
||||
const struct list_head *l2)
|
||||
{
|
||||
struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node);
|
||||
struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node);
|
||||
|
||||
return t1->temperature - t2->temperature;
|
||||
}
|
||||
|
||||
static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds,
|
||||
int temperature)
|
||||
{
|
||||
struct user_threshold *t;
|
||||
|
||||
list_for_each_entry(t, thresholds, list_node)
|
||||
if (t->temperature == temperature)
|
||||
return t;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature,
|
||||
int last_temperature, int direction,
|
||||
int *low, int *high)
|
||||
{
|
||||
|
||||
if (temperature >= threshold->temperature) {
|
||||
if (threshold->temperature > *low &&
|
||||
THERMAL_THRESHOLD_WAY_DOWN & threshold->direction)
|
||||
*low = threshold->temperature;
|
||||
|
||||
if (last_temperature < threshold->temperature &&
|
||||
threshold->direction & direction)
|
||||
return true;
|
||||
} else {
|
||||
if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP
|
||||
& threshold->direction)
|
||||
*high = threshold->temperature;
|
||||
|
||||
if (last_temperature >= threshold->temperature &&
|
||||
threshold->direction & direction)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature,
|
||||
int last_temperature, int *low, int *high)
|
||||
{
|
||||
struct user_threshold *t;
|
||||
|
||||
list_for_each_entry(t, thresholds, list_node) {
|
||||
if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
|
||||
THERMAL_THRESHOLD_WAY_UP, low, high))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature,
|
||||
int last_temperature, int *low, int *high)
|
||||
{
|
||||
struct user_threshold *t;
|
||||
|
||||
list_for_each_entry_reverse(t, thresholds, list_node) {
|
||||
if (__thermal_threshold_is_crossed(t, temperature, last_temperature,
|
||||
THERMAL_THRESHOLD_WAY_DOWN, low, high))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high)
|
||||
{
|
||||
struct list_head *thresholds = &tz->user_thresholds;
|
||||
|
||||
int temperature = tz->temperature;
|
||||
int last_temperature = tz->last_temperature;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
/*
|
||||
* We need a second update in order to detect a threshold being crossed
|
||||
*/
|
||||
if (last_temperature == THERMAL_TEMP_INVALID)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The temperature is stable, so obviously we can not have
|
||||
* crossed a threshold.
|
||||
*/
|
||||
if (last_temperature == temperature)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Since last update the temperature:
|
||||
* - increased : thresholds are crossed the way up
|
||||
* - decreased : thresholds are crossed the way down
|
||||
*/
|
||||
if (temperature > last_temperature) {
|
||||
if (thermal_thresholds_handle_raising(thresholds, temperature,
|
||||
last_temperature, low, high))
|
||||
thermal_notify_threshold_up(tz);
|
||||
} else {
|
||||
if (thermal_thresholds_handle_dropping(thresholds, temperature,
|
||||
last_temperature, low, high))
|
||||
thermal_notify_threshold_down(tz);
|
||||
}
|
||||
}
|
||||
|
||||
int thermal_thresholds_add(struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
struct list_head *thresholds = &tz->user_thresholds;
|
||||
struct user_threshold *t;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
t = __thermal_thresholds_find(thresholds, temperature);
|
||||
if (t) {
|
||||
if (t->direction == direction)
|
||||
return -EEXIST;
|
||||
|
||||
t->direction |= direction;
|
||||
} else {
|
||||
|
||||
t = kmalloc(sizeof(*t), GFP_KERNEL);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&t->list_node);
|
||||
t->temperature = temperature;
|
||||
t->direction = direction;
|
||||
list_add(&t->list_node, thresholds);
|
||||
list_sort(NULL, thresholds, __thermal_thresholds_cmp);
|
||||
}
|
||||
|
||||
thermal_notify_threshold_add(tz, temperature, direction);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_thresholds_delete(struct thermal_zone_device *tz,
|
||||
int temperature, int direction)
|
||||
{
|
||||
struct list_head *thresholds = &tz->user_thresholds;
|
||||
struct user_threshold *t;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
t = __thermal_thresholds_find(thresholds, temperature);
|
||||
if (!t)
|
||||
return -ENOENT;
|
||||
|
||||
if (t->direction == direction) {
|
||||
list_del(&t->list_node);
|
||||
kfree(t);
|
||||
} else {
|
||||
t->direction &= ~direction;
|
||||
}
|
||||
|
||||
thermal_notify_threshold_delete(tz, temperature, direction);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_thresholds_for_each(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct user_threshold *, void *arg), void *arg)
|
||||
{
|
||||
struct list_head *thresholds = &tz->user_thresholds;
|
||||
struct user_threshold *entry;
|
||||
int ret;
|
||||
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
list_for_each_entry(entry, thresholds, list_node) {
|
||||
ret = cb(entry, arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
19
drivers/thermal/thermal_thresholds.h
Normal file
19
drivers/thermal/thermal_thresholds.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __THERMAL_THRESHOLDS_H__
|
||||
#define __THERMAL_THRESHOLDS_H__
|
||||
|
||||
struct user_threshold {
|
||||
struct list_head list_node;
|
||||
int temperature;
|
||||
int direction;
|
||||
};
|
||||
|
||||
int thermal_thresholds_init(struct thermal_zone_device *tz);
|
||||
void thermal_thresholds_exit(struct thermal_zone_device *tz);
|
||||
void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
|
||||
void thermal_thresholds_flush(struct thermal_zone_device *tz);
|
||||
int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
|
||||
int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
|
||||
int thermal_thresholds_for_each(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct user_threshold *, void *arg), void *arg);
|
||||
#endif
|
@ -45,13 +45,9 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *data)
|
||||
{
|
||||
int ret;
|
||||
guard(thermal_zone)(tz);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
ret = for_each_thermal_trip(tz, cb, data);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
return for_each_thermal_trip(tz, cb, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip);
|
||||
|
||||
@ -92,43 +88,3 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
*/
|
||||
return trip_to_trip_desc(trip) - tz->trips;
|
||||
}
|
||||
|
||||
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int hyst)
|
||||
{
|
||||
WRITE_ONCE(trip->hysteresis, hyst);
|
||||
thermal_notify_tz_trip_change(tz, trip);
|
||||
}
|
||||
|
||||
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int temp)
|
||||
{
|
||||
if (trip->temperature == temp)
|
||||
return;
|
||||
|
||||
WRITE_ONCE(trip->temperature, temp);
|
||||
thermal_notify_tz_trip_change(tz, trip);
|
||||
|
||||
if (temp == THERMAL_TEMP_INVALID) {
|
||||
struct thermal_trip_desc *td = trip_to_trip_desc(trip);
|
||||
|
||||
if (tz->temperature >= td->threshold) {
|
||||
/*
|
||||
* The trip has been crossed on the way up, so some
|
||||
* adjustments are needed to compensate for the lack
|
||||
* of it going forward.
|
||||
*/
|
||||
if (trip->type == THERMAL_TRIP_PASSIVE) {
|
||||
tz->passive--;
|
||||
WARN_ON_ONCE(tz->passive < 0);
|
||||
}
|
||||
thermal_zone_trip_down(tz, trip);
|
||||
}
|
||||
/*
|
||||
* Invalidate the threshold to avoid triggering a spurious
|
||||
* trip crossing notification when the trip becomes valid.
|
||||
*/
|
||||
td->threshold = INT_MAX;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);
|
||||
|
@ -56,6 +56,9 @@ enum thermal_notify_event {
|
||||
THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */
|
||||
THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */
|
||||
THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */
|
||||
THERMAL_TZ_ADD_THRESHOLD, /* Threshold added */
|
||||
THERMAL_TZ_DEL_THRESHOLD, /* Threshold deleted */
|
||||
THERMAL_TZ_FLUSH_THRESHOLDS, /* All thresholds deleted */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -137,6 +140,9 @@ struct thermal_cooling_device {
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_GUARD(cooling_dev, struct thermal_cooling_device *, mutex_lock(&_T->lock),
|
||||
mutex_unlock(&_T->lock))
|
||||
|
||||
/* Structure to define Thermal Zone parameters */
|
||||
struct thermal_zone_params {
|
||||
const char *governor_name;
|
||||
|
@ -3,6 +3,8 @@
|
||||
#define _UAPI_LINUX_THERMAL_H
|
||||
|
||||
#define THERMAL_NAME_LENGTH 20
|
||||
#define THERMAL_THRESHOLD_WAY_UP BIT(0)
|
||||
#define THERMAL_THRESHOLD_WAY_DOWN BIT(1)
|
||||
|
||||
enum thermal_device_mode {
|
||||
THERMAL_DEVICE_DISABLED = 0,
|
||||
@ -18,7 +20,7 @@ enum thermal_trip_type {
|
||||
|
||||
/* Adding event notification support elements */
|
||||
#define THERMAL_GENL_FAMILY_NAME "thermal"
|
||||
#define THERMAL_GENL_VERSION 0x01
|
||||
#define THERMAL_GENL_VERSION 0x02
|
||||
#define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling"
|
||||
#define THERMAL_GENL_EVENT_GROUP_NAME "event"
|
||||
|
||||
@ -28,6 +30,7 @@ enum thermal_genl_attr {
|
||||
THERMAL_GENL_ATTR_TZ,
|
||||
THERMAL_GENL_ATTR_TZ_ID,
|
||||
THERMAL_GENL_ATTR_TZ_TEMP,
|
||||
THERMAL_GENL_ATTR_TZ_PREV_TEMP,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_ID,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
|
||||
@ -48,6 +51,9 @@ enum thermal_genl_attr {
|
||||
THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
|
||||
THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
|
||||
THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
|
||||
THERMAL_GENL_ATTR_THRESHOLD,
|
||||
THERMAL_GENL_ATTR_THRESHOLD_TEMP,
|
||||
THERMAL_GENL_ATTR_THRESHOLD_DIRECTION,
|
||||
__THERMAL_GENL_ATTR_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
|
||||
@ -75,6 +81,11 @@ enum thermal_genl_event {
|
||||
THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */
|
||||
THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */
|
||||
THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */
|
||||
THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A thresold has been added */
|
||||
THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A thresold has been deleted */
|
||||
THERMAL_GENL_EVENT_THRESHOLD_FLUSH, /* All thresolds have been deleted */
|
||||
THERMAL_GENL_EVENT_THRESHOLD_UP, /* A thresold has been crossed the way up */
|
||||
THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A thresold has been crossed the way down */
|
||||
__THERMAL_GENL_EVENT_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
|
||||
@ -88,6 +99,10 @@ enum thermal_genl_cmd {
|
||||
THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */
|
||||
THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */
|
||||
THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */
|
||||
THERMAL_GENL_CMD_THRESHOLD_GET, /* List of thresholds */
|
||||
THERMAL_GENL_CMD_THRESHOLD_ADD, /* Add a threshold */
|
||||
THERMAL_GENL_CMD_THRESHOLD_DELETE, /* Delete a threshold */
|
||||
THERMAL_GENL_CMD_THRESHOLD_FLUSH, /* Flush all the thresholds */
|
||||
__THERMAL_GENL_CMD_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <thermal.h>
|
||||
#include "thermal_nl.h"
|
||||
@ -33,6 +34,11 @@ static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
|
||||
[THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING },
|
||||
|
||||
/* Thresholds */
|
||||
[THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
|
||||
@ -182,6 +188,48 @@ static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
|
||||
return THERMAL_SUCCESS;
|
||||
}
|
||||
|
||||
static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz)
|
||||
{
|
||||
struct nlattr *attr;
|
||||
struct thermal_threshold *__tt = NULL;
|
||||
size_t size = 0;
|
||||
int rem;
|
||||
|
||||
/*
|
||||
* The size contains the size of the array and we want to
|
||||
* access the last element, size - 1.
|
||||
*
|
||||
* The variable size is initialized to zero but it will be
|
||||
* then incremented by the first if() statement. The message
|
||||
* attributes are ordered, so the first if() statement will be
|
||||
* always called before the second one. If it happens that is
|
||||
* not the case, then it is a kernel bug.
|
||||
*/
|
||||
nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) {
|
||||
|
||||
if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) {
|
||||
|
||||
size++;
|
||||
|
||||
__tt = realloc(__tt, sizeof(*__tt) * (size + 2));
|
||||
if (!__tt)
|
||||
return THERMAL_ERROR;
|
||||
|
||||
__tt[size - 1].temperature = nla_get_u32(attr);
|
||||
}
|
||||
|
||||
if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION)
|
||||
__tt[size - 1].direction = nla_get_u32(attr);
|
||||
}
|
||||
|
||||
if (__tt)
|
||||
__tt[size].temperature = INT_MAX;
|
||||
|
||||
tz->thresholds = __tt;
|
||||
|
||||
return THERMAL_SUCCESS;
|
||||
}
|
||||
|
||||
static int handle_netlink(struct nl_cache_ops *unused,
|
||||
struct genl_cmd *cmd,
|
||||
struct genl_info *info, void *arg)
|
||||
@ -210,6 +258,10 @@ static int handle_netlink(struct nl_cache_ops *unused,
|
||||
ret = parse_tz_get_gov(info, arg);
|
||||
break;
|
||||
|
||||
case THERMAL_GENL_CMD_THRESHOLD_GET:
|
||||
ret = parse_threshold_get(info, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return THERMAL_ERROR;
|
||||
}
|
||||
@ -253,6 +305,34 @@ static struct genl_cmd thermal_cmds[] = {
|
||||
.c_maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.c_attr_policy = thermal_genl_policy,
|
||||
},
|
||||
{
|
||||
.c_id = THERMAL_GENL_CMD_THRESHOLD_GET,
|
||||
.c_name = (char *)"Get thresholds list",
|
||||
.c_msg_parser = handle_netlink,
|
||||
.c_maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.c_attr_policy = thermal_genl_policy,
|
||||
},
|
||||
{
|
||||
.c_id = THERMAL_GENL_CMD_THRESHOLD_ADD,
|
||||
.c_name = (char *)"Add a threshold",
|
||||
.c_msg_parser = handle_netlink,
|
||||
.c_maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.c_attr_policy = thermal_genl_policy,
|
||||
},
|
||||
{
|
||||
.c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE,
|
||||
.c_name = (char *)"Delete a threshold",
|
||||
.c_msg_parser = handle_netlink,
|
||||
.c_maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.c_attr_policy = thermal_genl_policy,
|
||||
},
|
||||
{
|
||||
.c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
|
||||
.c_name = (char *)"Flush the thresholds",
|
||||
.c_msg_parser = handle_netlink,
|
||||
.c_maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.c_attr_policy = thermal_genl_policy,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_ops thermal_cmd_ops = {
|
||||
@ -261,9 +341,41 @@ static struct genl_ops thermal_cmd_ops = {
|
||||
.o_ncmds = ARRAY_SIZE(thermal_cmds),
|
||||
};
|
||||
|
||||
static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd,
|
||||
int flags, void *arg)
|
||||
struct cmd_param {
|
||||
int tz_id;
|
||||
int temp;
|
||||
int direction;
|
||||
};
|
||||
|
||||
typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *);
|
||||
|
||||
static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p)
|
||||
{
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p)
|
||||
{
|
||||
if (thermal_genl_tz_id_encode(msg, p))
|
||||
return -1;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp))
|
||||
return -1;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb,
|
||||
struct cmd_param *param,
|
||||
int cmd, int flags, void *arg)
|
||||
{
|
||||
thermal_error_t ret = THERMAL_ERROR;
|
||||
struct nl_msg *msg;
|
||||
void *hdr;
|
||||
|
||||
@ -274,45 +386,95 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int
|
||||
hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id,
|
||||
0, flags, cmd, THERMAL_GENL_VERSION);
|
||||
if (!hdr)
|
||||
return THERMAL_ERROR;
|
||||
goto out;
|
||||
|
||||
if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id))
|
||||
return THERMAL_ERROR;
|
||||
if (cmd_cb && cmd_cb(msg, param))
|
||||
goto out;
|
||||
|
||||
if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
|
||||
return THERMAL_ERROR;
|
||||
goto out;
|
||||
|
||||
ret = THERMAL_SUCCESS;
|
||||
out:
|
||||
nlmsg_free(msg);
|
||||
|
||||
return THERMAL_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
|
||||
{
|
||||
return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID,
|
||||
return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID,
|
||||
NLM_F_DUMP | NLM_F_ACK, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
|
||||
{
|
||||
return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET,
|
||||
return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET,
|
||||
NLM_F_DUMP | NLM_F_ACK, tc);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz)
|
||||
{
|
||||
return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP,
|
||||
0, tz);
|
||||
struct cmd_param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
|
||||
THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz)
|
||||
{
|
||||
return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
|
||||
struct cmd_param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
|
||||
THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz)
|
||||
{
|
||||
return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
|
||||
struct cmd_param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
|
||||
THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th,
|
||||
struct thermal_zone *tz)
|
||||
{
|
||||
struct cmd_param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
|
||||
THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th,
|
||||
struct thermal_zone *tz,
|
||||
int temperature,
|
||||
int direction)
|
||||
{
|
||||
struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
|
||||
THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th,
|
||||
struct thermal_zone *tz,
|
||||
int temperature,
|
||||
int direction)
|
||||
{
|
||||
struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
|
||||
THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th,
|
||||
struct thermal_zone *tz)
|
||||
{
|
||||
struct cmd_param p = { .tz_id = tz->id };
|
||||
|
||||
return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
|
||||
THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz);
|
||||
}
|
||||
|
||||
thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
|
||||
|
@ -94,6 +94,30 @@ static int handle_thermal_event(struct nl_msg *n, void *arg)
|
||||
case THERMAL_GENL_EVENT_TZ_GOV_CHANGE:
|
||||
return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
|
||||
nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg);
|
||||
|
||||
case THERMAL_GENL_EVENT_THRESHOLD_ADD:
|
||||
return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg);
|
||||
|
||||
case THERMAL_GENL_EVENT_THRESHOLD_DELETE:
|
||||
return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg);
|
||||
|
||||
case THERMAL_GENL_EVENT_THRESHOLD_FLUSH:
|
||||
return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
|
||||
|
||||
case THERMAL_GENL_EVENT_THRESHOLD_UP:
|
||||
return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg);
|
||||
|
||||
case THERMAL_GENL_EVENT_THRESHOLD_DOWN:
|
||||
return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]),
|
||||
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@ -114,6 +138,11 @@ static void thermal_events_ops_init(struct thermal_events_ops *ops)
|
||||
enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete;
|
||||
enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update;
|
||||
enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change;
|
||||
enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add;
|
||||
enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete;
|
||||
enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush;
|
||||
enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up;
|
||||
enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down;
|
||||
}
|
||||
|
||||
thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg)
|
||||
|
@ -4,11 +4,20 @@
|
||||
#define __LIBTHERMAL_H
|
||||
|
||||
#include <linux/thermal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef LIBTHERMAL_API
|
||||
#define LIBTHERMAL_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifndef THERMAL_THRESHOLD_WAY_UP
|
||||
#define THERMAL_THRESHOLD_WAY_UP 0x1
|
||||
#endif
|
||||
|
||||
#ifndef THERMAL_THRESHOLD_WAY_DOWN
|
||||
#define THERMAL_THRESHOLD_WAY_DOWN 0x2
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -31,6 +40,11 @@ struct thermal_events_ops {
|
||||
int (*cdev_delete)(int cdev_id, void *arg);
|
||||
int (*cdev_update)(int cdev_id, int cur_state, void *arg);
|
||||
int (*gov_change)(int tz_id, const char *gov_name, void *arg);
|
||||
int (*threshold_add)(int tz_id, int temperature, int direction, void *arg);
|
||||
int (*threshold_delete)(int tz_id, int temperature, int direction, void *arg);
|
||||
int (*threshold_flush)(int tz_id, void *arg);
|
||||
int (*threshold_up)(int tz_id, int temp, int prev_temp, void *arg);
|
||||
int (*threshold_down)(int tz_id, int temp, int prev_temp, void *arg);
|
||||
};
|
||||
|
||||
struct thermal_ops {
|
||||
@ -45,12 +59,18 @@ struct thermal_trip {
|
||||
int hyst;
|
||||
};
|
||||
|
||||
struct thermal_threshold {
|
||||
int temperature;
|
||||
int direction;
|
||||
};
|
||||
|
||||
struct thermal_zone {
|
||||
int id;
|
||||
int temp;
|
||||
char name[THERMAL_NAME_LENGTH];
|
||||
char governor[THERMAL_NAME_LENGTH];
|
||||
struct thermal_trip *trip;
|
||||
struct thermal_threshold *thresholds;
|
||||
};
|
||||
|
||||
struct thermal_cdev {
|
||||
@ -74,12 +94,16 @@ typedef int (*cb_tt_t)(struct thermal_trip *, void *);
|
||||
|
||||
typedef int (*cb_tc_t)(struct thermal_cdev *, void *);
|
||||
|
||||
typedef int (*cb_th_t)(struct thermal_threshold *, void *);
|
||||
|
||||
LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg);
|
||||
|
||||
LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg);
|
||||
|
||||
LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg);
|
||||
|
||||
LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg);
|
||||
|
||||
LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz,
|
||||
const char *name);
|
||||
|
||||
@ -124,6 +148,22 @@ LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler *
|
||||
LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th,
|
||||
struct thermal_zone *tz);
|
||||
|
||||
LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th,
|
||||
struct thermal_zone *tz);
|
||||
|
||||
LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th,
|
||||
struct thermal_zone *tz,
|
||||
int temperature,
|
||||
int direction);
|
||||
|
||||
LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th,
|
||||
struct thermal_zone *tz,
|
||||
int temperature,
|
||||
int direction);
|
||||
|
||||
LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th,
|
||||
struct thermal_zone *tz);
|
||||
|
||||
/*
|
||||
* Netlink thermal samples
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@ LIBTHERMAL_0.0.1 {
|
||||
for_each_thermal_zone;
|
||||
for_each_thermal_trip;
|
||||
for_each_thermal_cdev;
|
||||
for_each_thermal_threshold;
|
||||
thermal_zone_find_by_name;
|
||||
thermal_zone_find_by_id;
|
||||
thermal_zone_discover;
|
||||
@ -17,6 +18,10 @@ LIBTHERMAL_0.0.1 {
|
||||
thermal_cmd_get_trip;
|
||||
thermal_cmd_get_governor;
|
||||
thermal_cmd_get_temp;
|
||||
thermal_cmd_threshold_get;
|
||||
thermal_cmd_threshold_add;
|
||||
thermal_cmd_threshold_delete;
|
||||
thermal_cmd_threshold_flush;
|
||||
thermal_sampling_init;
|
||||
thermal_sampling_handle;
|
||||
thermal_sampling_fd;
|
||||
|
@ -1,10 +1,24 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1+
|
||||
// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <thermal.h>
|
||||
|
||||
#include "thermal_nl.h"
|
||||
|
||||
int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
if (!th)
|
||||
return 0;
|
||||
|
||||
for (i = 0; th[i].temperature != INT_MAX; i++)
|
||||
ret |= cb(&th[i], arg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg)
|
||||
{
|
||||
int i, ret = 0;
|
||||
@ -80,6 +94,9 @@ static int __thermal_zone_discover(struct thermal_zone *tz, void *th)
|
||||
if (thermal_cmd_get_trip(th, tz) < 0)
|
||||
return -1;
|
||||
|
||||
if (thermal_cmd_threshold_get(th, tz))
|
||||
return -1;
|
||||
|
||||
if (thermal_cmd_get_governor(th, tz))
|
||||
return -1;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
LIBTHERMAL_TOOLS_VERSION = 0
|
||||
LIBTHERMAL_TOOLS_PATCHLEVEL = 0
|
||||
LIBTHERMAL_TOOLS_EXTRAVERSION = 1
|
||||
LIBTHERMAL_TOOLS_EXTRAVERSION = 2
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
|
@ -38,6 +38,14 @@ struct thermal_data {
|
||||
struct thermal_handler *th;
|
||||
};
|
||||
|
||||
static int show_threshold(struct thermal_threshold *th, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("threshold temp=%d, direction=%d\n",
|
||||
th->temperature, th->direction);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n",
|
||||
@ -70,6 +78,8 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg)
|
||||
|
||||
for_each_thermal_trip(tz->trip, show_trip, NULL);
|
||||
|
||||
for_each_thermal_threshold(tz->thresholds, show_threshold, NULL);
|
||||
|
||||
show_temp(tz, arg);
|
||||
|
||||
show_governor(tz, arg);
|
||||
@ -77,6 +87,30 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_threshold(struct thermal_zone *tz, __maybe_unused void *arg)
|
||||
{
|
||||
struct thermal_handler *th = arg;
|
||||
int thresholds[] = { 43000, 65000, 49000, 55000, 57000 };
|
||||
size_t i;
|
||||
|
||||
INFO("Setting threshold for thermal zone '%s', id=%d\n", tz->name, tz->id);
|
||||
|
||||
if (thermal_cmd_threshold_flush(th, tz)) {
|
||||
ERROR("Failed to flush all previous thresholds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); i++)
|
||||
if (thermal_cmd_threshold_add(th, tz, thresholds[i],
|
||||
THERMAL_THRESHOLD_WAY_UP |
|
||||
THERMAL_THRESHOLD_WAY_DOWN)) {
|
||||
ERROR("Failed to set threshold\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tz_create(const char *name, int tz_id, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Thermal zone '%s'/%d created\n", name, tz_id);
|
||||
@ -197,6 +231,43 @@ static int gov_change(int tz_id, const char *name, __maybe_unused void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int threshold_add(int tz_id, int temp, int direction, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Threshold added tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int threshold_delete(int tz_id, int temp, int direction, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Threshold deleted tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int threshold_flush(int tz_id, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Thresholds flushed tz_id=%d\n", tz_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int threshold_up(int tz_id, int temp, int prev_temp, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Threshold crossed way up tz_id=%d: temp=%d, prev_temp=%d\n",
|
||||
tz_id, temp, prev_temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int threshold_down(int tz_id, int temp, int prev_temp, __maybe_unused void *arg)
|
||||
{
|
||||
INFO("Threshold crossed way down tz_id=%d: temp=%d, prev_temp=%d\n",
|
||||
tz_id, temp, prev_temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_ops ops = {
|
||||
.events.tz_create = tz_create,
|
||||
.events.tz_delete = tz_delete,
|
||||
@ -210,7 +281,12 @@ static struct thermal_ops ops = {
|
||||
.events.cdev_add = cdev_add,
|
||||
.events.cdev_delete = cdev_delete,
|
||||
.events.cdev_update = cdev_update,
|
||||
.events.gov_change = gov_change
|
||||
.events.gov_change = gov_change,
|
||||
.events.threshold_add = threshold_add,
|
||||
.events.threshold_delete = threshold_delete,
|
||||
.events.threshold_flush = threshold_flush,
|
||||
.events.threshold_up = threshold_up,
|
||||
.events.threshold_down = threshold_down,
|
||||
};
|
||||
|
||||
static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg)
|
||||
@ -280,6 +356,7 @@ enum {
|
||||
THERMAL_ENGINE_DAEMON_ERROR,
|
||||
THERMAL_ENGINE_LOG_ERROR,
|
||||
THERMAL_ENGINE_THERMAL_ERROR,
|
||||
THERMAL_ENGINE_THRESHOLD_ERROR,
|
||||
THERMAL_ENGINE_MAINLOOP_ERROR,
|
||||
};
|
||||
|
||||
@ -318,6 +395,8 @@ int main(int argc, char *argv[])
|
||||
return THERMAL_ENGINE_THERMAL_ERROR;
|
||||
}
|
||||
|
||||
for_each_thermal_zone(td.tz, set_threshold, td.th);
|
||||
|
||||
for_each_thermal_zone(td.tz, show_tz, td.th);
|
||||
|
||||
if (mainloop_init()) {
|
||||
|
Loading…
Reference in New Issue
Block a user