2024-02-14 12:51:41

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 0/6] thermal: Store trips table and ops in thermal_zone_device

Hi Everyone,

This is an update of

https://lore.kernel.org/linux-pm/2728491.mvXUDI8C0e@kreacher/

that has been rebased on top of

https://lore.kernel.org/linux-pm/6017196.lOV4Wx5bFT@kreacher/

and includes some bug fixes.

The original series description still applies:

"This series changes the PM core to copy the trips and zone ops directly
into struct thermal_zone_device so as to allow the callers of the zone
registration function to discard their own copies of those things after
zone registration and/or possibly allocate them as read-only.

The first patch makes the thermal core create a copy of the trips table which
is declared as a flex array to enable additional bounds checking on it. The
next two patches update the ACPI thermal driver and Intel thermal drivers to
take benefit of that change.

In a similar pattern, patch [4/6] makes the thermal core create an internal
copy of the zone ops supplied by the zone creator, so as to allow the
original ops structure to be discarded after zone registration or allocated
as read-only, and the next two patches update the ACPI thermal driver and Intel
thermal drivers to actually do that.

The other thermal drivers need not be changed, although in principle they may
be simplified a bit too in the future.

As usual, please refer to the individual patch changelogs for details."

However, the imx thermal driver is modified by patch [1/6], because it uses its
local trips table and expects it to contain the current passive trip temperature
value (as set via sysfs), and the thermal_of driver is modified by patch [4/6],
because it uses the ops pointer from the thermal zone device to free the ops.

Thanks!





2024-02-14 12:51:42

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

From: Rafael J. Wysocki <[email protected]>

The current code expects thermal zone creators to pass a pointer to a
writable trips table to thermal_zone_device_register_with_trips() and
that trips table is then used by the thermal core going forward.

Consequently, the callers of thermal_zone_device_register_with_trips()
are required to hold on to the trips table passed to it until the given
thermal zone is unregistered, at which point the trips table can be
freed, but at the same time they are not expected to access that table
directly. This is both error prone and confusing.

To address it, turn the trips table pointer in struct thermal_zone_device
into a flex array (counted by its num_trips field), allocate it during
thermal zone device allocation and copy the contents of the trips table
supplied by the zone creator (which can be const now) into it, which
will allow the callers of thermal_zone_device_register_with_trips() to
drop their trip tables right after the zone registration.

This requires the imx thermal driver to be adjusted to store the new
temperature in its internal trips table in imx_set_trip_temp(), because
it will be separate from the core's trips table now and it has to be
explicitly kept in sync with the latter.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Reviewed-by: Stanislaw Gruszka <[email protected]>
---

v1 -> v2:
* Rebase.
* Drop all of the redundant trips[] checks against NULL.
* Add imx change to still allow it to use its local trips table.
* Add R-by from Stanislaw (which is still applicable IMV).

---
drivers/thermal/imx_thermal.c | 1 +
drivers/thermal/thermal_core.c | 17 ++++++++---------
drivers/thermal/thermal_trip.c | 2 +-
include/linux/thermal.h | 10 +++++-----
4 files changed, 15 insertions(+), 15 deletions(-)

Index: linux-pm/include/linux/thermal.h
===================================================================
--- linux-pm.orig/include/linux/thermal.h
+++ linux-pm/include/linux/thermal.h
@@ -137,7 +137,6 @@ struct thermal_cooling_device {
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
- * @trips: an array of struct thermal_trip
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
@@ -167,6 +166,7 @@ struct thermal_cooling_device {
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
+ * @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
@@ -179,7 +179,6 @@ struct thermal_zone_device {
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
- struct thermal_trip *trips;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
@@ -200,10 +199,11 @@ struct thermal_zone_device {
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
+ bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
- bool suspended;
+ struct thermal_trip trips[] __counted_by(num_trips);
};

/**
@@ -322,7 +322,7 @@ int thermal_zone_get_crit_temp(struct th
#ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, void *devdata,
struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
@@ -381,7 +381,7 @@ void thermal_zone_device_critical(struct
#else
static inline struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, void *devdata,
struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -1227,9 +1227,6 @@ int thermal_zone_get_crit_temp(struct th
if (tz->ops->get_crit_temp)
return tz->ops->get_crit_temp(tz, temp);

- if (!tz->trips)
- return -EINVAL;
-
mutex_lock(&tz->lock);

for (i = 0; i < tz->num_trips; i++) {
@@ -1271,10 +1268,12 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_crit_
* IS_ERR*() helpers.
*/
struct thermal_zone_device *
-thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips,
- void *devdata, struct thermal_zone_device_ops *ops,
- const struct thermal_zone_params *tzp, int passive_delay,
- int polling_delay)
+thermal_zone_device_register_with_trips(const char *type,
+ const struct thermal_trip *trips,
+ int num_trips, void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz;
int id;
@@ -1308,7 +1307,7 @@ thermal_zone_device_register_with_trips(
if (!thermal_class)
return ERR_PTR(-ENODEV);

- tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+ tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);

@@ -1340,7 +1339,7 @@ thermal_zone_device_register_with_trips(
tz->ops = ops;
tz->device.class = thermal_class;
tz->devdata = devdata;
- tz->trips = trips;
+ memcpy(tz->trips, trips, num_trips * sizeof(*trips));
tz->num_trips = num_trips;

thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
Index: linux-pm/drivers/thermal/imx_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/imx_thermal.c
+++ linux-pm/drivers/thermal/imx_thermal.c
@@ -355,6 +355,7 @@ static int imx_set_trip_temp(struct ther
return -EINVAL;

imx_set_alarm_temp(data, temp);
+ trips[IMX_TRIP_PASSIVE].temperature = temp;

pm_runtime_put(data->dev);

Index: linux-pm/drivers/thermal/thermal_trip.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_trip.c
+++ linux-pm/drivers/thermal/thermal_trip.c
@@ -122,7 +122,7 @@ void __thermal_zone_set_trips(struct the
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
- if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip)
+ if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;

*trip = tz->trips[trip_id];




2024-02-14 12:51:47

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 6/6] thermal: intel: Adjust ops handling during thermal zone registration

From: Rafael J. Wysocki <[email protected]>

Because thermal zone operations are now stored directly in struct
thermal_zone_device, thermal zone creators can discard the operations
structure after the zone registration is complete, or it can be made
read-only.

Accordingly, make int340x_thermal_zone_add() use a local variable to
represent thermal zone operations, so it is freed automatically upon the
function exit, and make the other Intel thermal drivers use const zone
operations structures.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Reviewed-by: Stanislaw Gruszka <[email protected]>
---

v1 -> v2:
* Rebase.
* Add R-by from Stanislaw.

---
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c | 26 ++--------
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h | 1
drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c | 2
drivers/thermal/intel/intel_pch_thermal.c | 2
drivers/thermal/intel/intel_quark_dts_thermal.c | 2
drivers/thermal/intel/intel_soc_dts_iosf.c | 2
drivers/thermal/intel/x86_pkg_temp_thermal.c | 2
7 files changed, 11 insertions(+), 26 deletions(-)

Index: linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
+++ linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -61,12 +61,6 @@ static void int340x_thermal_critical(str
dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
}

-static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
- .get_temp = int340x_thermal_get_zone_temp,
- .set_trip_temp = int340x_thermal_set_trip_temp,
- .critical = int340x_thermal_critical,
-};
-
static inline void *int_to_trip_priv(int i)
{
return (void *)(long)i;
@@ -126,6 +120,11 @@ static struct thermal_zone_params int340
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
int (*get_temp) (struct thermal_zone_device *, int *))
{
+ const struct thermal_zone_device_ops zone_ops = {
+ .set_trip_temp = int340x_thermal_set_trip_temp,
+ .critical = int340x_thermal_critical,
+ .get_temp = get_temp ? get_temp : int340x_thermal_get_zone_temp,
+ };
struct int34x_thermal_zone *int34x_zone;
struct thermal_trip *zone_trips;
unsigned long long trip_cnt = 0;
@@ -139,16 +138,6 @@ struct int34x_thermal_zone *int340x_ther

int34x_zone->adev = adev;

- int34x_zone->ops = kmemdup(&int340x_thermal_zone_ops,
- sizeof(int340x_thermal_zone_ops), GFP_KERNEL);
- if (!int34x_zone->ops) {
- ret = -ENOMEM;
- goto err_ops_alloc;
- }
-
- if (get_temp)
- int34x_zone->ops->get_temp = get_temp;
-
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
if (ACPI_SUCCESS(status))
int34x_zone->aux_trip_nr = trip_cnt;
@@ -183,7 +172,7 @@ struct int34x_thermal_zone *int340x_ther
acpi_device_bid(adev),
zone_trips, trip_cnt,
int34x_zone,
- int34x_zone->ops,
+ &zone_ops,
&int340x_thermal_params,
0, 0);
kfree(zone_trips);
@@ -203,8 +192,6 @@ err_enable:
err_thermal_zone:
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
err_trips_alloc:
- kfree(int34x_zone->ops);
-err_ops_alloc:
kfree(int34x_zone);
return ERR_PTR(ret);
}
@@ -214,7 +201,6 @@ void int340x_thermal_zone_remove(struct
{
thermal_zone_device_unregister(int34x_zone->zone);
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
- kfree(int34x_zone->ops);
kfree(int34x_zone);
}
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
Index: linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
+++ linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
@@ -22,7 +22,6 @@ struct int34x_thermal_zone {
struct acpi_device *adev;
int aux_trip_nr;
struct thermal_zone_device *zone;
- struct thermal_zone_device_ops *ops;
void *priv_data;
struct acpi_lpat_conversion_table *lpat_table;
};
Index: linux-pm/drivers/thermal/intel/intel_pch_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_pch_thermal.c
+++ linux-pm/drivers/thermal/intel/intel_pch_thermal.c
@@ -132,7 +132,7 @@ static void pch_critical(struct thermal_
thermal_zone_device_type(tzd));
}

-static struct thermal_zone_device_ops tzd_ops = {
+static const struct thermal_zone_device_ops tzd_ops = {
.get_temp = pch_thermal_get_temp,
.critical = pch_critical,
};
Index: linux-pm/drivers/thermal/intel/intel_quark_dts_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_quark_dts_thermal.c
+++ linux-pm/drivers/thermal/intel/intel_quark_dts_thermal.c
@@ -288,7 +288,7 @@ static int sys_change_mode(struct therma
return ret;
}

-static struct thermal_zone_device_ops tzone_ops = {
+static const struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.set_trip_temp = sys_set_trip_temp,
.change_mode = sys_change_mode,
Index: linux-pm/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ linux-pm/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -233,7 +233,7 @@ static int get_trip_temp(struct proc_the
return temp;
}

-static struct thermal_zone_device_ops tzone_ops = {
+static const struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.set_trip_temp = sys_set_trip_temp,
};
Index: linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_soc_dts_iosf.c
+++ linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.c
@@ -168,7 +168,7 @@ static int sys_get_curr_temp(struct ther
return 0;
}

-static struct thermal_zone_device_ops tzone_ops = {
+static const struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.set_trip_temp = sys_set_trip_temp,
};
Index: linux-pm/drivers/thermal/intel/x86_pkg_temp_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/x86_pkg_temp_thermal.c
+++ linux-pm/drivers/thermal/intel/x86_pkg_temp_thermal.c
@@ -166,7 +166,7 @@ sys_set_trip_temp(struct thermal_zone_de
}

/* Thermal zone callback registry */
-static struct thermal_zone_device_ops tzone_ops = {
+static const struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.set_trip_temp = sys_set_trip_temp,
};




2024-02-14 12:52:15

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 3/6] thermal: intel: Discard trip tables after zone registration

From: Rafael J. Wysocki <[email protected]>

Because the thermal core creates and uses its own copy of the trips
table passed to thermal_zone_device_register_with_trips(), it is not
necessary to hold on to a local copy of it any more after the given
thermal zone has been registered.

Accordingly, modify Intel thermal drivers to discard the trips tables
passed to thermal_zone_device_register_with_trips() after thermal zone
registration, for example by storing them in local variables which are
automatically discarded when the zone registration is complete.

Also make some additional code simplifications unlocked by the above
changes.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

v1 -> v2: Rebase.

---
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c | 6 -
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h | 1
drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c | 9 +-
drivers/thermal/intel/intel_pch_thermal.c | 24 +++---
drivers/thermal/intel/intel_quark_dts_thermal.c | 4 -
drivers/thermal/intel/intel_soc_dts_iosf.c | 14 ++--
drivers/thermal/intel/intel_soc_dts_iosf.h | 1
drivers/thermal/intel/x86_pkg_temp_thermal.c | 35 +++-------
8 files changed, 40 insertions(+), 54 deletions(-)

Index: linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
+++ linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -177,8 +177,6 @@ struct int34x_thermal_zone *int340x_ther
for (i = 0; i < trip_cnt; ++i)
zone_trips[i].hysteresis = hyst;

- int34x_zone->trips = zone_trips;
-
int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);

int34x_zone->zone = thermal_zone_device_register_with_trips(
@@ -188,6 +186,8 @@ struct int34x_thermal_zone *int340x_ther
int34x_zone->ops,
&int340x_thermal_params,
0, 0);
+ kfree(zone_trips);
+
if (IS_ERR(int34x_zone->zone)) {
ret = PTR_ERR(int34x_zone->zone);
goto err_thermal_zone;
@@ -201,7 +201,6 @@ struct int34x_thermal_zone *int340x_ther
err_enable:
thermal_zone_device_unregister(int34x_zone->zone);
err_thermal_zone:
- kfree(int34x_zone->trips);
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
err_trips_alloc:
kfree(int34x_zone->ops);
@@ -215,7 +214,6 @@ void int340x_thermal_zone_remove(struct
{
thermal_zone_device_unregister(int34x_zone->zone);
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
- kfree(int34x_zone->trips);
kfree(int34x_zone->ops);
kfree(int34x_zone);
}
Index: linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
+++ linux-pm/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
@@ -20,7 +20,6 @@ struct active_trip {

struct int34x_thermal_zone {
struct acpi_device *adev;
- struct thermal_trip *trips;
int aux_trip_nr;
struct thermal_zone_device *zone;
struct thermal_zone_device_ops *ops;
Index: linux-pm/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ linux-pm/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -233,11 +233,6 @@ static int get_trip_temp(struct proc_the
return temp;
}

-static struct thermal_trip psv_trip = {
- .type = THERMAL_TRIP_PASSIVE,
- .flags = THERMAL_TRIP_FLAG_RW_TEMP,
-};
-
static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.set_trip_temp = sys_set_trip_temp,
@@ -252,6 +247,10 @@ static int proc_thermal_pci_probe(struct
{
struct proc_thermal_device *proc_priv;
struct proc_thermal_pci *pci_info;
+ struct thermal_trip psv_trip = {
+ .type = THERMAL_TRIP_PASSIVE,
+ .flags = THERMAL_TRIP_FLAG_RW_TEMP,
+ };
int irq_flag = 0, irq, ret;
bool msi_irq = false;

Index: linux-pm/drivers/thermal/intel/intel_pch_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_pch_thermal.c
+++ linux-pm/drivers/thermal/intel/intel_pch_thermal.c
@@ -84,7 +84,6 @@ struct pch_thermal_device {
void __iomem *hw_base;
struct pci_dev *pdev;
struct thermal_zone_device *tzd;
- struct thermal_trip trips[PCH_MAX_TRIPS];
bool bios_enabled;
};

@@ -94,7 +93,8 @@ struct pch_thermal_device {
* passive trip temperature using _PSV method. There is no specific
* passive temperature setting in MMIO interface of this PCI device.
*/
-static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
+static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
+ struct thermal_trip *trip)
{
struct acpi_device *adev;
int temp;
@@ -106,12 +106,13 @@ static int pch_wpt_add_acpi_psv_trip(str
if (thermal_acpi_passive_trip_temp(adev, &temp) || temp <= 0)
return 0;

- ptd->trips[trip].type = THERMAL_TRIP_PASSIVE;
- ptd->trips[trip].temperature = temp;
+ trip->type = THERMAL_TRIP_PASSIVE;
+ trip->temperature = temp;
return 1;
}
#else
-static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
+static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
+ struct thermal_trip *trip)
{
return 0;
}
@@ -159,6 +160,7 @@ static const char *board_names[] = {
static int intel_pch_thermal_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ struct thermal_trip ptd_trips[PCH_MAX_TRIPS] = { 0 };
enum pch_board_ids board_id = id->driver_data;
struct pch_thermal_device *ptd;
int nr_trips = 0;
@@ -220,21 +222,21 @@ read_trips:
trip_temp = readw(ptd->hw_base + WPT_CTT);
trip_temp &= 0x1FF;
if (trip_temp) {
- ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
- ptd->trips[nr_trips++].type = THERMAL_TRIP_CRITICAL;
+ ptd_trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
+ ptd_trips[nr_trips++].type = THERMAL_TRIP_CRITICAL;
}

trip_temp = readw(ptd->hw_base + WPT_PHL);
trip_temp &= 0x1FF;
if (trip_temp) {
- ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
- ptd->trips[nr_trips++].type = THERMAL_TRIP_HOT;
+ ptd_trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
+ ptd_trips[nr_trips++].type = THERMAL_TRIP_HOT;
}

- nr_trips += pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
+ nr_trips += pch_wpt_add_acpi_psv_trip(ptd, &ptd_trips[nr_trips]);

ptd->tzd = thermal_zone_device_register_with_trips(board_names[board_id],
- ptd->trips, nr_trips,
+ ptd_trips, nr_trips,
ptd, &tzd_ops,
NULL, 0, 0);
if (IS_ERR(ptd->tzd)) {
Index: linux-pm/drivers/thermal/intel/intel_quark_dts_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_quark_dts_thermal.c
+++ linux-pm/drivers/thermal/intel/intel_quark_dts_thermal.c
@@ -101,7 +101,6 @@ struct soc_sensor_entry {
u32 store_ptps;
u32 store_dts_enable;
struct thermal_zone_device *tzone;
- struct thermal_trip trips[QRK_MAX_DTS_TRIPS];
};

static struct soc_sensor_entry *soc_dts;
@@ -316,8 +315,8 @@ static void free_soc_dts(struct soc_sens

static struct soc_sensor_entry *alloc_soc_dts(void)
{
+ struct thermal_trip trips[QRK_MAX_DTS_TRIPS];
struct soc_sensor_entry *aux_entry;
- struct thermal_trip *trips;
int err;
u32 out;

@@ -326,7 +325,6 @@ static struct soc_sensor_entry *alloc_so
err = -ENOMEM;
return ERR_PTR(-ENOMEM);
}
- trips = aux_entry->trips;

/* Check if DTS register is locked */
err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
Index: linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_soc_dts_iosf.c
+++ linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.c
@@ -201,7 +201,8 @@ static void remove_dts_thermal_zone(stru
thermal_zone_device_unregister(dts->tzone);
}

-static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts)
+static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
+ struct thermal_trip *trips)
{
char name[10];
u32 store_ptps;
@@ -223,11 +224,11 @@ static int add_dts_thermal_zone(int id,

for (i = 0; i <= 1; i++) {
if (store_ptps & (0xFFU << i * 8))
- dts->trips[i].flags &= ~THERMAL_TRIP_FLAG_RW_TEMP;
+ trips[i].flags &= ~THERMAL_TRIP_FLAG_RW_TEMP;
}
}
snprintf(name, sizeof(name), "soc_dts%d", id);
- dts->tzone = thermal_zone_device_register_with_trips(name, dts->trips,
+ dts->tzone = thermal_zone_device_register_with_trips(name, trips,
SOC_MAX_DTS_TRIPS,
dts, &tzone_ops,
NULL, 0, 0);
@@ -303,6 +304,7 @@ struct intel_soc_dts_sensors *
intel_soc_dts_iosf_init(enum intel_soc_dts_interrupt_type intr_type,
bool critical_trip, int crit_offset)
{
+ struct thermal_trip trips[SOC_MAX_DTS_SENSORS][SOC_MAX_DTS_TRIPS] = { 0 };
struct intel_soc_dts_sensors *sensors;
int tj_max;
int ret;
@@ -335,7 +337,7 @@ intel_soc_dts_iosf_init(enum intel_soc_d
if (ret)
goto err_reset_trips;

- set_trip(&sensors->soc_dts[i].trips[0], THERMAL_TRIP_PASSIVE,
+ set_trip(&trips[i][0], THERMAL_TRIP_PASSIVE,
THERMAL_TRIP_FLAG_RW_TEMP, 0);

if (critical_trip) {
@@ -351,11 +353,11 @@ intel_soc_dts_iosf_init(enum intel_soc_d
if (ret)
goto err_reset_trips;

- set_trip(&sensors->soc_dts[i].trips[1], trip_type, trip_flags, temp);
+ set_trip(&trips[i][1], trip_type, trip_flags, temp);
}

for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
- ret = add_dts_thermal_zone(i, &sensors->soc_dts[i]);
+ ret = add_dts_thermal_zone(i, &sensors->soc_dts[i], trips[i]);
if (ret)
goto err_remove_zone;
}
Index: linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.h
===================================================================
--- linux-pm.orig/drivers/thermal/intel/intel_soc_dts_iosf.h
+++ linux-pm/drivers/thermal/intel/intel_soc_dts_iosf.h
@@ -28,7 +28,6 @@ struct intel_soc_dts_sensors;
struct intel_soc_dts_sensor_entry {
int id;
u32 store_status;
- struct thermal_trip trips[SOC_MAX_DTS_TRIPS];
struct thermal_zone_device *tzone;
struct intel_soc_dts_sensors *sensors;
};
Index: linux-pm/drivers/thermal/intel/x86_pkg_temp_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/intel/x86_pkg_temp_thermal.c
+++ linux-pm/drivers/thermal/intel/x86_pkg_temp_thermal.c
@@ -53,7 +53,6 @@ struct zone_device {
u32 msr_pkg_therm_high;
struct delayed_work work;
struct thermal_zone_device *tzone;
- struct thermal_trip *trips;
struct cpumask cpumask;
};

@@ -268,17 +267,13 @@ static int pkg_thermal_notify(u64 msr_va
return 0;
}

-static struct thermal_trip *pkg_temp_thermal_trips_init(int cpu, int tj_max, int num_trips)
+static int pkg_temp_thermal_trips_init(int cpu, int tj_max,
+ struct thermal_trip *trips, int num_trips)
{
- struct thermal_trip *trips;
unsigned long thres_reg_value;
u32 mask, shift, eax, edx;
int ret, i;

- trips = kzalloc(sizeof(*trips) * num_trips, GFP_KERNEL);
- if (!trips)
- return ERR_PTR(-ENOMEM);
-
for (i = 0; i < num_trips; i++) {

if (i) {
@@ -291,10 +286,8 @@ static struct thermal_trip *pkg_temp_the

ret = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
&eax, &edx);
- if (ret < 0) {
- kfree(trips);
- return ERR_PTR(ret);
- }
+ if (ret < 0)
+ return ret;

thres_reg_value = (eax & mask) >> shift;

@@ -308,11 +301,12 @@ static struct thermal_trip *pkg_temp_the
__func__, cpu, i, trips[i].temperature);
}

- return trips;
+ return 0;
}

static int pkg_temp_thermal_device_add(unsigned int cpu)
{
+ struct thermal_trip trips[MAX_NUMBER_OF_TRIPS] = { 0 };
int id = topology_logical_die_id(cpu);
u32 eax, ebx, ecx, edx;
struct zone_device *zonedev;
@@ -337,20 +331,18 @@ static int pkg_temp_thermal_device_add(u
if (!zonedev)
return -ENOMEM;

- zonedev->trips = pkg_temp_thermal_trips_init(cpu, tj_max, thres_count);
- if (IS_ERR(zonedev->trips)) {
- err = PTR_ERR(zonedev->trips);
+ err = pkg_temp_thermal_trips_init(cpu, tj_max, trips, thres_count);
+ if (err)
goto out_kfree_zonedev;
- }

INIT_DELAYED_WORK(&zonedev->work, pkg_temp_thermal_threshold_work_fn);
zonedev->cpu = cpu;
zonedev->tzone = thermal_zone_device_register_with_trips("x86_pkg_temp",
- zonedev->trips, thres_count,
+ trips, thres_count,
zonedev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
if (IS_ERR(zonedev->tzone)) {
err = PTR_ERR(zonedev->tzone);
- goto out_kfree_trips;
+ goto out_kfree_zonedev;
}
err = thermal_zone_device_enable(zonedev->tzone);
if (err)
@@ -369,8 +361,6 @@ static int pkg_temp_thermal_device_add(u

out_unregister_tz:
thermal_zone_device_unregister(zonedev->tzone);
-out_kfree_trips:
- kfree(zonedev->trips);
out_kfree_zonedev:
kfree(zonedev);
return err;
@@ -457,10 +447,9 @@ static int pkg_thermal_cpu_offline(unsig
raw_spin_unlock_irq(&pkg_temp_lock);

/* Final cleanup if this is the last cpu */
- if (lastcpu) {
- kfree(zonedev->trips);
+ if (lastcpu)
kfree(zonedev);
- }
+
return 0;
}





2024-02-14 12:59:57

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

From: Rafael J. Wysocki <[email protected]>

The current code requires thermal zone creators to pass pointers to
writable ops structures to thermal_zone_device_register_with_trips()
which needs to modify the target struct thermal_zone_device_ops object
if the "critical" operation in it is NULL.

Moreover, the callers of thermal_zone_device_register_with_trips() are
required to hold on to the struct thermal_zone_device_ops object passed
to it until the given thermal zone is unregistered.

Both of these requirements are quite inconvenient, so modify struct
thermal_zone_device to contain struct thermal_zone_device_ops as field and
make thermal_zone_device_register_with_trips() copy the contents of the
struct thermal_zone_device_ops passed to it via a pointer (which can be
const now) to that field.

Also adjust the code using thermal zone ops accordingly and modify
thermal_of_zone_register() to use a local ops variable during
thermal zone registration so ops do not need to be freed in
thermal_of_zone_unregister() any more.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Reviewed-by: Stanislaw Gruszka <[email protected]>
---

v1 -> v2:
* Rebase.
* Add thermal_of modifications.
* Add R-by from Stanislaw (still applicable IMV).

---
drivers/thermal/thermal_core.c | 40 +++++++++++++++++++-------------------
drivers/thermal/thermal_helpers.c | 10 ++++-----
drivers/thermal/thermal_hwmon.c | 4 +--
drivers/thermal/thermal_of.c | 26 +++++++-----------------
drivers/thermal/thermal_sysfs.c | 8 +++----
drivers/thermal/thermal_trip.c | 4 +--
include/linux/thermal.h | 6 ++---
7 files changed, 44 insertions(+), 54 deletions(-)

Index: linux-pm/include/linux/thermal.h
===================================================================
--- linux-pm.orig/include/linux/thermal.h
+++ linux-pm/include/linux/thermal.h
@@ -189,7 +189,7 @@ struct thermal_zone_device {
int prev_low_trip;
int prev_high_trip;
atomic_t need_update;
- struct thermal_zone_device_ops *ops;
+ struct thermal_zone_device_ops ops;
struct thermal_zone_params *tzp;
struct thermal_governor *governor;
void *governor_data;
@@ -324,14 +324,14 @@ struct thermal_zone_device *thermal_zone
const char *type,
const struct thermal_trip *trips,
int num_trips, void *devdata,
- struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
int passive_delay, int polling_delay);

struct thermal_zone_device *thermal_tripless_zone_device_register(
const char *type,
void *devdata,
- struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp);

void thermal_zone_device_unregister(struct thermal_zone_device *tz);
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -356,9 +356,9 @@ static void handle_critical_trips(struct
trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type);

if (trip->type == THERMAL_TRIP_CRITICAL)
- tz->ops->critical(tz);
- else if (tz->ops->hot)
- tz->ops->hot(tz);
+ tz->ops.critical(tz);
+ else if (tz->ops.hot)
+ tz->ops.hot(tz);
}

static void handle_thermal_trip(struct thermal_zone_device *tz,
@@ -493,8 +493,8 @@ static int thermal_zone_device_set_mode(
return ret;
}

- if (tz->ops->change_mode)
- ret = tz->ops->change_mode(tz, mode);
+ if (tz->ops.change_mode)
+ ret = tz->ops.change_mode(tz, mode);

if (!ret)
tz->mode = mode;
@@ -867,8 +867,8 @@ static void bind_cdev(struct thermal_coo
struct thermal_zone_device *pos = NULL;

list_for_each_entry(pos, &thermal_tz_list, node) {
- if (pos->ops->bind) {
- ret = pos->ops->bind(pos, cdev);
+ if (pos->ops.bind) {
+ ret = pos->ops.bind(pos, cdev);
if (ret)
print_bind_err_msg(pos, cdev, ret);
}
@@ -1184,8 +1184,8 @@ void thermal_cooling_device_unregister(s

/* Unbind all thermal zones associated with 'this' cdev */
list_for_each_entry(tz, &thermal_tz_list, node) {
- if (tz->ops->unbind)
- tz->ops->unbind(tz, cdev);
+ if (tz->ops.unbind)
+ tz->ops.unbind(tz, cdev);
}

mutex_unlock(&thermal_list_lock);
@@ -1199,13 +1199,13 @@ static void bind_tz(struct thermal_zone_
int ret;
struct thermal_cooling_device *pos = NULL;

- if (!tz->ops->bind)
+ if (!tz->ops.bind)
return;

mutex_lock(&thermal_list_lock);

list_for_each_entry(pos, &thermal_cdev_list, node) {
- ret = tz->ops->bind(tz, pos);
+ ret = tz->ops.bind(tz, pos);
if (ret)
print_bind_err_msg(tz, pos, ret);
}
@@ -1224,8 +1224,8 @@ int thermal_zone_get_crit_temp(struct th
{
int i, ret = -EINVAL;

- if (tz->ops->get_crit_temp)
- return tz->ops->get_crit_temp(tz, temp);
+ if (tz->ops.get_crit_temp)
+ return tz->ops.get_crit_temp(tz, temp);

mutex_lock(&tz->lock);

@@ -1271,7 +1271,7 @@ struct thermal_zone_device *
thermal_zone_device_register_with_trips(const char *type,
const struct thermal_trip *trips,
int num_trips, void *devdata,
- struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
int passive_delay, int polling_delay)
{
@@ -1333,10 +1333,10 @@ thermal_zone_device_register_with_trips(
tz->id = id;
strscpy(tz->type, type, sizeof(tz->type));

- if (!ops->critical)
- ops->critical = thermal_zone_device_critical;
+ tz->ops = *ops;
+ if (!tz->ops.critical)
+ tz->ops.critical = thermal_zone_device_critical;

- tz->ops = ops;
tz->device.class = thermal_class;
tz->devdata = devdata;
memcpy(tz->trips, trips, num_trips * sizeof(*trips));
@@ -1422,7 +1422,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_device_re
struct thermal_zone_device *thermal_tripless_zone_device_register(
const char *type,
void *devdata,
- struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp)
{
return thermal_zone_device_register_with_trips(type, NULL, 0, devdata,
@@ -1484,8 +1484,8 @@ void thermal_zone_device_unregister(stru

/* Unbind all cdevs associated with 'this' thermal zone */
list_for_each_entry(cdev, &thermal_cdev_list, node)
- if (tz->ops->unbind)
- tz->ops->unbind(tz, cdev);
+ if (tz->ops.unbind)
+ tz->ops.unbind(tz, cdev);

mutex_unlock(&thermal_list_lock);

Index: linux-pm/drivers/thermal/thermal_sysfs.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_sysfs.c
+++ linux-pm/drivers/thermal/thermal_sysfs.c
@@ -128,8 +128,8 @@ trip_point_temp_store(struct device *dev
}

if (temp != trip->temperature) {
- if (tz->ops->set_trip_temp) {
- ret = tz->ops->set_trip_temp(tz, trip_id, temp);
+ if (tz->ops.set_trip_temp) {
+ ret = tz->ops.set_trip_temp(tz, trip_id, temp);
if (ret)
goto unlock;
}
@@ -254,10 +254,10 @@ emul_temp_store(struct device *dev, stru

mutex_lock(&tz->lock);

- if (!tz->ops->set_emul_temp)
+ if (!tz->ops.set_emul_temp)
tz->emul_temperature = temperature;
else
- ret = tz->ops->set_emul_temp(tz, temperature);
+ ret = tz->ops.set_emul_temp(tz, temperature);

if (!ret)
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
Index: linux-pm/drivers/thermal/thermal_helpers.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_helpers.c
+++ linux-pm/drivers/thermal/thermal_helpers.c
@@ -26,8 +26,8 @@ int get_tz_trend(struct thermal_zone_dev
{
enum thermal_trend trend;

- if (tz->emul_temperature || !tz->ops->get_trend ||
- tz->ops->get_trend(tz, trip, &trend)) {
+ if (tz->emul_temperature || !tz->ops.get_trend ||
+ tz->ops.get_trend(tz, trip, &trend)) {
if (tz->temperature > tz->last_temperature)
trend = THERMAL_TREND_RAISING;
else if (tz->temperature < tz->last_temperature)
@@ -75,7 +75,7 @@ EXPORT_SYMBOL(get_thermal_instance);
* temperature and fill @temp.
*
* Both tz and tz->ops must be valid pointers when calling this function,
- * and the tz->ops->get_temp callback must be provided.
+ * and the tz->ops.get_temp callback must be provided.
* The function must be called under tz->lock.
*
* Return: On success returns 0, an error code otherwise
@@ -88,7 +88,7 @@ int __thermal_zone_get_temp(struct therm

lockdep_assert_held(&tz->lock);

- ret = tz->ops->get_temp(tz, temp);
+ ret = tz->ops.get_temp(tz, temp);

if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
for_each_trip(tz, trip) {
@@ -132,7 +132,7 @@ int thermal_zone_get_temp(struct thermal

mutex_lock(&tz->lock);

- if (!tz->ops->get_temp) {
+ if (!tz->ops.get_temp) {
ret = -EINVAL;
goto unlock;
}
Index: linux-pm/drivers/thermal/thermal_trip.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_trip.c
+++ linux-pm/drivers/thermal/thermal_trip.c
@@ -70,7 +70,7 @@ void __thermal_zone_set_trips(struct the

lockdep_assert_held(&tz->lock);

- if (!tz->ops->set_trips)
+ if (!tz->ops.set_trips)
return;

for_each_trip(tz, trip) {
@@ -114,7 +114,7 @@ void __thermal_zone_set_trips(struct the
* Set a temperature window. When this window is left the driver
* must inform the thermal core via thermal_zone_device_update.
*/
- ret = tz->ops->set_trips(tz, low, high);
+ ret = tz->ops.set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
}
Index: linux-pm/drivers/thermal/thermal_hwmon.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_hwmon.c
+++ linux-pm/drivers/thermal/thermal_hwmon.c
@@ -80,7 +80,7 @@ temp_crit_show(struct device *dev, struc

mutex_lock(&tz->lock);

- ret = tz->ops->get_crit_temp(tz, &temperature);
+ ret = tz->ops.get_crit_temp(tz, &temperature);

mutex_unlock(&tz->lock);

@@ -132,7 +132,7 @@ thermal_hwmon_lookup_temp(const struct t
static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
{
int temp;
- return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp);
+ return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);
}

int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
Index: linux-pm/drivers/thermal/thermal_of.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_of.c
+++ linux-pm/drivers/thermal/thermal_of.c
@@ -441,12 +441,10 @@ static int thermal_of_unbind(struct ther
static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
{
struct thermal_trip *trips = tz->trips;
- struct thermal_zone_device_ops *ops = tz->ops;

thermal_zone_device_disable(tz);
thermal_zone_device_unregister(tz);
kfree(trips);
- kfree(ops);
}

/**
@@ -472,33 +470,27 @@ static void thermal_of_zone_unregister(s
static struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data,
const struct thermal_zone_device_ops *ops)
{
+ struct thermal_zone_device_ops of_ops = *ops;
struct thermal_zone_device *tz;
struct thermal_trip *trips;
struct thermal_zone_params tzp = {};
- struct thermal_zone_device_ops *of_ops;
struct device_node *np;
const char *action;
int delay, pdelay;
int ntrips;
int ret;

- of_ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);
- if (!of_ops)
- return ERR_PTR(-ENOMEM);
-
np = of_thermal_zone_find(sensor, id);
if (IS_ERR(np)) {
if (PTR_ERR(np) != -ENODEV)
pr_err("Failed to find thermal zone for %pOFn id=%d\n", sensor, id);
- ret = PTR_ERR(np);
- goto out_kfree_of_ops;
+ return ERR_CAST(np);
}

trips = thermal_of_trips_init(np, &ntrips);
if (IS_ERR(trips)) {
pr_err("Failed to find trip points for %pOFn id=%d\n", sensor, id);
- ret = PTR_ERR(trips);
- goto out_kfree_of_ops;
+ return ERR_CAST(trips);
}

ret = thermal_of_monitor_init(np, &delay, &pdelay);
@@ -509,16 +501,16 @@ static struct thermal_zone_device *therm

thermal_of_parameters_init(np, &tzp);

- of_ops->bind = thermal_of_bind;
- of_ops->unbind = thermal_of_unbind;
+ of_ops.bind = thermal_of_bind;
+ of_ops.unbind = thermal_of_unbind;

ret = of_property_read_string(np, "critical-action", &action);
if (!ret)
- if (!of_ops->critical && !strcasecmp(action, "reboot"))
- of_ops->critical = thermal_zone_device_critical_reboot;
+ if (!of_ops.critical && !strcasecmp(action, "reboot"))
+ of_ops.critical = thermal_zone_device_critical_reboot;

tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips,
- data, of_ops, &tzp,
+ data, &of_ops, &tzp,
pdelay, delay);
if (IS_ERR(tz)) {
ret = PTR_ERR(tz);
@@ -538,8 +530,6 @@ static struct thermal_zone_device *therm

out_kfree_trips:
kfree(trips);
-out_kfree_of_ops:
- kfree(of_ops);

return ERR_PTR(ret);
}




2024-02-22 10:30:11

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

On 14/02/2024 13:28, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> The current code expects thermal zone creators to pass a pointer to a
> writable trips table to thermal_zone_device_register_with_trips() and
> that trips table is then used by the thermal core going forward.
>
> Consequently, the callers of thermal_zone_device_register_with_trips()
> are required to hold on to the trips table passed to it until the given
> thermal zone is unregistered, at which point the trips table can be
> freed, but at the same time they are not expected to access that table
> directly. This is both error prone and confusing.
>
> To address it, turn the trips table pointer in struct thermal_zone_device
> into a flex array (counted by its num_trips field), allocate it during
> thermal zone device allocation and copy the contents of the trips table
> supplied by the zone creator (which can be const now) into it, which
> will allow the callers of thermal_zone_device_register_with_trips() to
> drop their trip tables right after the zone registration.
>
> This requires the imx thermal driver to be adjusted to store the new
> temperature in its internal trips table in imx_set_trip_temp(), because
> it will be separate from the core's trips table now and it has to be
> explicitly kept in sync with the latter.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> Reviewed-by: Stanislaw Gruszka <[email protected]>

Reviewed-by: Daniel Lezcano <[email protected]>


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 10:35:13

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 3/6] thermal: intel: Discard trip tables after zone registration

On 14/02/2024 13:42, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Because the thermal core creates and uses its own copy of the trips
> table passed to thermal_zone_device_register_with_trips(), it is not
> necessary to hold on to a local copy of it any more after the given
> thermal zone has been registered.
>
> Accordingly, modify Intel thermal drivers to discard the trips tables
> passed to thermal_zone_device_register_with_trips() after thermal zone
> registration, for example by storing them in local variables which are
> automatically discarded when the zone registration is complete.
>
> Also make some additional code simplifications unlocked by the above
> changes.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>

Acked-by: Daniel Lezcano <[email protected]>

--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 10:44:37

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On 14/02/2024 13:45, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> The current code requires thermal zone creators to pass pointers to
> writable ops structures to thermal_zone_device_register_with_trips()
> which needs to modify the target struct thermal_zone_device_ops object
> if the "critical" operation in it is NULL.
>
> Moreover, the callers of thermal_zone_device_register_with_trips() are
> required to hold on to the struct thermal_zone_device_ops object passed
> to it until the given thermal zone is unregistered.
>
> Both of these requirements are quite inconvenient, so modify struct
> thermal_zone_device to contain struct thermal_zone_device_ops as field and
> make thermal_zone_device_register_with_trips() copy the contents of the
> struct thermal_zone_device_ops passed to it via a pointer (which can be
> const now) to that field.
>
> Also adjust the code using thermal zone ops accordingly and modify
> thermal_of_zone_register() to use a local ops variable during
> thermal zone registration so ops do not need to be freed in
> thermal_of_zone_unregister() any more.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> Reviewed-by: Stanislaw Gruszka <[email protected]>
> ---

Reviewed-by: Daniel Lezcano <[email protected]>


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 10:48:05

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On 14/02/2024 13:45, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> The current code requires thermal zone creators to pass pointers to
> writable ops structures to thermal_zone_device_register_with_trips()
> which needs to modify the target struct thermal_zone_device_ops object
> if the "critical" operation in it is NULL.
>
> Moreover, the callers of thermal_zone_device_register_with_trips() are
> required to hold on to the struct thermal_zone_device_ops object passed
> to it until the given thermal zone is unregistered.
>
> Both of these requirements are quite inconvenient, so modify struct
> thermal_zone_device to contain struct thermal_zone_device_ops as field and
> make thermal_zone_device_register_with_trips() copy the contents of the
> struct thermal_zone_device_ops passed to it via a pointer (which can be
> const now) to that field.
>
> Also adjust the code using thermal zone ops accordingly and modify
> thermal_of_zone_register() to use a local ops variable during
> thermal zone registration so ops do not need to be freed in
> thermal_of_zone_unregister() any more.

[ ... ]

> static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> {
> struct thermal_trip *trips = tz->trips;
> - struct thermal_zone_device_ops *ops = tz->ops;
>
> thermal_zone_device_disable(tz);
> thermal_zone_device_unregister(tz);
> kfree(trips);

Not related to the current patch but with patch 1/6. Freeing the trip
points here will lead to a double free given it is already freed from
thermal_zone_device_unregister() after the changes introduces in patch
1, right ?


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 11:00:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On Thu, Feb 22, 2024 at 11:47 AM Daniel Lezcano
<[email protected]> wrote:
>
> On 14/02/2024 13:45, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > The current code requires thermal zone creators to pass pointers to
> > writable ops structures to thermal_zone_device_register_with_trips()
> > which needs to modify the target struct thermal_zone_device_ops object
> > if the "critical" operation in it is NULL.
> >
> > Moreover, the callers of thermal_zone_device_register_with_trips() are
> > required to hold on to the struct thermal_zone_device_ops object passed
> > to it until the given thermal zone is unregistered.
> >
> > Both of these requirements are quite inconvenient, so modify struct
> > thermal_zone_device to contain struct thermal_zone_device_ops as field and
> > make thermal_zone_device_register_with_trips() copy the contents of the
> > struct thermal_zone_device_ops passed to it via a pointer (which can be
> > const now) to that field.
> >
> > Also adjust the code using thermal zone ops accordingly and modify
> > thermal_of_zone_register() to use a local ops variable during
> > thermal zone registration so ops do not need to be freed in
> > thermal_of_zone_unregister() any more.
>
> [ ... ]
>
> > static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> > {
> > struct thermal_trip *trips = tz->trips;
> > - struct thermal_zone_device_ops *ops = tz->ops;
> >
> > thermal_zone_device_disable(tz);
> > thermal_zone_device_unregister(tz);
> > kfree(trips);
>
> Not related to the current patch but with patch 1/6. Freeing the trip
> points here will lead to a double free given it is already freed from
> thermal_zone_device_unregister() after the changes introduces in patch
> 1, right ?

No, patch [1/6] doesn't free the caller-supplied ops, just copies them
into the local instance. Attempting to free a static ops would not be
a good idea, for example.

BTW, thanks for all of the reviews, but this series is not applicable
without the one at

https://lore.kernel.org/linux-pm/6017196.lOV4Wx5bFT@kreacher/

2024-02-22 11:04:24

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On 22/02/2024 11:52, Rafael J. Wysocki wrote:
> On Thu, Feb 22, 2024 at 11:47 AM Daniel Lezcano
> <[email protected]> wrote:
>>
>> On 14/02/2024 13:45, Rafael J. Wysocki wrote:
>>> From: Rafael J. Wysocki <[email protected]>
>>>
>>> The current code requires thermal zone creators to pass pointers to
>>> writable ops structures to thermal_zone_device_register_with_trips()
>>> which needs to modify the target struct thermal_zone_device_ops object
>>> if the "critical" operation in it is NULL.
>>>
>>> Moreover, the callers of thermal_zone_device_register_with_trips() are
>>> required to hold on to the struct thermal_zone_device_ops object passed
>>> to it until the given thermal zone is unregistered.
>>>
>>> Both of these requirements are quite inconvenient, so modify struct
>>> thermal_zone_device to contain struct thermal_zone_device_ops as field and
>>> make thermal_zone_device_register_with_trips() copy the contents of the
>>> struct thermal_zone_device_ops passed to it via a pointer (which can be
>>> const now) to that field.
>>>
>>> Also adjust the code using thermal zone ops accordingly and modify
>>> thermal_of_zone_register() to use a local ops variable during
>>> thermal zone registration so ops do not need to be freed in
>>> thermal_of_zone_unregister() any more.
>>
>> [ ... ]
>>
>>> static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
>>> {
>>> struct thermal_trip *trips = tz->trips;
>>> - struct thermal_zone_device_ops *ops = tz->ops;
>>>
>>> thermal_zone_device_disable(tz);
>>> thermal_zone_device_unregister(tz);
>>> kfree(trips);
>>
>> Not related to the current patch but with patch 1/6. Freeing the trip
>> points here will lead to a double free given it is already freed from
>> thermal_zone_device_unregister() after the changes introduces in patch
>> 1, right ?
>
> No, patch [1/6] doesn't free the caller-supplied ops, just copies them
> into the local instance. Attempting to free a static ops would not be
> a good idea, for example.

I'm referring to the trip points not the ops.

The patch 1 does:

tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);

Then the last line of thermal_zone_device_unregister() does:

kfree(tz);

That includes the trip points in the flexible array.

Now in thermal_of_zone_unregister(), we do:

trips = tz->trips;
thermal_zone_device_unregister(tz);
kfree(trips);

Hence double kfree, right?



> BTW, thanks for all of the reviews, but this series is not applicable
> without the one at
>
> https://lore.kernel.org/linux-pm/6017196.lOV4Wx5bFT@kreacher/



--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 11:06:28

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 6/6] thermal: intel: Adjust ops handling during thermal zone registration

On 14/02/2024 13:49, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Because thermal zone operations are now stored directly in struct
> thermal_zone_device, thermal zone creators can discard the operations
> structure after the zone registration is complete, or it can be made
> read-only.
>
> Accordingly, make int340x_thermal_zone_add() use a local variable to
> represent thermal zone operations, so it is freed automatically upon the
> function exit, and make the other Intel thermal drivers use const zone
> operations structures.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> Reviewed-by: Stanislaw Gruszka <[email protected]>

Reviewed-by: Daniel Lezcano <[email protected]>

--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 11:12:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On Thu, Feb 22, 2024 at 11:58 AM Daniel Lezcano
<[email protected]> wrote:
>
> On 22/02/2024 11:52, Rafael J. Wysocki wrote:
> > On Thu, Feb 22, 2024 at 11:47 AM Daniel Lezcano
> > <[email protected]> wrote:
> >>
> >> On 14/02/2024 13:45, Rafael J. Wysocki wrote:
> >>> From: Rafael J. Wysocki <[email protected]>
> >>>
> >>> The current code requires thermal zone creators to pass pointers to
> >>> writable ops structures to thermal_zone_device_register_with_trips()
> >>> which needs to modify the target struct thermal_zone_device_ops object
> >>> if the "critical" operation in it is NULL.
> >>>
> >>> Moreover, the callers of thermal_zone_device_register_with_trips() are
> >>> required to hold on to the struct thermal_zone_device_ops object passed
> >>> to it until the given thermal zone is unregistered.
> >>>
> >>> Both of these requirements are quite inconvenient, so modify struct
> >>> thermal_zone_device to contain struct thermal_zone_device_ops as field and
> >>> make thermal_zone_device_register_with_trips() copy the contents of the
> >>> struct thermal_zone_device_ops passed to it via a pointer (which can be
> >>> const now) to that field.
> >>>
> >>> Also adjust the code using thermal zone ops accordingly and modify
> >>> thermal_of_zone_register() to use a local ops variable during
> >>> thermal zone registration so ops do not need to be freed in
> >>> thermal_of_zone_unregister() any more.
> >>
> >> [ ... ]
> >>
> >>> static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> >>> {
> >>> struct thermal_trip *trips = tz->trips;
> >>> - struct thermal_zone_device_ops *ops = tz->ops;
> >>>
> >>> thermal_zone_device_disable(tz);
> >>> thermal_zone_device_unregister(tz);
> >>> kfree(trips);
> >>
> >> Not related to the current patch but with patch 1/6. Freeing the trip
> >> points here will lead to a double free given it is already freed from
> >> thermal_zone_device_unregister() after the changes introduces in patch
> >> 1, right ?
> >
> > No, patch [1/6] doesn't free the caller-supplied ops, just copies them
> > into the local instance. Attempting to free a static ops would not be
> > a good idea, for example.
>
> I'm referring to the trip points not the ops.

Ah, sorry for the confusion.

> The patch 1 does:
>
> tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);
>
> Then the last line of thermal_zone_device_unregister() does:
>
> kfree(tz);
>
> That includes the trip points in the flexible array.

Right.

> Now in thermal_of_zone_unregister(), we do:
>
> trips = tz->trips;

I missed this.

> thermal_zone_device_unregister(tz);
> kfree(trips);
>
> Hence double kfree, right?

Indeed, so patch [1/6] is missing a thermal_of change to stop freeing
trips separately. Let me send an update of just that patch.

2024-02-22 13:10:42

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2.1 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

From: Rafael J. Wysocki <[email protected]>

The current code expects thermal zone creators to pass a pointer to a
writable trips table to thermal_zone_device_register_with_trips() and
that trips table is then used by the thermal core going forward.

Consequently, the callers of thermal_zone_device_register_with_trips()
are required to hold on to the trips table passed to it until the given
thermal zone is unregistered, at which point the trips table can be
freed, but at the same time they are not expected to access that table
directly. This is both error prone and confusing.

To address it, turn the trips table pointer in struct thermal_zone_device
into a flex array (counted by its num_trips field), allocate it during
thermal zone device allocation and copy the contents of the trips table
supplied by the zone creator (which can be const now) into it, which
will allow the callers of thermal_zone_device_register_with_trips() to
drop their trip tables right after the zone registration.

This requires the imx thermal driver to be adjusted to store the new
temperature in its internal trips table in imx_set_trip_temp(), because
it will be separate from the core's trips table now and it has to be
explicitly kept in sync with the latter.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Reviewed-by: Stanislaw Gruszka <[email protected]>
Reviewed-by: Daniel Lezcano <[email protected]>
---

v2 -> v2.1:
* Remove the trips table freeing from thermal_of (Daniel).
* Add R-by from Daniel.

v1 -> v2:
* Rebase.
* Drop all of the redundant trips[] checks against NULL.
* Add imx change to still allow it to use its local trips table.
* Add R-by from Stanislaw (which is still applicable IMV).

---
drivers/thermal/imx_thermal.c | 1 +
drivers/thermal/thermal_core.c | 17 ++++++++---------
drivers/thermal/thermal_of.c | 2 --
drivers/thermal/thermal_trip.c | 2 +-
include/linux/thermal.h | 10 +++++-----
5 files changed, 15 insertions(+), 17 deletions(-)

Index: linux-pm/include/linux/thermal.h
===================================================================
--- linux-pm.orig/include/linux/thermal.h
+++ linux-pm/include/linux/thermal.h
@@ -137,7 +137,6 @@ struct thermal_cooling_device {
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
- * @trips: an array of struct thermal_trip
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
@@ -167,6 +166,7 @@ struct thermal_cooling_device {
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
+ * @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
@@ -179,7 +179,6 @@ struct thermal_zone_device {
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
- struct thermal_trip *trips;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
@@ -200,10 +199,11 @@ struct thermal_zone_device {
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
+ bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
- bool suspended;
+ struct thermal_trip trips[] __counted_by(num_trips);
};

/**
@@ -322,7 +322,7 @@ int thermal_zone_get_crit_temp(struct th
#ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, void *devdata,
struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
@@ -381,7 +381,7 @@ void thermal_zone_device_critical(struct
#else
static inline struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, void *devdata,
struct thermal_zone_device_ops *ops,
const struct thermal_zone_params *tzp,
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -1227,9 +1227,6 @@ int thermal_zone_get_crit_temp(struct th
if (tz->ops->get_crit_temp)
return tz->ops->get_crit_temp(tz, temp);

- if (!tz->trips)
- return -EINVAL;
-
mutex_lock(&tz->lock);

for (i = 0; i < tz->num_trips; i++) {
@@ -1271,10 +1268,12 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_crit_
* IS_ERR*() helpers.
*/
struct thermal_zone_device *
-thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips,
- void *devdata, struct thermal_zone_device_ops *ops,
- const struct thermal_zone_params *tzp, int passive_delay,
- int polling_delay)
+thermal_zone_device_register_with_trips(const char *type,
+ const struct thermal_trip *trips,
+ int num_trips, void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz;
int id;
@@ -1308,7 +1307,7 @@ thermal_zone_device_register_with_trips(
if (!thermal_class)
return ERR_PTR(-ENODEV);

- tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+ tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);

@@ -1340,7 +1339,7 @@ thermal_zone_device_register_with_trips(
tz->ops = ops;
tz->device.class = thermal_class;
tz->devdata = devdata;
- tz->trips = trips;
+ memcpy(tz->trips, trips, num_trips * sizeof(*trips));
tz->num_trips = num_trips;

thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
Index: linux-pm/drivers/thermal/imx_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/imx_thermal.c
+++ linux-pm/drivers/thermal/imx_thermal.c
@@ -355,6 +355,7 @@ static int imx_set_trip_temp(struct ther
return -EINVAL;

imx_set_alarm_temp(data, temp);
+ trips[IMX_TRIP_PASSIVE].temperature = temp;

pm_runtime_put(data->dev);

Index: linux-pm/drivers/thermal/thermal_trip.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_trip.c
+++ linux-pm/drivers/thermal/thermal_trip.c
@@ -122,7 +122,7 @@ void __thermal_zone_set_trips(struct the
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
- if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip)
+ if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;

*trip = tz->trips[trip_id];
Index: linux-pm/drivers/thermal/thermal_of.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_of.c
+++ linux-pm/drivers/thermal/thermal_of.c
@@ -440,12 +440,10 @@ static int thermal_of_unbind(struct ther
*/
static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
{
- struct thermal_trip *trips = tz->trips;
struct thermal_zone_device_ops *ops = tz->ops;

thermal_zone_device_disable(tz);
thermal_zone_device_unregister(tz);
- kfree(trips);
kfree(ops);
}





2024-02-22 13:47:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2.1 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

On Thu, Feb 22, 2024 at 2:38 PM Daniel Lezcano
<[email protected]> wrote:
>
> On 22/02/2024 14:10, Rafael J. Wysocki wrote:
>
> [ ... ]
>
> > Index: linux-pm/drivers/thermal/thermal_of.c
> > ===================================================================
> > --- linux-pm.orig/drivers/thermal/thermal_of.c
> > +++ linux-pm/drivers/thermal/thermal_of.c
> > @@ -440,12 +440,10 @@ static int thermal_of_unbind(struct ther
> > */
> > static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> > {
> > - struct thermal_trip *trips = tz->trips;
> > struct thermal_zone_device_ops *ops = tz->ops;
> >
> > thermal_zone_device_disable(tz);
> > thermal_zone_device_unregister(tz);
> > - kfree(trips);
>
> thermal_of_zone_register() must free the allocated trip points after
> registering the thermal zone.
>
> > kfree(ops);
> > }

Good point.

Let me send another update, then.

2024-02-22 13:47:59

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2.1 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

On 22/02/2024 14:10, Rafael J. Wysocki wrote:

[ ... ]

> Index: linux-pm/drivers/thermal/thermal_of.c
> ===================================================================
> --- linux-pm.orig/drivers/thermal/thermal_of.c
> +++ linux-pm/drivers/thermal/thermal_of.c
> @@ -440,12 +440,10 @@ static int thermal_of_unbind(struct ther
> */
> static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> {
> - struct thermal_trip *trips = tz->trips;
> struct thermal_zone_device_ops *ops = tz->ops;
>
> thermal_zone_device_disable(tz);
> thermal_zone_device_unregister(tz);
> - kfree(trips);

thermal_of_zone_register() must free the allocated trip points after
registering the thermal zone.

> kfree(ops);
> }


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2024-02-22 13:52:17

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2.2 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

From: Rafael J. Wysocki <[email protected]>

The current code expects thermal zone creators to pass a pointer to a
writable trips table to thermal_zone_device_register_with_trips() and
that trips table is then used by the thermal core going forward.

Consequently, the callers of thermal_zone_device_register_with_trips()
are required to hold on to the trips table passed to it until the given
thermal zone is unregistered, at which point the trips table can be
freed, but at the same time they are not expected to access that table
directly. This is both error prone and confusing.

To address it, turn the trips table pointer in struct thermal_zone_device
into a flex array (counted by its num_trips field), allocate it during
thermal zone device allocation and copy the contents of the trips table
supplied by the zone creator (which can be const now) into it, which
will allow the callers of thermal_zone_device_register_with_trips() to
drop their trip tables right after the zone registration.

This requires the imx thermal driver to be adjusted to store the new
temperature in its internal trips table in imx_set_trip_temp(), because
it will be separate from the core's trips table now and it has to be
explicitly kept in sync with the latter.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Reviewed-by: Stanislaw Gruszka <[email protected]>
Reviewed-by: Daniel Lezcano <[email protected]>
---

v2.1 -> v2.2:
* Add missing kfree(trips) to thermal_of_zone_register() (Daniel).

v2 -> v2.1:
* Remove the trips table freeing from thermal_of (Daniel).
* Add R-by from Daniel.

v1 -> v2:
* Rebase.
* Drop all of the redundant trips[] checks against NULL.
* Add imx change to still allow it to use its local trips table.
* Add R-by from Stanislaw (which is still applicable IMV).

---
drivers/thermal/imx_thermal.c | 1 +
drivers/thermal/thermal_core.c | 18 +++++++++---------
drivers/thermal/thermal_of.c | 4 ++--
drivers/thermal/thermal_trip.c | 2 +-
include/linux/thermal.h | 10 +++++-----
5 files changed, 18 insertions(+), 17 deletions(-)

Index: linux-pm/include/linux/thermal.h
===================================================================
--- linux-pm.orig/include/linux/thermal.h
+++ linux-pm/include/linux/thermal.h
@@ -130,7 +130,6 @@ struct thermal_cooling_device {
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
- * @trips: an array of struct thermal_trip
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
@@ -160,6 +159,7 @@ struct thermal_cooling_device {
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
+ * @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
@@ -172,7 +172,6 @@ struct thermal_zone_device {
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
- struct thermal_trip *trips;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
@@ -193,10 +192,11 @@ struct thermal_zone_device {
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
+ bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
- bool suspended;
+ struct thermal_trip trips[] __counted_by(num_trips);
};

/**
@@ -315,7 +315,7 @@ int thermal_zone_get_crit_temp(struct th
#ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, int mask,
void *devdata,
struct thermal_zone_device_ops *ops,
@@ -375,7 +375,7 @@ void thermal_zone_device_critical(struct
#else
static inline struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,
- struct thermal_trip *trips,
+ const struct thermal_trip *trips,
int num_trips, int mask,
void *devdata,
struct thermal_zone_device_ops *ops,
Index: linux-pm/drivers/thermal/thermal_core.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_core.c
+++ linux-pm/drivers/thermal/thermal_core.c
@@ -1227,9 +1227,6 @@ int thermal_zone_get_crit_temp(struct th
if (tz->ops->get_crit_temp)
return tz->ops->get_crit_temp(tz, temp);

- if (!tz->trips)
- return -EINVAL;
-
mutex_lock(&tz->lock);

for (i = 0; i < tz->num_trips; i++) {
@@ -1272,10 +1269,13 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_crit_
* IS_ERR*() helpers.
*/
struct thermal_zone_device *
-thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips, int mask,
- void *devdata, struct thermal_zone_device_ops *ops,
- const struct thermal_zone_params *tzp, int passive_delay,
- int polling_delay)
+thermal_zone_device_register_with_trips(const char *type,
+ const struct thermal_trip *trips,
+ int num_trips, int mask,
+ void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz;
int id;
@@ -1322,7 +1322,7 @@ thermal_zone_device_register_with_trips(
if (!thermal_class)
return ERR_PTR(-ENODEV);

- tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+ tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);

@@ -1354,7 +1354,7 @@ thermal_zone_device_register_with_trips(
tz->ops = ops;
tz->device.class = thermal_class;
tz->devdata = devdata;
- tz->trips = trips;
+ memcpy(tz->trips, trips, num_trips * sizeof(*trips));
tz->num_trips = num_trips;

thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
Index: linux-pm/drivers/thermal/imx_thermal.c
===================================================================
--- linux-pm.orig/drivers/thermal/imx_thermal.c
+++ linux-pm/drivers/thermal/imx_thermal.c
@@ -354,6 +354,7 @@ static int imx_set_trip_temp(struct ther
return -EINVAL;

imx_set_alarm_temp(data, temp);
+ trips[IMX_TRIP_PASSIVE].temperature = temp;

pm_runtime_put(data->dev);

Index: linux-pm/drivers/thermal/thermal_trip.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_trip.c
+++ linux-pm/drivers/thermal/thermal_trip.c
@@ -122,7 +122,7 @@ void __thermal_zone_set_trips(struct the
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
- if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip)
+ if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;

*trip = tz->trips[trip_id];
Index: linux-pm/drivers/thermal/thermal_of.c
===================================================================
--- linux-pm.orig/drivers/thermal/thermal_of.c
+++ linux-pm/drivers/thermal/thermal_of.c
@@ -438,12 +438,10 @@ static int thermal_of_unbind(struct ther
*/
static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
{
- struct thermal_trip *trips = tz->trips;
struct thermal_zone_device_ops *ops = tz->ops;

thermal_zone_device_disable(tz);
thermal_zone_device_unregister(tz);
- kfree(trips);
kfree(ops);
}

@@ -526,6 +524,8 @@ static struct thermal_zone_device *therm
goto out_kfree_trips;
}

+ kfree(trips);
+
ret = thermal_zone_device_enable(tz);
if (ret) {
pr_err("Failed to enabled thermal zone '%s', id=%d: %d\n",




2024-02-22 14:07:39

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] thermal: core: Store zone ops in struct thermal_zone_device

On Thu, Feb 22, 2024 at 11:52 AM Rafael J. Wysocki <[email protected]> wrote:
>
> On Thu, Feb 22, 2024 at 11:47 AM Daniel Lezcano
> <[email protected]> wrote:
> >
> > On 14/02/2024 13:45, Rafael J. Wysocki wrote:
> > > From: Rafael J. Wysocki <[email protected]>
> > >
> > > The current code requires thermal zone creators to pass pointers to
> > > writable ops structures to thermal_zone_device_register_with_trips()
> > > which needs to modify the target struct thermal_zone_device_ops object
> > > if the "critical" operation in it is NULL.
> > >
> > > Moreover, the callers of thermal_zone_device_register_with_trips() are
> > > required to hold on to the struct thermal_zone_device_ops object passed
> > > to it until the given thermal zone is unregistered.
> > >
> > > Both of these requirements are quite inconvenient, so modify struct
> > > thermal_zone_device to contain struct thermal_zone_device_ops as field and
> > > make thermal_zone_device_register_with_trips() copy the contents of the
> > > struct thermal_zone_device_ops passed to it via a pointer (which can be
> > > const now) to that field.
> > >
> > > Also adjust the code using thermal zone ops accordingly and modify
> > > thermal_of_zone_register() to use a local ops variable during
> > > thermal zone registration so ops do not need to be freed in
> > > thermal_of_zone_unregister() any more.
> >
> > [ ... ]
> >
> > > static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
> > > {
> > > struct thermal_trip *trips = tz->trips;
> > > - struct thermal_zone_device_ops *ops = tz->ops;
> > >
> > > thermal_zone_device_disable(tz);
> > > thermal_zone_device_unregister(tz);
> > > kfree(trips);
> >
> > Not related to the current patch but with patch 1/6. Freeing the trip
> > points here will lead to a double free given it is already freed from
> > thermal_zone_device_unregister() after the changes introduces in patch
> > 1, right ?
>
> No, patch [1/6] doesn't free the caller-supplied ops, just copies them
> into the local instance. Attempting to free a static ops would not be
> a good idea, for example.
>
> BTW, thanks for all of the reviews, but this series is not applicable
> without the one at
>
> https://lore.kernel.org/linux-pm/6017196.lOV4Wx5bFT@kreacher/

As it turns out, this really is not a big deal, because the rebase is
trivial for everything except for the Intel patches, but for those two
I have the v1 versions that apply just fine without the other series
and have been reviewed.

So I can apply this series before the above one and rebase the latter
(I'd rather not send a new version of it though, so it can be reviewed
as is).

So never mind.

2024-02-22 15:50:15

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2.2 1/6] thermal: core: Store zone trips table in struct thermal_zone_device

On 22/02/2024 14:52, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> The current code expects thermal zone creators to pass a pointer to a
> writable trips table to thermal_zone_device_register_with_trips() and
> that trips table is then used by the thermal core going forward.
>
> Consequently, the callers of thermal_zone_device_register_with_trips()
> are required to hold on to the trips table passed to it until the given
> thermal zone is unregistered, at which point the trips table can be
> freed, but at the same time they are not expected to access that table
> directly. This is both error prone and confusing.
>
> To address it, turn the trips table pointer in struct thermal_zone_device
> into a flex array (counted by its num_trips field), allocate it during
> thermal zone device allocation and copy the contents of the trips table
> supplied by the zone creator (which can be const now) into it, which
> will allow the callers of thermal_zone_device_register_with_trips() to
> drop their trip tables right after the zone registration.
>
> This requires the imx thermal driver to be adjusted to store the new
> temperature in its internal trips table in imx_set_trip_temp(), because
> it will be separate from the core's trips table now and it has to be
> explicitly kept in sync with the latter.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> Reviewed-by: Stanislaw Gruszka <[email protected]>
> Reviewed-by: Daniel Lezcano <[email protected]>
> ---
>
> v2.1 -> v2.2:
> * Add missing kfree(trips) to thermal_of_zone_register() (Daniel).

OK for me


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog