上一篇博客我们分析到用户空间在操作/sys/module/msm_thermal/parameters/enabled节点的时候,会去执行msm_thermal的set_enabled函数,然后又会执行interrupt_mode_init函数,我们继续分析。
一、interrupt_mode_init函数
这个函数执行之后用户空间的thermal-engine就接手了KTM cpu调频等功能。
static void interrupt_mode_init(void)
{
if (!msm_thermal_probed)
return;
if (polling_enabled) {
polling_enabled = 0;
create_sensor_zone_id_map();
disable_msm_thermal();
hotplug_init();
freq_mitigation_init();
thermal_monitor_init();
msm_thermal_add_cx_nodes();
msm_thermal_add_gfx_nodes();
}
}
1.1 disable_msm_thermal
我们来看下disable_msm_thermal函数,先是取消了check_temp_work这样KTM就不会执行check_temp函数来进行cpu调频等动作了。然后就是把每个cpu的频率值恢复最大。
static void __ref disable_msm_thermal(void)
{
uint32_t cpu = 0;
/* make sure check_temp is no longer running */
cancel_delayed_work_sync(&check_temp_work);
get_online_cpus();
for_each_possible_cpu(cpu) {
if (cpus[cpu].limited_max_freq == UINT_MAX &&
cpus[cpu].limited_min_freq == 0)
continue;
pr_info("Max frequency reset for CPU%d\n", cpu);
cpus[cpu].limited_max_freq = UINT_MAX;
cpus[cpu].vdd_max_freq = UINT_MAX;
cpus[cpu].limited_min_freq = 0;
if (!SYNC_CORE(cpu))
update_cpu_freq(cpu);
}
update_cluster_freq();
put_online_cpus();
}
1.2 do_hotplug函数
我们再来看下hotplug_init函数,这个函数主要是启动了一个thread执行do_hotplug函数。有两种情况需要hot_plug,一种是用户进程的thermal-engine 需要hotplug;还有一种是thermal驱动中设置的hotplug的temp(dts中的hotplug-temp )放在HOTPLUG_THRESHOLD_HIGH、HOTPLUG_THRESHOLD_LOW
static void hotplug_init(void)
{
uint32_t cpu = 0;
struct sensor_threshold *hi_thresh = NULL, *low_thresh = NULL;
if (hotplug_task)
return;
if (!hotplug_enabled)
goto init_kthread;
for_each_possible_cpu(cpu) {
cpus[cpu].sensor_id =
sensor_get_id((char *)cpus[cpu].sensor_type);
cpus[cpu].id_type = THERM_ZONE_ID;
if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu)))
continue;
hi_thresh = &cpus[cpu].threshold[HOTPLUG_THRESHOLD_HIGH];
low_thresh = &cpus[cpu].threshold[HOTPLUG_THRESHOLD_LOW];
hi_thresh->temp = (msm_thermal_info.hotplug_temp_degC)
* tsens_scaling_factor;
hi_thresh->trip = THERMAL_TRIP_CONFIGURABLE_HI;
low_thresh->temp = (msm_thermal_info.hotplug_temp_degC -
msm_thermal_info.hotplug_temp_hysteresis_degC)
* tsens_scaling_factor;
low_thresh->trip = THERMAL_TRIP_CONFIGURABLE_LOW;
hi_thresh->notify = low_thresh->notify = hotplug_notify;
hi_thresh->data = low_thresh->data = (void *)&cpus[cpu];
sensor_mgr_set_threshold(cpus[cpu].sensor_id, hi_thresh);
}
init_kthread:
init_completion(&hotplug_notify_complete);
hotplug_task = kthread_run(do_hotplug, NULL, "msm_thermal:hotplug");
if (IS_ERR(hotplug_task)) {
pr_err("Failed to create do_hotplug thread. err:%ld\n",
PTR_ERR(hotplug_task));
return;
}
/*
* Adjust cpus offlined bit when hotplug intitializes so that the new
* cpus offlined state is based on hotplug threshold range
*/
if (hotplug_init_cpu_offlined())
kthread_stop(hotplug_task);
}
我们再来看do_hotplug函数,这里区分了offline和user_offline主要是驱动自己检测的offline和thermal-engine发起的offline。
static __ref int do_hotplug(void *data)
{
int ret = 0;
uint32_t cpu = 0, mask = 0;
struct device_clnt_data *clnt = NULL;
struct sched_param param = {.sched_priority = MAX_RT_PRIO-2};
if (!core_control_enabled) {
pr_debug("Core control disabled\n");
return -EINVAL;
}
sched_setscheduler(current, SCHED_FIFO, ¶m);
while (!kthread_should_stop()) {
while (wait_for_completion_interruptible(
&hotplug_notify_complete) != 0)
;
reinit_completion(&hotplug_notify_complete);
/*
* Suspend framework will have disabled the
* hotplug functionality. So wait till the suspend exits
* and then re-evaluate.
*/
if (in_suspend)
continue;
mask = 0;
mutex_lock(&core_control_mutex);
for_each_possible_cpu(cpu) {
if (hotplug_enabled &&
cpus[cpu].hotplug_thresh_clear) {
ret =
sensor_mgr_set_threshold(cpus[cpu].sensor_id,
&cpus[cpu].threshold[HOTPLUG_THRESHOLD_HIGH]);
if (cpus[cpu].offline
&& !IS_LOW_THRESHOLD_SET(ret))
cpus[cpu].offline = 0;
cpus[cpu].hotplug_thresh_clear = false;
}
if (cpus[cpu].offline || cpus[cpu].user_offline)//user_offline代表用户进程的offline
mask |= BIT(cpu);
}
if (devices && devices->hotplug_dev) {
mutex_lock(&devices->hotplug_dev->clnt_lock);
for_each_cpu_mask(cpu,
devices->hotplug_dev->active_req.offline_mask)
mask |= BIT(cpu);
mutex_unlock(&devices->hotplug_dev->clnt_lock);
}
if (mask != cpus_offlined)
update_offline_cores(mask);
mutex_unlock(&core_control_mutex);
if (devices && devices->hotplug_dev) {
union device_request req;
req.offline_mask = CPU_MASK_NONE;
mutex_lock(&devices->hotplug_dev->clnt_lock);
for_each_cpu_mask(cpu,
devices->hotplug_dev->active_req.offline_mask)
if (mask & BIT(cpu))
cpumask_test_and_set_cpu(cpu,
&req.offline_mask);
list_for_each_entry(clnt,
&devices->hotplug_dev->client_list,
clnt_ptr) {
if (clnt->callback)
clnt->callback(clnt, &req,
clnt->usr_data);
}
mutex_unlock(&devices->hotplug_dev->clnt_lock);
}
sysfs_notify(cc_kobj, NULL, "cpus_offlined");
}
return ret;
}
我们再来看看thermal-engine是如何发起hotplug的,是通过节点/sys/module/msm_thermal/core_control/cpus_offlined来发起的。这个我们稍后再分析。
继续分析interrupt_mode_init函数freq_mitigation_init函数,这个函数我们在上一篇博客分析过了,他的调频数据只来自thermal-engine,thermal驱动的调频之前我们分析过了,在check_temp的do_freq_control函数,而thermal-engine起来之后,check_temp就不再执行了,调频的数据就有thermal-engine通过对/dev/msm_thermal_query的ioctl方式来发起。
二、msm_thermal_late_init函数
msm_thermal_late_init函数主要是创建/sys/module/msm_thermal下面的节点。当然/sys/module/msm_thermal/parameters和sensor_info节点我们前面分析过了,下面我们主要分析下core_control节点,这个只有cpu的数量大于1才会去创建该节点。
int __init msm_thermal_late_init(void)
{
if (!msm_thermal_probed)
return 0;
probe_therm_ddr_lm(msm_thermal_info.pdev);
if (num_possible_cpus() > 1)
msm_thermal_add_cc_nodes();//只有cpu数量大于1,才去创建cc的节点
msm_thermal_add_psm_nodes();
msm_thermal_add_vdd_rstr_nodes();
msm_thermal_add_sensor_info_nodes();
if (ocr_reg_init_defer) {
if (!ocr_reg_init(msm_thermal_info.pdev)) {
ocr_enabled = true;
msm_thermal_add_ocr_nodes();
}
}
msm_thermal_add_mx_nodes();
create_cpu_topology_sysfs();
create_thermal_debugfs();
msm_thermal_add_bucket_info_nodes();
uio_init(msm_thermal_info.pdev);
return 0;
}
late_initcall(msm_thermal_late_init);
2.1 cc的enabled节点
我们来分析下msm_thermal_add_cc_nodes函数,show_cc_enabled就是来看下core_control_enabled变量。
static __refdata struct kobj_attribute cc_enabled_attr =
__ATTR(enabled, 0644, show_cc_enabled, store_cc_enabled);
static __refdata struct kobj_attribute cpus_offlined_attr =
__ATTR(cpus_offlined, 0644, show_cpus_offlined, store_cpus_offlined);
static __refdata struct attribute *cc_attrs[] = {
&cc_enabled_attr.attr,
&cpus_offlined_attr.attr,
NULL,
};
static __refdata struct attribute_group cc_attr_group = {
.attrs = cc_attrs,
};
static __init int msm_thermal_add_cc_nodes(void)
{
struct kobject *module_kobj = NULL;
int ret = 0;
module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
if (!module_kobj) {
pr_err("cannot find kobject\n");
ret = -ENOENT;
goto done_cc_nodes;
}
cc_kobj = kobject_create_and_add("core_control", module_kobj);
if (!cc_kobj) {
pr_err("cannot create core control kobj\n");
ret = -ENOMEM;
goto done_cc_nodes;
}
ret = sysfs_create_group(cc_kobj, &cc_attr_group);
if (ret) {
pr_err("cannot create sysfs group. err:%d\n", ret);
goto done_cc_nodes;
}
return 0;
done_cc_nodes:
if (cc_kobj)
kobject_del(cc_kobj);
return ret;
}
我们来看下store_cc_enabled函数也就是操作/sys/module/msm_thermal/core_control下的enabled节点,当使能之后会去调用hotplug_init_cpu_offlined重新更新下cpu 是否该online的状况,主要就是看temp时候超过hotplug_temp_degC(dts的hotplug-temp)。
另外一点当core_control_enabled位false的时候,do_core_control和do_hotplug都暂停工作。
1. 当KTM工作的时候,dts中的core-limit-temp不起作用了,不会在check_temp中执行offline的检测了
2. 而当thermal-engine接管KTM后do_hotplug暂停工作,也就是dts中的hotplug-temp和thermal-engine设置cpu offline都不会生效
static ssize_t __ref store_cc_enabled(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
int val = 0;
uint32_t cpu = 0;
if (!mitigation) {
pr_err("Thermal Mitigations disabled.\n");
goto done_store_cc;
}
ret = kstrtoint(buf, 10, &val);
if (ret) {
pr_err("Invalid input %s. err:%d\n", buf, ret);
goto done_store_cc;
}
if (core_control_enabled == !!val)
goto done_store_cc;
core_control_enabled = !!val;
if (core_control_enabled) {
pr_info("Core control enabled\n");
cpus_previously_online_update();
register_cpu_notifier(&msm_thermal_cpu_notifier);
/*
* Re-evaluate thermal core condition, update current status
* and set threshold for all cpus.
*/
hotplug_init_cpu_offlined();
mutex_lock(&core_control_mutex);
update_offline_cores(cpus_offlined);
if (hotplug_enabled && hotplug_task) {
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.core_control_mask &
BIT(cpus[cpu].cpu)))
continue;
sensor_mgr_set_threshold(cpus[cpu].sensor_id,
&cpus[cpu].threshold[HOTPLUG_THRESHOLD_HIGH]);
}
}
mutex_unlock(&core_control_mutex);
} else {
pr_info("Core control disabled\n");
unregister_cpu_notifier(&msm_thermal_cpu_notifier);
}
done_store_cc:
return count;
}
2.2 cc的cpus_offlined节点
这小节我们来看下cpus_offlined节点,show就是看下cpus_offlined这个变量。这个代表现在是否有cpu已经offline了。
static ssize_t show_cpus_offlined(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", cpus_offlined);
}
而store_cpu_offlined就是thermal-engine操作/sys/module/msm_thermal/core_control/cpus_offlined节点,来请求某个cpu的hotplug。
static ssize_t __ref store_cpus_offlined(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
uint32_t val = 0;
uint32_t cpu;
if (!mitigation) {
pr_err("Thermal Mitigations disabled.\n");
goto done_cc;
}
mutex_lock(&core_control_mutex);
ret = kstrtouint(buf, 10, &val);
if (ret) {
pr_err("Invalid input %s. err:%d\n", buf, ret);
goto done_cc;
}
if (polling_enabled) {
pr_err("Ignoring request; polling thread is enabled.\n");
goto done_cc;
}
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.core_control_mask & BIT(cpu)))
continue;
cpus[cpu].user_offline = !!(val & BIT(cpu));
pr_debug("\"%s\"(PID:%i) requests %s CPU%d.\n", current->comm,
current->pid, (cpus[cpu].user_offline) ? "offline" :
"online", cpu);
}
if (hotplug_task)
complete(&hotplug_notify_complete);
else
pr_err("Hotplug task is not initialized\n");
done_cc:
mutex_unlock(&core_control_mutex);
return count;
}
static __refdata struct kobj_attribute cc_enabled_attr =
__ATTR(enabled, 0644, show_cc_enabled, store_cc_enabled);
static __refdata struct kobj_attribute cpus_offlined_attr =
__ATTR(cpus_offlined, 0644, show_cpus_offlined, store_cpus_offlined);
static __refdata struct attribute *cc_attrs[] = {
&cc_enabled_attr.attr,
&cpus_offlined_attr.attr,
NULL,
};