Linux Thermal机制源码分析之框架概述

一、概述

       Thermal,中文意思是热的、保暖的。在 Linux 内核中,Thermal 特指一套关于温控机制的驱动框架,其目的是为了防止 SoC 等硬件芯片因过热而造成系统不稳定,甚至缩减芯片寿命。

       Thermal 框架是在软件层面上对自然界散热系统的抽象。试想一下空调的工作机制,假设场景是夏天,室内温度 35 度,用户设定目标温度 25 度。对房间降温首先得知道房间里当前温度是多少,这个工作需要温度传感器来完成。当温度传感器探知室内温度并传给空调内部的 MCU(姑且认为空调的控制系统是由一颗内嵌的 MCU 来完成的吧) 后,MCU 得知当前温差是10度,比较大,所以加大功率,让房间迅速降温(我们知道空调内部的降温设备是雪种)。一段时间后,温度降至 28 度,这个时候 MCU 开始减小功率,降温速度开始趋缓,直至稳定在 25 度。当然,空调实际的工作方式不一定和上面描述的一模一样,但是基本原理是这样。我们试图通过空调的例子抽取温控系统的必要组件,然后过渡到 Linux 内核中的 Thermal 框架:

1、既然要控制房间温度,当然要能知道房间当前的温度,这个必要组件就是温度传感器;

2、降温或者升温,需要实际的设备来支撑,这个必要组件就是雪种;

3、温差大,加大功率以便迅速降温,温差小,降低功率省电,这个必要组件就是温控策略,或者叫温控算法;

4、整个温控系统需要有一个类似大脑的设备来统筹管理,这个必要组件就是 MCU。

       类比到 Thermal 框架(我们主要考虑 CPU/GPU 的温控),其必要组件和上面的空调例子十分相似:

1、Thermal sensor driver,SoC 内部 CPU 和 GPU 的旁边通常会有用于获取它们温度的传感器,比如 tsadc(Temperature Sensor ADC)。关于传感器的更多细节我们在 sensor driver 章节再进行深入探讨。

2、Thermal cooling device,降温设备,比如风扇。这里有点特殊的是,CPU 和 GPU 不仅是发热设备(即需要实施温控策略的设备),也可以是降温设备,当我们降低 CPU/GPU 的运行频率的时候,它们就在充当降温设备。降低产热量即是在降温。

3、Thermal governer,温控策略,Linux 内核中的温控策略要比上面的空调控制精细得多,而且也提供了多种策略。

4、Thermal core,组织并管理上面三个组件,并通过 sysfs 和用户空间交互。

       Thermal 的软件框架大致如下:

                 

二、代码路径

       本文源码分析基于瑞芯微 RK3399 Android7.1(kernel 4.4)平台,除了 sensor driver 是平台驱动工程师写的外,其余组件均为 Linux 内核提供。

Thermal sensor driver 代码:
drivers/thermal/rockchip_thermal.c  /* tsadc驱动 */

Thermal cooling device 相关代码:
drivers/thermal/devfreq_cooling.c
drivers/thermal/cpu_cooling.c

Thermal governor 相关代码:
drivers/thermal/power_allocator.c    /* power allocator 温控策略 */
drivers/thermal/step_wise.c              /* step wise 温控策略 */
drivers/thermal/fair_share.c              /* fair share 温控策略 */
drivers/thermal/user_space.c            /* userspace 温控策略 */

Thermal core 相关代码:
drivers/thermal/thermal_core.c
drivers/thermal/of_thermal.c

三、重要结构体

       这些重要结构体,这里先只做一个简要的介绍,混个脸熟,到后面各个组件的源码分析时再来进行详细的成员解析。

1、sensor driver相关

/**
 * struct rockchip_thermal_sensor - hold the information of thermal sensor
 * @thermal: pointer to the platform/configuration data
 * @tzd: pointer to a thermal zone
 * @id: identifier of the thermal sensor
 */
struct rockchip_thermal_sensor {
	struct rockchip_thermal_data *thermal;
	struct thermal_zone_device *tzd;
	int id;
};

struct rockchip_thermal_sensor:RK 平台上该结构体代表了一个 tsadc;

       struct rockchip_thermal_data:见下面的介绍;

       struct thermal_zone_device:一个 tsadc 会和一个 thermal zone 绑定;

       int id:该 tsadc 的编号,一般来说 RK 的 SoC 内部有两个 tsadc;

