温控驱动(二)msm_thermal(2)

上一篇博客我们分析到用户空间在操作/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, &param);
	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,
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值