Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thermal/thresholds: Fix boundaries and detection routine

The current implementation does not work if the thermal zone is
interrupt driven only.

The boundaries are not correctly checked and computed as it happens
only when the temperature is increasing or decreasing.

The problem arises because the routine to detect when we cross a
threshold is correlated with the computation of the boundaries. We
assume we have to recompute the boundaries when a threshold is crossed
but actually we should do that even if the it is not the case.

Mixing the boundaries computation and the threshold detection for the
sake of optimizing the routine is much more complex as it appears
intuitively and prone to errors.

This fix separates the boundaries computation and the threshold
crossing detection into different routines. The result is a code much
more simple to understand, thus easier to maintain.

The drawback is we browse the thresholds list several time but we can
consider that as neglictible because that happens when the temperature
is updated. There are certainly some aeras to improve in the
temperature update routine but it would be not adequate as this change
aims to fix the thresholds for v6.13.

Fixes: 445936f9e258 ("thermal: core: Add user thresholds support")
Tested-by: Daniel Lezcano <daniel.lezcano@linaro.org> # rock5b, Lenovo x13s
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://patch.msgid.link/20241216212644.1145122-1-daniel.lezcano@linaro.org
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Daniel Lezcano and committed by
Rafael J. Wysocki
4feaedf7 65c8c78c

+40 -36
+40 -36
drivers/thermal/thermal_thresholds.c
··· 69 69 return NULL; 70 70 } 71 71 72 - static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature, 73 - int last_temperature, int direction, 74 - int *low, int *high) 75 - { 76 - 77 - if (temperature >= threshold->temperature) { 78 - if (threshold->temperature > *low && 79 - THERMAL_THRESHOLD_WAY_DOWN & threshold->direction) 80 - *low = threshold->temperature; 81 - 82 - if (last_temperature < threshold->temperature && 83 - threshold->direction & direction) 84 - return true; 85 - } else { 86 - if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP 87 - & threshold->direction) 88 - *high = threshold->temperature; 89 - 90 - if (last_temperature >= threshold->temperature && 91 - threshold->direction & direction) 92 - return true; 93 - } 94 - 95 - return false; 96 - } 97 - 98 72 static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature, 99 - int last_temperature, int *low, int *high) 73 + int last_temperature) 100 74 { 101 75 struct user_threshold *t; 102 76 103 77 list_for_each_entry(t, thresholds, list_node) { 104 - if (__thermal_threshold_is_crossed(t, temperature, last_temperature, 105 - THERMAL_THRESHOLD_WAY_UP, low, high)) 78 + 79 + if (!(t->direction & THERMAL_THRESHOLD_WAY_UP)) 80 + continue; 81 + 82 + if (temperature >= t->temperature && 83 + last_temperature < t->temperature) 106 84 return true; 107 85 } 108 86 ··· 88 110 } 89 111 90 112 static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature, 91 - int last_temperature, int *low, int *high) 113 + int last_temperature) 92 114 { 93 115 struct user_threshold *t; 94 116 95 117 list_for_each_entry_reverse(t, thresholds, list_node) { 96 - if (__thermal_threshold_is_crossed(t, temperature, last_temperature, 97 - THERMAL_THRESHOLD_WAY_DOWN, low, high)) 118 + 119 + if (!(t->direction & THERMAL_THRESHOLD_WAY_DOWN)) 120 + continue; 121 + 122 + if (temperature <= t->temperature && 123 + last_temperature > t->temperature) 98 124 return true; 99 125 } 100 126 101 127 return false; 128 + } 129 + 130 + static void thermal_threshold_find_boundaries(struct list_head *thresholds, int temperature, 131 + int *low, int *high) 132 + { 133 + struct user_threshold *t; 134 + 135 + list_for_each_entry(t, thresholds, list_node) { 136 + if (temperature < t->temperature && 137 + (t->direction & THERMAL_THRESHOLD_WAY_UP) && 138 + *high > t->temperature) 139 + *high = t->temperature; 140 + } 141 + 142 + list_for_each_entry_reverse(t, thresholds, list_node) { 143 + if (temperature > t->temperature && 144 + (t->direction & THERMAL_THRESHOLD_WAY_DOWN) && 145 + *low < t->temperature) 146 + *low = t->temperature; 147 + } 102 148 } 103 149 104 150 void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high) ··· 133 131 int last_temperature = tz->last_temperature; 134 132 135 133 lockdep_assert_held(&tz->lock); 134 + 135 + thermal_threshold_find_boundaries(thresholds, temperature, low, high); 136 136 137 137 /* 138 138 * We need a second update in order to detect a threshold being crossed ··· 155 151 * - decreased : thresholds are crossed the way down 156 152 */ 157 153 if (temperature > last_temperature) { 158 - if (thermal_thresholds_handle_raising(thresholds, temperature, 159 - last_temperature, low, high)) 154 + if (thermal_thresholds_handle_raising(thresholds, 155 + temperature, last_temperature)) 160 156 thermal_notify_threshold_up(tz); 161 157 } else { 162 - if (thermal_thresholds_handle_dropping(thresholds, temperature, 163 - last_temperature, low, high)) 158 + if (thermal_thresholds_handle_dropping(thresholds, 159 + temperature, last_temperature)) 164 160 thermal_notify_threshold_down(tz); 165 161 } 166 162 }