cpufreq: update_limits 实现和引用


void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
{
	int ret;

	if (ignore_ppc || !pr->performance) {
		/*
		 * Only when it is notification event, the _OST object
		 * will be evaluated. Otherwise it is skipped.
		 */
		if (event_flag)
			acpi_processor_ppc_ost(pr->handle, 1);
		return;
	}

	ret = acpi_processor_get_platform_limit(pr);
	/*
	 * Only when it is notification event, the _OST object
	 * will be evaluated. Otherwise it is skipped.
	 */
	if (event_flag) {
		if (ret < 0)
			acpi_processor_ppc_ost(pr->handle, 1);
		else
			acpi_processor_ppc_ost(pr->handle, 0);
	}
	if (ret >= 0)
		cpufreq_update_limits(pr->id);//调用下面的update_limits入口,
}
/**
 * cpufreq_update_limits - Update policy limits for a given CPU.
 * @cpu: CPU to update the policy limits for.
 *
 * Invoke the driver's ->update_limits callback if present or call
 * cpufreq_update_policy() for @cpu.
 */
void cpufreq_update_limits(unsigned int cpu)
{
	if (cpufreq_driver->update_limits)
		cpufreq_driver->update_limits(cpu);
	else
		cpufreq_update_policy(cpu);
}
EXPORT_SYMBOL_GPL(cpufreq_update_limits);
static struct cpufreq_driver intel_pstate = {
	.flags		= CPUFREQ_CONST_LOOPS,
	.verify		= intel_pstate_verify_policy,
	.setpolicy	= intel_pstate_set_policy,
	.suspend	= intel_pstate_suspend,
	.resume		= intel_pstate_resume,
	.init		= intel_pstate_cpu_init,
	.exit		= intel_pstate_cpu_exit,
	.offline	= intel_pstate_cpu_offline,
	.online		= intel_pstate_cpu_online,
	.update_limits	= intel_pstate_update_limits,//频率更新的入口函数
	.name		= "intel_pstate",
};

来看下具体的调用流程, 说明比较少. 看函数call的过程

static void intel_pstate_update_limits(unsigned int cpu)
{
	mutex_lock(&intel_pstate_driver_lock);

	update_turbo_state();
	/*
	 * If turbo has been turned on or off globally, policy limits for
	 * all CPUs need to be updated to reflect that.
	 */
	if (global.turbo_disabled_mf != global.turbo_disabled) {
		global.turbo_disabled_mf = global.turbo_disabled;
		arch_set_max_freq_ratio(global.turbo_disabled);
		for_each_possible_cpu(cpu)
			intel_pstate_update_max_freq(cpu);
	} else {
		cpufreq_update_policy(cpu);
	}

	mutex_unlock(&intel_pstate_driver_lock);
}
static void intel_pstate_update_max_freq(unsigned int cpu)
{
	struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
	struct cpudata *cpudata;

	if (!policy)
		return;

	cpudata = all_cpu_data[cpu];
	policy->cpuinfo.max_freq = global.turbo_disabled_mf ?
			cpudata->pstate.max_freq : cpudata->pstate.turbo_freq;

	refresh_frequency_limits(policy);//会调用set policy 来更新策略

	cpufreq_cpu_release(policy);
}
void refresh_frequency_limits(struct cpufreq_policy *policy)
{
	if (!policy_is_inactive(policy)) {
		pr_debug("updating policy for CPU %u\n", policy->cpu);

		cpufreq_set_policy(policy, policy->governor, policy->policy);
	}
}
EXPORT_SYMBOL(refresh_frequency_limits);

会调用到cpufreq_set_policy() 来更新调频策略

-> ret = cpufreq_driver->verify(&new_data);
-> return cpufreq_driver->setpolicy(policy);

所以来看 refresh_frequency_limits 这个函数的主要内容就是重新来verify 频点和设置新的policy 状态.

static int cpufreq_set_policy(struct cpufreq_policy *policy,
			      struct cpufreq_governor *new_gov,
			      unsigned int new_pol)
{
	struct cpufreq_policy_data new_data;
	struct cpufreq_governor *old_gov;
	int ret;
pr_err("+++++++ cpufreq_set_policy 1 ++++++++\n");
	memcpy(&new_data.cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
	new_data.freq_table = policy->freq_table;
	new_data.cpu = policy->cpu;
	/*
	 * PM QoS framework collects all the requests from users and provide us
	 * the final aggregated value here.
	 */
	new_data.min = freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
	new_data.max = freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);

	pr_err("setting new policy for CPU %u: %u - %u kHz\n",
		 new_data.cpu, new_data.min, new_data.max);

	/*
	 * Verify that the CPU speed can be set within these limits and make sure
	 * that min <= max.
	 */
	pr_err("+++++++ cpufreq_set_policy 2 ++++++++\n");
	ret = cpufreq_driver->verify(&new_data);
	if (ret)
		return ret;
pr_err("+++++++ cpufreq_set_policy 3 ++++++++\n");
	/*
	 * Resolve policy min/max to available frequencies. It ensures
	 * no frequency resolution will neither overshoot the requested maximum
	 * nor undershoot the requested minimum.
	 */
	policy->min = new_data.min;
	policy->max = new_data.max;
	policy->min = __resolve_freq(policy, policy->min, CPUFREQ_RELATION_L);
	policy->max = __resolve_freq(policy, policy->max, CPUFREQ_RELATION_H);
	trace_cpu_frequency_limits(policy);
pr_err("+++++++ cpufreq_set_policy 4 ++++++++\n");
	policy->cached_target_freq = UINT_MAX;

	pr_debug("new min and max freqs are %u - %u kHz\n",
		 policy->min, policy->max);

	if (cpufreq_driver->setpolicy) {
		policy->policy = new_pol;
		pr_debug("setting range\n");
		pr_err("+++++++ cpufreq_set_policy 4.1 ++++++++\n");
		return cpufreq_driver->setpolicy(policy);
	}
pr_err("+++++++ cpufreq_set_policy 5 ++++++++\n");
	if (new_gov == policy->governor) {
		pr_debug("governor limits update\n");
		cpufreq_governor_limits(policy);
		return 0;
	}
pr_err("+++++++ cpufreq_set_policy 6 ++++++++\n");
	pr_err("governor switch\n");

	/* save old, working values */
	old_gov = policy->governor;
	/* end old governor */
	if (old_gov) {
		cpufreq_stop_governor(policy);
		cpufreq_exit_governor(policy);
	}

	/* start new governor */
	policy->governor = new_gov;
	ret = cpufreq_init_governor(policy);
	if (!ret) {
		ret = cpufreq_start_governor(policy);
		if (!ret) {
			pr_debug("governor change\n");
			sched_cpufreq_governor_change(policy, old_gov);
			return 0;
		}
		cpufreq_exit_governor(policy);
	}

	/* new governor failed, so re-start old one */
	pr_debug("starting governor %s failed\n", policy->governor->name);
	if (old_gov) {
		policy->governor = old_gov;
		if (cpufreq_init_governor(policy))
			policy->governor = NULL;
		else
			cpufreq_start_governor(policy);
	}

	return ret;
}