From f2675e588f928f7f3051765563a18f90332bd57b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Jan 2024 18:55:13 +0100 Subject: [PATCH 1/8] thermal: gov_fair_share: Fix dependency on trip points ordering The computation in the fair share governor's get_trip_level() function currently works under the assumption that the temperature ordering of trips[] in a thermal zone is ascending, which need not be the case. However, get_trip_level() can be made work regardless of whether or not the trips table is ordered by temperature in any way, so change it accordingly. Signed-off-by: Rafael J. Wysocki --- drivers/thermal/gov_fair_share.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index 538abb7de4e2b..4da25a0009d73 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -18,22 +18,24 @@ static int get_trip_level(struct thermal_zone_device *tz) { const struct thermal_trip *trip, *level_trip = NULL; - int trip_level; + int trip_level = -1; for_each_trip(tz, trip) { if (trip->temperature >= tz->temperature) - break; + continue; + + trip_level++; - level_trip = trip; + if (!level_trip || trip->temperature > level_trip->temperature) + level_trip = trip; } /* Bail out if the temperature is not greater than any trips. */ - if (!level_trip) + if (trip_level < 0) return 0; - trip_level = thermal_zone_trip_id(tz, level_trip); - - trace_thermal_zone_trip(tz, trip_level, level_trip->type); + trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, level_trip), + level_trip->type); return trip_level; } From 54d94009cb6f51d3ecd56bfc63c83e789c0b18b4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Jan 2024 18:57:06 +0100 Subject: [PATCH 2/8] thermal: gov_bang_bang: Fix possible cooling device state ping-pong The current behavior of thermal_zone_trip_update() in the bang-bang thermal governor may be problematic for trip points with 0 hysteresis, because when the zone temperature reaches the trip temperature and stays there, it will then cause the cooling device go "on" and "off" alternately, which is not desirable. Address this by requiring the zone temperature to actually fall below trip->temperature - trip->hysteresis for the cooling device to go off. Signed-off-by: Rafael J. Wysocki --- drivers/thermal/gov_bang_bang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index 6ddf0accdc98f..c3b2943a2db8b 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -49,7 +49,7 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, if (instance->target == 0 && tz->temperature >= trip->temperature) instance->target = 1; else if (instance->target == 1 && - tz->temperature <= trip->temperature - trip->hysteresis) + tz->temperature < trip->temperature - trip->hysteresis) instance->target = 0; dev_dbg(&instance->cdev->device, "target=%d\n", From b377252eeec91f347cd538011f956a4fe73794b3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 30 Jan 2024 12:12:33 +0100 Subject: [PATCH 3/8] thermal: core: Change governor name to const char pointer All users are already assigning a const char * to the `governor_name` member of struct thermal_zone_params and to the `name` member of struct thermal_governor. Even if users are technically wrong, it just makes more sense to change this member to be a const char pointer instead of doing the other way around. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Rafael J. Wysocki --- include/linux/thermal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index b7a3deb372fd4..65d8f92a9a0db 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -214,7 +214,7 @@ struct thermal_zone_device { * @governor_list: node in thermal_governor_list (in thermal_core.c) */ struct thermal_governor { - char name[THERMAL_NAME_LENGTH]; + const char *name; int (*bind_to_tz)(struct thermal_zone_device *tz); void (*unbind_from_tz)(struct thermal_zone_device *tz); int (*throttle)(struct thermal_zone_device *tz, @@ -226,7 +226,7 @@ struct thermal_governor { /* Structure to define Thermal Zone parameters */ struct thermal_zone_params { - char governor_name[THERMAL_NAME_LENGTH]; + const char *governor_name; /* * a boolean to indicate if the thermal to hwmon sysfs interface From 2e171a57c312cb732d633c5ccc51551588b7a855 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Feb 2024 20:09:15 +0100 Subject: [PATCH 4/8] iwlwifi: mvm: Drop unused fw_trips_index[] from iwl_mvm_thermal_device The fw_trips_index[] array in struct iwl_mvm_thermal_device is only populated, but never read, so drop it. Note that the iwl_mvm_send_temp_report_ths_cmd() code populating fw_trips_index[] is questionable, because it accesses a trips table of a thermal zone directly, which is not guaranteed to work in the future. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 -- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 13 +------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 40627961b834a..a618824f0b658 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -539,12 +539,10 @@ struct iwl_mvm_tt_mgmt { /** *struct iwl_mvm_thermal_device - thermal zone related data * @temp_trips: temperature thresholds for report - * @fw_trips_index: keep indexes to original array - temp_trips * @tzone: thermal zone device data */ struct iwl_mvm_thermal_device { struct thermal_trip trips[IWL_MAX_DTS_TRIPS]; - u8 fw_trips_index[IWL_MAX_DTS_TRIPS]; struct thermal_zone_device *tzone; }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index dee9c367dcd35..a2f921a1f5bd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -562,7 +562,7 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) struct temp_report_ths_cmd cmd = {0}; int ret; #ifdef CONFIG_THERMAL - int i, j, idx = 0; + int i, idx = 0; lockdep_assert_held(&mvm->mutex); @@ -588,17 +588,6 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) /*sort cmd array*/ sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL); - /* we should save the indexes of trips because we sort - * and compress the orginal array - */ - for (i = 0; i < idx; i++) { - for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) { - if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) == - mvm->tz_device.trips[j].temperature) - mvm->tz_device.fw_trips_index[i] = j; - } - } - send: #endif ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP, From 61d88437546f5f40b6411aac17c55d0298c7a19d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Feb 2024 20:10:24 +0100 Subject: [PATCH 5/8] iwlwifi: mvm: Populate trip table before registering thermal zone The trip table in iwl_mvm_thermal_zone_register() is populated after passing it to thermal_zone_device_register_with_trips(), so it may be accessed (for instance, via sysfs) before it is ready. To prevent that from happening, modify the function to populate the trip table before calling thermal_zone_device_register_with_trips(). Also make the code use THERMAL_TEMP_INVALID as the "invalid temperature" value which is also meaningful for the core. Signed-off-by: Rafael J. Wysocki Acked-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index a2f921a1f5bd6..4fcbf02b415e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -575,7 +575,7 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) /* compress trips to cmd array, remove uninitialized values*/ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) { - if (mvm->tz_device.trips[i].temperature != INT_MIN) { + if (mvm->tz_device.trips[i].temperature != THERMAL_TEMP_INVALID) { cmd.thresholds[idx++] = cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000)); } @@ -675,6 +675,14 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF); + /* + * 0 is a valid temperature, + * so initialize the array with S16_MIN which invalid temperature + */ + for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) { + mvm->tz_device.trips[i].temperature = THERMAL_TEMP_INVALID; + mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE; + } mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name, mvm->tz_device.trips, IWL_MAX_DTS_TRIPS, @@ -693,15 +701,6 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) if (ret) { IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n"); thermal_zone_device_unregister(mvm->tz_device.tzone); - return; - } - - /* 0 is a valid temperature, - * so initialize the array with S16_MIN which invalid temperature - */ - for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) { - mvm->tz_device.trips[i].temperature = INT_MIN; - mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE; } } From 85af3310df34b0931daba8732d5b2c64a81c3b8d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Feb 2024 20:12:38 +0100 Subject: [PATCH 6/8] iwlwifi: mvm: Use for_each_thermal_trip() for walking trip points The code walking trip points in iwl_mvm_send_temp_report_ths_cmd() reads the trip table passed to thermal_zone_device_register_with_trips() in order to get the current trip temperatures, but this is not guaranteed to work in the future, because the thermal zone will store trip points information internally. For this reason, make iwl_mvm_send_temp_report_ths_cmd() use for_each_thermal_trip() as appropriate for walking trip points in a given thermal zone. No intentional functional impact, but it is requisite for future thermal core improvements. Signed-off-by: Rafael J. Wysocki Acked-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 40 +++++++++++++-------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 4fcbf02b415e5..d2a00cbcbc428 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -555,6 +555,22 @@ static int compare_temps(const void *a, const void *b) return ((s16)le16_to_cpu(*(__le16 *)a) - (s16)le16_to_cpu(*(__le16 *)b)); } + +struct iwl_trip_walk_data { + __le16 *thresholds; + int count; +}; + +static int iwl_trip_temp_cb(struct thermal_trip *trip, void *arg) +{ + struct iwl_trip_walk_data *twd = arg; + + if (trip->temperature == THERMAL_TEMP_INVALID) + return 0; + + twd->thresholds[twd->count++] = cpu_to_le16((s16)(trip->temperature / 1000)); + return 0; +} #endif int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) @@ -562,31 +578,25 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) struct temp_report_ths_cmd cmd = {0}; int ret; #ifdef CONFIG_THERMAL - int i, idx = 0; + struct iwl_trip_walk_data twd = { .thresholds = cmd.thresholds, .count = 0 }; lockdep_assert_held(&mvm->mutex); if (!mvm->tz_device.tzone) goto send; - /* The driver holds array of temperature trips that are unsorted - * and uncompressed, the FW should get it compressed and sorted + /* + * The thermal core holds an array of temperature trips that are + * unsorted and uncompressed, the FW should get it compressed and + * sorted. */ /* compress trips to cmd array, remove uninitialized values*/ - for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) { - if (mvm->tz_device.trips[i].temperature != THERMAL_TEMP_INVALID) { - cmd.thresholds[idx++] = - cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000)); - } - } - cmd.num_temps = cpu_to_le32(idx); - - if (!idx) - goto send; + for_each_thermal_trip(mvm->tz_device.tzone, iwl_trip_temp_cb, &twd); - /*sort cmd array*/ - sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL); + cmd.num_temps = cpu_to_le32(twd.count); + if (twd.count) + sort(cmd.thresholds, twd.count, sizeof(s16), compare_temps, NULL); send: #endif From ccd975daa807543a0050ab0a84f8581e3aae62a6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 9 Feb 2024 14:49:49 +0100 Subject: [PATCH 7/8] thermal: sysfs: Fix up white space in trip_point_temp_store() Remove an excess tab character from an otherwise empty code line. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Stanislaw Gruszka --- drivers/thermal/thermal_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index f4033865b093a..d55f9303afb5b 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -136,7 +136,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, unlock: mutex_unlock(&tz->lock); - + return ret ? ret : count; } From 0fac6893ff6c08d6fab6f56b9550a2c4cee589fd Mon Sep 17 00:00:00 2001 From: Di Shen Date: Wed, 7 Feb 2024 10:09:23 +0800 Subject: [PATCH 8/8] thermal: gov_power_allocator: Avoid overwriting PID coefficients from setup time When the PID coefficients k_* are set via sysfs before the IPA algorithm is triggered then the coefficients would be overwritten after IPA throttle() is called. The old configuration values might be different than the new values estimated by the IPA internal algorithm. There might be a time delay when this overwriting happens. It depends on the thermal zone temperature value. The temperature value needs to cross the first trip point value then IPA algorithms start operating. Although, the PID coefficients setup time should not be affected or linked to any later operating phase and values must not be overwritten. This patch initializes params->sustainable_power when the governor binds to thermal zone to avoid overwriting k_*. The basic function won't be affected, as the k_* still can be estimated if the sustainable_power is modified. Signed-off-by: Di Shen Reviewed-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- drivers/thermal/gov_power_allocator.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 81e061f183ad1..1b17dc4c219cc 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -711,6 +711,8 @@ static int power_allocator_bind(struct thermal_zone_device *tz) if (!tz->tzp->sustainable_power) dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n"); + else + params->sustainable_power = tz->tzp->sustainable_power; estimate_pid_constants(tz, tz->tzp->sustainable_power, params->trip_switch_on,