2019-05-30 02:57:24 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-05-18 03:42:02 +04:00
/*
* x86_pkg_temp_thermal driver
* Copyright ( c ) 2013 , Intel Corporation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/init.h>
2022-12-19 18:46:19 +03:00
# include <linux/intel_tcc.h>
2013-05-18 03:42:02 +04:00
# include <linux/err.h>
# include <linux/param.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/cpu.h>
# include <linux/smp.h>
# include <linux/slab.h>
# include <linux/pm.h>
# include <linux/thermal.h>
# include <linux/debugfs.h>
2021-01-07 15:29:05 +03:00
2013-05-18 03:42:02 +04:00
# include <asm/cpu_device_id.h>
2021-01-07 15:29:05 +03:00
# include "thermal_interrupt.h"
2013-05-18 03:42:02 +04:00
/*
* Rate control delay : Idea is to introduce denounce effect
* This should be long enough to avoid reduce events , when
* threshold is set to a temperature , which is constantly
* violated , but at the short enough to take any action .
* The action can be remove threshold or change it to next
* interesting setting . Based on experiments , in around
* every 5 seconds under load will give us a significant
* temperature change .
*/
# define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000
static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY ;
module_param ( notify_delay_ms , int , 0644 ) ;
MODULE_PARM_DESC ( notify_delay_ms ,
" User space notification delay in milli seconds. " ) ;
/* Number of trip points in thermal zone. Currently it can't
* be more than 2. MSR can allow setting and getting notifications
* for only 2 thresholds . This define enforces this , if there
* is some wrong values returned by cpuid for number of thresholds .
*/
# define MAX_NUMBER_OF_TRIPS 2
2019-05-13 20:59:00 +03:00
struct zone_device {
2016-11-22 20:57:14 +03:00
int cpu ;
2016-11-22 20:57:12 +03:00
bool work_scheduled ;
2016-11-22 20:57:08 +03:00
u32 msr_pkg_therm_low ;
u32 msr_pkg_therm_high ;
2016-11-22 20:57:13 +03:00
struct delayed_work work ;
2016-11-22 20:57:08 +03:00
struct thermal_zone_device * tzone ;
2022-10-03 12:26:02 +03:00
struct thermal_trip * trips ;
2016-11-22 20:57:10 +03:00
struct cpumask cpumask ;
2013-05-18 03:42:02 +04:00
} ;
2015-03-03 18:30:50 +03:00
static struct thermal_zone_params pkg_temp_tz_params = {
2014-03-02 18:33:35 +04:00
. no_hwmon = true ,
} ;
2019-05-13 20:59:00 +03:00
/* Keep track of how many zone pointers we allocated in init() */
static int max_id __read_mostly ;
/* Array of zone pointers */
static struct zone_device * * zones ;
2016-11-22 20:57:10 +03:00
/* Serializes interrupt notification, work and hotplug */
2019-10-08 14:00:21 +03:00
static DEFINE_RAW_SPINLOCK ( pkg_temp_lock ) ;
2016-11-22 20:57:10 +03:00
/* Protects zone operation in the work function against hotplug removal */
static DEFINE_MUTEX ( thermal_zone_mutex ) ;
2013-05-18 03:42:02 +04:00
2016-11-22 20:57:15 +03:00
/* The dynamically assigned cpu hotplug state for module_exit() */
static enum cpuhp_state pkg_thermal_hp_state __read_mostly ;
2013-05-18 03:42:02 +04:00
/* Debug counters to show using debugfs */
static struct dentry * debugfs ;
static unsigned int pkg_interrupt_cnt ;
static unsigned int pkg_work_cnt ;
2019-06-13 21:38:30 +03:00
static void pkg_temp_debugfs_init ( void )
2013-05-18 03:42:02 +04:00
{
debugfs = debugfs_create_dir ( " pkg_temp_thermal " , NULL ) ;
2019-06-13 21:38:30 +03:00
debugfs_create_u32 ( " pkg_thres_interrupt " , S_IRUGO , debugfs ,
& pkg_interrupt_cnt ) ;
debugfs_create_u32 ( " pkg_thres_work " , S_IRUGO , debugfs ,
& pkg_work_cnt ) ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:10 +03:00
/*
* Protection :
*
* - cpu hotplug : Read serialized by cpu hotplug lock
* Write must hold pkg_temp_lock
*
* - Other callsites : Must hold pkg_temp_lock
*/
2019-05-13 20:59:00 +03:00
static struct zone_device * pkg_temp_thermal_get_dev ( unsigned int cpu )
2013-05-18 03:42:02 +04:00
{
2019-05-13 20:59:00 +03:00
int id = topology_logical_die_id ( cpu ) ;
2013-05-18 03:42:02 +04:00
2019-05-13 20:59:00 +03:00
if ( id > = 0 & & id < max_id )
return zones [ id ] ;
2013-05-18 03:42:02 +04:00
return NULL ;
}
2015-07-24 09:12:54 +03:00
static int sys_get_curr_temp ( struct thermal_zone_device * tzd , int * temp )
2013-05-18 03:42:02 +04:00
{
2023-03-01 23:14:30 +03:00
struct zone_device * zonedev = thermal_zone_device_priv ( tzd ) ;
2022-12-19 18:46:19 +03:00
int val ;
2013-05-18 03:42:02 +04:00
2022-12-19 18:46:19 +03:00
val = intel_tcc_get_temp ( zonedev - > cpu , true ) ;
if ( val < 0 )
return val ;
* temp = val * 1000 ;
pr_debug ( " sys_get_curr_temp %d \n " , * temp ) ;
return 0 ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:08 +03:00
static int
sys_set_trip_temp ( struct thermal_zone_device * tzd , int trip , int temp )
2013-05-18 03:42:02 +04:00
{
2023-03-01 23:14:30 +03:00
struct zone_device * zonedev = thermal_zone_device_priv ( tzd ) ;
2016-11-22 20:57:08 +03:00
u32 l , h , mask , shift , intr ;
2023-03-08 06:07:11 +03:00
int tj_max , val , ret ;
2013-05-18 03:42:02 +04:00
2022-12-19 18:46:20 +03:00
tj_max = intel_tcc_get_tjmax ( zonedev - > cpu ) ;
if ( tj_max < 0 )
return tj_max ;
tj_max * = 1000 ;
2023-03-08 06:07:11 +03:00
val = ( tj_max - temp ) / 1000 ;
if ( trip > = MAX_NUMBER_OF_TRIPS | | val < 0 | | val > 0x7f )
2013-05-18 03:42:02 +04:00
return - EINVAL ;
2019-05-13 20:59:00 +03:00
ret = rdmsr_on_cpu ( zonedev - > cpu , MSR_IA32_PACKAGE_THERM_INTERRUPT ,
2016-11-22 20:57:07 +03:00
& l , & h ) ;
2013-05-18 03:42:02 +04:00
if ( ret < 0 )
2016-11-22 20:57:09 +03:00
return ret ;
2013-05-18 03:42:02 +04:00
if ( trip ) {
mask = THERM_MASK_THRESHOLD1 ;
shift = THERM_SHIFT_THRESHOLD1 ;
intr = THERM_INT_THRESHOLD1_ENABLE ;
} else {
mask = THERM_MASK_THRESHOLD0 ;
shift = THERM_SHIFT_THRESHOLD0 ;
intr = THERM_INT_THRESHOLD0_ENABLE ;
}
l & = ~ mask ;
/*
* When users space sets a trip temperature = = 0 , which is indication
* that , it is no longer interested in receiving notifications .
*/
2016-11-22 20:57:08 +03:00
if ( ! temp ) {
2013-05-18 03:42:02 +04:00
l & = ~ intr ;
2016-11-22 20:57:08 +03:00
} else {
2023-03-08 06:07:11 +03:00
l | = val < < shift ;
2013-05-18 03:42:02 +04:00
l | = intr ;
}
2019-05-13 20:59:00 +03:00
return wrmsr_on_cpu ( zonedev - > cpu , MSR_IA32_PACKAGE_THERM_INTERRUPT ,
l , h ) ;
2013-05-18 03:42:02 +04:00
}
/* Thermal zone callback registry */
static struct thermal_zone_device_ops tzone_ops = {
. get_temp = sys_get_curr_temp ,
. set_trip_temp = sys_set_trip_temp ,
} ;
2016-11-22 20:57:06 +03:00
static bool pkg_thermal_rate_control ( void )
2013-05-18 03:42:02 +04:00
{
return true ;
}
/* Enable threshold interrupt on local package/cpu */
static inline void enable_pkg_thres_interrupt ( void )
{
u8 thres_0 , thres_1 ;
2016-11-22 20:57:09 +03:00
u32 l , h ;
2013-05-18 03:42:02 +04:00
rdmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT , l , h ) ;
/* only enable/disable if it had valid threshold value */
thres_0 = ( l & THERM_MASK_THRESHOLD0 ) > > THERM_SHIFT_THRESHOLD0 ;
thres_1 = ( l & THERM_MASK_THRESHOLD1 ) > > THERM_SHIFT_THRESHOLD1 ;
if ( thres_0 )
l | = THERM_INT_THRESHOLD0_ENABLE ;
if ( thres_1 )
l | = THERM_INT_THRESHOLD1_ENABLE ;
wrmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT , l , h ) ;
}
/* Disable threshold interrupt on local package/cpu */
static inline void disable_pkg_thres_interrupt ( void )
{
u32 l , h ;
2016-11-22 20:57:09 +03:00
2013-05-18 03:42:02 +04:00
rdmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT , l , h ) ;
2016-11-22 20:57:09 +03:00
l & = ~ ( THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE ) ;
wrmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT , l , h ) ;
2013-05-18 03:42:02 +04:00
}
static void pkg_temp_thermal_threshold_work_fn ( struct work_struct * work )
{
2016-11-22 20:57:10 +03:00
struct thermal_zone_device * tzone = NULL ;
2016-11-22 20:57:12 +03:00
int cpu = smp_processor_id ( ) ;
2019-05-13 20:59:00 +03:00
struct zone_device * zonedev ;
2013-05-18 03:42:02 +04:00
2016-11-22 20:57:10 +03:00
mutex_lock ( & thermal_zone_mutex ) ;
2019-10-08 14:00:21 +03:00
raw_spin_lock_irq ( & pkg_temp_lock ) ;
2013-05-18 03:42:02 +04:00
+ + pkg_work_cnt ;
2016-11-22 20:57:10 +03:00
2019-05-13 20:59:00 +03:00
zonedev = pkg_temp_thermal_get_dev ( cpu ) ;
if ( ! zonedev ) {
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irq ( & pkg_temp_lock ) ;
2016-11-22 20:57:10 +03:00
mutex_unlock ( & thermal_zone_mutex ) ;
2013-05-18 03:42:02 +04:00
return ;
}
2019-05-13 20:59:00 +03:00
zonedev - > work_scheduled = false ;
2013-05-18 03:42:02 +04:00
2022-11-16 05:54:17 +03:00
thermal_clear_package_intr_status ( PACKAGE_LEVEL , THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1 ) ;
tzone = zonedev - > tzone ;
2016-11-22 20:57:04 +03:00
enable_pkg_thres_interrupt ( ) ;
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irq ( & pkg_temp_lock ) ;
2016-11-22 20:57:04 +03:00
2016-11-22 20:57:10 +03:00
/*
* If tzone is not NULL , then thermal_zone_mutex will prevent the
* concurrent removal in the cpu offline callback .
*/
if ( tzone )
thermal_zone_device_update ( tzone , THERMAL_EVENT_UNSPECIFIED ) ;
mutex_unlock ( & thermal_zone_mutex ) ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:13 +03:00
static void pkg_thermal_schedule_work ( int cpu , struct delayed_work * work )
{
unsigned long ms = msecs_to_jiffies ( notify_delay_ms ) ;
schedule_delayed_work_on ( cpu , work , ms ) ;
}
2016-11-22 20:57:09 +03:00
static int pkg_thermal_notify ( u64 msr_val )
2013-05-18 03:42:02 +04:00
{
int cpu = smp_processor_id ( ) ;
2019-05-13 20:59:00 +03:00
struct zone_device * zonedev ;
2016-11-22 20:57:09 +03:00
unsigned long flags ;
2013-05-18 03:42:02 +04:00
2019-10-08 14:00:21 +03:00
raw_spin_lock_irqsave ( & pkg_temp_lock , flags ) ;
2013-05-18 03:42:02 +04:00
+ + pkg_interrupt_cnt ;
disable_pkg_thres_interrupt ( ) ;
2016-11-22 20:57:10 +03:00
/* Work is per package, so scheduling it once is enough. */
2019-05-13 20:59:00 +03:00
zonedev = pkg_temp_thermal_get_dev ( cpu ) ;
if ( zonedev & & ! zonedev - > work_scheduled ) {
zonedev - > work_scheduled = true ;
pkg_thermal_schedule_work ( zonedev - > cpu , & zonedev - > work ) ;
2016-11-22 20:57:10 +03:00
}
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irqrestore ( & pkg_temp_lock , flags ) ;
2013-05-18 03:42:02 +04:00
return 0 ;
}
2022-10-03 12:26:02 +03:00
static struct thermal_trip * pkg_temp_thermal_trips_init ( int cpu , int tj_max , int num_trips )
{
struct thermal_trip * trips ;
unsigned long thres_reg_value ;
u32 mask , shift , eax , edx ;
int ret , i ;
trips = kzalloc ( sizeof ( * trips ) * num_trips , GFP_KERNEL ) ;
if ( ! trips )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; i < num_trips ; i + + ) {
if ( i ) {
mask = THERM_MASK_THRESHOLD1 ;
shift = THERM_SHIFT_THRESHOLD1 ;
} else {
mask = THERM_MASK_THRESHOLD0 ;
shift = THERM_SHIFT_THRESHOLD0 ;
}
ret = rdmsr_on_cpu ( cpu , MSR_IA32_PACKAGE_THERM_INTERRUPT ,
& eax , & edx ) ;
if ( ret < 0 ) {
kfree ( trips ) ;
return ERR_PTR ( ret ) ;
}
thres_reg_value = ( eax & mask ) > > shift ;
trips [ i ] . temperature = thres_reg_value ?
tj_max - thres_reg_value * 1000 : THERMAL_TEMP_INVALID ;
trips [ i ] . type = THERMAL_TRIP_PASSIVE ;
pr_debug ( " %s: cpu=%d, trip=%d, temp=%d \n " ,
__func__ , cpu , i , trips [ i ] . temperature ) ;
}
return trips ;
}
2013-05-18 03:42:02 +04:00
static int pkg_temp_thermal_device_add ( unsigned int cpu )
{
2019-05-13 20:59:00 +03:00
int id = topology_logical_die_id ( cpu ) ;
2022-12-19 18:46:20 +03:00
u32 eax , ebx , ecx , edx ;
2019-05-13 20:59:00 +03:00
struct zone_device * zonedev ;
2016-11-22 20:57:08 +03:00
int thres_count , err ;
2023-01-24 23:12:49 +03:00
int tj_max ;
2013-05-18 03:42:02 +04:00
2019-05-13 20:59:00 +03:00
if ( id > = max_id )
2016-11-22 20:57:14 +03:00
return - ENOMEM ;
2013-05-18 03:42:02 +04:00
cpuid ( 6 , & eax , & ebx , & ecx , & edx ) ;
thres_count = ebx & 0x07 ;
if ( ! thres_count )
return - ENODEV ;
thres_count = clamp_val ( thres_count , 0 , MAX_NUMBER_OF_TRIPS ) ;
2023-01-24 23:12:49 +03:00
tj_max = intel_tcc_get_tjmax ( cpu ) ;
if ( tj_max < 0 )
return tj_max ;
2013-05-18 03:42:02 +04:00
2019-05-13 20:59:00 +03:00
zonedev = kzalloc ( sizeof ( * zonedev ) , GFP_KERNEL ) ;
if ( ! zonedev )
2016-11-22 20:57:10 +03:00
return - ENOMEM ;
2013-05-18 03:42:02 +04:00
2022-10-03 12:26:02 +03:00
zonedev - > trips = pkg_temp_thermal_trips_init ( cpu , tj_max , thres_count ) ;
if ( IS_ERR ( zonedev - > trips ) ) {
err = PTR_ERR ( zonedev - > trips ) ;
goto out_kfree_zonedev ;
}
2019-05-13 20:59:00 +03:00
INIT_DELAYED_WORK ( & zonedev - > work , pkg_temp_thermal_threshold_work_fn ) ;
zonedev - > cpu = cpu ;
2022-10-03 12:26:02 +03:00
zonedev - > tzone = thermal_zone_device_register_with_trips ( " x86_pkg_temp " ,
zonedev - > trips , thres_count ,
2016-11-22 20:57:08 +03:00
( thres_count = = MAX_NUMBER_OF_TRIPS ) ? 0x03 : 0x01 ,
2019-05-13 20:59:00 +03:00
zonedev , & tzone_ops , & pkg_temp_tz_params , 0 , 0 ) ;
if ( IS_ERR ( zonedev - > tzone ) ) {
err = PTR_ERR ( zonedev - > tzone ) ;
2022-10-03 12:26:02 +03:00
goto out_kfree_trips ;
2013-05-18 03:42:02 +04:00
}
2020-06-29 15:29:22 +03:00
err = thermal_zone_device_enable ( zonedev - > tzone ) ;
2022-10-03 12:26:02 +03:00
if ( err )
goto out_unregister_tz ;
2013-05-18 03:42:02 +04:00
/* Store MSR value for package thermal interrupt, to restore at exit */
2019-05-13 20:59:00 +03:00
rdmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT , zonedev - > msr_pkg_therm_low ,
zonedev - > msr_pkg_therm_high ) ;
2013-05-18 03:42:02 +04:00
2019-05-13 20:59:00 +03:00
cpumask_set_cpu ( cpu , & zonedev - > cpumask ) ;
2019-10-08 14:00:21 +03:00
raw_spin_lock_irq ( & pkg_temp_lock ) ;
2019-05-13 20:59:00 +03:00
zones [ id ] = zonedev ;
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irq ( & pkg_temp_lock ) ;
2022-10-03 12:26:02 +03:00
2013-05-18 03:42:02 +04:00
return 0 ;
2022-10-03 12:26:02 +03:00
out_unregister_tz :
thermal_zone_device_unregister ( zonedev - > tzone ) ;
out_kfree_trips :
kfree ( zonedev - > trips ) ;
out_kfree_zonedev :
kfree ( zonedev ) ;
return err ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:15 +03:00
static int pkg_thermal_cpu_offline ( unsigned int cpu )
2013-05-18 03:42:02 +04:00
{
2019-05-13 20:59:00 +03:00
struct zone_device * zonedev = pkg_temp_thermal_get_dev ( cpu ) ;
2016-11-22 20:57:13 +03:00
bool lastcpu , was_target ;
2016-11-22 20:57:07 +03:00
int target ;
2013-05-18 03:42:02 +04:00
2019-05-13 20:59:00 +03:00
if ( ! zonedev )
2016-11-22 20:57:15 +03:00
return 0 ;
2016-11-22 20:57:10 +03:00
2019-05-13 20:59:00 +03:00
target = cpumask_any_but ( & zonedev - > cpumask , cpu ) ;
cpumask_clear_cpu ( cpu , & zonedev - > cpumask ) ;
2016-11-22 20:57:10 +03:00
lastcpu = target > = nr_cpu_ids ;
/*
* Remove the sysfs files , if this is the last cpu in the package
* before doing further cleanups .
*/
if ( lastcpu ) {
2019-05-13 20:59:00 +03:00
struct thermal_zone_device * tzone = zonedev - > tzone ;
2013-05-18 03:42:02 +04:00
2016-11-22 20:57:10 +03:00
/*
* We must protect against a work function calling
* thermal_zone_update , after / while unregister . We null out
* the pointer under the zone mutex , so the worker function
* won ' t try to call .
*/
mutex_lock ( & thermal_zone_mutex ) ;
2019-05-13 20:59:00 +03:00
zonedev - > tzone = NULL ;
2016-11-22 20:57:10 +03:00
mutex_unlock ( & thermal_zone_mutex ) ;
2016-11-22 20:57:05 +03:00
2016-11-22 20:57:10 +03:00
thermal_zone_device_unregister ( tzone ) ;
}
2016-11-22 20:57:13 +03:00
/* Protect against work and interrupts */
2019-10-08 14:00:21 +03:00
raw_spin_lock_irq ( & pkg_temp_lock ) ;
2016-11-22 20:57:13 +03:00
2016-11-22 20:57:10 +03:00
/*
2016-11-22 20:57:13 +03:00
* Check whether this cpu was the current target and store the new
* one . When we drop the lock , then the interrupt notify function
* will see the new target .
*/
2019-05-13 20:59:00 +03:00
was_target = zonedev - > cpu = = cpu ;
zonedev - > cpu = target ;
2016-11-22 20:57:13 +03:00
/*
* If this is the last CPU in the package remove the package
2016-11-22 20:57:14 +03:00
* reference from the array and restore the interrupt MSR . When we
2016-11-22 20:57:13 +03:00
* drop the lock neither the interrupt notify function nor the
* worker will see the package anymore .
2016-11-22 20:57:10 +03:00
*/
if ( lastcpu ) {
2019-05-13 20:59:00 +03:00
zones [ topology_logical_die_id ( cpu ) ] = NULL ;
2016-11-22 20:57:15 +03:00
/* After this point nothing touches the MSR anymore. */
wrmsr ( MSR_IA32_PACKAGE_THERM_INTERRUPT ,
2019-05-13 20:59:00 +03:00
zonedev - > msr_pkg_therm_low , zonedev - > msr_pkg_therm_high ) ;
2016-11-22 20:57:05 +03:00
}
2016-11-22 20:57:07 +03:00
2016-11-22 20:57:10 +03:00
/*
2016-11-22 20:57:13 +03:00
* Check whether there is work scheduled and whether the work is
* targeted at the outgoing CPU .
2016-11-22 20:57:10 +03:00
*/
2019-05-13 20:59:00 +03:00
if ( zonedev - > work_scheduled & & was_target ) {
2016-11-22 20:57:13 +03:00
/*
* To cancel the work we need to drop the lock , otherwise
* we might deadlock if the work needs to be flushed .
*/
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irq ( & pkg_temp_lock ) ;
2019-05-13 20:59:00 +03:00
cancel_delayed_work_sync ( & zonedev - > work ) ;
2019-10-08 14:00:21 +03:00
raw_spin_lock_irq ( & pkg_temp_lock ) ;
2016-11-22 20:57:13 +03:00
/*
* If this is not the last cpu in the package and the work
* did not run after we dropped the lock above , then we
* need to reschedule the work , otherwise the interrupt
* stays disabled forever .
*/
2019-05-13 20:59:00 +03:00
if ( ! lastcpu & & zonedev - > work_scheduled )
pkg_thermal_schedule_work ( target , & zonedev - > work ) ;
2016-11-22 20:57:13 +03:00
}
2019-10-08 14:00:21 +03:00
raw_spin_unlock_irq ( & pkg_temp_lock ) ;
2016-11-22 20:57:13 +03:00
/* Final cleanup if this is the last cpu */
2022-10-03 12:26:02 +03:00
if ( lastcpu ) {
kfree ( zonedev - > trips ) ;
2019-05-13 20:59:00 +03:00
kfree ( zonedev ) ;
2022-10-03 12:26:02 +03:00
}
2016-11-22 20:57:15 +03:00
return 0 ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:15 +03:00
static int pkg_thermal_cpu_online ( unsigned int cpu )
2013-05-18 03:42:02 +04:00
{
2019-05-13 20:59:00 +03:00
struct zone_device * zonedev = pkg_temp_thermal_get_dev ( cpu ) ;
2013-05-18 03:42:02 +04:00
struct cpuinfo_x86 * c = & cpu_data ( cpu ) ;
2016-11-22 20:57:09 +03:00
/* Paranoia check */
if ( ! cpu_has ( c , X86_FEATURE_DTHERM ) | | ! cpu_has ( c , X86_FEATURE_PTS ) )
return - ENODEV ;
2016-11-22 20:57:08 +03:00
2016-11-22 20:57:09 +03:00
/* If the package exists, nothing to do */
2019-05-13 20:59:00 +03:00
if ( zonedev ) {
cpumask_set_cpu ( cpu , & zonedev - > cpumask ) ;
2016-11-22 20:57:09 +03:00
return 0 ;
2016-11-22 20:57:10 +03:00
}
2016-11-22 20:57:09 +03:00
return pkg_temp_thermal_device_add ( cpu ) ;
2013-05-18 03:42:02 +04:00
}
static const struct x86_cpu_id __initconst pkg_temp_thermal_ids [ ] = {
2020-03-20 16:13:58 +03:00
X86_MATCH_VENDOR_FEATURE ( INTEL , X86_FEATURE_PTS , NULL ) ,
2013-05-18 03:42:02 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , pkg_temp_thermal_ids ) ;
static int __init pkg_temp_thermal_init ( void )
{
2016-11-22 20:57:15 +03:00
int ret ;
2013-05-18 03:42:02 +04:00
if ( ! x86_match_cpu ( pkg_temp_thermal_ids ) )
return - ENODEV ;
2019-05-13 20:59:00 +03:00
max_id = topology_max_packages ( ) * topology_max_die_per_package ( ) ;
zones = kcalloc ( max_id , sizeof ( struct zone_device * ) ,
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:03:40 +03:00
GFP_KERNEL ) ;
2019-05-13 20:59:00 +03:00
if ( ! zones )
2016-11-22 20:57:14 +03:00
return - ENOMEM ;
2016-11-22 20:57:15 +03:00
ret = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " thermal/x86_pkg:online " ,
pkg_thermal_cpu_online , pkg_thermal_cpu_offline ) ;
if ( ret < 0 )
goto err ;
/* Store the state for module exit */
pkg_thermal_hp_state = ret ;
2013-05-18 03:42:02 +04:00
2016-11-22 20:57:06 +03:00
platform_thermal_package_notify = pkg_thermal_notify ;
platform_thermal_package_rate_control = pkg_thermal_rate_control ;
2016-11-22 20:57:09 +03:00
/* Don't care if it fails */
pkg_temp_debugfs_init ( ) ;
2013-05-18 03:42:02 +04:00
return 0 ;
2016-11-22 20:57:15 +03:00
err :
2019-05-13 20:59:00 +03:00
kfree ( zones ) ;
2016-11-22 20:57:15 +03:00
return ret ;
2013-05-18 03:42:02 +04:00
}
2016-11-22 20:57:09 +03:00
module_init ( pkg_temp_thermal_init )
2013-05-18 03:42:02 +04:00
static void __exit pkg_temp_thermal_exit ( void )
{
2016-11-22 20:57:06 +03:00
platform_thermal_package_notify = NULL ;
platform_thermal_package_rate_control = NULL ;
2016-11-22 20:57:15 +03:00
cpuhp_remove_state ( pkg_thermal_hp_state ) ;
2013-05-18 03:42:02 +04:00
debugfs_remove_recursive ( debugfs ) ;
2019-05-13 20:59:00 +03:00
kfree ( zones ) ;
2013-05-18 03:42:02 +04:00
}
module_exit ( pkg_temp_thermal_exit )
2022-12-19 18:46:19 +03:00
MODULE_IMPORT_NS ( INTEL_TCC ) ;
2013-05-18 03:42:02 +04:00
MODULE_DESCRIPTION ( " X86 PKG TEMP Thermal Driver " ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;