[PATCH v3 5/8] ACPI: thermal: Hold thermal zone lock around trip updates

From: Rafael J. Wysocki
Date: Tue Jul 25 2023 - 08:25:41 EST


From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>

There is a race condition between acpi_thermal_trips_update() and
acpi_thermal_check_fn(), because the trip points may get updated while
the latter is running which in theory may lead to inconsistent results.
For example, if two trips are updated together, using the temperature
value of one of them from before the update and the temperature value
of the other one from after the update may not lead to the expected
outcome.

To address this, make acpi_thermal_trips_update() hold the thermal zone
lock across the entire update of trip points.

While at it, change the acpi_thermal_trips_update() return data type
to void as that function always returns 0 anyway.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---

v2 -> v3: No changes.

v1 -> v2:
* Hold the thermal zone lock instead of thermal_check_lock around trip
point updates (this also helps to protect thermal_get_trend() from using
stale trip temperatures).
* Add a comment documenting the purpose of the locking.
* Make acpi_thermal_trips_update() void.

---
drivers/acpi/thermal.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/acpi/thermal.c
===================================================================
--- linux-pm.orig/drivers/acpi/thermal.c
+++ linux-pm/drivers/acpi/thermal.c
@@ -190,7 +190,7 @@ static int acpi_thermal_get_polling_freq
return 0;
}

-static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
+static void __acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
{
acpi_status status;
unsigned long long tmp;
@@ -398,17 +398,28 @@ static int acpi_thermal_trips_update(str
ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device");
}
}
+}

- return 0;
+static void acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
+{
+ /*
+ * The locking is needed here to protect thermal_get_trend() from using
+ * a stale passive trip temperature and to synchronize with the trip
+ * temperature updates in acpi_thermal_check_fn().
+ */
+ thermal_zone_device_lock(tz->thermal_zone);
+
+ __acpi_thermal_trips_update(tz, flag);
+
+ thermal_zone_device_unlock(tz->thermal_zone);
}

static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
{
- int i, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
bool valid;
+ int i;

- if (ret)
- return ret;
+ __acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);

valid = tz->trips.critical.valid |
tz->trips.hot.valid |