Thermal control updates for 6.2-rc1

- Fix race conditions related to thermal device operations that are not
    protected against thermal device removal (Guenter Roeck).
 
  - Fix error code in __thermal_cooling_device_register() (Dan Carpenter).
 
  - Validate new cooling device state (coming from user space) in
    cur_state_store() and reuse the max_state value from cooling device
    structure in the sysfs interface (Viresh Kumar).
 
  - Fix some possible name leaks in error paths in the thermal control
    core code (Yang Yingliang).
 
  - Detect TCC lock bit set in the intel_tcc_cooling driver and make it
    refuse to update the TCC offset in that case (Zhang Rui).
 
  - Add TCC cooling support for RaptorLake-S (Zhang Rui).
 
  - Prevent accidental clearing of HFI status by one of the other
    drivers using the same status register (Srinivas Pandruvada).
 
  - Protect clearing of thermal status bits in Intel thermal control
    drivers (Srinivas Pandruvada).
 
  - Allow the HFI thermal control driver to ACK an HFI event for the
    previously observed timestamp (Srinivas Pandruvada).
 
  - Remove a pointless die_id check from the HFI thermal driver and
    adjust the definition a data structure used by it (Ricardo Neri).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmOXWUoSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxAhoQAI3OFLiQ/boXpTUgPFWbio+E5RZbDGTa
 Tb38JEAhREpXoce3QiT1ZDqoLUGS9tMpyfIBu8QkUXtgdyHkxFJc/AqwvFKUUmVN
 oS5GMn6s3ufQKTeiFoBd/hJ6zaGwcW4e/2QXzTZjq8oUaI0yca0HCc0QUYx6kWF6
 HqSD2KtFRVw4xe6uqZmwTVQddyIqA5FWRaZvhGolDPmVpg8NRTYVbsOzMf/StnRu
 lXyGrgk1tNIkJnGlOKnqsV9xIsH5iS8SRAgZaUc9wK/Wzor486VzH4TdVn6oYdUn
 fGCA1W982VX+mUlvVZQpyjGj8bL1JylZZSAcn8cg5cyKnTP1OinM6j/TIKc1JHK3
 ZFTvTxLuQbJD90frCKub/iIigcTX/GTk8iVRSuzmmzUiBkZ/3/15X6PVA15OEcQU
 zzHaiz0lir7LP0l2BhPDNbfTa1wJkiAtxVW7hcYUOtxL8S8vBldSEB9Kr0+e9A6i
 Lis8OOFS2nZYbGZxplX68JXp3Lo42pcF2QcdekU+iOp0eLziLc2WCB7Lj97TWJoR
 QAeyYf4eAsdKrW6vAiB4ygVxNDm4RPmesqlaNtlfhsFLUY7b5tc5vTinv6it5ktE
 CCNEi0N0NCIGifkIBnuthjToPsvhrTaYF9C1BPTi7p6mZp6VVacScA/qZ3rw2DG6
 W7FV8CJt5m5/
 =ZdzX
 -----END PGP SIGNATURE-----

Merge tag 'thermal-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "These include thermal core fixes to protect thermal device operations
  against thermal device removal, other thermal core fixes and updates
  of Intel thermal control drivers.

  Specifics:

   - Fix race conditions related to thermal device operations that are
     not protected against thermal device removal (Guenter Roeck)

   - Fix error code in __thermal_cooling_device_register() (Dan
     Carpenter)

   - Validate new cooling device state (coming from user space) in
     cur_state_store() and reuse the max_state value from cooling device
     structure in the sysfs interface (Viresh Kumar)

   - Fix some possible name leaks in error paths in the thermal control
     core code (Yang Yingliang)

   - Detect TCC lock bit set in the intel_tcc_cooling driver and make it
     refuse to update the TCC offset in that case (Zhang Rui)

   - Add TCC cooling support for RaptorLake-S (Zhang Rui)

   - Prevent accidental clearing of HFI status by one of the other
     drivers using the same status register (Srinivas Pandruvada)

   - Protect clearing of thermal status bits in Intel thermal control
     drivers (Srinivas Pandruvada)

   - Allow the HFI thermal control driver to ACK an HFI event for the
     previously observed timestamp (Srinivas Pandruvada)

   - Remove a pointless die_id check from the HFI thermal driver and
     adjust the definition a data structure used by it (Ricardo Neri)"