/**
 * struct rockchip_thermal_data - hold the private data of thermal driver
 * @chip: pointer to the platform/configuration data
 * @pdev: platform device of thermal
 * @reset: the reset controller of tsadc
 * @sensors[SOC_MAX_SENSORS]: the thermal sensor
 * @clk: the controller clock is divided by the exteral 24MHz
 * @pclk: the advanced peripherals bus clock
 * @grf: the general register file will be used to do static set by software
 * @regs: the base address of tsadc controller
 * @tshut_temp: the hardware-controlled shutdown temperature value
 * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
 * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
 */
struct rockchip_thermal_data {
	const struct rockchip_tsadc_chip *chip;
	struct platform_device *pdev;
	struct reset_control *reset;

	struct rockchip_thermal_sensor sensors[SOC_MAX_SENSORS];

	struct clk *clk;
	struct clk *pclk;

	struct regmap *grf;
	void __iomem *regs;

	int tshut_temp;
	enum tshut_mode tshut_mode;
	enum tshut_polarity tshut_polarity;
};

struct rockchip_thermal_data:sensor driver 的私有数据,详见注释。

/**
 * struct rockchip_tsadc_chip - hold the private data of tsadc chip
 * @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel
 * @chn_num: the channel number of tsadc chip
 * @tshut_temp: the hardware-controlled shutdown temperature value
 * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
 * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
 * @initialize: SoC special initialize tsadc controller method
 * @irq_ack: clear the interrupt
 * @get_temp: get the temperature
 * @set_alarm_temp: set the high temperature interrupt
 * @set_tshut_temp: set the hardware-controlled shutdown temperature
 * @set_tshut_mode: set the hardware-controlled shutdown mode
 * @table: the chip-specific conversion table
 */
struct rockchip_tsadc_chip {
	/* The sensor id of chip correspond to the ADC channel */
	int chn_id[SOC_MAX_SENSORS];
	int chn_num;

	/* The hardware-controlled tshut property */
	int tshut_temp;
	enum tshut_mode tshut_mode;
	enum tshut_polarity tshut_polarity;

	/* Chip-wide methods */
	void (*initialize)(struct regmap *grf,
			   void __iomem *reg, enum tshut_polarity p);
	void (*irq_ack)(void __iomem *reg);
	void (*control)(void __iomem *reg, bool on);

	/* Per-sensor methods */
	int (*get_temp)(struct chip_tsadc_table table,
			int chn, void __iomem *reg, int *temp);
	void (*set_alarm_temp)(struct chip_tsadc_table table,
			       int chn, void __iomem *reg, int temp);
	void (*set_tshut_temp)(struct chip_tsadc_table table,
			       int chn, void __iomem *reg, int temp);
	void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);

	/* Per-table methods */
	struct chip_tsadc_table table;
};

struct rockchip_tsadc_chip:详见注释。

RK 的 sensor driver 为了兼容他们家很多 SoC 的 tsadc,把差异性的东西抽出来。比如那些函数指针,由于寄存器地址的不一样函数体的具体内容也会不一样,如 RK3399 和 PX30 之间。再比如由于 SoC 制程不一样,默认的关机温度也可能不一样。

2、governor相关

/**
 * struct thermal_governor - structure that holds thermal governor information
 * @name: name of the governor
 * @bind_to_tz: callback called when binding to a thermal zone.  If it
 *		returns 0, the governor is bound to the thermal zone,
 *		otherwise it fails.
 * @unbind_from_tz: callback called when a governor is unbound from a thermal zone.
 * @throttle: callback called for every trip point even if temperature is
 *		below the trip point temperature
 * @governor_list: node in thermal_governor_list (in thermal_core.c)
 */
struct thermal_governor {
	char name[THERMAL_NAME_LENGTH];
	int (*bind_to_tz) (struct thermal_zone_device *tz);
	void (*unbind_from_tz) (struct thermal_zone_device *tz);
	int (*throttle) (struct thermal_zone_device *tz, int trip);
	struct list_head governor_list;
};

struct thermal_governor:用来描述一个 governor(即温控策略) 信息。

内核目前有五种 governor:

1、power_allocator:引⼊ PID(⽐例-积分-微分)控制,根据当前温度,动态给各 cooling device 分配 power,并将 power 转换为频率,从而达到根据温度限制频率的效果。

2、step_wise:根据当前温度,cooling device 逐级降频。

3、fair share:频率档位⽐较多的 cooling device 优先降频。

4、bang bang:两点温度调节,可用于 cooling device 有风扇的场景。

5、userspace:用户空间控制。

RK 平台统一使用 power_allocator 策略。

3、cooling device相关

struct thermal_cooling_device {
	int id;
	char type[THERMAL_NAME_LENGTH];
	struct device device;
	struct device_node *np;
	void *devdata;
	const struct thermal_cooling_device_ops *ops;
	bool updated; /* true if the cooling device does not need update */
	struct mutex lock; /* protect thermal_instances list */
	struct list_head thermal_instances;
	struct list_head node;
};


struct thermal_cooling_device_ops {
	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
	int (*get_requested_power) (struct thermal_cooling_device *,
				   struct thermal_zone_device *, u32 *);
	int (*state2power) (struct thermal_cooling_device *,
			   struct thermal_zone_device *, unsigned long, u32 *);
	int (*power2state) (struct thermal_cooling_device *,
			   struct thermal_zone_device *, u32, unsigned long *);
};

struct thermal_cooling_device:用来描述一个 cooling device(即降温设备) 信息,并将函数操作集抽取出来。

4、thermal zone

/**
 * struct thermal_zone_device - structure for a thermal zone
 * @id:		unique id number for each thermal zone
 * @type:	the thermal zone device type
 * @device:	&struct device for this thermal zone
 * @trip_temp_attrs:	attributes for trip points for sysfs: trip temperature
 * @trip_type_attrs:	attributes for trip points for sysfs: trip type
 * @trip_hyst_attrs:	attributes for trip points for sysfs: trip hysteresis
 * @devdata:	private pointer for device private data
 * @trips:	number of trip points the thermal zone supports
 * @trips_disabled;	bitmap for disabled trips
 * @passive_delay:	number of milliseconds to wait between polls when
 *			performing passive cooling.
 * @polling_delay:	number of milliseconds to wait between polls when
 *			checking whether trip points have been crossed (0 for
 *			interrupt driven systems)
 * @temperature:	current temperature.  This is only for core code,
 *			drivers should use thermal_zone_get_temp() to get the
 *			current temperature
 * @last_temperature:	previous temperature read
 * @emul_temperature:	emulated temperature when using CONFIG_THERMAL_EMULATION
 * @passive:		1 if you've crossed a passive trip point, 0 otherwise.
 * @forced_passive:	If > 0, temperature at which to switch on all ACPI
 *			processor cooling devices.  Currently only used by the
 *			step-wise governor.
 * @need_update:	if equals 1, thermal_zone_device_update needs to be invoked.
 * @ops:	operations this &thermal_zone_device supports
 * @tzp:	thermal zone parameters
 * @governor:	pointer to the governor for this thermal zone
 * @governor_data:	private pointer for governor data
 * @thermal_instances:	list of &struct thermal_instance of this thermal zone
 * @idr:	&struct idr to generate unique id for this zone's cooling
 *		devices
 * @lock:	lock to protect thermal_instances list
 * @node:	node in thermal_tz_list (in thermal_core.c)
 * @poll_queue:	delayed work for polling
 */
struct thermal_zone_device {
	int id;
	char type[THERMAL_NAME_LENGTH];
	struct device device;
	struct thermal_attr *trip_temp_attrs;
	struct thermal_attr *trip_type_attrs;
	struct thermal_attr *trip_hyst_attrs;
	void *devdata;
	int trips;
	unsigned long trips_disabled;	/* bitmap for disabled trips */
	int passive_delay;
	int polling_delay;
	int temperature;
	int last_temperature;
	int emul_temperature;
	int passive;
	unsigned int forced_passive;
	atomic_t need_update;
	struct thermal_zone_device_ops *ops;
	struct thermal_zone_params *tzp;
	struct thermal_governor *governor;
	void *governor_data;
	struct list_head thermal_instances;
	struct idr idr;
	struct mutex lock;
	struct list_head node;
	struct delayed_work poll_queue;
};


