diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 22:45:21 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-12 22:45:21 +0100 |
commit | 691806e977a3a64895bd891878ed726cdbd282c0 (patch) | |
tree | 38a3726e1437eedd240df30e9dceded392c2b12c /drivers/thermal | |
parent | Merge tag 'acpi-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/raf... (diff) | |
parent | Merge branch 'thermal-intel' (diff) | |
download | linux-691806e977a3a64895bd891878ed726cdbd282c0.tar.xz linux-691806e977a3a64895bd891878ed726cdbd282c0.zip |
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()
Diffstat (limited to 'drivers/thermal')
-rw-r--r-- | drivers/thermal/gov_fair_share.c | 6 | ||||
-rw-r--r-- | drivers/thermal/intel/intel_hfi.c | 34 | ||||
-rw-r--r-- | drivers/thermal/intel/intel_tcc_cooling.c | 11 | ||||
-rw-r--r-- | drivers/thermal/intel/therm_throt.c | 25 | ||||
-rw-r--r-- | drivers/thermal/intel/thermal_interrupt.h | 6 | ||||
-rw-r--r-- | drivers/thermal/intel/x86_pkg_temp_thermal.c | 9 | ||||
-rw-r--r-- | drivers/thermal/thermal_core.c | 110 | ||||
-rw-r--r-- | drivers/thermal/thermal_core.h | 3 | ||||
-rw-r--r-- | drivers/thermal/thermal_helpers.c | 67 | ||||
-rw-r--r-- | drivers/thermal/thermal_hwmon.c | 10 | ||||
-rw-r--r-- | drivers/thermal/thermal_sysfs.c | 116 |
11 files changed, 255 insertions, 142 deletions
diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index a4ee4661e9cc..1cfeac16e7ac 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -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); } /** diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index a0640f762dc5..6e604bda2b93 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -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]; diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index 95adac427b6f..a89e7e1890e4 100644 --- a/drivers/thermal/intel/intel_tcc_cooling.c +++ b/drivers/thermal/intel/intel_tcc_cooling.c @@ -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 = diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 8352083b87c7..4bb7fddaa143 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -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); } diff --git a/drivers/thermal/intel/thermal_interrupt.h b/drivers/thermal/intel/thermal_interrupt.h index 01e7bed2ffc7..01dfd4cdb5df 100644 --- a/drivers/thermal/intel/thermal_interrupt.h +++ b/drivers/thermal/intel/thermal_interrupt.h @@ -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 */ diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c index a0e234fce71a..84c3a116ed04 100644 --- a/drivers/thermal/intel/x86_pkg_temp_thermal.c +++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c @@ -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); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 117eeaf7dd24..f17ab2316dbd 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -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); } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 1571917bd3c8..b834cb273429 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -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); diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index c65cdce8f856..56aa2e88f34f 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -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) { diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index f53f4ceb6a5d..c594c42bea6d 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -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; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index ec495c7dff03..d97f0bc0a26b 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -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); + mutex_lock(&tz->lock); + + if (!device_is_registered(dev)) { + ret = -ENODEV; + goto unlock; + } + + if (!tz->ops->set_emul_temp) tz->emul_temperature = temperature; - mutex_unlock(&tz->lock); - } else { + 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); |