* tag 'thermal-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  thermal: intel: hfi: Remove a pointless die_id check
  thermal: core: fix some possible name leaks in error paths
  thermal: intel: hfi: ACK HFI for the same timestamp
  thermal: intel: Protect clearing of thermal status bits
  thermal: intel: Prevent accidental clearing of HFI status
  thermal/core: Protect thermal device operations against thermal device removal
  thermal/core: Remove thermal_zone_set_trips()
  thermal/core: Protect sysfs accesses to thermal operations with thermal zone mutex
  thermal/core: Protect hwmon accesses to thermal operations with thermal zone mutex
  thermal/core: Introduce locked version of thermal_zone_device_update
  thermal/core: Move parameter validation from __thermal_zone_get_temp to thermal_zone_get_temp
  thermal/core: Ensure that thermal device is registered in thermal_zone_get_temp
  thermal/core: Delete device under thermal device zone lock
  thermal/core: Destroy thermal zone device mutex in release function
  thermal: intel: intel_tcc_cooling: Add TCC cooling support for RaptorLake-S
  thermal: intel: intel_tcc_cooling: Detect TCC lock bit
  thermal: intel: hfi: Improve the type of hfi_features::nr_table_pages
  thermal/core: fix error code in __thermal_cooling_device_register()
  thermal: sysfs: Reuse cdev->max_state
  thermal: Validate new state in cur_state_store()
This commit is contained in:
Linus Torvalds 2022-12-12 13:45:21 -08:00
Родитель 456ed864fd 75b15aa0d8
Коммит 691806e977
12 изменённых файлов: 257 добавлений и 143 удалений

Просмотреть файл

@ -49,11 +49,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
static long get_target_state(struct thermal_zone_device *tz,
struct thermal_cooling_device *cdev, int percentage, int level)
{
unsigned long max_state;
cdev->ops->get_max_state(cdev, &max_state);
return (long)(percentage * level * max_state) / (100 * tz->num_trips);
return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips);
}
/**

Просмотреть файл

@ -42,9 +42,7 @@
#include "../thermal_core.h"
#include "intel_hfi.h"
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \
BIT(9) | BIT(11) | BIT(26))
#include "thermal_interrupt.h"
/* Hardware Feedback Interface MSR configuration bits */
#define HW_FEEDBACK_PTR_VALID_BIT BIT(0)
@ -137,7 +135,7 @@ struct hfi_instance {
* Parameters and supported features that are common to all HFI instances
*/
struct hfi_features {
unsigned int nr_table_pages;
size_t nr_table_pages;
unsigned int cpu_stride;
unsigned int hdr_size;
};
@ -252,7 +250,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
struct hfi_instance *hfi_instance;
int cpu = smp_processor_id();
struct hfi_cpu_info *info;
u64 new_timestamp;
u64 new_timestamp, msr, hfi;
if (!pkg_therm_status_msr_val)
return;
@ -281,9 +279,21 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
if (!raw_spin_trylock(&hfi_instance->event_lock))
return;
/* Skip duplicated updates. */
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr);
hfi = msr & PACKAGE_THERM_STATUS_HFI_UPDATED;
if (!hfi) {
raw_spin_unlock(&hfi_instance->event_lock);
return;
}
/*
* Ack duplicate update. Since there is an active HFI
* status from HW, it must be a new event, not a case
* where a lagging CPU entered the locked region.
*/
new_timestamp = *(u64 *)hfi_instance->hw_table;
if (*hfi_instance->timestamp == new_timestamp) {
thermal_clear_package_intr_status(PACKAGE_LEVEL, PACKAGE_THERM_STATUS_HFI_UPDATED);
raw_spin_unlock(&hfi_instance->event_lock);
return;
}
@ -297,16 +307,14 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
memcpy(hfi_instance->local_table, hfi_instance->hw_table,
hfi_features.nr_table_pages << PAGE_SHIFT);
raw_spin_unlock(&hfi_instance->table_lock);
raw_spin_unlock(&hfi_instance->event_lock);
/*
* Let hardware know that we are done reading the HFI table and it is
* free to update it again.
*/
pkg_therm_status_msr_val &= THERM_STATUS_CLEAR_PKG_MASK &
~PACKAGE_THERM_STATUS_HFI_UPDATED;
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, pkg_therm_status_msr_val);
thermal_clear_package_intr_status(PACKAGE_LEVEL, PACKAGE_THERM_STATUS_HFI_UPDATED);
raw_spin_unlock(&hfi_instance->table_lock);
raw_spin_unlock(&hfi_instance->event_lock);
queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work,
HFI_UPDATE_INTERVAL);
@ -371,7 +379,7 @@ void intel_hfi_online(unsigned int cpu)
die_id = topology_logical_die_id(cpu);
hfi_instance = info->hfi_instance;
if (!hfi_instance) {
if (die_id < 0 || die_id >= max_hfi_instances)
if (die_id >= max_hfi_instances)
return;
hfi_instance = &hfi_instances[die_id];

Просмотреть файл

@ -14,6 +14,7 @@
#define TCC_SHIFT 24
#define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30)
#define TCC_LOCKED BIT(31)
static struct thermal_cooling_device *tcc_cdev;
@ -84,6 +85,7 @@ static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
{}
};
@ -108,6 +110,15 @@ static int __init tcc_cooling_init(void)
if (!(val & TCC_PROGRAMMABLE))
return -ENODEV;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
if (val & TCC_LOCKED) {
pr_info("TCC Offset locked\n");
return -ENODEV;
}
pr_info("Programmable TCC Offset detected\n");
tcc_cdev =

