Skip to content

Commit 6d5537d

Browse files
committed
thermal: core: Use trip lists for trip crossing detection
Modify the thermal core to use three lists of trip points: trips_high, containing trips with thresholds strictly above the current thermal zone temperature, trips_reached, containing trips with thresholds at or below the current zone temperature, trips_invalid, containing trips with temperature equal to THERMAL_ZONE_INVALID, where the first two lists are always sorted by the current trip threshold. For each trip in trips_high, there is no mitigation under way and the trip threshold is equal to its temperature. In turn, for each trip in trips_reached, there is mitigation under way and the trip threshold is equal to its low temperature. The trips in trips_invalid, of course, need not be taken into consideration. The idea is to make __thermal_zone_device_update() walk trips_high and trips_reached instead of walking the entire table of trip points in a thermal zone. Usually, it will only need to walk a few entries in one of the lists and check one entry in the other list, depending on the direction of the zone temperature changes, because crossing many trips by the zone temperature in one go between two consecutive temperature checks should be unlikely (if it occurs often, the thermal zone temperature should probably be checked more often either or there are too many trips). This also helps to eliminate one temporary trip list used for trip crossing notification (only one temporary list is needed for this purpose instead of two) and the remaining temporary list may be sorted by the current trip threshold value, like the trips_reached list, so the additional notify_temp field in struct thermal_trip_desc is not necessary any more. Moreover, since the trips_reached and trips_high lists are sorted, the "low" and "high" values needed by thermal_zone_set_trips() can be determined in a straightforward way by looking at one end of each list. Of course, additional work is needed in some places in order to maintain the ordering of the lists, but it is limited to situations that should be rare, like updating a trip point temperature or hysteresis, thermal zone initialization, or system resume. Signed-off-by: Rafael J. Wysocki <[email protected]> Reviewed-by: Lukasz Luba <[email protected]> Link: https://patch.msgid.link/[email protected] [ rjw: Added a comment to thermal_zone_handle_trips() ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent a44b5e3 commit 6d5537d

File tree

2 files changed

+137
-82
lines changed

2 files changed

+137
-82
lines changed

drivers/thermal/thermal_core.c

Lines changed: 131 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -421,61 +421,14 @@ static void move_trip_to_sorted_list(struct thermal_trip_desc *td,
421421

422422
/* Assume that the new entry is likely to be the last one. */
423423
list_for_each_entry_reverse(entry, list, list_node) {
424-
if (entry->notify_temp <= td->notify_temp) {
424+
if (entry->threshold <= td->threshold) {
425425
list_add(&td->list_node, &entry->list_node);
426426
return;
427427
}
428428
}
429429
list_add(&td->list_node, list);
430430
}
431431

432-
static void handle_thermal_trip(struct thermal_zone_device *tz,
433-
struct thermal_trip_desc *td,
434-
struct list_head *way_up_list,
435-
struct list_head *way_down_list)
436-
{
437-
const struct thermal_trip *trip = &td->trip;
438-
int old_threshold;
439-
440-
if (trip->temperature == THERMAL_TEMP_INVALID)
441-
return;
442-
443-
/*
444-
* If the trip temperature or hysteresis has been updated recently,
445-
* the threshold needs to be computed again using the new values.
446-
* However, its initial value still reflects the old ones and that
447-
* is what needs to be compared with the previous zone temperature
448-
* to decide which action to take.
449-
*/
450-
old_threshold = td->threshold;
451-
td->threshold = trip->temperature;
452-
453-
if (tz->last_temperature >= old_threshold &&
454-
tz->last_temperature != THERMAL_TEMP_INIT) {
455-
/*
456-
* Mitigation is under way, so it needs to stop if the zone
457-
* temperature falls below the low temperature of the trip.
458-
* In that case, the trip temperature becomes the new threshold.
459-
*/
460-
if (tz->temperature < trip->temperature - trip->hysteresis) {
461-
td->notify_temp = trip->temperature - trip->hysteresis;
462-
move_trip_to_sorted_list(td, way_down_list);
463-
} else {
464-
td->threshold -= trip->hysteresis;
465-
}
466-
} else if (tz->temperature >= trip->temperature) {
467-
/*
468-
* There is no mitigation under way, so it needs to be started
469-
* if the zone temperature exceeds the trip one. The new
470-
* threshold is then set to the low temperature of the trip.
471-
*/
472-
td->notify_temp = trip->temperature;
473-
move_trip_to_sorted_list(td, way_up_list);
474-
475-
td->threshold -= trip->hysteresis;
476-
}
477-
}
478-
479432
static void thermal_zone_device_check(struct work_struct *work)
480433
{
481434
struct thermal_zone_device *tz = container_of(work, struct
@@ -484,9 +437,30 @@ static void thermal_zone_device_check(struct work_struct *work)
484437
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
485438
}
486439

440+
static void move_to_trips_high(struct thermal_zone_device *tz,
441+
struct thermal_trip_desc *td)
442+
{
443+
td->threshold = td->trip.temperature;
444+
move_trip_to_sorted_list(td, &tz->trips_high);
445+
}
446+
447+
static void move_to_trips_reached(struct thermal_zone_device *tz,
448+
struct thermal_trip_desc *td)
449+
{
450+
td->threshold = td->trip.temperature - td->trip.hysteresis;
451+
move_trip_to_sorted_list(td, &tz->trips_reached);
452+
}
453+
454+
static void move_to_trips_invalid(struct thermal_zone_device *tz,
455+
struct thermal_trip_desc *td)
456+
{
457+
td->threshold = INT_MAX;
458+
list_move(&td->list_node, &tz->trips_invalid);
459+
}
460+
487461
static void thermal_zone_device_init(struct thermal_zone_device *tz)
488462
{
489-
struct thermal_trip_desc *td;
463+
struct thermal_trip_desc *td, *next;
490464

491465
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
492466

@@ -500,6 +474,21 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
500474
list_for_each_entry(instance, &td->thermal_instances, trip_node)
501475
instance->initialized = false;
502476
}
477+
/*
478+
* At this point, all valid trips need to be moved to trips_high so that
479+
* mitigation can be started if the zone temperature is above them.
480+
*/
481+
list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) {
482+
if (td->trip.temperature != THERMAL_TEMP_INVALID)
483+
move_to_trips_high(tz, td);
484+
}
485+
/* The trips_reached list may not be empty during system resume. */
486+
list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) {
487+
if (td->trip.temperature == THERMAL_TEMP_INVALID)
488+
move_to_trips_invalid(tz, td);
489+
else
490+
move_to_trips_high(tz, td);
491+
}
503492
}
504493

