Linux Power Management development
 help / color / mirror / Atom feed
From: Nicolas Pitre <nico@fluxnic.net>
To: Daniel Lezcano <daniel.lezcano@linaro.org>,
	"Rafael J . Wysocki" <rafael@kernel.org>,
	linux-pm@vger.kernel.org, linux-mediatek@lists.infradead.org,
	devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Nicolas Pitre <npitre@baylibre.com>,
	Alexandre Bailon <abailon@baylibre.com>
Subject: [PATCH 4/5] thermal: automatic aggregation support
Date: Tue, 12 Nov 2024 00:19:41 -0500	[thread overview]
Message-ID: <20241112052211.3087348-5-nico@fluxnic.net> (raw)
In-Reply-To: <20241112052211.3087348-1-nico@fluxnic.net>

From: Nicolas Pitre <npitre@baylibre.com>

Automatically apply thermal aggregation of multiple related thermal zones
into a single one. Here "related" means such zones must have the same trip
points and cooling devices bound to them. This is an alternative to the
device tree's "thermal-sensors" list for testing purpose without actually
modifying the DTB.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
 drivers/thermal/Kconfig        |  12 ++
 drivers/thermal/thermal_core.c | 227 +++++++++++++++++++++++++++++++++
 2 files changed, 239 insertions(+)

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 111f07b52a..1b2f319838 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -235,6 +235,18 @@ config THERMAL_AGGREGATION
 	  found in the device tree's "thermal-sensors" list when it contains
 	  more than one entry.
 