Просмотреть файл

@ -190,32 +190,33 @@ static const struct attribute_group thermal_attr_group = {
};
#endif /* CONFIG_SYSFS */
#define CORE_LEVEL 0
#define PACKAGE_LEVEL 1
#define THERM_THROT_POLL_INTERVAL HZ
#define THERM_STATUS_PROCHOT_LOG BIT(1)
#define THERM_STATUS_CLEAR_CORE_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(13) | BIT(15))
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11))
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(26))
static void clear_therm_status_log(int level)
/*
* Clear the bits in package thermal status register for bit = 1
* in bitmask
*/
void thermal_clear_package_intr_status(int level, u64 bit_mask)
{
u64 msr_val;
int msr;
u64 mask, msr_val;
if (level == CORE_LEVEL) {
msr = MSR_IA32_THERM_STATUS;
mask = THERM_STATUS_CLEAR_CORE_MASK;
msr_val = THERM_STATUS_CLEAR_CORE_MASK;
} else {
msr = MSR_IA32_PACKAGE_THERM_STATUS;
mask = THERM_STATUS_CLEAR_PKG_MASK;
msr_val = THERM_STATUS_CLEAR_PKG_MASK;
}
rdmsrl(msr, msr_val);
msr_val &= mask;
wrmsrl(msr, msr_val & ~THERM_STATUS_PROCHOT_LOG);
msr_val &= ~bit_mask;
wrmsrl(msr, msr_val);
}
EXPORT_SYMBOL_GPL(thermal_clear_package_intr_status);
static void get_therm_status(int level, bool *proc_hot, u8 *temp)
{
@ -295,7 +296,7 @@ static void __maybe_unused throttle_active_work(struct work_struct *work)
state->average = avg;
re_arm:
clear_therm_status_log(state->level);
thermal_clear_package_intr_status(state->level, THERM_STATUS_PROCHOT_LOG);
schedule_delayed_work_on(this_cpu, &state->therm_work, THERM_THROT_POLL_INTERVAL);
}

Просмотреть файл

@ -2,6 +2,9 @@
#ifndef _INTEL_THERMAL_INTERRUPT_H
#define _INTEL_THERMAL_INTERRUPT_H
#define CORE_LEVEL 0
#define PACKAGE_LEVEL 1
/* Interrupt Handler for package thermal thresholds */
extern int (*platform_thermal_package_notify)(__u64 msr_val);
@ -15,4 +18,7 @@ extern bool (*platform_thermal_package_rate_control)(void);
/* Handle HWP interrupt */
extern void notify_hwp_interrupt(void);
/* Common function to clear Package thermal status register */
extern void thermal_clear_package_intr_status(int level, u64 bit_mask);
#endif /* _INTEL_THERMAL_INTERRUPT_H */

Просмотреть файл