505494
static void thermal_governor_trip_crossed(struct thermal_governor *governor,
@@ -544,45 +533,121 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz,
544533
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
545534
struct thermal_trip *trip, int hyst)
546535
{
536+
struct thermal_trip_desc *td = trip_to_trip_desc(trip);
537+
547538
WRITE_ONCE(trip->hysteresis, hyst);
548539
thermal_notify_tz_trip_change(tz, trip);
540+
/*
541+
* If the zone temperature is above or at the trip tmperature, the trip
542+
* is in the trips_reached list and its threshold is equal to its low
543+
* temperature. It needs to stay in that list, but its threshold needs
544+
* to be updated and the list ordering may need to be restored.
545+
*/
546+
if (tz->temperature >= td->threshold)
547+
move_to_trips_reached(tz, td);
549548
}
550549

551550
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
552551
struct thermal_trip *trip, int temp)
553552
{
554-
if (trip->temperature == temp)
553+
struct thermal_trip_desc *td = trip_to_trip_desc(trip);
554+
int old_temp = trip->temperature;
555+
556+
if (old_temp == temp)
555557
return;
556558

557559
WRITE_ONCE(trip->temperature, temp);
558560
thermal_notify_tz_trip_change(tz, trip);
559561

560-
if (temp == THERMAL_TEMP_INVALID) {
561-
struct thermal_trip_desc *td = trip_to_trip_desc(trip);
562+
if (old_temp == THERMAL_TEMP_INVALID) {
563+
/*
564+
* The trip was invalid before the change, so move it to the
565+
* trips_high list regardless of the new temperature value
566+
* because there is no mitigation under way for it. If a
567+
* mitigation needs to be started, the trip will be moved to the
568+
* trips_reached list later.
569+
*/
570+
move_to_trips_high(tz, td);
571+
return;
572+
}
562573

574+
if (temp == THERMAL_TEMP_INVALID) {
563575
/*
564-
* If the trip has been crossed on the way up, some adjustments
565-
* are needed to compensate for the lack of it going forward.
576+
* If the trip is in the trips_reached list, mitigation is under
577+
* way for it and it needs to be stopped because the trip is
578+
* effectively going away.
566579
*/
567580
if (tz->temperature >= td->threshold)
568581
thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false);
569582

583+
move_to_trips_invalid(tz, td);
584+
return;
585+
}
586+
587+
/*
588+
* The trip stays on its current list, but its threshold needs to be
589+
* updated due to the temperature change and the list ordering may need
590+
* to be restored.
591+
*/
592+
if (tz->temperature >= td->threshold)
593+
move_to_trips_reached(tz, td);
594+
else
595+
move_to_trips_high(tz, td);
596+
}
597+
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);
598+
599+
static void thermal_zone_handle_trips(struct thermal_zone_device *tz,
600+
struct thermal_governor *governor,
601+
int *low, int *high)
602+
{
603+
struct thermal_trip_desc *td, *next;
604+
LIST_HEAD(way_down_list);
605+
606+
/* Check the trips that were below or at the zone temperature. */
607+
list_for_each_entry_safe_reverse(td, next, &tz->trips_reached, list_node) {
608+
if (td->threshold <= tz->temperature)
609+
break;
610+
611+
thermal_trip_crossed(tz, td, governor, false);
612+
/*
613+
* The current trips_high list needs to be processed before
614+
* adding new entries to it, so put them on a temporary list.
615+
*/
616+
list_move(&td->list_node, &way_down_list);
617+
}
618+
/* Check the trips that were previously above the zone temperature. */
619+
list_for_each_entry_safe(td, next, &tz->trips_high, list_node) {
620+
if (td->threshold > tz->temperature)
621+
break;
622+
623+
thermal_trip_crossed(tz, td, governor, true);
624+
move_to_trips_reached(tz, td);
625+
}
626+
/* Move all of the trips from the temporary list to trips_high. */
627+
list_for_each_entry_safe(td, next, &way_down_list, list_node)
628+
move_to_trips_high(tz, td);
629+
630+
if (!list_empty(&tz->trips_reached)) {
631+
td = list_last_entry(&tz->trips_reached,
632+
struct thermal_trip_desc, list_node);
570633
/*
571-
* Invalidate the threshold to avoid triggering a spurious
572-
* trip crossing notification when the trip becomes valid.
634+
* Set the "low" value below the current trip threshold in case
635+
* the zone temperature is at that threshold and stays there,
636+
* which would trigger a new interrupt immediately in vain.
573637
*/
574-
td->threshold = INT_MAX;
638+
*low = td->threshold - 1;
639+
}
640+
if (!list_empty(&tz->trips_high)) {
641+
td = list_first_entry(&tz->trips_high,
642+
struct thermal_trip_desc, list_node);
643+
*high = td->threshold;
575644
}
576645
}
577-
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);
578646

579647
void __thermal_zone_device_update(struct thermal_zone_device *tz,
580648
enum thermal_notify_event event)
581649
{
582650
struct thermal_governor *governor = thermal_get_tz_governor(tz);
583-
struct thermal_trip_desc *td, *next;
584-
LIST_HEAD(way_down_list);
585-
LIST_HEAD(way_up_list);
586651
int low = -INT_MAX, high = INT_MAX;
587652
int temp, ret;
588653

@@ -614,25 +679,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
614679

615680
tz->notify_event = event;
616681

617-
for_each_trip_desc(tz, td) {
618-
handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
619-
620-
if (td->threshold <= tz->temperature && td->threshold > low)
621-
low = td->threshold;
622-
623-
if (td->threshold >= tz->temperature && td->threshold < high)
624-
high = td->threshold;
625-
}
626-
627-
list_for_each_entry_safe(td, next, &way_up_list, list_node) {
628-
thermal_trip_crossed(tz, td, governor, true);
629-
list_del_init(&td->list_node);
630-
}
631-
632-
list_for_each_entry_safe_reverse(td, next, &way_down_list, list_node) {
633-
thermal_trip_crossed(tz, td, governor, false);
634-
list_del_init(&td->list_node);
635-
}
682+
thermal_zone_handle_trips(tz, governor, &low, &high);
636683

637684
thermal_thresholds_handle(tz, &low, &high);
638685

@@ -1501,6 +1548,9 @@ thermal_zone_device_register_with_trips(const char *type,
15011548
}
15021549

15031550
INIT_LIST_HEAD(&tz->node);
1551+
INIT_LIST_HEAD(&tz->trips_high);
1552+
INIT_LIST_HEAD(&tz->trips_reached);
1553+
INIT_LIST_HEAD(&tz->trips_invalid);
15041554
ida_init(&tz->ida);
15051555
mutex_init(&tz->lock);
15061556
init_completion(&tz->removal);
@@ -1530,7 +1580,7 @@ thermal_zone_device_register_with_trips(const char *type,
15301580
* this only matters for the trips that start as invalid and
15311581
* become valid later.
15321582
*/
1533-
td->threshold = INT_MAX;
1583+
move_to_trips_invalid(tz, td);
15341584
}
15351585

15361586
tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay);

drivers/thermal/thermal_core.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ struct thermal_trip_desc {
3333
struct thermal_trip_attrs trip_attrs;
3434
struct list_head list_node;
3535
struct list_head thermal_instances;
36-
int notify_temp;
3736
int threshold;
3837
};
3938

@@ -78,6 +77,9 @@ struct thermal_governor {
7877
* @device: &struct device for this thermal zone
7978
* @removal: removal completion
8079
* @resume: resume completion
80+
* @trips_high: trips above the current zone temperature
81+
* @trips_reached: trips below or at the current zone temperature
82+
* @trips_invalid: trips with invalid temperature
8183
* @mode: current mode of this thermal zone
8284
* @devdata: private pointer for device private data
8385
* @num_trips: number of trip points the thermal zone supports
@@ -118,6 +120,9 @@ struct thermal_zone_device {
118120
struct completion removal;
119121
struct completion resume;
120122
struct attribute_group trips_attribute_group;
123+
struct list_head trips_high;
124+
struct list_head trips_reached;
125+
struct list_head trips_invalid;
121126
enum thermal_device_mode mode;
122127
void *devdata;
123128
int num_trips;

0 commit comments

Comments
 (0)