mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 12:13:51 -04:00
Merge tag 'thermal-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull thermal control updates from Rafael Wysocki:
"These include an introduction of Intel Directed Package Thermal
Interrupt support into the thermal throttling driver for Intel
processors, probe failure code path fixes and code cleanups in Intel
thermal drivers, a thermal core fix related to hwmon, a sysfs-related
cleanup of that code, and a thermometer utility fix:
- Add support for the Directed Package-level Thermal Interrupt to the
Intel thermal throttling driver to allow package-level thermal
interrupts to go to one specific CPU in a processor package instead
of going to all of the CPUs in it (Ricardo Neri)
- Remove hwmon class devices created for thermal zones when the
thermal zone devices holding them are removed (Rafael Wysocki)
- Use sysfs_emit_at() in trans_table_show() (Thorsten Blum)
- Clean up RFIM groups on DVFS failure and clean up ODVP on probe
failures in the int340x thermal driver (Pengpeng Hou)
- Remove redundant dev_err() from the int340x thermal driver and the
bxt_pmic driver (Pan Chuang)
- Simplify ptc_temperature_write() in the int340x thermal driver by
using kstrtou32_from_user() (Dmitry Antipov)
- Close fd on realloc() failure in the thermometer utility (Amarjeet)"
* tag 'thermal-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
thermal: hwmon: Remove hwmon class device along with its parent
thermal: sysfs: Use sysfs_emit_at() in trans_table_show()
tools/thermal/thermometer: close fd on realloc() failure
thermal: intel: int340x: simplify ptc_temperature_write()
thermal: intel: bxt_pmic: Remove redundant dev_err()
thermal: intel: int340x: Remove redundant dev_err()
thermal: intel: int3400: clean up ODVP on probe failures
thermal: intel: int340x: clean up RFIM groups on DVFS failure
thermal: intel: Add a syscore shutdown callback for kexec reboot
thermal: intel: Add syscore callbacks for suspend and resume
thermal: intel: Enable the Directed Package-level Thermal Interrupt
thermal: intel: Add resources to handle directed package-level thermal interrupts
x86/thermal: Add bit definitions for Intel Directed Package Thermal Interrupt
This commit is contained in:
@@ -365,6 +365,8 @@
|
||||
#define X86_FEATURE_HWP_HIGHEST_PERF_CHANGE (14*32+15) /* HWP Highest perf change */
|
||||
#define X86_FEATURE_HFI (14*32+19) /* "hfi" Hardware Feedback Interface */
|
||||
|
||||
#define X86_FEATURE_DPTI (14*32+24) /* Intel Directed Package Thermal Interrupt */
|
||||
|
||||
/* AMD SVM Feature Identification, CPUID level 0x8000000a (EDX), word 15 */
|
||||
#define X86_FEATURE_NPT (15*32+ 0) /* "npt" Nested Page Table support */
|
||||
#define X86_FEATURE_LBRV (15*32+ 1) /* "lbrv" LBR Virtualization support */
|
||||
|
||||
@@ -1007,6 +1007,7 @@
|
||||
#define THERM_INT_HIGH_ENABLE (1 << 0)
|
||||
#define THERM_INT_LOW_ENABLE (1 << 1)
|
||||
#define THERM_INT_PLN_ENABLE (1 << 24)
|
||||
#define THERM_INT_DPTI_ENABLE (1 << 25)
|
||||
|
||||
#define MSR_IA32_THERM_STATUS 0x0000019c
|
||||
|
||||
@@ -1036,6 +1037,7 @@
|
||||
|
||||
#define PACKAGE_THERM_STATUS_PROCHOT (1 << 0)
|
||||
#define PACKAGE_THERM_STATUS_POWER_LIMIT (1 << 10)
|
||||
#define PACKAGE_THERM_STATUS_DPTI_ACK (1 << 25)
|
||||
#define PACKAGE_THERM_STATUS_HFI_UPDATED (1 << 26)
|
||||
|
||||
#define MSR_IA32_PACKAGE_THERM_INTERRUPT 0x000001b2
|
||||
|
||||
@@ -356,8 +356,10 @@ static void cleanup_odvp(struct int3400_thermal_priv *priv)
|
||||
kfree(priv->odvp_attrs[i].attr.attr.name);
|
||||
}
|
||||
kfree(priv->odvp_attrs);
|
||||
priv->odvp_attrs = NULL;
|
||||
}
|
||||
kfree(priv->odvp);
|
||||
priv->odvp = NULL;
|
||||
priv->odvp_count = 0;
|
||||
}
|
||||
|
||||
@@ -635,7 +637,6 @@ static int int3400_thermal_probe(struct platform_device *pdev)
|
||||
acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
|
||||
int3400_notify);
|
||||
free_sysfs:
|
||||
cleanup_odvp(priv);
|
||||
if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
|
||||
device_remove_bin_file(&pdev->dev, &bin_attr_data_vault);
|
||||
kfree(priv->data_vault);
|
||||
@@ -649,6 +650,7 @@ static int int3400_thermal_probe(struct platform_device *pdev)
|
||||
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
|
||||
thermal_zone_device_unregister(priv->thermal);
|
||||
free_art_trt:
|
||||
cleanup_odvp(priv);
|
||||
kfree(priv->trts);
|
||||
kfree(priv->arts);
|
||||
free_priv:
|
||||
|
||||
@@ -227,17 +227,12 @@ static ssize_t ptc_temperature_write(struct file *file, const char __user *data,
|
||||
{
|
||||
struct ptc_data *ptc_instance = file->private_data;
|
||||
struct pci_dev *pdev = ptc_instance->pdev;
|
||||
char buf[32];
|
||||
ssize_t len;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
len = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, data, len))
|
||||
return -EFAULT;
|
||||
|
||||
buf[len] = '\0';
|
||||
if (kstrtouint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
ret = kstrtou32_from_user(data, count, 0, &value);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units)
|
||||
value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units;
|
||||
|
||||
@@ -308,10 +308,8 @@ static int proc_thermal_setup_msi(struct pci_dev *pdev, struct proc_thermal_pci
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler,
|
||||
proc_thermal_irq_thread_handler,
|
||||
0, KBUILD_MODNAME, pci_info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Request IRQ %d failed\n", irq);
|
||||
if (ret)
|
||||
goto err_free_msi_vectors;
|
||||
}
|
||||
|
||||
proc_thermal_msi_map[i] = irq;
|
||||
}
|
||||
@@ -394,10 +392,8 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler,
|
||||
proc_thermal_irq_thread_handler, irq_flag,
|
||||
KBUILD_MODNAME, pci_info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq);
|
||||
if (ret)
|
||||
goto err_ret_tzone;
|
||||
}
|
||||
}
|
||||
|
||||
ret = thermal_zone_device_enable(pci_info->tzone);
|
||||
|
||||
@@ -491,12 +491,11 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc
|
||||
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group);
|
||||
if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
|
||||
sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
|
||||
return ret;
|
||||
}
|
||||
if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) {
|
||||
sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group);
|
||||
if (ret) {
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group);
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,10 +245,8 @@ static int pmic_thermal_probe(struct platform_device *pdev)
|
||||
NULL, pmic_thermal_irq_handler,
|
||||
IRQF_ONESHOT, "pmic_thermal", pdev);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
pmic_irq_count++;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
* Credits: Adapted from Zwane Mwaikambo's original code in mce_intel.c.
|
||||
* Inspired by Ross Biro's and Al Borchers' counter code.
|
||||
*/
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
@@ -244,16 +246,23 @@ static void thermal_intr_init_pkg_clear_mask(void)
|
||||
* IA32_PACKAGE_THERM_STATUS.
|
||||
*/
|
||||
|
||||
/* All bits except BIT 26 depend on CPUID.06H: EAX[6] = 1 */
|
||||
/* All bits except BITs 25 and 26 depend on CPUID.06H: EAX[6] = 1 */
|
||||
if (boot_cpu_has(X86_FEATURE_PTS))
|
||||
therm_intr_pkg_clear_mask = (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11));
|
||||
|
||||
/*
|
||||
* Intel SDM Volume 2A: Thermal and Power Management Leaf
|
||||
* Intel SDM Volume 1: Thermal and Power Management Leaf
|
||||
* Bit 26: CPUID.06H: EAX[19] = 1
|
||||
*/
|
||||
if (boot_cpu_has(X86_FEATURE_HFI))
|
||||
therm_intr_pkg_clear_mask |= BIT(26);
|
||||
|
||||
/*
|
||||
* Intel SDM Volume 1: Thermal and Power Management Leaf
|
||||
* Bit 25: CPUID.06H: EAX[24] = 1
|
||||
*/
|
||||
if (boot_cpu_has(X86_FEATURE_DPTI))
|
||||
therm_intr_pkg_clear_mask |= BIT(25);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -524,6 +533,254 @@ static void thermal_throttle_remove_dev(struct device *dev)
|
||||
sysfs_remove_group(&dev->kobj, &thermal_attr_group);
|
||||
}
|
||||
|
||||
static int check_directed_thermal_pkg_intr_ack(void)
|
||||
{
|
||||
unsigned int count = 15000;
|
||||
u64 msr_val;
|
||||
|
||||
/*
|
||||
* Hardware acknowledges the directed interrupt setup in 10ms or less.
|
||||
* Wait 15ms to be safe.
|
||||
*/
|
||||
do {
|
||||
rdmsrq(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
|
||||
udelay(1);
|
||||
} while (!(msr_val & PACKAGE_THERM_STATUS_DPTI_ACK) && --count);
|
||||
|
||||
if (!count)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL,
|
||||
PACKAGE_THERM_STATUS_DPTI_ACK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void config_directed_thermal_pkg_intr(void *info)
|
||||
{
|
||||
bool enable = *((bool *)info);
|
||||
u64 msr_val;
|
||||
|
||||
rdmsrq(MSR_IA32_THERM_INTERRUPT, msr_val);
|
||||
|
||||
if (enable)
|
||||
msr_val |= THERM_INT_DPTI_ENABLE;
|
||||
else
|
||||
msr_val &= ~THERM_INT_DPTI_ENABLE;
|
||||
|
||||
wrmsrq(MSR_IA32_THERM_INTERRUPT, msr_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessed from CPU hotplug callbacks and from code that runs while CPU
|
||||
* hotplug is inactive: the init and cleanup paths as well as syscore callbacks.
|
||||
* No extra locking needed.
|
||||
*/
|
||||
static unsigned int *directed_intr_handler_cpus;
|
||||
|
||||
static bool directed_thermal_pkg_intr_supported(void)
|
||||
{
|
||||
if (!boot_cpu_has(X86_FEATURE_DPTI))
|
||||
return false;
|
||||
|
||||
if (!directed_intr_handler_cpus)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called with cpu_hotplug_lock held to prevent CPUs from going offline
|
||||
* while iterating through packages and interrupts must be enabled to avoid
|
||||
* deadlocks in SMP function calls. The syscore shutdown callback also calls
|
||||
* this function, but runs with CPU hotplug disabled (and interrupts enabled).
|
||||
*/
|
||||
static void disable_directed_thermal_pkg_intr_all(void)
|
||||
{
|
||||
bool enable = false;
|
||||
int i;
|
||||
|
||||
if (!directed_thermal_pkg_intr_supported())
|
||||
return;
|
||||
|
||||
for (i = 0; i < topology_max_packages(); i++) {
|
||||
if (directed_intr_handler_cpus[i] == nr_cpu_ids)
|
||||
continue;
|
||||
|
||||
smp_call_function_single(directed_intr_handler_cpus[i],
|
||||
config_directed_thermal_pkg_intr,
|
||||
&enable, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int enable_directed_thermal_pkg_intr(unsigned int cpu)
|
||||
{
|
||||
bool enable = true;
|
||||
u16 pkg_id;
|
||||
|
||||
if (!directed_thermal_pkg_intr_supported())
|
||||
return 0;
|
||||
|
||||
pkg_id = topology_logical_package_id(cpu);
|
||||
if (pkg_id >= topology_max_packages())
|
||||
return -EINVAL;
|
||||
|
||||
/* Another CPU in this package already handles the directed interrupt. */
|
||||
if (directed_intr_handler_cpus[pkg_id] != nr_cpu_ids)
|
||||
return 0;
|
||||
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL,
|
||||
PACKAGE_THERM_STATUS_DPTI_ACK);
|
||||
|
||||
config_directed_thermal_pkg_intr(&enable);
|
||||
if (!check_directed_thermal_pkg_intr_ack()) {
|
||||
directed_intr_handler_cpus[pkg_id] = cpu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A failure indicates faulty hardware. Roll back completely so that
|
||||
* no other CPU tries. This is especially important during boot as all
|
||||
* CPUs may come online and would otherwise keep trying.
|
||||
*/
|
||||
enable = false;
|
||||
config_directed_thermal_pkg_intr(&enable);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void disable_directed_thermal_pkg_intr(unsigned int cpu)
|
||||
{
|
||||
unsigned int new_cpu;
|
||||
bool enable;
|
||||
u16 pkg_id;
|
||||
|
||||
if (!directed_thermal_pkg_intr_supported())
|
||||
return;
|
||||
|
||||
pkg_id = topology_logical_package_id(cpu);
|
||||
if (pkg_id >= topology_max_packages())
|
||||
return;
|
||||
|
||||
/* Not the CPU handling the directed interrupt. */
|
||||
if (directed_intr_handler_cpus[pkg_id] != cpu)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The package-level interrupt must remain directed after this CPU goes
|
||||
* offline.
|
||||
*/
|
||||
new_cpu = cpumask_any_but(topology_core_cpumask(cpu), cpu);
|
||||
if (new_cpu < nr_cpu_ids) {
|
||||
enable = true;
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL,
|
||||
PACKAGE_THERM_STATUS_DPTI_ACK);
|
||||
|
||||
/*
|
||||
* We are here via CPU hotplug. Since we are holding the
|
||||
* cpu_hotplug_lock, @new_cpu cannot go offline and interrupts
|
||||
* are enabled, so the SMP function call is safe.
|
||||
*
|
||||
* The syscore suspend callback runs with interrupts disabled,
|
||||
* but it does not reach this path because all the secondary
|
||||
* CPUs are offline.
|
||||
*/
|
||||
smp_call_function_single(new_cpu, config_directed_thermal_pkg_intr,
|
||||
&enable, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* If hardware does not acknowledge the directed interrupt setup on
|
||||
* @new_cpu, disable the redirection. Since no other CPU is configured
|
||||
* to receive the package-level interrupt, all CPUs in the package will
|
||||
* receive it.
|
||||
*/
|
||||
enable = false;
|
||||
if (new_cpu < nr_cpu_ids && check_directed_thermal_pkg_intr_ack()) {
|
||||
smp_call_function_single(new_cpu, config_directed_thermal_pkg_intr,
|
||||
&enable, true);
|
||||
|
||||
pr_warn_once("Failed to redirect package thermal interrupt from CPU%u to CPU%u; reverting to broadcast.\n",
|
||||
cpu, new_cpu);
|
||||
|
||||
new_cpu = nr_cpu_ids;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the directed interrupt on @cpu. Hardware acknowledgment can be
|
||||
* ignored since @cpu is going offline.
|
||||
*/
|
||||
config_directed_thermal_pkg_intr(&enable);
|
||||
|
||||
directed_intr_handler_cpus[pkg_id] = (new_cpu < nr_cpu_ids) ? new_cpu : nr_cpu_ids;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU0 may be handling the directed interrupt, but the CPU hotplug callbacks
|
||||
* are not called for CPU0 during suspend and resume.
|
||||
*/
|
||||
static void directed_pkg_intr_syscore_resume(void *data)
|
||||
{
|
||||
/*
|
||||
* We can't do anything to handle errors. If direction fails for CPU0,
|
||||
* another CPU will take over or disable direction entirely during CPU
|
||||
* hotplug.
|
||||
*/
|
||||
enable_directed_thermal_pkg_intr(0);
|
||||
}
|
||||
|
||||
static int directed_pkg_intr_syscore_suspend(void *data)
|
||||
{
|
||||
disable_directed_thermal_pkg_intr(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void directed_pkg_intr_syscore_shutdown(void *data)
|
||||
{
|
||||
disable_directed_thermal_pkg_intr_all();
|
||||
}
|
||||
|
||||
static const struct syscore_ops directed_pkg_intr_pm_ops = {
|
||||
.resume = directed_pkg_intr_syscore_resume,
|
||||
.suspend = directed_pkg_intr_syscore_suspend,
|
||||
.shutdown = directed_pkg_intr_syscore_shutdown,
|
||||
};
|
||||
|
||||
static struct syscore directed_pkg_intr_pm = {
|
||||
.ops = &directed_pkg_intr_pm_ops,
|
||||
};
|
||||
|
||||
static __init void init_directed_pkg_intr(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!boot_cpu_has(X86_FEATURE_DPTI))
|
||||
return;
|
||||
|
||||
directed_intr_handler_cpus = kmalloc_array(topology_max_packages(),
|
||||
sizeof(*directed_intr_handler_cpus),
|
||||
GFP_KERNEL);
|
||||
if (!directed_intr_handler_cpus)
|
||||
return;
|
||||
|
||||
for (i = 0; i < topology_max_packages(); i++)
|
||||
directed_intr_handler_cpus[i] = nr_cpu_ids;
|
||||
|
||||
register_syscore(&directed_pkg_intr_pm);
|
||||
}
|
||||
|
||||
static void cleanup_directed_pkg_thermal_intr(void)
|
||||
{
|
||||
if (!directed_thermal_pkg_intr_supported())
|
||||
return;
|
||||
|
||||
unregister_syscore(&directed_pkg_intr_pm);
|
||||
disable_directed_thermal_pkg_intr_all();
|
||||
kfree(directed_intr_handler_cpus);
|
||||
directed_intr_handler_cpus = NULL;
|
||||
}
|
||||
|
||||
/* Get notified when a cpu comes on/off. Be hotplug friendly. */
|
||||
static int thermal_throttle_online(unsigned int cpu)
|
||||
{
|
||||
@@ -549,6 +806,11 @@ static int thermal_throttle_online(unsigned int cpu)
|
||||
*/
|
||||
intel_hfi_online(cpu);
|
||||
|
||||
if (enable_directed_thermal_pkg_intr(cpu)) {
|
||||
pr_info_once("Failed to direct package thermal interrupts. All CPUs will receive it.\n");
|
||||
cleanup_directed_pkg_thermal_intr();
|
||||
}
|
||||
|
||||
/* Unmask the thermal vector after the above workqueues are initialized. */
|
||||
l = apic_read(APIC_LVTTHMR);
|
||||
apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
|
||||
@@ -566,6 +828,8 @@ static int thermal_throttle_offline(unsigned int cpu)
|
||||
l = apic_read(APIC_LVTTHMR);
|
||||
apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED);
|
||||
|
||||
disable_directed_thermal_pkg_intr(cpu);
|
||||
|
||||
intel_hfi_offline(cpu);
|
||||
|
||||
cancel_delayed_work_sync(&state->package_throttle.therm_work);
|
||||
@@ -585,12 +849,19 @@ static __init int thermal_throttle_init_device(void)
|
||||
if (!atomic_read(&therm_throt_en))
|
||||
return 0;
|
||||
|
||||
init_directed_pkg_intr();
|
||||
|
||||
intel_hfi_init();
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/therm:online",
|
||||
thermal_throttle_online,
|
||||
thermal_throttle_offline);
|
||||
return ret < 0 ? ret : 0;
|
||||
if (ret >= 0)
|
||||
return 0;
|
||||
|
||||
cleanup_directed_pkg_thermal_intr();
|
||||
|
||||
return ret;
|
||||
}
|
||||
device_initcall(thermal_throttle_init_device);
|
||||
|
||||
|
||||
@@ -95,34 +95,12 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
|
||||
strscpy(type, tz->type);
|
||||
strreplace(type, '-', '_');
|
||||
if (!strcmp(hwmon->type, type)) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
if (!strcmp(hwmon->type, type))
|
||||
return hwmon;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the temperature input matching a given thermal zone */
|
||||
static struct thermal_hwmon_temp *
|
||||
thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
|
||||
const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_temp *temp;
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
|
||||
if (temp->tz == tz) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
return temp;
|
||||
}
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -138,7 +116,9 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
struct thermal_hwmon_temp *temp;
|
||||
int new_hwmon_device = 1;
|
||||
int result;
|
||||
int result = 0;
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
|
||||
hwmon = thermal_hwmon_lookup_by_type(tz);
|
||||
if (hwmon) {
|
||||
@@ -147,8 +127,10 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
}
|
||||
|
||||
hwmon = kzalloc_obj(*hwmon);
|
||||
if (!hwmon)
|
||||
return -ENOMEM;
|
||||
if (!hwmon) {
|
||||
result = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&hwmon->tz_list);
|
||||
strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
|
||||
@@ -196,24 +178,24 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
temp->temp_crit_present = true;
|
||||
}
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
if (new_hwmon_device)
|
||||
list_add_tail(&hwmon->node, &thermal_hwmon_list);
|
||||
list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return 0;
|
||||
goto unlock;
|
||||
|
||||
unregister_input:
|
||||
unregister_input:
|
||||
device_remove_file(hwmon->device, &temp->temp_input.attr);
|
||||
free_temp_mem:
|
||||
free_temp_mem:
|
||||
kfree(temp);
|
||||
unregister_name:
|
||||
unregister_name:
|
||||
if (new_hwmon_device)
|
||||
hwmon_device_unregister(hwmon->device);
|
||||
free_mem:
|
||||
free_mem:
|
||||
if (new_hwmon_device)
|
||||
kfree(hwmon);
|
||||
unlock:
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -221,8 +203,11 @@ EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
|
||||
|
||||
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_temp *temp, *entry;
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
struct thermal_hwmon_temp *temp;
|
||||
bool unregister;
|
||||
|
||||
guard(mutex)(&thermal_hwmon_list_lock);
|
||||
|
||||
hwmon = thermal_hwmon_lookup_by_type(tz);
|
||||
if (unlikely(!hwmon)) {
|
||||
@@ -231,29 +216,25 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
return;
|
||||
}
|
||||
|
||||
temp = thermal_hwmon_lookup_temp(hwmon, tz);
|
||||
if (unlikely(!temp)) {
|
||||
/* Should never happen... */
|
||||
dev_dbg(&tz->device, "temperature input lookup failed!\n");
|
||||
return;
|
||||
unregister = hwmon->device->parent == &tz->device;
|
||||
|
||||
list_for_each_entry_safe_reverse(temp, entry, &hwmon->tz_list, hwmon_node) {
|
||||
if (!unregister && temp->tz != tz)
|
||||
continue;
|
||||
|
||||
device_remove_file(hwmon->device, &temp->temp_input.attr);
|
||||
if (temp->temp_crit_present)
|
||||
device_remove_file(hwmon->device, &temp->temp_crit.attr);
|
||||
|
||||
list_del(&temp->hwmon_node);
|
||||
kfree(temp);
|
||||
}
|
||||
|
||||
device_remove_file(hwmon->device, &temp->temp_input.attr);
|
||||
if (temp->temp_crit_present)
|
||||
device_remove_file(hwmon->device, &temp->temp_crit.attr);
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_del(&temp->hwmon_node);
|
||||
kfree(temp);
|
||||
if (!list_empty(&hwmon->tz_list)) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
return;
|
||||
if (unregister) {
|
||||
list_del(&hwmon->node);
|
||||
hwmon_device_unregister(hwmon->device);
|
||||
kfree(hwmon);
|
||||
}
|
||||
list_del(&hwmon->node);
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
hwmon_device_unregister(hwmon->device);
|
||||
kfree(hwmon);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);
|
||||
|
||||
|
||||
@@ -706,7 +706,7 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||
struct cooling_dev_stats *stats;
|
||||
ssize_t len = 0;
|
||||
int i, j;
|
||||
int i, j, copied;
|
||||
|
||||
guard(cooling_dev)(cdev);
|
||||
|
||||
@@ -714,41 +714,40 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
if (!stats)
|
||||
return -ENODATA;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
|
||||
len += sysfs_emit_at(buf, len, " From : To\n");
|
||||
len += sysfs_emit_at(buf, len, " : ");
|
||||
for (i = 0; i <= cdev->max_state; i++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
|
||||
copied = sysfs_emit_at(buf, len, "state%2u ", i);
|
||||
if (!copied)
|
||||
goto buf_full;
|
||||
len += copied;
|
||||
}
|
||||
if (len >= PAGE_SIZE)
|
||||
return PAGE_SIZE;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
len += sysfs_emit_at(buf, len, "\n");
|
||||
|
||||
for (i = 0; i <= cdev->max_state; i++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
|
||||
copied = sysfs_emit_at(buf, len, "state%2u:", i);
|
||||
if (!copied)
|
||||
goto buf_full;
|
||||
len += copied;
|
||||
|
||||
for (j = 0; j <= cdev->max_state; j++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
|
||||
copied = sysfs_emit_at(buf, len, "%8u ",
|
||||
stats->trans_table[i * (cdev->max_state + 1) + j]);
|
||||
if (!copied)
|
||||
goto buf_full;
|
||||
len += copied;
|
||||
}
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
}
|
||||
|
||||
if (len >= PAGE_SIZE) {
|
||||
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
|
||||
len = -EFBIG;
|
||||
copied = sysfs_emit_at(buf, len, "\n");
|
||||
if (!copied)
|
||||
goto buf_full;
|
||||
len += copied;
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
buf_full:
|
||||
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(total_trans);
|
||||
|
||||
@@ -272,6 +272,7 @@ static int thermometer_add_tz(const char *path, const char *name, int polling,
|
||||
tz = realloc(thermometer->tz, sizeof(*thermometer->tz) * (thermometer->nr_tz + 1));
|
||||
if (!tz) {
|
||||
ERROR("Failed to allocate thermometer->tz\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user