zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Linux内核Thermal框架详解十五、Thermal Governor(5)

Linux内核框架 详解 十五
2023-09-14 09:09:19 时间

本文部分内容参考

万字长文 | Thermal框架源码剖析

Linux Thermal机制源码分析之框架概述_不捡风筝的玖伍贰柒的博客-CSDN博客

Linux内核中最牛逼的温控方案——IPA(二)_啪一个右边腿的博客-CSDN博客

Linux thermal governor之IPA分析_内核工匠的博客-CSDN博客

特此致谢!

接前一篇文章Linux内核Thermal框架详解十四、Thermal Governor(4)

二、具体温控策略

上一篇文章介绍并详细分析了step_wise governor的源码。本文介绍第4种温控策略:power_allocator。

4. power_allocator

IPA(Intelligent PowerAllocation)是由ARM开发的符合linux内核Thermal框架的governor,代码中的名字为power_allocator,旨在满足温控效果的条件下最大化性能。IPA模型的核心是利用 PID 控制器,ThermalZone 的温度作为输入,可分配功耗值作为输出,调节 Allocator 的频率和电压值。

power_allocator governor的代码在drivers/thermal/drivers/thermal/gov_power_allocator.c.c中,总共有753行代码(Linux内核版本:5.15.8)。从代码行数上就能看出来,IPA远比其它温控策略复杂得多,完全不是一个数量级的。因此,本文会分几篇文章来对IPA即power_allocator governor代码进行解析。

(1)THERMAL_GOVERNOR_DECLARE相关代码

先来看THERMAL_GOVERNOR_DECLARE。它是一个宏定义,在drivers/thermal/thermal_core.h中,代码如下:

/* Init section thermal table */
extern struct thermal_governor *__governor_thermal_table[];
extern struct thermal_governor *__governor_thermal_table_end[];

#define THERMAL_TABLE_ENTRY(table, name)			\
	static typeof(name) *__thermal_table_entry_##name	\
	__used __section("__" #table "_thermal_table") = &name

#define THERMAL_GOVERNOR_DECLARE(name)	THERMAL_TABLE_ENTRY(governor, name)

实际上这段代码在前文Linux内核Thermal框架详解四、Thermal Core(3)中已经进行了详细分析,这里就不再赘述了。不过为了便于理解和加深印象,将power_allocator governor展开后的代码再次列出:

static struct thermal_governor thermal_gov_power_allocator = {
	.name		= "power_allocator",
	.bind_to_tz	= power_allocator_bind,
	.unbind_from_tz	= power_allocator_unbind,
	.throttle	= power_allocator_throttle,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_power_allocator    \
    __used __section("__governor_thermal_table") = &thermal_gov_power_allocator

Thermal Governor都是通过THERMAL_GOVERNOR_DECLARE定义到了__governor_thermal_table这段空间内。然后在thermal core初始化时通过调用thermal_register_governors来注册到thermal_governor_list链表中。再之后通过经由“thermal_init->thermal_register_governors-> thermal_set_governor”路径和thermal zone device关联上。

(2)handle_non_critical_trips

struct thermal_governor中有一个成员throttle,其是一个函数指针:

int (*throttle)(struct thermal_zone_device *tz, int trip);

对于对象thermal_gov_power_allocator来说,指向了power_allocator_throttle函数。在解析power_allocator_throttle函数之前,有一个问题必须弄清楚:这个函数是何时被调用的?

是在drivers/thermal/thermal_core.c的handle_non_critical_trips函数中,代码如下:

static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip)
{
	tz->governor ? tz->governor->throttle(tz, trip) :
		       def_governor->throttle(tz, trip);
}

那么又是哪里调用的handle_non_critical_trips?是在drivers/thermal/thermal_core.c的handle_thermal_trip函数中,代码如下:

static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
	enum thermal_trip_type type;
	int trip_temp, hyst = 0;

	/* Ignore disabled trip points */
	if (test_bit(trip, &tz->trips_disabled))
		return;

	tz->ops->get_trip_temp(tz, trip, &trip_temp);
	tz->ops->get_trip_type(tz, trip, &type);
	if (tz->ops->get_trip_hyst)
		tz->ops->get_trip_hyst(tz, trip, &hyst);

	if (tz->last_temperature != THERMAL_TEMP_INVALID) {
		if (tz->last_temperature < trip_temp &&
		    tz->temperature >= trip_temp)
			thermal_notify_tz_trip_up(tz->id, trip,
						  tz->temperature);
		if (tz->last_temperature >= trip_temp &&
		    tz->temperature < (trip_temp - hyst))
			thermal_notify_tz_trip_down(tz->id, trip,
						    tz->temperature);
	}

	if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
		handle_critical_trips(tz, trip, type);
	else
		handle_non_critical_trips(tz, trip);
	/*
	 * Alright, we handled this trip successfully.
	 * So, start monitoring again.
	 */
	monitor_thermal_zone(tz);
}

对于handle_thermal_trip函数的详细分析有专门的文章章节,由于本篇文章专注于bang_bang governor,故在此不深入展开。

(3)power_allocator_throttle

power_allocator_throttle函数无疑是power_allocator governor的核心。代码如下:

static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
{
	int ret;
	int switch_on_temp, control_temp;
	struct power_allocator_params *params = tz->governor_data;
	bool update;

	/*
	 * We get called for every trip point but we only need to do
	 * our calculations once
	 */
	if (trip != params->trip_max_desired_temperature)
		return 0;

	ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
				     &switch_on_temp);
	if (!ret && (tz->temperature < switch_on_temp)) {
		update = (tz->last_temperature >= switch_on_temp);
		tz->passive = 0;
		reset_pid_controller(params);
		allow_maximum_power(tz, update);
		return 0;
	}

	tz->passive = 1;

	ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
				&control_temp);
	if (ret) {
		dev_warn(&tz->device,
			 "Failed to get the maximum desired temperature: %d\n",
			 ret);
		return ret;
	}

	return allocate_power(tz, control_temp);
}

不同于其它几个策略中的相应函数,这个函数没有注释。不过还比较好理解。

第一个tz->ops->get_trip_temp函数即tz->ops->get_trip_temp(tz, params->trip_switch_on, &switch_on_temp)的意思是获取trip温度,作为switch_on触发温度。

第二个tz->ops->get_trip_temp函数即tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, &control_temp)的意思是获取trip温度,作为目标温度。

最后就进入到了IPA注算法逻辑allocate_power函数。我们放在下一篇文章中进行详解。