zl程序教程

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

当前栏目

Linux内核Thermal框架详解五、Thermal Core(4)

2023-09-14 09:09:19 时间

本文部分内容参考Linux Thermal 学习笔记 - 爱码网。特此致谢!

接前一篇文章Linux内核Thermal框架详解四、Thermal Core(3)

三、相关源码及分析

2. thermal_register_governors

上一回说到这一段代码:

for (__governor = __governor_thermal_table;	\
	     __governor < __governor_thermal_table_end;	\
	     __governor++) {
    ret = thermal_register_governor(*governor);
	if (ret) {
		pr_err("Failed to register governor: '%s'",
			      (*governor)->name);
		break;
	}
 
	pr_info("Registered thermal governor '%s'",
		(*governor)->name);
}

__governor_thermal_table上一回仔细分析了其来龙去脉,现在该关注__governor这个变量了。

__governor这个变量在之前代码中出现过,如下:

struct thermal_governor **governor;

这是一个二重指针,即指向指针的指针。在上述代码中,它一开始指向了__governor_thermal_table,还记得__governor_thermal_table是怎样定义的吗?

在drivers/thermal/thermal_core.h中:

extern struct thermal_governor *__governor_thermal_table[];
extern struct thermal_governor *__governor_thermal_table_end[];

虽然这里是extern,但是这比include/asm-generic/vmlinux.lds.h中真正定义__governor_thermal_table的地方好理解。__governor_thermal_table实际上是一个指针数组。所以使用二重指针governor指向它也就合情合理了。

再来回顾一下各种governor策略:

  • step_wise

drivers/thermal/gov_step_wise.c中:

static struct thermal_governor thermal_gov_step_wise = {
	.name		= "step_wise",
	.throttle	= step_wise_throttle,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_step_wise    \
    __used __section("__governor_thermal_table") = &thermal_gov_step_wise
  • power_allocator

drivers/thermal/gov_power_allocator.c中:

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
  • fair_share

drivers/thermal/gov_fair_share.c中:

static struct thermal_governor thermal_gov_fair_share = {
	.name		= "fair_share",
	.throttle	= fair_share_throttle,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_fair_share    \
    __used __section("__governor_thermal_table") = &thermal_gov_fair_share
  • user_space

drivers/thermal/gov_user_space.c中:

static struct thermal_governor thermal_gov_user_space = {
	.name		= "user_space",
	.throttle	= notify_user_space,
	.bind_to_tz	= user_space_bind,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_user_space    \
    __used __section("__governor_thermal_table") = &thermal_gov_user_space
  • bang_bang

drivers/thermal/gov_bang_bang.c中:

static struct thermal_governor thermal_gov_bang_bang = {
	.name		= "bang_bang",
	.throttle	= bang_bang_control,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_bang_bang    \
    __used __section("__governor_thermal_table") = &thermal_gov_bang_bang

弄清楚了以上细节后就能知道,本文开头的代码的意义是:遍历所有的governor策略并进行注册。注册具体都完成了哪些工作?下边接着来看。

(2)thermal_register_governor

thermal_register_governor函数同样在drivers/thermal/thermal_core.c中实现,代码如下:

int thermal_register_governor(struct thermal_governor *governor)
{
	int err;
	const char *name;
	struct thermal_zone_device *pos;

	if (!governor)
		return -EINVAL;

	mutex_lock(&thermal_governor_lock);

	err = -EBUSY;
	if (!__find_governor(governor->name)) {
		bool match_default;

		err = 0;
		list_add(&governor->governor_list, &thermal_governor_list);
		match_default = !strncmp(governor->name,
					 DEFAULT_THERMAL_GOVERNOR,
					 THERMAL_NAME_LENGTH);

		if (!def_governor && match_default)
			def_governor = governor;
	}

	mutex_lock(&thermal_list_lock);

	list_for_each_entry(pos, &thermal_tz_list, node) {
		/*
		 * only thermal zones with specified tz->tzp->governor_name
		 * may run with tz->govenor unset
		 */
		if (pos->governor)
			continue;

		name = pos->tzp->governor_name;

		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
			int ret;

			ret = thermal_set_governor(pos, governor);
			if (ret)
				dev_err(&pos->device,
					"Failed to set governor %s for thermal zone %s: %d\n",
					governor->name, pos->type, ret);
		}
	}

	mutex_unlock(&thermal_list_lock);
	mutex_unlock(&thermal_governor_lock);

	return err;
}

函数虽然不算太长,但也不算太短,还是有一些内容的。逐段来看:

一开始是判断并确保入参governor不为空。

接下来加锁mutex_lock(&thermal_governor_lock)。thermal_governor_lock在同文件(drivers/thermal/thermal_core.c)中定义并初始化,代码如下:

static DEFINE_MUTEX(thermal_governor_lock);

接下来是一个判断if (!__find_governor(governor->name))。__find_governor函数同样在drivers/thermal/thermal_core.c中,代码如下:

/*
 * Governor section: set of functions to handle thermal governors
 *
 * Functions to help in the life cycle of thermal governors within
 * the thermal core and by the thermal governor code.
 */

static struct thermal_governor *__find_governor(const char *name)
{
	struct thermal_governor *pos;

	if (!name || !name[0])
		return def_governor;

	list_for_each_entry(pos, &thermal_governor_list, governor_list)
		if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
			return pos;

	return NULL;
}

要弄清楚这个函数的功能,就必须弄清楚list_for_each_entry的含义。list_for_each_entry是一个宏,在include/linux/list.h中,代码如下:

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_first_entry(head, typeof(*pos), member);	\
	     !list_entry_is_head(pos, head, member);			\
	     pos = list_next_entry(pos, member))

list_first_entry当然也在include/linux/list.h中,代码如下:

/**
 * list_first_entry - get the first element from a list
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

list_entry也在include/linux/linux.h中,就在list_first_entry宏定义的上边,代码如下:

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

由上,list_first_entry展开为:

#define list_first_entry(ptr, type, member)    container_of((ptr)->next, type, member)

list_entry_is_head同样在include/linux/linux.h中,代码如下:

/**
 * list_entry_is_head - test if the entry points to the head of the list
 * @pos:	the type * to cursor
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 */
#define list_entry_is_head(pos, head, member)				\
	(&pos->member == (head))

list_next_entry同样在include/linux/linux.h中,代码如下:

/**
 * list_next_entry - get the next element in list
 * @pos:	the type * to cursor
 * @member:	the name of the list_head within the struct.
 */
#define list_next_entry(pos, member) \
	list_entry((pos)->member.next, typeof(*(pos)), member)

由上,list_first_entry展开为:

#define list_next_entry(pos, member)    container_of((pos)->member.next, typeof(*(pos)), member)

综上所有,推导出list_for_each_entry的最终形式如下:

原始:

#define list_for_each_entry(pos, head, member)				\
	for (pos = list_first_entry(head, typeof(*pos), member);	\
	     !list_entry_is_head(pos, head, member);			\
	     pos = list_next_entry(pos, member))

最终展开形式:

#define list_for_each_entry(pos, head, member) \
    for (pos = container_of((head)->next, typeof(*pos), member); \
        !(&pos->member == (head)); \
        container_of((pos)->member.next, typeof(*(pos)), member)

代入实际的参数的形式为:

代入前:

list_for_each_entry(pos, &thermal_governor_list, governor_list)

代入后:

for (pos = container_of((&thermal_governor_list)->next, struct thermal_governor, governor_list); \
    !(&pos->governor_list == (&thermal_governor_list)); \
    container_of((pos)->governor_list.next, struct thermal_governor), governor_list)

thermal_governor_list在drivers/thermal/thermal_core.c中定义并初始化:

static LIST_HEAD(thermal_governor_list);

而LIST_HEAD宏定义也在include/linux/linux.h中,代码如下:

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

则static LIST_HEAD(thermal_governor_list);整个展开为:

static struct list_head thermal_governor_list = { &(thermal_governor_list), &(thermal_governor_list) };

索性一探到底,struct list_head在include/linux/types.h中定义,代码如下:

struct list_head {
	struct list_head *next, *prev;
};

struct thermal_governor的定义在include/linux/thermal.h中,代码如下:

/**
 * 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;
};

至此,就能够了解整个thermal governor注册机制了:

(0)各个温控策略将自己放到__governor_thermal_table中

 step wise:

static struct thermal_governor thermal_gov_step_wise = {
	.name		= "step_wise",
	.throttle	= step_wise_throttle,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_step_wise    \
    __used __section("__governor_thermal_table") = &thermal_gov_step_wise

intelligent power allocator:

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

fair share:

static struct thermal_governor thermal_gov_fair_share = {
	.name		= "fair_share",
	.throttle	= fair_share_throttle,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_fair_share    \
    __used __section("__governor_thermal_table") = &thermal_gov_fair_share

user space:

static struct thermal_governor thermal_gov_user_space = {
	.name		= "user_space",
	.throttle	= notify_user_space,
	.bind_to_tz	= user_space_bind,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_user_space    \
    __used __section("__governor_thermal_table") = &thermal_gov_user_space

 bang bang:

static struct thermal_governor thermal_gov_bang_bang = {
	.name		= "bang_bang",
	.throttle	= bang_bang_control,
};

static struct thermal_governor *__thermal_table_entry_thermal_gov_bang_bang    \
    __used __section("__governor_thermal_table") = &thermal_gov_bang_bang

(1)遍历__governor_thermal_table,对于每一个项分别进行注册

for (__governor = __governor_thermal_table;	\
	     __governor < __governor_thermal_table_end;	\
	     __governor++) {
    ret = thermal_register_governor(*governor);
	if (ret) {
		pr_err("Failed to register governor: '%s'",
			      (*governor)->name);
		break;
	}
 
	pr_info("Registered thermal governor '%s'",
		(*governor)->name);
}

如果thermal_resister_governor函数返回非0值,则说明出错,跳出循环。

(2)将每种温控策略加入到thermal_governor_list中

if (!__find_governor(governor->name)) {
	bool match_default;

	err = 0;
	list_add(&governor->governor_list, &thermal_governor_list);
	match_default = !strncmp(governor->name,
					DEFAULT_THERMAL_GOVERNOR,
					THERMAL_NAME_LENGTH);

	if (!def_governor && match_default)
		def_governor = governor;
}
/*
 * Governor section: set of functions to handle thermal governors
 *
 * Functions to help in the life cycle of thermal governors within
 * the thermal core and by the thermal governor code.
 */

static struct thermal_governor *__find_governor(const char *name)
{
	struct thermal_governor *pos;

	if (!name || !name[0])
		return def_governor;

	list_for_each_entry(pos, &thermal_governor_list, governor_list)
		if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
			return pos;

	return NULL;
}

进入到thermal_register_governor函数中。在thermal_governor_list中如果没有找到某一种温控策略,则把该温控策略通过list_add(&governor->governor_list, &thermal_governor_list)加入到thermal_governor_list中,实际上就是将其governor_list成员链接到以thermal_governor_list为表头的链表上;如果在thermal_governor_list中找到了某种策略,即该策略已经添加到thermal_governor_list链表上,则跳过添加这一步骤,继续往下进行。

list_add函数同样在include/linux/list.h中定义,代码如下:

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	if (!__list_add_valid(new, prev, next))
		return;

	next->prev = new;
	new->next = next;
	new->prev = prev;
	WRITE_ONCE(prev->next, new);
}

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

一个经典的后插入模型,插入到head之后。

(3)在添加到thermal_governor_list链表的过程中,还同时检查并设置了默认温控策略

match_default = !strncmp(governor->name,
				DEFAULT_THERMAL_GOVERNOR,
				THERMAL_NAME_LENGTH);

if (!def_governor && match_default)
	def_governor = governor;

DEFAULT_THERMAL_GOVERNOR相关代码在drivers/thermal/thermal_core.h中,如下所示:

/* Default Thermal Governor */
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
#define DEFAULT_THERMAL_GOVERNOR       "step_wise"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)
#define DEFAULT_THERMAL_GOVERNOR       "fair_share"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
#define DEFAULT_THERMAL_GOVERNOR       "user_space"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
#endif

具体哪一种温控策略是默认的温控策略,这是要根据配置文件确定的。

def_governor也在drivers/thermal/thermal_core.c文件中定义,如下所示:

static struct thermal_governor *def_governor;

接下来就来到了thermal_register_governor函数中thermal zones相关的处理了。由于本文篇幅已较长,因此放到下一篇文章中进行详细说明。