一,代码分析
定义 CONFIG_CPU_FREQ_GOV_SCHEDPLUS=y
1. @LINUX/android/kernel-4.9/drivers/misc/mediatek/sched/Kconfig
config CPU_FREQ_DEFAULT_GOV_SCHEDPLUS bool "To make sched+ as default governor" select CPU_FREQ_GOV_SCHEDPLUS help Use the CPUfreq governor 'schedplus' as default. This scales cpu frequency using CPU utilization estimates from the scheduler not only but also support some of platform dependent features.
config CPU_FREQ_GOV_SCHEDPLUS bool "'sched+' cpufreq governor" depends on CPU_FREQ select CPU_FREQ_GOV_COMMON help 'sched+' - this governor scales cpu frequency from the scheduler as a function of cpu capacity utilization. It does not evaluate utilization on a periodic basis (as ondemand does) but instead is event-driven by the scheduler and support below additional features. 1) platform dependent(micro controller). 2) sched-assistant (a assistnat for major governor). 3) hotplug (cpu on/off). If in doubt, say N.
2. @LINUX/android/device/mediatek/mt6761/init.mt6761.rc
on property:sys.boot_completed=1
# switch to sched-dvfs write /sys/devices/system/cpu/cpufreq/policy0/scaling_governor "schedplus" write /sys/devices/system/cpu/cpufreq/policy4/scaling_governor "schedplus"
3. @Cpufreq_schedplus.c (kernel\sched)
/* next throttling period expiry if increasing OPP */
#define THROTTLE_DOWN_NSEC 4000000 /* 4ms default */ 降频的最小时间间隔
/* next throttling period expiry if decreasing OPP */
#define THROTTLE_UP_NSEC 0 /* 0us */ 及时提频无最小时间间隔限制
#define THROTTLE_NSEC 2000000 /* 2ms default */
late_initcall(cpufreq_sched_init);
static int __init cpufreq_sched_init(void)
{
for (i = 0; i < MAX_CLUSTER_NR; i++) {
g_gd[i] = kzalloc(sizeof(struct gov_data), GFP_KERNEL);
if (!g_gd[i]) {
WARN_ON(1);
return -ENOMEM;
}
g_gd[i]->up_throttle_nsec = THROTTLE_UP_NSEC; //0
g_gd[i]->down_throttle_nsec = THROTTLE_DOWN_NSEC; //4ms, 被eas重新设为0
g_gd[i]->up_throttle_nsec_bk = THROTTLE_UP_NSEC;
g_gd[i]->down_throttle_nsec_bk = THROTTLE_DOWN_NSEC;
g_gd[i]->throttle_nsec = THROTTLE_NSEC; //2ms
g_gd[i]->last_freq_update_time = 0;
/* keep cid needed */
g_gd[i]->cid = i;
}
cpu_hotplug.notifier_call = cpu_hotplug_handler;
register_hotcpu_notifier(&cpu_hotplug);
cpu_pm_register_notifier(&cpu_pm_notifier_block);
return cpufreq_register_governor(&cpufreq_gov_sched);//注册cpu governor
}
down_throttle_nsec 被EAS设置为0, 为了能快速降频而省电
@kernel-4.9/drivers/misc/mediatek/performance/boost_ctrl/eas_ctrl/eas_ctrl.c
update_schedplus_down_throttle_ns(int kicker, int nsec)会调用
void schedplus_set_down_throttle_nsec(unsigned long val) //val = 0
{
for (cid = 0; cid < MAX_CLUSTER_NR; cid++) {
gd = g_gd[cid];
if (!gd)
return;
gd->down_throttle_nsec = val;
gd->down_throttle_nsec_bk = val;
}
}
如何触发CPU调度?
scheduler_tick@Core.c (kernel\sched) //每tick调用一次, CONFIG_HZ=500表示每2ms调用一次
sched_freq_tick
sched_freq_tick_pelt
set_cfs_cpu_capacity@Sched.h (kernel\sched)
set_rt_cpu_capacity
set_dl_cpu_capacity
update_cpu_capacity_request@Cpufreq_schedplus.c (kernel\sched)
update_fdomain_capacity_request
static void update_fdomain_capacity_request(int cpu, int type)
{
policy = cpufreq_cpu_get(cpu);
cpu_max_freq = policy->cpuinfo.max_freq;
arch_get_cluster_cpus(&cls_cpus, cid);
/* find max capacity requested by cpus in this policy */
for_each_cpu(cpu_tmp, &cls_cpus) {
/* In schedplus, util is scaling by capacity_orig_of*/
uclamp_min = (uclamp_min << SCHED_CAPACITY_SHIFT) /
capacity_orig_of(cpu_tmp);
/* convert IO boosted freq to capacity */
boosted_util = (sg_cpu->iowait_boost << SCHED_CAPACITY_SHIFT) /
cpu_max_freq;
scr = &per_cpu(cpu_sched_capacity_reqs, cpu_tmp);
/*
* If the CPU utilization was last updated before the previous
* frequency update and the time elapsed between the last update
* of the CPU utilization and the last frequency update is long
* enough, don't take the CPU into account as it probably is
* idle now (and clear iowait_boost for it).
*/
delta_ns = gd->last_freq_update_time - cpu_rq(cpu_tmp)->clock;
if (delta_ns > TICK_NSEC * 2) {/* 2tick */ 该CPU有可能已经IDLE
sg_cpu->iowait_boost = 0;
sg_cpu->idle = 1;
continue;
}
/* check if IO boosting */
if (boosted_util > scr->total)
capacity = max(capacity, boosted_util);
else
capacity = max(capacity, scr->total); //获取一个Cluster里CPU的最大负载
capacity = uclamp_min > capacity ? uclamp_min : capacity;
}
/* get real world frequency */ 根据负载计算需要的新频率
freq_new = capacity * cpu_max_freq >> SCHED_CAPACITY_SHIFT;
/* to get frequency in real world */ 从CPU支持的频率中找打最匹配的一个
freq_new = mt_cpufreq_find_close_freq(cid, freq_new);
/* get throttling type */
throttle = freq_new < cur_freq ?
gd->down_throttle : gd->up_throttle;
gd->thro_type = freq_new < cur_freq ?
DVFS_THROTTLE_DOWN : DVFS_THROTTLE_UP;
/* No throttling in time? Bail and return. */
if (ktime_before(now, throttle))
goto out;
//重新设置CPU频率
cpufreq_sched_try_driver_target(cpu, policy, freq_new, type);
}
static void cpufreq_sched_try_driver_target(int target_cpu, struct cpufreq_policy *policy,
unsigned int freq, int type)
{
mt_cpufreq_set_by_wfi_load_cluster(cid, freq);
/ * update throttle time:
* avoid inteference betwewn increasing/decreasing OPP. */
gd->up_throttle = ktime_add_ns(cur_time, gd->up_throttle_nsec);
gd->down_throttle = ktime_add_ns(cur_time, gd->down_throttle_nsec);
gd->throttle = ktime_add_ns(cur_time, gd->throttle_nsec);
}
二,DEBUG
cat /sys/devices/system/cpu/cpufreq/policy0/sched/down_throttle_nsec
0
cat /sys/devices/system/cpu/cpufreq/policy0/sched/up_throttle_nsec
0
#cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
schedplus ondemand userspace powersave interactive performance
三,小结
1. MT6761的CPU频率可以快速调整,不需要创建线程做慢速调整
2. 可以调高down_throttle_nsec = 10ms, 减少CPU降频的频度,由此提高性能