+config THERMAL_AGGREGATION_AUTO
+	bool "Automatic Thermal Aggregation support"
+	depends on THERMAL_AGGREGATION
+	help
+	  Automatically apply thermal aggregation of multiple related thermal
+	  zones into a single one. Here "related" means such zones must have
+	  the same trip points and cooling devices bound to them. This is an
+	  alternative to the device tree's "thermal-sensors" list for testing
+	  purpose without actually modifying the DTB. It is highly recommended
+	  that the device tree method be used in preference to this for actual
+	  system deployment.
+
 config THERMAL_EMULATION
 	bool "Thermal emulation mode support"
 	help
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 73a1b30081..934d248aa9 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -755,6 +755,16 @@ static inline void thermal_remove_tz_from_aggregator(struct thermal_zone_device
 {}
 #endif /* CONFIG_THERMAL_AGGREGATION */
 
+#ifdef CONFIG_THERMAL_AGGREGATION_AUTO
+static void thermal_check_zone_for_aggregation(struct thermal_zone_device *target_tz);
+static void thermal_check_cdev_for_aggregation(struct thermal_cooling_device *new_cdev);
+#else
+static inline void thermal_check_zone_for_aggregation(struct thermal_zone_device *target_tz)
+{}
+static inline void thermal_check_cdev_for_aggregation(struct thermal_cooling_device *new_cdev)
+{}
+#endif /* CONFIG_THERMAL_AGGREGATION_AUTO */
+
 /**
  * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone
  * @tz:		pointer to struct thermal_zone_device
@@ -1073,6 +1083,8 @@ __thermal_cooling_device_register(struct device_node *np,
 
 	mutex_unlock(&thermal_list_lock);
 
+	thermal_check_cdev_for_aggregation(cdev);
+
 	return cdev;
 
 out_cooling_dev:
@@ -1515,6 +1527,8 @@ thermal_zone_device_register_with_trips(const char *type,
 	if (atomic_cmpxchg(&tz->need_update, 1, 0))
 		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
+	thermal_check_zone_for_aggregation(tz);
+
 	thermal_notify_tz_create(tz);
 
 	thermal_debug_tz_add(tz);
@@ -2068,6 +2082,219 @@ void thermal_zone_device_aggregate(struct thermal_zone_device *tz,
 	mutex_unlock(&thermal_list_lock);
 }
 
+#ifdef CONFIG_THERMAL_AGGREGATION_AUTO
+
+static bool is_aggregator(struct thermal_zone_device *tz)
+{
+	return !strcmp(tz->type, "aggregator");
+}
+
+/**
+ * thermal_trip_related - determine if two trips are equivalent
+ *
+ * @tt1, @tt2: thermal trip specs to compare
+ *
+ * Determine if given trips may be candidates for aggregation.
+ *
+ * Return: true if related for aggregation, false otherwise
+ */
+static bool thermal_trip_related(struct thermal_trip *tt1,
+				 struct thermal_trip *tt2)
+{
+	return tt1->temperature == tt2->temperature &&
+	       tt1->hysteresis == tt2->hysteresis &&
+	       tt1->type == tt2->type &&
+	       tt1->flags == tt2->flags;
+}
+
+static struct thermal_cooling_device *
+trip_to_cdev(struct thermal_zone_device *tz, int trip_idx)
+{
+	struct thermal_instance *ti;
+
+	list_for_each_entry(ti, &tz->thermal_instances, tz_node)
+		if (trip_to_trip_desc(ti->trip) == &tz->trips[trip_idx])
+			return ti->cdev;
+
+	return NULL;
+}
+
+/**
+ * thermal_zone_related - determine if two tz's are candidates for aggregation
+ *
+ * @tz1, @tz2: thermal zones to compare
+ *
+ * Return: true if related for aggregation, false otherwise
+ */
+static bool thermal_zone_related(struct thermal_zone_device *tz1,
+				 struct thermal_zone_device *tz2)
+{
+	/* a tz can't aggregate with itself */
+	if (tz1 == tz2)
+		return false;
+
+	/* no relation possible if ops.should_bind is unset */
+	if (!tz1->ops.should_bind || !tz2->ops.should_bind)
+		return false;
+
+	/* a tz always relates to its aggregator */
+	if (tz1->aggregator == tz2 || tz2->aggregator == tz1)
+		return true;
+
+	/* related tz's must have the same number of trip points */
+	if (tz1->num_trips != tz2->num_trips)
+		return false;
+
+	/* tz's with no cdev bindings are not (yet) considered */
+	if (list_empty(&tz1->thermal_instances) ||
+	    list_empty(&tz2->thermal_instances))
+		return false;
+
+	for (int i = 0; i < tz1->num_trips; i++) {
+		/* all trips must be related */
+		if (!thermal_trip_related(&tz1->trips[i].trip, &tz2->trips[i].trip))
+			return false;
+		/* cdevs for given trips must be the same */
+		if (trip_to_cdev(tz1, i) != trip_to_cdev(tz2, i))
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * find_related_tz - look for a tz aggregation candidate
+ *
+ * @target_tz: tz to compare against
+ *
+ * Return: candidate tz for aggregation, or NULL if none
+ */
+static struct thermal_zone_device *
+find_related_tz(struct thermal_zone_device *target_tz)
+{
+	struct thermal_zone_device *tz;
+
+	list_for_each_entry(tz, &thermal_tz_list, node) {
+		if (is_aggregated(tz))
+			continue;
+		if (is_aggregator(tz))
+			continue;
+		if (!thermal_zone_related(tz, target_tz))
+			continue;
+		return tz;
+	}
+
+	return NULL;
+}
+
+/**
+ * thermal_check_zone_for_aggregation - consider tz for aggregation
+ *
+ * @target_tz: tz to compare against
+ *
+ * Adds the provided tz to a compatible aggregator. If none found, look for
+ * the possibility to create a new aggregator if another compatible tz exists.
+ * This is called, notably, when a new tz is registered and potentially bound
+ * to existing cdevs.
+ */
+static void thermal_check_zone_for_aggregation(struct thermal_zone_device *target_tz)
+{
+	struct thermal_zone_aggregator *aggr;
+	struct thermal_zone_device *aggr_tz, *tz;
+
+	if (is_aggregator(target_tz))
+		return;
+
+	mutex_lock(&thermal_list_lock);
+	if (!thermal_zone_is_present(target_tz)) {
+		mutex_unlock(&thermal_list_lock);
+		return;
+	}
+
+	/* see if existing aggregators can appropriate this zone  */
+	list_for_each_entry(aggr, &thermal_aggregator_list, node) {
+		aggr_tz = aggr->tz;
+		if (!thermal_zone_related(aggr_tz, target_tz))
+			continue;
+		pr_debug("aggr %s(%d) and zone %s(%d) are related\n",
+			 aggr_tz->type, aggr_tz->id, target_tz->type, target_tz->id);
+		add_tz_to_aggregator(aggr_tz, target_tz);
+		mutex_unlock(&thermal_list_lock);
+		return;
+	}
+
+	/* see if non-aggregated zones can be aggregated */
+	tz = find_related_tz(target_tz);
+	if (!tz) {
+		mutex_unlock(&thermal_list_lock);
+		return;
+	}
+
+	pr_debug("zones %s(%d) and %s(%d) are related\n",
+		 tz->type, tz->id, target_tz->type, target_tz->id);
+
+	mutex_unlock(&thermal_list_lock);
+	aggr_tz = create_thermal_aggregator(target_tz, "aggregator");
+	if (IS_ERR(aggr_tz)) {
+		pr_err("unable to create thermal aggregator (%ld)\n",
+		       PTR_ERR(aggr_tz));
+		return;
+	}
+
+	mutex_lock(&thermal_list_lock);
+
+	/* the lock was momentarily dropped so need to revalide everything */
+	if (thermal_zone_is_present(target_tz)) {
+		tz = find_related_tz(target_tz);
+		if (tz) {
+			add_tz_to_aggregator(aggr_tz, target_tz);
+			add_tz_to_aggregator(aggr_tz, tz);
+			mutex_unlock(&thermal_list_lock);
+			return;
+		}
+	}
+
+	/* our match disappeared in the mean time */
+	free_thermal_aggregator_unlock(aggr_tz);
+}
+
+/**
+ * thermal_check_cdev_for_aggregation - consider aggregation after new cdev registration
+ *
+ * @new_cdev: cdev for which new thermal bindings might create aggregation candidates
+ *
+ * Consider tz's having thermal instance bindings with this new cdev as
+ * candidates for aggregation. This is called when a new cdev is registered
+ * and potentially bound to existing tz's.
+ */
+static void thermal_check_cdev_for_aggregation(struct thermal_cooling_device *new_cdev)
+{
+	struct thermal_zone_device *tz, *last_tz = NULL;
+	struct thermal_instance *ti;
+
+start_over:
+	mutex_lock(&thermal_list_lock);
+
+	list_for_each_entry(tz, &thermal_tz_list, node) {
+		if (tz == last_tz)
+			continue;
+		if (is_aggregator(tz))
+			continue;
+		list_for_each_entry(ti, &tz->thermal_instances, tz_node) {
+			if (ti->cdev == new_cdev) {
+				last_tz = tz;
+				mutex_unlock(&thermal_list_lock);
+				thermal_check_zone_for_aggregation(tz);
+				/* because the lock was dropped ... */
+				goto start_over;
+			}
+		}
+	}
+
+	mutex_unlock(&thermal_list_lock);
+}
+
+#endif /* CONFIG_THERMAL_AGGREGATION_AUTO */
 #endif /* CONFIG_THERMAL_AGGREGATION */
 
 static void thermal_zone_device_resume(struct work_struct *work)
-- 
2.47.0


  parent reply	other threads:[~2024-11-12  5:22 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-12  5:19 [PATCH 0/5] thermal: multi-sensor aggregation support Nicolas Pitre
2024-11-12  5:19 ` [PATCH 1/5] thermal: of: properly parse coefficients with multiple thermal-sensors entries Nicolas Pitre
2024-11-12  5:19 ` [PATCH 2/5] dt-bindings: thermal: Restore the thermal-sensors property Nicolas Pitre
2024-11-12  5:19 ` [PATCH 3/5] thermal: aggregation support Nicolas Pitre
2024-11-12  5:19 ` Nicolas Pitre [this message]
2024-11-12  5:19 ` [PATCH 5/5] ARM64: mt8195: Use thermal aggregation for big and little cpu Nicolas Pitre
2024-11-27 22:05 ` [PATCH 0/5] thermal: multi-sensor aggregation support Nicolas Pitre
2024-11-28 17:38   ` Daniel Lezcano
2024-11-29 20:00 ` Daniel Lezcano
2024-12-02  3:41   ` Chen-Yu Tsai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241112052211.3087348-5-nico@fluxnic.net \
    --to=nico@fluxnic.net \
    --cc=abailon@baylibre.com \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=npitre@baylibre.com \
    --cc=rafael@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox