Linux-PM Archive on lore.kernel.org
 help / color / Atom feed
* Re: [PATCH v4 06/11] thermal: Add mode helpers
@ 2020-05-29 15:52 Guenter Roeck
  2020-06-01 11:16 ` Andrzej Pietrasiewicz
  0 siblings, 1 reply; 140+ messages in thread
From: Guenter Roeck @ 2020-05-29 15:52 UTC (permalink / raw)
  To: Andrzej Pietrasiewicz
  Cc: linux-pm, linux-acpi, netdev, linux-wireless,
	platform-driver-x86, linux-arm-kernel, linux-renesas-soc,
	linux-rockchip, Emmanuel Grumbach, Heiko Stuebner,
	Rafael J . Wysocki, Vishal Kulkarni, Luca Coelho, Miquel Raynal,
	kernel, Fabio Estevam, Amit Kucheria, Chunyan Zhang,
	Daniel Lezcano, Allison Randal, NXP Linux Team, Darren Hart,
	Zhang Rui, Gayatri Kammela, Len Brown, Johannes Berg,
	Intel Linux Wireless, Sascha Hauer, Ido Schimmel, Baolin Wang,
	Jiri Pirko, Orson Zhai, Thomas Gleixner, Kalle Valo,
	Support Opensource, Enrico Weigelt, Peter Kaestle,
	Sebastian Reichel, Bartlomiej Zolnierkiewicz,
	Pengutronix Kernel Team, Niklas Söderlund, Shawn Guo,
	David S . Miller, Andy Shevchenko

On Thu, May 28, 2020 at 09:20:46PM +0200, Andrzej Pietrasiewicz wrote:
> Prepare for making the drivers not access tzd's private members.
> 
> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
> ---
>  drivers/thermal/thermal_core.c | 53 ++++++++++++++++++++++++++++++++++
>  include/linux/thermal.h        | 13 +++++++++
>  2 files changed, 66 insertions(+)
> 
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index 14d3b1b94c4f..f2a5c5ee3455 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -459,6 +459,59 @@ static void thermal_zone_device_reset(struct thermal_zone_device *tz)
>  	thermal_zone_device_init(tz);
>  }
>  
> +int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
> +				 enum thermal_device_mode mode)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&tz->lock);
> +
> +	/* do nothing if mode isn't changing */
> +	if (mode == tz->mode) {
> +		mutex_unlock(&tz->lock);
> +
Nit: unnecessary empty line.

> +		return ret;
> +	}
> +
> +	if (tz->ops->set_mode)
> +		ret = tz->ops->set_mode(tz, mode);
> +
> +	if (!ret)
> +		tz->mode = mode;
> +
> +	mutex_unlock(&tz->lock);
> +
> +	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
> +
> +	return ret;
> +}
> +
> +int thermal_zone_device_enable(struct thermal_zone_device *tz)
> +{
> +	return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED);
> +}
> +EXPORT_SYMBOL(thermal_zone_device_enable);

Other exports in thermal/ use EXPORT_SYMBOL_GPL.

> +
> +int thermal_zone_device_disable(struct thermal_zone_device *tz)
> +{
> +	return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED);
> +}
> +EXPORT_SYMBOL(thermal_zone_device_disable);
> +
> +int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
> +{
> +	enum thermal_device_mode mode;
> +
> +	mutex_lock(&tz->lock);
> +
> +	mode = tz->mode;
> +
> +	mutex_unlock(&tz->lock);
> +
> +	return mode == THERMAL_DEVICE_ENABLED;
> +}
> +EXPORT_SYMBOL(thermal_zone_device_is_enabled);
> +
>  void thermal_zone_device_update(struct thermal_zone_device *tz,
>  				enum thermal_notify_event event)
>  {
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index a808f6fa2777..df013c39ba9b 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -416,6 +416,9 @@ int thermal_zone_get_offset(struct thermal_zone_device *tz);
>  
>  void thermal_cdev_update(struct thermal_cooling_device *);
>  void thermal_notify_framework(struct thermal_zone_device *, int);
> +int thermal_zone_device_enable(struct thermal_zone_device *tz);
> +int thermal_zone_device_disable(struct thermal_zone_device *tz);
> +int thermal_zone_device_is_enabled(struct thermal_zone_device *tz);
>  #else
>  static inline struct thermal_zone_device *thermal_zone_device_register(
>  	const char *type, int trips, int mask, void *devdata,
> @@ -463,6 +466,16 @@ static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
>  static inline void thermal_notify_framework(struct thermal_zone_device *tz,
>  	int trip)
>  { }
> +
> +static inline int thermal_zone_device_enable(struct thermal_zone_device *tz)
> +{ return -ENODEV; }
> +
> +static inline int thermal_zone_device_disable(struct thermal_zone_device *tz)
> +{ return -ENODEV; }
> +
> +static inline int
> +thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
> +{ return -ENODEV; }
>  #endif /* CONFIG_THERMAL */
>  
>  #endif /* __THERMAL_H__ */

^ permalink raw reply	[flat|nested] 140+ messages in thread
* [RFC 0/8] Stop monitoring disabled devices
@ 2020-04-07 17:49 Andrzej Pietrasiewicz
  2020-04-07 17:49 ` [RFC 1/8] thermal: int3400_thermal: Statically initialize .get_mode()/.set_mode() ops Andrzej Pietrasiewicz
                   ` (8 more replies)
  0 siblings, 9 replies; 140+ messages in thread
From: Andrzej Pietrasiewicz @ 2020-04-07 17:49 UTC (permalink / raw)
  To: linux-pm
  Cc: Zhang Rui, Rafael J . Wysocki, Len Brown, Jiri Pirko,
	Ido Schimmel, David S . Miller, Peter Kaestle, Darren Hart,
	Andy Shevchenko, Support Opensource, Daniel Lezcano,
	Amit Kucheria, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, NXP Linux Team, Allison Randal, Enrico Weigelt,
	Gayatri Kammela, Thomas Gleixner, linux-acpi, netdev,
	platform-driver-x86, linux-arm-kernel, kernel,
	Andrzej Pietrasiewicz

The current kernel behavior is to keep polling the thermal zone devices
regardless of their current mode. This is not desired, as all such "disabled"
devices are meant to be handled by userspace, so polling them makes no sense.

There was an attempt to solve this issue:

https://lkml.org/lkml/2018/2/26/498

and it ultimately has not succeeded:

https://lkml.org/lkml/2018/2/27/910

This is a new attempt addressing all the relevant drivers, and I have
identified them with:

$ git grep "thermal_zone_device_ops" | grep "= {" | cut -f1 -d: | sort | uniq

The idea is to modify thermal_zone_device_update() and monitor_thermal_zone()
in such a way that they stop polling a disabled device. To do decide what to
do they should call ->get_mode() operation of the specialized thermal zone
device in question (e.g. drivers/acpi/thermal.c's). But here comes problem:
sometimes a thermal zone device must be initially disabled and becomes enabled
only after its sensors appear on the system. If such thermal zone's
->get_mode() /* in the context of thermal_zone_device_update() or
monitor_thermal_zone() */ is called _before_ the sensors are available, it will
be reported as "disabled" and consequently polling it will be ceased. This is
a change in behavior from userspace's perspective.

To solve the above described problem I want to introduce the third mode of a
thermal_zone_device: initial. The idea is that when the device is in its
initial mode, then its polling will be handled as it is now. This is a good
thing: should the temperature be just about hitting the critical treshnold
early during the boot process, it might be too late if we wait for the
userspace to run to save the system from overheating. The initial mode should
be reported in sysfs as "enabled" to keep the userspace interface intact.
From the initial mode there will be two possible transitions: to enabled or
disabled mode, but there will be no transition back to initial. If the
transition is from initial to enabled, then keep polling. If the transition is
from initial to disabled, then stop polling. If the transition is from enabled
to disabled, then stop polling. The transition from disabled to enabled must
be handled in a special way: there must be a mandatory call to
monitor_thermal_zone(), otherwise the polling will not start. If this
transition is triggeted from sysfs, then it can be easily handled at the
thermal framework level. However, if drivers call their own ->set_mode()
operation then they must also call "monitor_thermal_zone()" afterwards.
The latter being a sensible thing anyway, so perhaps all/most of the drivers
in question do. The plan for implementation is this:

- ensure ALL users use symbolic enum names (THERMAL_DEVICE_DISABLED,
THERMAL_DEVICE_ENABLED) for thermal device mode rather than the numeric
values of enum thermal_device_mode elements
- add THERMAL_DEVICE_INITIAL to the said enum making its value 0 (so that
kzalloc() results in the initial state)
- modify thermal zone device's mode_show() (thermal framework level) so that
it reports "enabled" for THERMAL_DEVICE_INITIAL
- modify thermal zone device's mode_store() (thermal framework level) so that
it calls monitor_thermal_zone() upon mode change
- modify ALL thermal drivers so that their code is prepared to return
THERMAL_DEVICE_INITIAL before they call thermal_zone_device_register(); when
the invocation of the latter completes then polling is expected to be started
- verify ALL drivers which call their own ->set_mode() to ensure they do call
monitor_thermal_zone() afterwards
- modify thermal_zone_device_update() and monitor_thermal_zone() so that they
cancel polling for disabled thermal zone devices (but not for those in
THERMAL_DEVICE_INITIAL mode)

This RFC series does all the above steps in more or less that order.

I kindly ask for comments/suggestions/improvements.

Rebased onto v5.6.

Andrzej Pietrasiewicz (8):
  thermal: int3400_thermal: Statically initialize
    .get_mode()/.set_mode() ops
  thermal: Properly handle mode values in .set_mode()
  thermal: Store thermal mode in a dedicated enum
  thermal: core: Introduce THERMAL_DEVICE_INITIAL
  thermal: core: Monitor thermal zone after mode change
  thermal: Set initial state to THERMAL_DEVICE_INITIAL
  thermal: of: Monitor thermal zone after enabling it
  thermal: Stop polling DISABLED thermal devices

 drivers/acpi/thermal.c                        | 28 +++++-----
 .../ethernet/mellanox/mlxsw/core_thermal.c    | 11 +++-
 drivers/platform/x86/acerhdf.c                | 17 ++++--
 drivers/thermal/da9062-thermal.c              |  2 +-
 drivers/thermal/imx_thermal.c                 |  5 +-
 .../intel/int340x_thermal/int3400_thermal.c   | 24 ++++-----
 .../thermal/intel/intel_quark_dts_thermal.c   |  6 ++-
 drivers/thermal/of-thermal.c                  |  9 +++-
 drivers/thermal/thermal_core.c                | 52 ++++++++++++++++++-
 drivers/thermal/thermal_core.h                |  2 +
 drivers/thermal/thermal_sysfs.c               | 12 +++--
 include/linux/thermal.h                       |  3 +-
 12 files changed, 123 insertions(+), 48 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 140+ messages in thread