admin管理员组文章数量:1658732
一、thermal_init()
在开始源码分析之前,需要先说明一下。Linux 内核代码庞大而复杂,如何 reading the Fxxking source code 相信是很多从事 Linux 内核/驱动开发人员非常关注也一直苦恼的事情,尤其是初涉内核开发的同行。本文在剖析 Thermal 机制的同时,也试图向读者展示作者如何来分析内核源码,以期读者可以借鉴一二。当然,由于本人能力有限,有错误的地方也请读者朋友不吝指正。同时,如果有更好的源码分析方法,也请各位不吝赐教,在此感激不尽。
分析当然是从 thermal 驱动模块的入口开始(thermal_core.c 里):
static int __init thermal_init(void)
{
int result;
/* 重点分析:向thermal core注册governor */
result = thermal_register_governors();
if (result)
goto error;
/* 向内核注册thermal class: /sys/class/thermal/... */
result = class_register(&thermal_class);
if (result)
goto unregister_governors;
/* netlink机制,猜测应该是用于用户空间和内核空间通信的 */
result = genetlink_init();
if (result)
goto unregister_class;
/* 重点分析:解析dts里的thermal_zones */
result = of_parse_thermal_zones();
if (result)
goto exit_netlink;
/* notifier机制,对pm事件感兴趣 */
result = register_pm_notifier(&thermal_pm_nb);
if (result)
pr_warn("Thermal: Can not register suspend notifier, return %d\n",
result);
return 0;
exit_netlink:
genetlink_exit();
unregister_class:
class_unregister(&thermal_class);
unregister_governors:
thermal_unregister_governors();
error:
idr_destroy(&thermal_tz_idr);
idr_destroy(&thermal_cdev_idr);
mutex_destroy(&thermal_idr_lock);
mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock);
return result;
}
static void __exit thermal_exit(void)
{
unregister_pm_notifier(&thermal_pm_nb);
of_thermal_destroy_zones();
genetlink_exit();
class_unregister(&thermal_class);
thermal_unregister_governors();
idr_destroy(&thermal_tz_idr);
idr_destroy(&thermal_cdev_idr);
mutex_destroy(&thermal_idr_lock);
mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock);
}
/* thermal驱动模块会早于tsadc驱动加载 */
fs_initcall(thermal_init);
module_exit(thermal_exit);
thermal 模块加载进内核用的 fs_initcall(),tsadc 驱动一般用的是 module_init(),前者会早于后者加载,这点比较重要,有些代码流程上会依赖这种先后关系,需要留意。thermal_init() 函数里比较简单,先后调用了 5 个函数:
1、thermal_register_governors():注册 governor,这个函数会重点分析。
2、class_register():注册 thermal class,这个函数不会跟进去分析。class 框架性代码,大概知道是干什么用的即可,详细分析对当前所要学习的 Thermal 框架没有太大帮助。
3、genetlink_init():不太了解,百度查了下,好像是用于用户空间和内核空间通信的,先放一放(从后面的分析来看,netlink 机制不了解也不影响分析 Thermal 框架)。
4、of_parse_thermal_zones():解析 dts 里面的 thermal_zones,这个是重中之重。
5、register_pm_notifier():notifier 机制,表明 Thermal core 对 pm 事件感兴趣。
在开始分析 thermal_register_governors() 和 of_parse_thermal_zones() 之前,先扫清一下比较简单的 register_pm_notifier()。Notifier 机制属于内核基础设施,本文就不展开分析了,以后会开专文来分析,网上也有相关文章。注意传进去的参数是 thermal_pm_nb 的地址,thermal_pm_nb 必须要提供 notifier_call 回调:
static struct notifier_block thermal_pm_nb = {
.notifier_call = thermal_pm_notify,
};
系统休眠唤醒时,会去调用这个回调:
static int thermal_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
struct thermal_zone_device *tz;
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
/* 原子变量,标记suspend事件 */
atomic_set(&in_suspend, 1);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
atomic_set(&in_suspend, 0);
list_for_each_entry(tz, &thermal_tz_list, node) {
thermal_zone_device_reset(tz);
thermal_zone_device_update(tz);
}
break;
default:
break;
}
return 0;
}
case 的这几个宏定义如下:
/* Hibernation(冬眠) and suspend events */
#define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */
#define PM_SUSPEND_PREPARE 0x0003 /* Going to suspend the system */
#define PM_POST_SUSPEND 0x0004 /* Suspend finished */
#define PM_RESTORE_PREPARE 0x0005 /* Going to restore a saved image */
#define PM_POST_RESTORE 0x0006 /* Restore failed */
每个宏的具体含义需要详细了解 PM 相关代码才能知道了,但是可以知道的是 PM_xxx_PREPARE 宏表明系统 suspend 了,PM_POST_xxx 宏表明系统 resume 了。in_suspend 是一个静态的原子变量:
static atomic_t in_suspend;
原子变量主要用于同步机制,也是内核的基础设施之一,这里不展开了。可以看到,函数 thermal_pm_nb 做的事情很简单:
1、系统 suspend 的时候,标记 in_suspend 为 1;
2、系统 resume 的时候,标记 in_suspend 为 0,并调用 thermal core 提供的复位和更新函数,因为我们还没开始整个 Thermal 框架的代码分析,所以这两个函数在这里先不展开,后面会再次碰到,尤其是 update 函数,还非常重要。
既然对 in_suspend 做了标记,那肯定是在什么地方要用到,好在 in_suspend 是静态全局的,所以很自然地搜索了整个 thermal_core.c 文件,发现只在一个地方用到了:
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count;
if (atomic_read(&in_suspend))
return;
...
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
是的,就是这个 update 函数,为了只关注 in_suspend 的代码逻辑,我删掉了 update 函数后面的内容。这样一看,in_suspend 的作用就很明显了:如果系统 suspend 了,那么就不 update 了,直接返回;否则正常走 update 流程。
二、thermal_register_governors()
这个部分是本篇博文的核心:governor 注册。让我们看看 governor 到底是个什么东东:
static int __init thermal_register_governors(void)
{
int result;
result = thermal_gov_step_wise_register();
if (result)
return result;
result = thermal_gov_fair_share_register();
if (result)
return result;
result = thermal_gov_bang_bang_register();
if (result)
return result;
result = thermal_gov_user_space_register();
if (result)
return result;
return thermal_gov_power_allocator_register();
}
可以看到,5 种 governor 依次注册,由于 RK 平台默认使用 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,
};
int thermal_gov_power_allocator_register(void)
{
return thermal_register_governor(&thermal_gov_power_allocator);
}
再次调用 thermal_register_governor() 这个通用接口,之所以说是通用,是因为另外 4 个 governor 也都是用的这个函数,只不过各自的传参不一样。传参过来的结构体就是上节介绍的 struct thermal_governor,提供了三个回调,回调后面再来分析。跟着代码流程先分析 thermal_register_governor():
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;
/*
* 在全局链表thermal_governor_list里查找现在要注册的
* thermal_governor是否已经存在了,通过name来判断存在与否
*/
if (__find_governor(governor->name) == NULL) {
err = 0;
/* 如果没有找到,就把当前的thermal_governor加入该全局链表 */
list_add(&governor->governor_list, &thermal_governor_list);
/*
* 全局变量def_governor的处理
* 如果def_governor指针为空,并且当前的thermal_governor
* 的名字和DEFAULT_THERMAL_GOVERNOR一致,那么就赋值给
* def_governor
*/
if (!def_governor && !strncmp(governor->name,
DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
def_governor = governor;
}
/* 也是静态初始化的 */
mutex_lock(&thermal_list_lock);
/* 此时链表thermal_tz_list下其实并没有成员 */
list_for_each_entry(pos, &thermal_tz_list, node) {
/*
* only thermal zones with specified tz->tzp->governor_name
* may run with tz->governor unset
*/
if (pos->governor)
continue;
name = pos->tzp->governor_name;
if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
int ret;
/*
* name匹配,thermal_zone_device和当前governor绑定
* 这个函数暂时就不进去了,后面会再次碰到,到时再详细分析
*/
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 是非空指针。然后使用了 thermal_governor_lock 互斥锁,互斥锁也是内核同步机制之一,不做展开介绍。有两点说明:
1、这是一个已经静态初始化的全局变量,所以这里可以直接使用 mutex_lock 对临界区上锁,初始化代码如下(thermal_core.c):
static DEFINE_MUTEX(thermal_governor_lock);
2、见名知意,thermal_governor_lock 主要用于保证全局链表 thermal_governor_list 的增删操作有序进行,具体到这里的代码上下文,就是 list_add()。
__find_governor() 上方的注释说明了这个函数的作用。让我们看看源码:
static struct thermal_governor *__find_governor(const char *name)
{
struct thermal_governor *pos;
/* name不能为空,否则返回默认的governor */
if (!name || !name[0])
return def_governor;
/* 遍历全局链表,通过name来找到目标governor */
list_for_each_entry(pos, &thermal_governor_list, governor_list)
if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
return pos;
/* 没有找到就返回空指针 */
return NULL;
}
可以看到 5 个 governor 都会进入 if 代码块里面,里面做了两件事情,注释已经说明的很清楚了,不再赘述。默认的 governor 可以进入 menuconfig 里进行选择,RK 平台选择的是 power allocator。DEFAULT_THERMAL_GOVERNOR 的宏定义如下:
/* 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
接着往下分析,thermal_list_lock,又是互斥锁,访问全局链表 thermal_tz_list 时都需要上锁,并要约定好。thermal_tz_list 链表串起来的是 struct thermal_zone_device 结构体,关于 thermal_zone_device 后面会专文来分析。事实上,代码跑到这里时链表 thermal_tz_list 元素为空,所以不会跑到里面。里面调用的 thermal_set_governor() 函数在 thermal_zone_device 代码分析时会再次碰到,届时再具体分析。最后,关于两个互斥锁的情况需要留意,因为 AB-BA 死锁问题还是很常见的,所以自己在编写驱动程序时要谨慎处理这种情况。
至此,governor 的注册流程分析完了,但是 governor 的角色还没有开始发挥作用,后面会再次登场,让我们拭目以待。
本文标签: 源码机制LinuxThermalgovernor
版权声明:本文标题:Linux Thermal机制源码分析之Governor 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1729813912a1213670.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论