@ -265,7 +265,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
struct thermal_zone_device *tzone = NULL;
int cpu = smp_processor_id();
struct zone_device *zonedev;
u64 msr_val, wr_val;
mutex_lock(&thermal_zone_mutex);
raw_spin_lock_irq(&pkg_temp_lock);
@ -279,12 +278,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
}
zonedev->work_scheduled = false;
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
if (wr_val != msr_val) {
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
tzone = zonedev->tzone;
}
thermal_clear_package_intr_status(PACKAGE_LEVEL, THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
tzone = zonedev->tzone;
enable_pkg_thres_interrupt();
raw_spin_unlock_irq(&pkg_temp_lock);

Просмотреть файл

@ -203,6 +203,9 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
mutex_lock(&thermal_governor_lock);
mutex_lock(&tz->lock);
if (!device_is_registered(&tz->device))
goto exit;
gov = __find_governor(strim(policy));
if (!gov)
goto exit;
@ -403,6 +406,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
pos->initialized = false;
}
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
int count;
if (atomic_read(&in_suspend))
return;
if (WARN_ONCE(!tz->ops->get_temp,
"'%s' must not be called without 'get_temp' ops set\n",
__func__))
return;
if (!thermal_zone_device_is_enabled(tz))
return;
update_temperature(tz);
__thermal_zone_set_trips(tz);
tz->notify_event = event;
for (count = 0; count < tz->num_trips; count++)
handle_thermal_trip(tz, count);
monitor_thermal_zone(tz);
}
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode)
{
@ -417,15 +448,21 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
return ret;
}
if (!device_is_registered(&tz->device)) {
mutex_unlock(&tz->lock);
return -ENODEV;
}
if (tz->ops->change_mode)
ret = tz->ops->change_mode(tz, mode);
if (!ret)
tz->mode = mode;
mutex_unlock(&tz->lock);
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
mutex_unlock(&tz->lock);
if (mode == THERMAL_DEVICE_ENABLED)
thermal_notify_tz_enable(tz->id);
@ -457,31 +494,9 @@ int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
int count;
if (atomic_read(&in_suspend))
return;
if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without "
"'get_temp' ops set\n", __func__))
return;
mutex_lock(&tz->lock);
if (!thermal_zone_device_is_enabled(tz))
goto out;
update_temperature(tz);
__thermal_zone_set_trips(tz);
tz->notify_event = event;
for (count = 0; count < tz->num_trips; count++)
handle_thermal_trip(tz, count);
monitor_thermal_zone(tz);
out:
if (device_is_registered(&tz->device))
__thermal_zone_device_update(tz, event);
mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
@ -603,8 +618,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
struct thermal_instance *pos;
struct thermal_zone_device *pos1;
struct thermal_cooling_device *pos2;
unsigned long max_state;
int result, ret;
int result;
if (trip >= tz->num_trips || trip < 0)
return -EINVAL;
@ -621,15 +635,11 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (tz != pos1 || cdev != pos2)
return -EINVAL;
ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
/* lower default 0, upper default max_state */
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
upper = upper == THERMAL_NO_LIMIT ? max_state : upper;
upper = upper == THERMAL_NO_LIMIT ? cdev->max_state : upper;
if (lower > upper || upper > max_state)
if (lower > upper || upper > cdev->max_state)
return -EINVAL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@ -759,6 +769,7 @@ static void thermal_release(struct device *dev)
sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
mutex_destroy(&tz->lock);
kfree(tz);
} else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) {
@ -883,10 +894,6 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->id = ret;
id = ret;
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret)
goto out_ida_remove;
cdev->type = kstrdup(type ? type : "", GFP_KERNEL);
if (!cdev->type) {
ret = -ENOMEM;
@ -900,7 +907,17 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->updated = false;
cdev->device.class = &thermal_class;
cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
if (ret)
goto out_kfree_type;
thermal_cooling_device_setup_sysfs(cdev);
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret) {
thermal_cooling_device_destroy_sysfs(cdev);
goto out_kfree_type;
}
ret = device_register(&cdev->device);
if (ret)
goto out_kfree_type;
@ -1234,10 +1251,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
tz->id = id;
strscpy(tz->type, type, sizeof(tz->type));
result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
if (result)
goto remove_id;
if (!ops->critical)
ops->critical = thermal_zone_device_critical;
@ -1260,6 +1273,11 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
/* A new thermal zone needs to be updated anyway. */
atomic_set(&tz->need_update, 1);
result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
if (result) {
thermal_zone_destroy_device_groups(tz);
goto remove_id;
}
result = device_register(&tz->device);
if (result)
goto release_device;
@ -1390,8 +1408,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_remove_hwmon_sysfs(tz);
ida_free(&thermal_tz_ida, tz->id);
ida_destroy(&tz->ida);
mutex_destroy(&tz->lock);
device_unregister(&tz->device);
mutex_lock(&tz->lock);
device_del(&tz->device);
mutex_unlock(&tz->lock);
put_device(&tz->device);
thermal_notify_tz_delete(tz_id);
}

Просмотреть файл

