Commit 691806e9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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()
parents 456ed864 75b15aa0
Loading
Loading
Loading
Loading
+1 −5
Original line number Diff line number Diff line
@@ -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);
}

/**
+21 −13
Original line number Diff line number Diff line
@@ -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];
+11 −0
Original line number Diff line number Diff line
@@ -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 =
+13 −12
Original line number Diff line number Diff line
@@ -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);
}

+6 −0
Original line number Diff line number Diff line
@@ -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 */
Loading