struct thermal_zone_device_ops {
	int (*bind) (struct thermal_zone_device *,
		     struct thermal_cooling_device *);
	int (*unbind) (struct thermal_zone_device *,
		       struct thermal_cooling_device *);
	int (*get_temp) (struct thermal_zone_device *, int *);
	int (*get_mode) (struct thermal_zone_device *,
			 enum thermal_device_mode *);
	int (*set_mode) (struct thermal_zone_device *,
		enum thermal_device_mode);
	int (*get_trip_type) (struct thermal_zone_device *, int,
		enum thermal_trip_type *);
	int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
	int (*set_trip_temp) (struct thermal_zone_device *, int, int);
	int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
	int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
	int (*get_crit_temp) (struct thermal_zone_device *, int *);
	int (*set_emul_temp) (struct thermal_zone_device *, int);
	int (*get_trend) (struct thermal_zone_device *, int,
			  enum thermal_trend *);
	int (*notify) (struct thermal_zone_device *, int,
		       enum thermal_trip_type);
};

struct thermal_zone_device:一个 thermal zone 是根据 dts 里的配置一步步解析并构建的,包含了很多信息,比如服务于该 thermal zone 的 tsadc,服务于该 thermal zone 的降温设备,该 thermal zone 所用的 governor,以及 thermal 机制工作时所需的一些参数,等等。更多细节在源码分析部分再来说明。

       通常,RK 平台上 thermal zone 的 dts 配置格式如下。其它平台应该和这个大同小异,因为都要基于 thermal core 来配置。

thermal_zones: thermal-zones {
	/* 一个节点对应一个thermal zone,并包含温控策略相关参数 */
	soc_thermal: soc-thermal {
		/* 温度高于trip-point-0指定的值,每隔20ms获取一次温度 */
		polling-delay-passive = <20>; /* milliseconds */
		/* 温度低于trip-point-0指定的值,每隔1000ms获取一次温度 */
		polling-delay = <1000>; /* milliseconds */
		/* 温度等于trip-point-1指定的值时,系统分配给cooling device的能量 */
		sustainable-power = <1000>; /* milliwatts */
		/* 当前thermal zone通过tsadc0获取温度 */
		thermal-sensors = <&tsadc 0>;

		/* trips包含不同温度阈值,不同的温控策略,配置不一定相同 */
		trips {
			/*
			 * 温控阈值,超过该值温控策略开始工作作,但不一定马上限制频率,
			 * power小到一定程度才开始限制频率
			 */
			threshold: trip-point-0 {
				/* 超过70摄氏度,温控策略开始工作,并且70度也是tsadc触发中断的一个阈值 */
				temperature = <70000>; /* millicelsius */
				/* 温度低于temperature-hysteresis时触发中断,当前未实现,但框架要求必须填 */
				hysteresis = <2000>; /* millicelsius */
				type = "passive"; /* 表示超过该温度值时,使用polling-delay-passive */
			};

			/* 温控目标温度,期望通过降频使得芯片不超过该值 */
			target: trip-point-1 {
				/* 期望通过降频使得芯片不超过85摄氏度,并且85度也是tsadc触发中断的一个阈值 */
				temperature = <85000>; /* millicelsius */
				/* 温度低于temperature-hysteresis时触发中断,当前未实现,但框架要求必须填 */
				hysteresis = <2000>; /* millicelsius */
				type = "passive"; /* 表示超过该温度值时,使用polling-delay-passive */
			};

			/* 过温保护阈值,如果降频后温度仍然上升,那么超过该值后,让系统重启 */
			soc_crit: soc-crit {
				/* 超过115摄氏度重启,并且115度也是tsadc触发中断的一个阈值 */
				temperature = <115000>; /* millicelsius */
				/* 温度低于temperature-hysteresis时触发中断,当前未实现,但框架要求必须填 */
				hysteresis = <2000>; /* millicelsius */
				type = "critical"; /* 表示超过该温度值时,重启 */
			};
		};

		/* cooling device配置节点,每个子节点代表一个cooling device */
		cooling-maps {
			map0 {
				/*
				 * 表示在target trip下,该cooling device才起作用,
				 * 对于power allocater策略必须填target
				 */
				trip = <&target>;
				/* A53做为cooloing device, THERMAL_NO_LIMIT不起作用,但必须填 */
				cooling-device = <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
				/* 计算功耗时乘以4096/1024倍,用于调整降频顺序和尺度 */
				contribution = <4096>;
			};

			map1 {
				/*
				 * 表示在target trip下,该cooling device才起作用,
				 * 对于power allocater策略必须填target
				 */
				trip = <&target>;
				/* A72做为cooloing device, THERMAL_NO_LIMIT不起作用,但必须填 */
				cooling-device = <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
				/* 计算功耗时乘以1024/1024倍,用于调整降频顺序和尺度 */
				contribution = <1024>;
			};

			map2 {
				/*
				 * 表示在target trip下,该cooling device才起作用,
				 * 对于power allocater策略必须填target
				 */
				trip = <&target>;
				/* GPU做为cooloing device, THERMAL_NO_LIMIT不起作用,但必须填 */
				cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
				/* 计算功耗时乘以4096/1024倍,用于调整降频顺序和尺度 */
				contribution = <4096>;
			};
		};
	};

	/* 一个节点对应一个thermal zone,并包含温控策略相关参数,当前thermal zone只用于获取温度 */
	gpu_thermal: gpu-thermal {
		/* 包含温控策略配置的情况下才起作用,框架要求必须填 */
		polling-delay-passive = <100>; /* milliseconds */
		/* 每隔1000ms获取一次温度 */
		polling-delay = <1000>; /* milliseconds */

		/* 当前thermal zone通过tsadc1获取温度 */
		thermal-sensors = <&tsadc 1>;
	};
};

上面每个子结点和每条属性的注释摘自 RK 平台的开发参考文档,这些注释都可以在源码里找到答案,到具体源码分析时读者应该会有更深刻的理解。

  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux thermal是一个用于管理系统温度的子系统。它可以监测系统的温度,并根据需要调整风扇转速或者降低CPU频率等措施来控制温度。这个子系统可以通过/sys/class/thermal目录下的文件进行配置和控制。在Linux系统中,thermal子系统是非常重要的,它可以保证系统的稳定性和可靠性。 ### 回答2: Linux thermal(温度)是Linux内核中的一个子系统,用于监测和管理硬件设备的温度。在计算机系统中,温度是一个非常重要的参数,因为过高的温度可能会导致硬件故障、性能下降甚至损坏。 Linux thermal子系统通过不同的方式来获取硬件的温度信息。例如,它可以通过传感器来读取处理器、显卡、硬盘等设备的温度数据。这些传感器通常嵌入在硬件设备中,并通过总线接口(如I2C)与计算机系统连接。 一旦获取到硬件设备的温度数据,Linux thermal子系统会根据预先设置的策略来采取相应的措施。例如,它可以通过调整风扇转速来降低设备的温度。它还可以通过降低处理器频率来减少能量消耗和温度。 此外,Linux thermal子系统还提供了用户空间接口,允许用户查询和控制温度相关的信息。用户可以使用命令行工具或编写脚本来监控和调整硬件设备的温度状态。 总之,Linux thermalLinux内核中的一个重要子系统,用于监控和管理硬件设备的温度。它通过传感器获取温度数据,并根据预先设置的策略来调整硬件设备的工作状态,以保持温度在可接受的范围内。这对于确保计算机系统的性能和可靠性非常重要。 ### 回答3: Linux thermalLinux 热管理系统)是Linux操作系统中的一个功能,旨在监测和管理计算机硬件的温度,在温度升高时采取适当的措施来保护硬件免受损坏。 Linux thermal通过硬件传感器监测计算机中各个部件(如处理器、图形卡、内存等)的温度。传感器可以是来自硬件设备的内置传感器,也可以是外部插件设备的传感器。通过读取这些传感器的数据,Linux thermal能够实时监测温度的变化。 当温度超过设定的安全阈值时,Linux thermal会采取相应的措施来降低硬件的温度。这些措施可以包括调整风扇的转速,降低电压供应,甚至通过暂停或降低某些任务的运行来减少硬件的功耗。 在服务器和桌面计算机等大型计算设备上,Linux thermal的作用非常重要。过高的温度会导致硬件故障、损坏甚至灾难性的停机。通过及时监测温度并进行热管理,Linux系统可以保护硬件的完整性和可靠性。 除了保护硬件免受损坏外,Linux thermal还可以提高计算机的性能和能效。通过优化温度控制,可以避免出现过度降温的情况,提高硬件的工作效率并减少能源消耗。 总之,Linux thermalLinux操作系统中用于监测和管理计算机硬件温度的功能,它通过读取传感器数据并采取适当的措施来保护硬件,提高性能和能效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值