@ -109,9 +109,10 @@ int thermal_register_governor(struct thermal_governor *);
void thermal_unregister_governor(struct thermal_governor *);
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf);
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event);
/* Helpers */
void thermal_zone_set_trips(struct thermal_zone_device *tz);
void __thermal_zone_set_trips(struct thermal_zone_device *tz);
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);

Просмотреть файл

@ -64,6 +64,20 @@ get_thermal_instance(struct thermal_zone_device *tz,
}
EXPORT_SYMBOL(get_thermal_instance);
/**
* __thermal_zone_get_temp() - returns the temperature of a thermal zone
* @tz: a valid pointer to a struct thermal_zone_device
* @temp: a valid pointer to where to store the resulting temperature.
*
* When a valid thermal zone reference is passed, it will fetch its
* 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.
* The function must be called under tz->lock.
*
* Return: On success returns 0, an error code otherwise
*/
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
int ret = -EINVAL;
@ -73,9 +87,6 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
lockdep_assert_held(&tz->lock);
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
return -EINVAL;
ret = tz->ops->get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
@ -114,14 +125,43 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
int ret;
if (IS_ERR_OR_NULL(tz))
return -EINVAL;
mutex_lock(&tz->lock);
ret = __thermal_zone_get_temp(tz, temp);
if (!tz->ops->get_temp) {
ret = -EINVAL;
goto unlock;
}
if (device_is_registered(&tz->device))
ret = __thermal_zone_get_temp(tz, temp);
else
ret = -ENODEV;
unlock:
mutex_unlock(&tz->lock);
return ret;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
/**
* __thermal_zone_set_trips - Computes the next trip points for the driver
* @tz: a pointer to a thermal zone device structure
*
* The function computes the next temperature boundaries by browsing
* the trip points. The result is the closer low and high trip points
* to the current temperature. These values are passed to the backend
* driver to let it set its own notification mechanism (usually an
* interrupt).
*
* This function must be called with tz->lock held. Both tz and tz->ops
* must be valid pointers.
*
* It does not return a value
*/
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{
int low = -INT_MAX;
@ -168,25 +208,6 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
}
/**
* thermal_zone_set_trips - Computes the next trip points for the driver
* @tz: a pointer to a thermal zone device structure
*
* The function computes the next temperature boundaries by browsing
* the trip points. The result is the closer low and high trip points
* to the current temperature. These values are passed to the backend
* driver to let it set its own notification mechanism (usually an
* interrupt).
*
* It does not return a value
*/
void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
mutex_lock(&tz->lock);
__thermal_zone_set_trips(tz);
mutex_unlock(&tz->lock);
}
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
int target)
{

Просмотреть файл

@ -77,7 +77,15 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
int temperature;
int ret;
ret = tz->ops->get_crit_temp(tz, &temperature);
mutex_lock(&tz->lock);
if (device_is_registered(&tz->device))
ret = tz->ops->get_crit_temp(tz, &temperature);
else
ret = -ENODEV;
mutex_unlock(&tz->lock);
if (ret)
return ret;

Просмотреть файл

@ -92,7 +92,14 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1)
return -EINVAL;
result = tz->ops->get_trip_type(tz, trip, &type);
mutex_lock(&tz->lock);
if (device_is_registered(dev))
result = tz->ops->get_trip_type(tz, trip, &type);
else
result = -ENODEV;
mutex_unlock(&tz->lock);
if (result)
return result;
@ -128,10 +135,17 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
if (tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip, temperature);
if (ret)
return ret;
goto unlock;
}
if (tz->trips)
@ -140,16 +154,22 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
if (tz->ops->get_trip_hyst) {
ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
if (ret)
return ret;
goto unlock;
}
ret = tz->ops->get_trip_type(tz, trip, &type);
if (ret)
return ret;
goto unlock;
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
unlock:
mutex_unlock(&tz->lock);
if (ret)
return ret;
return count;
}
@ -168,7 +188,14 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
return -EINVAL;
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
mutex_lock(&tz->lock);
if (device_is_registered(dev))
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
else
ret = -ENODEV;
mutex_unlock(&tz->lock);
if (ret)
return ret;
@ -193,6 +220,13 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
/*
* We are not doing any check on the 'temperature' value
* here. The driver implementing 'set_trip_hyst' has to
@ -201,7 +235,10 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
ret = tz->ops->set_trip_hyst(tz, trip, temperature);
if (!ret)
thermal_zone_set_trips(tz);
__thermal_zone_set_trips(tz);
unlock:
mutex_unlock(&tz->lock);
return ret ? ret : count;
}
@ -220,7 +257,14 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
return -EINVAL;
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
mutex_lock(&tz->lock);
if (device_is_registered(dev))
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
else
ret = -ENODEV;
mutex_unlock(&tz->lock);
return ret ? ret : sprintf(buf, "%d\n", temperature);
}
@ -269,16 +313,23 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
if (!tz->ops->set_emul_temp) {
mutex_lock(&tz->lock);
tz->emul_temperature = temperature;
mutex_unlock(&tz->lock);
} else {
ret = tz->ops->set_emul_temp(tz, temperature);
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
if (!tz->ops->set_emul_temp)
tz->emul_temperature = temperature;
else
ret = tz->ops->set_emul_temp(tz, temperature);
if (!ret)
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
unlock:
mutex_unlock(&tz->lock);
return ret ? ret : count;
}
@ -589,13 +640,8 @@ static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int ret;
ret = cdev->ops->get_max_state(cdev, &state);
if (ret)
return ret;
return sprintf(buf, "%ld\n", state);
return sprintf(buf, "%ld\n", cdev->max_state);
}
static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
@ -625,6 +671,10 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
if ((long)state < 0)
return -EINVAL;
/* Requested state should be less than max_state + 1 */
if (state > cdev->max_state)
return -EINVAL;
mutex_lock(&cdev->lock);
result = cdev->ops->set_cur_state(cdev, state);
@ -662,7 +712,6 @@ struct cooling_dev_stats {
spinlock_t lock;
unsigned int total_trans;
unsigned long state;
unsigned long max_states;
ktime_t last_time;
ktime_t *time_in_state;
unsigned int *trans_table;
@ -692,7 +741,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
goto unlock;
update_time_in_state(stats);
stats->trans_table[stats->state * stats->max_states + new_state]++;
stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++;
stats->state = new_state;
stats->total_trans++;
@ -726,7 +775,7 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
spin_lock(&stats->lock);
update_time_in_state(stats);
for (i = 0; i < stats->max_states; i++) {
for (i = 0; i <= cdev->max_state; i++) {
len += sprintf(buf + len, "state%u\t%llu\n", i,
ktime_to_ms(stats->time_in_state[i]));
}
@ -741,7 +790,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
struct cooling_dev_stats *stats = cdev->stats;
int i, states = stats->max_states;
int i, states = cdev->max_state + 1;
spin_lock(&stats->lock);
@ -750,7 +799,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
memset(stats->trans_table, 0,
states * states * sizeof(*stats->trans_table));
for (i = 0; i < stats->max_states; i++)
for (i = 0; i < states; i++)
stats->time_in_state[i] = ktime_set(0, 0);
spin_unlock(&stats->lock);
@ -768,7 +817,7 @@ static ssize_t trans_table_show(struct device *dev,
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
for (i = 0; i < stats->max_states; i++) {
for (i = 0; i <= cdev->max_state; i++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
@ -778,17 +827,17 @@ static ssize_t trans_table_show(struct device *dev,
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
for (i = 0; i < stats->max_states; i++) {
for (i = 0; i <= cdev->max_state; i++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
for (j = 0; j < stats->max_states; j++) {
for (j = 0; j <= cdev->max_state; j++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
stats->trans_table[i * stats->max_states + j]);
stats->trans_table[i * (cdev->max_state + 1) + j]);
}
if (len >= PAGE_SIZE)
break;
@ -824,14 +873,10 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
{
const struct attribute_group *stats_attr_group = NULL;
struct cooling_dev_stats *stats;
unsigned long states;
/* Total number of states is highest state + 1 */
unsigned long states = cdev->max_state + 1;
int var;
if (cdev->ops->get_max_state(cdev, &states))
goto out;
states++; /* Total number of states is highest state + 1 */
var = sizeof(*stats);
var += sizeof(*stats->time_in_state) * states;
var += sizeof(*stats->trans_table) * states * states;
@ -844,7 +889,6 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
stats->trans_table = (unsigned int *)(stats->time_in_state + states);
cdev->stats = stats;
stats->last_time = ktime_get();
stats->max_states = states;
spin_lock_init(&stats->lock);

Просмотреть файл

@ -100,6 +100,7 @@ struct thermal_cooling_device_ops {
struct thermal_cooling_device {
int id;
char *type;
unsigned long max_state;
struct device device;
struct device_node *np;
void *devdata;