diff mbox series

[v4,3/4] thermal/core: Build ascending ordered indexes for the trip points

Message ID 20220718145038.1114379-3-daniel.lezcano@linaro.org
State New
Headers show
Series [v4,1/4] thermal/core: Encapsulate the trip point crossed function | expand

Commit Message

Daniel Lezcano July 18, 2022, 2:50 p.m. UTC
By convention the trips points are declared in the ascending
temperature order. However, no specification for the device tree, ACPI
or documentation tells the trip points must be ordered this way.

In the other hand, we need those to be ordered to browse them at the
thermal events. But if we assume they are ordered and change the code
based on this assumption, any platform with shuffled trip points
description will be broken (if they exist).

Instead of taking the risk of breaking the existing platforms, use an
array of temperature ordered trip identifiers and make it available
for the code needing to browse the trip points in an ordered way.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---

V4:
  - Fix conflicts due to tz->trips renamed to tz->num_trips
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/thermal/thermal_core.c | 63 +++++++++++++++++++++++++++-------
 include/linux/thermal.h        |  2 ++
 2 files changed, 53 insertions(+), 12 deletions(-)

Comments

Rafael J. Wysocki July 19, 2022, 6:56 p.m. UTC | #1
On Mon, Jul 18, 2022 at 4:50 PM Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
>
> By convention the trips points are declared in the ascending
> temperature order. However, no specification for the device tree, ACPI
> or documentation tells the trip points must be ordered this way.
>
> In the other hand, we need those to be ordered to browse them at the

s/In/On/

> thermal events.

What if they are all inspected every time?

> But if we assume they are ordered and change the code
> based on this assumption, any platform with shuffled trip points
> description will be broken (if they exist).
>
> Instead of taking the risk of breaking the existing platforms, use an
> array of temperature ordered trip identifiers and make it available
> for the code needing to browse the trip points in an ordered way.

Well, having ops->get_trip_temp() suggests that the trip temperatures
can be dynamic.  Is the ordering guaranteed to be preserved in that
case?

Anyway, if they need to be sorted, why don't we just sort them
properly instead of adding this extra array?

> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
>
> V4:
>   - Fix conflicts due to tz->trips renamed to tz->num_trips
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
>  drivers/thermal/thermal_core.c | 63 +++++++++++++++++++++++++++-------
>  include/linux/thermal.h        |  2 ++
>  2 files changed, 53 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index fc6ccc5edbfb..f274dc7d9c48 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -355,7 +355,8 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
>  }
>
>  static void handle_thermal_trip_crossed(struct thermal_zone_device *tz, int trip,
> -                                       int trip_temp, int trip_hyst, enum thermal_trip_type trip_type)
> +                                       int trip_temp, int trip_hyst,
> +                                       enum thermal_trip_type trip_type)
>  {
>         if (tz->last_temperature == THERMAL_TEMP_INVALID)
>                 return;
> @@ -1171,6 +1172,47 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
>         if (delay_ms > 1000)
>                 *delay_jiffies = round_jiffies(*delay_jiffies);
>  }
> +
> +static void sort_trips_indexes(struct thermal_zone_device *tz)
> +{
> +       int i, j;
> +
> +       for (i = 0; i < tz->num_trips; i++)
> +               tz->trips_indexes[i] = i;
> +
> +       for (i = 0; i < tz->num_trips; i++) {
> +
> +               for (j = i + 1; j < tz->num_trips; j++) {
> +                       int t1, t2;
> +
> +                       tz->ops->get_trip_temp(tz, tz->trips_indexes[i], &t1);
> +                       tz->ops->get_trip_temp(tz, tz->trips_indexes[j], &t2);
> +
> +                       if (t1 > t2)
> +                               swap(tz->trips_indexes[i], tz->trips_indexes[j]);
> +               }
> +       }
> +}
> +
> +static int thermal_zone_device_trip_init(struct thermal_zone_device *tz)
> +{
> +       enum thermal_trip_type trip_type;
> +       int trip_temp, i;
> +
> +       tz->trips_indexes = kzalloc(tz->num_trips * sizeof(int), GFP_KERNEL);
> +       if (!tz->trips_indexes)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < tz->num_trips; i++) {
> +               if (tz->ops->get_trip_type(tz, i, &trip_type) ||
> +                   tz->ops->get_trip_temp(tz, i, &trip_temp) || !trip_temp)
> +                       set_bit(i, &tz->trips_disabled);
> +       }
> +
> +       sort_trips_indexes(tz);
> +
> +       return 0;
> +}
>
>  /**
>   * thermal_zone_device_register_with_trips() - register a new thermal zone device
> @@ -1204,11 +1246,8 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
>                                         int polling_delay)
>  {
>         struct thermal_zone_device *tz;
> -       enum thermal_trip_type trip_type;
> -       int trip_temp;
>         int id;
>         int result;
> -       int count;
>         struct thermal_governor *governor;
>
>         if (!type || strlen(type) == 0) {
> @@ -1281,12 +1320,9 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
>         if (result)
>                 goto release_device;
>
> -       for (count = 0; count < num_trips; count++) {
> -               if (tz->ops->get_trip_type(tz, count, &trip_type) ||
> -                   tz->ops->get_trip_temp(tz, count, &trip_temp) ||
> -                   !trip_temp)
> -                       set_bit(count, &tz->trips_disabled);
> -       }
> +       result = thermal_zone_device_trip_init(tz);
> +       if (result)
> +               goto unregister;
>
>         /* Update 'this' zone's governor information */
>         mutex_lock(&thermal_governor_lock);
> @@ -1299,7 +1335,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
>         result = thermal_set_governor(tz, governor);
>         if (result) {
>                 mutex_unlock(&thermal_governor_lock);
> -               goto unregister;
> +               goto kfree_indexes;
>         }
>
>         mutex_unlock(&thermal_governor_lock);
> @@ -1307,7 +1343,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
>         if (!tz->tzp || !tz->tzp->no_hwmon) {
>                 result = thermal_add_hwmon_sysfs(tz);
>                 if (result)
> -                       goto unregister;
> +                       goto kfree_indexes;
>         }
>
>         mutex_lock(&thermal_list_lock);
> @@ -1328,6 +1364,8 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
>
>         return tz;
>
> +kfree_indexes:
> +       kfree(tz->trips_indexes);
>  unregister:
>         device_del(&tz->device);
>  release_device:
> @@ -1406,6 +1444,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
>         thermal_remove_hwmon_sysfs(tz);
>         ida_simple_remove(&thermal_tz_ida, tz->id);
>         ida_destroy(&tz->ida);
> +       kfree(tz->trips_indexes);
>         mutex_destroy(&tz->lock);
>         device_unregister(&tz->device);
>
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 1386c713885d..4e576184df49 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -125,6 +125,7 @@ struct thermal_cooling_device {
>   * @devdata:   private pointer for device private data
>   * @trips:     an array of struct thermal_trip
>   * @num_trips: number of trip points the thermal zone supports
> + * @trips_indexes:     an array of sorted trip points indexes
>   * @trips_disabled;    bitmap for disabled trips
>   * @passive_delay_jiffies: number of jiffies to wait between polls when
>   *                     performing passive cooling.
> @@ -166,6 +167,7 @@ struct thermal_zone_device {
>         void *devdata;
>         struct thermal_trip *trips;
>         int num_trips;
> +       int *trips_indexes;
>         unsigned long trips_disabled;   /* bitmap for disabled trips */
>         unsigned long passive_delay_jiffies;
>         unsigned long polling_delay_jiffies;
> --
> 2.25.1
>
Daniel Lezcano July 21, 2022, 10:59 a.m. UTC | #2
On 19/07/2022 20:56, Rafael J. Wysocki wrote:
> On Mon, Jul 18, 2022 at 4:50 PM Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
>>
>> By convention the trips points are declared in the ascending
>> temperature order. However, no specification for the device tree, ACPI
>> or documentation tells the trip points must be ordered this way.
>>
>> In the other hand, we need those to be ordered to browse them at the
> 
> s/In/On/
> 
>> thermal events.
> 
> What if they are all inspected every time?

My bad, my sentence is confusing. The trip point are browsed every time 
and we need to have them ordered to detect correctly the thermal events.

>> But if we assume they are ordered and change the code
>> based on this assumption, any platform with shuffled trip points
>> description will be broken (if they exist).
>>
>> Instead of taking the risk of breaking the existing platforms, use an
>> array of temperature ordered trip identifiers and make it available
>> for the code needing to browse the trip points in an ordered way.
> 
> Well, having ops->get_trip_temp() suggests that the trip temperatures
> can be dynamic.  Is the ordering guaranteed to be preserved in that
> case?

The number of trips can not be changed. It is fixed when the thermal 
zone is created AFAICT. The get_trip_temp() is just a way to let the 
different driver declare their own trip structure which is actually 
something I'm trying to fix by moving the structure thermal_trip inside 
the thermal zone. But that is a longer and separate work.

> Anyway, if they need to be sorted, why don't we just sort them
> properly instead of adding this extra array?

We can not because ATM the trip points array is private to the different 
sensors.

[ ... ]
Rafael J. Wysocki July 21, 2022, 11:25 a.m. UTC | #3
On Thu, Jul 21, 2022 at 12:59 PM Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
>
> On 19/07/2022 20:56, Rafael J. Wysocki wrote:
> > On Mon, Jul 18, 2022 at 4:50 PM Daniel Lezcano
> > <daniel.lezcano@linaro.org> wrote:
> >>
> >> By convention the trips points are declared in the ascending
> >> temperature order. However, no specification for the device tree, ACPI
> >> or documentation tells the trip points must be ordered this way.
> >>
> >> In the other hand, we need those to be ordered to browse them at the
> >
> > s/In/On/
> >
> >> thermal events.
> >
> > What if they are all inspected every time?
>
> My bad, my sentence is confusing. The trip point are browsed every time
> and we need to have them ordered to detect correctly the thermal events.

I see.

So this mostly is a preparation for patch 4, isn't it?

> >> But if we assume they are ordered and change the code
> >> based on this assumption, any platform with shuffled trip points
> >> description will be broken (if they exist).
> >>
> >> Instead of taking the risk of breaking the existing platforms, use an
> >> array of temperature ordered trip identifiers and make it available
> >> for the code needing to browse the trip points in an ordered way.
> >
> > Well, having ops->get_trip_temp() suggests that the trip temperatures
> > can be dynamic.  Is the ordering guaranteed to be preserved in that
> > case?
>
> The number of trips can not be changed. It is fixed when the thermal
> zone is created AFAICT.

The current code appears to assume that and I think that this is a
reasonable expectation.

> The get_trip_temp() is just a way to let the
> different driver declare their own trip structure which is actually
> something I'm trying to fix by moving the structure thermal_trip inside
> the thermal zone. But that is a longer and separate work.

Well, I'm not sure.

Trip point temperatures can be set via trip_point_temp_store() at
least in principle.  How is it guaranteed that this won't affect the
ordering?

> > Anyway, if they need to be sorted, why don't we just sort them
> > properly instead of adding this extra array?
>
> We can not because ATM the trip points array is private to the different
> sensors.

Well, the core could create an array or list of trip points for the
thermal zone during registration and populate it from the
driver-provided data.  Then, it could be sorted at the creation time.

However, the above question needs to be addressed first.
Daniel Lezcano July 21, 2022, 9:15 p.m. UTC | #4
On 21/07/2022 13:25, Rafael J. Wysocki wrote:
> On Thu, Jul 21, 2022 at 12:59 PM Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
>>
>> On 19/07/2022 20:56, Rafael J. Wysocki wrote:
>>> On Mon, Jul 18, 2022 at 4:50 PM Daniel Lezcano
>>> <daniel.lezcano@linaro.org> wrote:
>>>>
>>>> By convention the trips points are declared in the ascending
>>>> temperature order. However, no specification for the device tree, ACPI
>>>> or documentation tells the trip points must be ordered this way.
>>>>
>>>> In the other hand, we need those to be ordered to browse them at the
>>>
>>> s/In/On/
>>>
>>>> thermal events.
>>>
>>> What if they are all inspected every time?
>>
>> My bad, my sentence is confusing. The trip point are browsed every time
>> and we need to have them ordered to detect correctly the thermal events.
> 
> I see.
> 
> So this mostly is a preparation for patch 4, isn't it?

Yes, it is correct

>>>> But if we assume they are ordered and change the code
>>>> based on this assumption, any platform with shuffled trip points
>>>> description will be broken (if they exist).
>>>>
>>>> Instead of taking the risk of breaking the existing platforms, use an
>>>> array of temperature ordered trip identifiers and make it available
>>>> for the code needing to browse the trip points in an ordered way.
>>>
>>> Well, having ops->get_trip_temp() suggests that the trip temperatures
>>> can be dynamic.  Is the ordering guaranteed to be preserved in that
>>> case?
>>
>> The number of trips can not be changed. It is fixed when the thermal
>> zone is created AFAICT.
> 
> The current code appears to assume that and I think that this is a
> reasonable expectation.
> 
>> The get_trip_temp() is just a way to let the
>> different driver declare their own trip structure which is actually
>> something I'm trying to fix by moving the structure thermal_trip inside
>> the thermal zone. But that is a longer and separate work.
> 
> Well, I'm not sure.
> 
> Trip point temperatures can be set via trip_point_temp_store() at
> least in principle.  How is it guaranteed that this won't affect the
> ordering?

Right, that is a good point. I don't see a logical use case where a trip 
point will be set below or above the previous or the next one, so the 
order should be kept. However, strictly speaking, nothing prevents that 
so I guess we need to reorder the trips when one is changed. It should 
be a one line call.


>>> Anyway, if they need to be sorted, why don't we just sort them
>>> properly instead of adding this extra array?
>>
>> We can not because ATM the trip points array is private to the different
>> sensors.
> 
> Well, the core could create an array or list of trip points for the
> thermal zone during registration and populate it from the
> driver-provided data.  Then, it could be sorted at the creation time.


There won't be any benefit ATM. The get_trip_temp/type/hyst ops are 
called all over the place. If we create a second sorted trip point array 
and use it in the core code, then all those ops should be replaced to 
use the sorted array instead of addressing the private trip structure. A 
big deal in terms of changes.

If we don't do the ops changes, then it is simpler to have an array of 
index->trip id and the impact is small.

But I agree we should have a sorted trip array per thermal zone and stop 
using the ops->get_trip_temp|type|hyst. That is part of the work I'm 
doing in parallel to cleanup the thermal-of and I've the plan to migrate 
all the sensors to use the struct thermal_trip instead of private data. 
 From there we will be able to get rid of the get_trip[*] and the sorted 
trip indexes array.

All these changes are not feasible in the short term. I would like to 
keep the indexes trip array approach to fix the trip cross events which 
is broken right now and then go forward with the struct thermal_trip 
changes and the thermal-of cleanups I've sent last week.

Does it sound reasonable ?


> However, the above question needs to be addressed first.
Daniel Lezcano July 25, 2022, 4:29 p.m. UTC | #5
Hi Rafael,

On 22/07/2022 19:40, Rafael J. Wysocki wrote:

[ ... ]

> They can be made in the opposite direction, starting at the core
> level.  Then, it would be clear where you were going.
> 
>> I would like to
>> keep the indexes trip array approach to fix the trip cross events which
>> is broken right now and then go forward with the struct thermal_trip
>> changes and the thermal-of cleanups I've sent last week.
>>
>> Does it sound reasonable ?
> 
> I'm not convinced about the need to make the code more complicated if
> the overall direction is to simplify it.
> 
> I understand that you want to avoid regressing things, but you want to
> make these changes eventually anyway, so why to you think that the
> risk of regressing things would be smaller in the future, after making
> the code more complicated than it is now?  Sounds counter-intuitive to
> me.

Ok, I'll rework the core code for that.

Having the series [1] and the new version of [2] will have the trip 
point partly reworked.

Thanks
   -- Daniel
Daniel Lezcano July 25, 2022, 4:35 p.m. UTC | #6
On 25/07/2022 18:29, Daniel Lezcano wrote:
> 
> Hi Rafael,
> 
> On 22/07/2022 19:40, Rafael J. Wysocki wrote:
> 
> [ ... ]
> 
>> They can be made in the opposite direction, starting at the core
>> level.  Then, it would be clear where you were going.
>>
>>> I would like to
>>> keep the indexes trip array approach to fix the trip cross events which
>>> is broken right now and then go forward with the struct thermal_trip
>>> changes and the thermal-of cleanups I've sent last week.
>>>
>>> Does it sound reasonable ?
>>
>> I'm not convinced about the need to make the code more complicated if
>> the overall direction is to simplify it.
>>
>> I understand that you want to avoid regressing things, but you want to
>> make these changes eventually anyway, so why to you think that the
>> risk of regressing things would be smaller in the future, after making
>> the code more complicated than it is now?  Sounds counter-intuitive to
>> me.
> 
> Ok, I'll rework the core code for that.
> 
> Having the series [1] and the new version of [2] will have the trip 
> point partly reworked.
> 
> Thanks
>    -- Daniel

Adding missing pointers:

[1] 
https://lore.kernel.org/lkml/20220722200007.1839356-7-daniel.lezcano@linexp.org/T/

[2] 
https://lore.kernel.org/lkml/20220710212423.681301-1-daniel.lezcano@linexp.org/
diff mbox series

Patch

diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index fc6ccc5edbfb..f274dc7d9c48 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -355,7 +355,8 @@  static void handle_critical_trips(struct thermal_zone_device *tz,
 }
 
 static void handle_thermal_trip_crossed(struct thermal_zone_device *tz, int trip,
-					int trip_temp, int trip_hyst, enum thermal_trip_type trip_type)
+					int trip_temp, int trip_hyst,
+					enum thermal_trip_type trip_type)
 {
 	if (tz->last_temperature == THERMAL_TEMP_INVALID)
 		return;
@@ -1171,6 +1172,47 @@  static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
 	if (delay_ms > 1000)
 		*delay_jiffies = round_jiffies(*delay_jiffies);
 }
+	
+static void sort_trips_indexes(struct thermal_zone_device *tz)
+{
+	int i, j;
+
+	for (i = 0; i < tz->num_trips; i++)
+		tz->trips_indexes[i] = i;
+ 
+	for (i = 0; i < tz->num_trips; i++) {
+
+		for (j = i + 1; j < tz->num_trips; j++) {
+			int t1, t2;
+
+			tz->ops->get_trip_temp(tz, tz->trips_indexes[i], &t1);
+			tz->ops->get_trip_temp(tz, tz->trips_indexes[j], &t2);
+
+			if (t1 > t2)
+				swap(tz->trips_indexes[i], tz->trips_indexes[j]);
+		}
+ 	}
+}
+
+static int thermal_zone_device_trip_init(struct thermal_zone_device *tz)
+{
+	enum thermal_trip_type trip_type;
+	int trip_temp, i;
+	
+	tz->trips_indexes = kzalloc(tz->num_trips * sizeof(int), GFP_KERNEL);
+	if (!tz->trips_indexes)
+		return -ENOMEM;
+
+	for (i = 0; i < tz->num_trips; i++) {
+		if (tz->ops->get_trip_type(tz, i, &trip_type) ||
+		    tz->ops->get_trip_temp(tz, i, &trip_temp) || !trip_temp)
+			set_bit(i, &tz->trips_disabled);
+	}
+
+	sort_trips_indexes(tz);
+
+	return 0;
+}
 
 /**
  * thermal_zone_device_register_with_trips() - register a new thermal zone device
@@ -1204,11 +1246,8 @@  thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
 					int polling_delay)
 {
 	struct thermal_zone_device *tz;
-	enum thermal_trip_type trip_type;
-	int trip_temp;
 	int id;
 	int result;
-	int count;
 	struct thermal_governor *governor;
 
 	if (!type || strlen(type) == 0) {
@@ -1281,12 +1320,9 @@  thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
 	if (result)
 		goto release_device;
 
-	for (count = 0; count < num_trips; count++) {
-		if (tz->ops->get_trip_type(tz, count, &trip_type) ||
-		    tz->ops->get_trip_temp(tz, count, &trip_temp) ||
-		    !trip_temp)
-			set_bit(count, &tz->trips_disabled);
-	}
+	result = thermal_zone_device_trip_init(tz);
+	if (result)
+		goto unregister;
 
 	/* Update 'this' zone's governor information */
 	mutex_lock(&thermal_governor_lock);
@@ -1299,7 +1335,7 @@  thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
 	result = thermal_set_governor(tz, governor);
 	if (result) {
 		mutex_unlock(&thermal_governor_lock);
-		goto unregister;
+		goto kfree_indexes;
 	}
 
 	mutex_unlock(&thermal_governor_lock);
@@ -1307,7 +1343,7 @@  thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
 	if (!tz->tzp || !tz->tzp->no_hwmon) {
 		result = thermal_add_hwmon_sysfs(tz);
 		if (result)
-			goto unregister;
+			goto kfree_indexes;
 	}
 
 	mutex_lock(&thermal_list_lock);
@@ -1328,6 +1364,8 @@  thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
 
 	return tz;
 
+kfree_indexes:
+	kfree(tz->trips_indexes);
 unregister:
 	device_del(&tz->device);
 release_device:
@@ -1406,6 +1444,7 @@  void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 	thermal_remove_hwmon_sysfs(tz);
 	ida_simple_remove(&thermal_tz_ida, tz->id);
 	ida_destroy(&tz->ida);
+	kfree(tz->trips_indexes);
 	mutex_destroy(&tz->lock);
 	device_unregister(&tz->device);
 
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 1386c713885d..4e576184df49 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -125,6 +125,7 @@  struct thermal_cooling_device {
  * @devdata:	private pointer for device private data
  * @trips:	an array of struct thermal_trip
  * @num_trips:	number of trip points the thermal zone supports
+ * @trips_indexes:	an array of sorted trip points indexes
  * @trips_disabled;	bitmap for disabled trips
  * @passive_delay_jiffies: number of jiffies to wait between polls when
  *			performing passive cooling.
@@ -166,6 +167,7 @@  struct thermal_zone_device {
 	void *devdata;
 	struct thermal_trip *trips;
 	int num_trips;
+	int *trips_indexes;
 	unsigned long trips_disabled;	/* bitmap for disabled trips */
 	unsigned long passive_delay_jiffies;
 	unsigned long polling_delay_jiffies;