admin管理员组

文章数量:1536136

本文部分内容参考

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

Linux Thermal机制源码分析之Governor_不捡风筝的玖伍贰柒的博客-CSDN博客

特此致谢!

三、相关源码及分析

Thermal Core主要是初始化driver,注册governor,解析dtsi文件,创建thermal zone和初始通信。相关的文件包括thermal_core.c(完整路径为drivers/thermal/thermal_core.c)、thermal_core.h(完整路径为drivers/thermal/thermal_core.h)和thermal.h(完整路径为include/linux/thermal.h)。

在此先列出前文中代码结构属于Thermal Core的部分,然后针对于各个部分进行源码分析。

注:本文所引用源码对应的内核版本为5.18.8。

1. thermal_init函数

thermal_init函数是Thermal驱动的入口函数,因此分析当然是从这里开始。thermal_init函数位于drivers/thermal/thermal_core.c中,源码如下:

static int __init thermal_init(void)
{
	int result;

	result = thermal_netlink_init();
	if (result)
		goto error;

	result = thermal_register_governors();
	if (result)
		goto error;

	result = class_register(&thermal_class);
	if (result)
		goto unregister_governors;

	result = of_parse_thermal_zones();
	if (result)
		goto unregister_class;

	result = register_pm_notifier(&thermal_pm_nb);
	if (result)
		pr_warn("Thermal: Can not register suspend notifier, return %d\n",
			result);

	return 0;

unregister_class:
	class_unregister(&thermal_class);
unregister_governors:
	thermal_unregister_governors();
error:
	ida_destroy(&thermal_tz_ida);
	ida_destroy(&thermal_cdev_ida);
	mutex_destroy(&thermal_list_lock);
	mutex_destroy(&thermal_governor_lock);
	return result;
}
postcore_initcall(thermal_init);

Thermal模块加载进内核用的是postcore_initcall(),tsadc驱动一般用的是 module_init(),前者会早于后者加载,这点比较重要,有些代码流程上会依赖这种先后关系,需要留意。thermal_init函数里比较简单,先后调用了5个函数:

(1)thermal_netlink_init:进行generic netlink的初始化。应该是用于用户空间和内核空间通信的(从后边的分析可以看出,对netlink机制不了解并不影响分析Thermal框架)。

(2)thermal_register_governors:注册 governor。

(3)class_register:注册 thermal class,这个函数不会跟进去分析。class 框架性代码,大概知道是干什么用的即可,详细分析对当前所要学习的Thermal框架没有太大帮助。

(4)of_parse_thermal_zones:解析dts里面的 thermal_zones。此为重中之重!

(5)register_pm_notifier:notifier机制,表明Thermal core对pm事件感兴趣。

下边对这5个函数逐个贴出代码并进行初步解析。

(1)thermal_netlink_init

thermal_netlink_init函数进行generic netlink的初始化。其代码位于drivers/thermal/thermal_netlink.c中,如下所示:

static const struct genl_small_ops thermal_genl_ops[] = {
	{
		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.dumpit = thermal_genl_cmd_dumpit,
	},
	{
		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = thermal_genl_cmd_doit,
	},
	{
		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = thermal_genl_cmd_doit,
	},
	{
		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = thermal_genl_cmd_doit,
	},
	{
		.cmd = THERMAL_GENL_CMD_CDEV_GET,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.dumpit = thermal_genl_cmd_dumpit,
	},
};

static struct genl_family thermal_gnl_family __ro_after_init = {
	.hdrsize	= 0,
	.name		= THERMAL_GENL_FAMILY_NAME,
	.version	= THERMAL_GENL_VERSION,
	.maxattr	= THERMAL_GENL_ATTR_MAX,
	.policy		= thermal_genl_policy,
	.small_ops	= thermal_genl_ops,
	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
	.mcgrps		= thermal_genl_mcgrps,
	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
};

int __init thermal_netlink_init(void)
{
	return genl_register_family(&thermal_gnl_family);
}

(2)thermal_register_governors

thermal_register_governors函数完成了Thermal Governor的注册。其在同文件(drivers/thermal/thermal_core.c)中,源码如下:

static int __init thermal_register_governors(void)
{
	int ret = 0;
	struct thermal_governor **governor;

	for_each_governor_table(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);
	}

	if (ret) {
		struct thermal_governor **gov;

		for_each_governor_table(gov) {
			if (gov == governor)
				break;
			thermal_unregister_governor(*gov);
		}
	}

	return ret;
}

(3)class_register

class_register函数属于内核接口层,其行为比较简单,仅仅是在/sys/class下面创建一个自己命名的目录,主要是提供给设备注册挂入链接,而实际挂链接的动作则在device_register中完成。

具体到本代码,class_register(&thermal_class)这一句代码实际的作用是创建了/sys/class/thermal结点。

thermal_class的定义及初始化在同文件(drivers/thermal/thermal_core.c)中,代码如下:

static struct class thermal_class = {
	.name = "thermal",
	.dev_release = thermal_release,
};

(4)of_parse_thermal_zones

of_parse_thermal_zones函数解析dtsi配置文件中的thermal-zones字段,并且注册thermal_zone_devices。其在同文件(drivers/thermal/thermal_of.c)中,源码如下:

/**
 * of_parse_thermal_zones - parse device tree thermal data
 *
 * Initialization function that can be called by machine initialization
 * code to parse thermal data and populate the thermal framework
 * with hardware thermal zones info. This function only parses thermal zones.
 * Cooling devices and sensor devices nodes are supposed to be parsed
 * by their respective drivers.
 *
 * Return: 0 on success, proper error code otherwise
 *
 */
int __init of_parse_thermal_zones(void)
{
	struct device_node *np, *child;
	struct __thermal_zone *tz;
	struct thermal_zone_device_ops *ops;

	np = of_find_node_by_name(NULL, "thermal-zones");
	if (!np) {
		pr_debug("unable to find thermal zones\n");
		return 0; /* Run successfully on systems without thermal DT */
	}

	for_each_available_child_of_node(np, child) {
		struct thermal_zone_device *zone;
		struct thermal_zone_params *tzp;
		int i, mask = 0;
		u32 prop;

		tz = thermal_of_build_thermal_zone(child);
		if (IS_ERR(tz)) {
			pr_err("failed to build thermal zone %pOFn: %ld\n",
			       child,
			       PTR_ERR(tz));
			continue;
		}

		ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
		if (!ops)
			goto exit_free;

		tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
		if (!tzp) {
			kfree(ops);
			goto exit_free;
		}

		/* No hwmon because there might be hwmon drivers registering */
		tzp->no_hwmon = true;

		if (!of_property_read_u32(child, "sustainable-power", &prop))
			tzp->sustainable_power = prop;

		for (i = 0; i < tz->ntrips; i++)
			mask |= 1 << i;

		/* these two are left for temperature drivers to use */
		tzp->slope = tz->slope;
		tzp->offset = tz->offset;

		zone = thermal_zone_device_register(child->name, tz->ntrips,
						    mask, tz,
						    ops, tzp,
						    tz->passive_delay,
						    tz->polling_delay);
		if (IS_ERR(zone)) {
			pr_err("Failed to build %pOFn zone %ld\n", child,
			       PTR_ERR(zone));
			kfree(tzp);
			kfree(ops);
			of_thermal_free_zone(tz);
			/* attempting to build remaining zones still */
		}
	}
	of_node_put(np);

	return 0;

exit_free:
	of_node_put(child);
	of_node_put(np);
	of_thermal_free_zone(tz);

	/* no memory available, so free what we have built */
	of_thermal_destroy_zones();

	return -ENOMEM;
}

(5)register_pm_notifier

register_pm_notifier(&thermal_pm_nb)注册了Thermal的notifier。register_pm_notifier函数在drivers/main/power.c中实现,源码如下:

int register_pm_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);

thermal_pm_nb在同文件(drivers/thermal/thermal_core.c)中定义和初始化(就在thermal_init的上边),代码如下:

static struct notifier_block thermal_pm_nb = {
	.notifier_call = thermal_pm_notify,
};

thermal_pm_notify函数在thermal_pm_nb的上边,代码如下:

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:
		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) {
			if (!thermal_zone_device_is_enabled(tz))
				continue;

			thermal_zone_device_init(tz);
			thermal_zone_device_update(tz,
						   THERMAL_EVENT_UNSPECIFIED);
		}
		break;
	default:
		break;
	}
	return 0;
}

本文标签: 内核详解框架Linuxcore