https://lkml.org/lkml/2017/7/6/268
431ead0 arm64: wire cpu-invariant accounting support up to the task scheduler
4e63ebe arm64: wire frequency-invariant accounting support up to the task scheduler
552c465 arm: wire cpu-invariant accounting support up to the task scheduler
3a1ed9c arm: wire frequency-invariant accounting support up to the task scheduler
8216f58 drivers base/arch_topology: allow inlining cpu-invariant accounting support
0e27c56 drivers base/arch_topology: provide frequency-invariant accounting support
400ec74 cpufreq: dt: invoke frequency-invariance setter function
518accf cpufreq: arm_big_little: invoke frequency-invariance setter function
e7d5459 cpufreq: provide default frequency-invariance setter function
5408211 drivers base/arch_topology: free cpumask cpus_to_visit
一. cpufreq notifier
cpufreq notifier有兩種,一種是變頻時的notifier;一種是cpufreq policy發生變化時的notifier。
drivers/cpufreq/cpufreq.c /*cpufreq暴露出註冊nb的接口函數*/
1913 /*********************************************************************
1914 * NOTIFIER LISTS INTERFACE *
1915 *********************************************************************/
1916
1917 /**
1918 * cpufreq_register_notifier - register a driver with cpufreq
1919 * @nb: notifier function to register
1920 * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
1921 *
1922 * Add a driver to one of two lists: either a list of drivers that
1923 * are notified about clock rate changes (once before and once after
1924 * the transition), or a list of drivers that are notified about
1925 * changes in cpufreq policy.
1926 *
1927 * This function may sleep, and has the same return conditions as
1928 * blocking_notifier_chain_register.
1929 */
1930 int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
1931 {
1932 int ret;
1933
1934 if (cpufreq_disabled())
1935 return -EINVAL;
1936
1937 switch (list) {
1938 case CPUFREQ_TRANSITION_NOTIFIER:
1939 mutex_lock(&cpufreq_fast_switch_lock);
1940
1941 if (cpufreq_fast_switch_count > 0) {
1942 mutex_unlock(&cpufreq_fast_switch_lock);
1943 return -EBUSY;
1944 }
1945 ret = srcu_notifier_chain_register(
1946 &cpufreq_transition_notifier_list, nb);
1947 if (!ret)
1948 cpufreq_fast_switch_count--;
1949
1950 mutex_unlock(&cpufreq_fast_switch_lock);
1951 break;
1952 case CPUFREQ_POLICY_NOTIFIER:
1953 ret = blocking_notifier_chain_register(
1954 &cpufreq_policy_notifier_list, nb);
1955 break;
1956 default:
1957 ret = -EINVAL;
1958 }
1959
1960 return ret;
1961 }
1962 EXPORT_SYMBOL(cpufreq_register_notifier);
- arch topology註冊nb到cpufreq的cpufreq_policy_notifier_list,在cpufreq policy發生變化時,通過調用註冊的nb中的callback函數init_cpu_capacity_notifier..init_cpu_capacity_callback,使用CIE(Capacity Invariant Engine)重新設置:
a. capacity_scale(最大cpu raw capacity值,單位=mips);
b. cpu的capacity(cpu_scale,scale到capacity_scale並歸一化到1024的值)。
註冊notifier:
core_initcall(register_cpufreq_notifier)->cpufreq_register_notifier(&init_cpu_capacity_notifier, CPUFREQ_POLICY_NOTIFIER)->blocking_notifier_chain_register(&cpufreq_policy_notifier_list, nb)調用notifier:
1. cpufreq_policy_free(struct cpufreq_policy *policy)->blocking_notifier_call_chain(&cpufreq_policy_notifier_list,CPUFREQ_REMOVE_POLICY, policy);
2. cpufreq_online(unsigned int cpu)--new_policy?->blocking_notifier_call_chain(&cpufreq_policy_notifier_list,CPUFREQ_CREATE_POLICY, policy)drivers/base/arch_topology.c (CIE) 231 static struct notifier_block init_cpu_capacity_notifier = { 232 .notifier_call = init_cpu_capacity_callback, 233 }; 194 static int 195 init_cpu_capacity_callback(struct notifier_block *nb, 196 unsigned long val, 197 void *data) 198 { 199 struct cpufreq_policy *policy = data; 200 int cpu; 201 202 if (!raw_capacity) 203 return 0; 204 205 if (val != CPUFREQ_CREATE_POLICY) 206 return 0; 207 208 pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n", 209 cpumask_pr_args(policy->related_cpus), 210 cpumask_pr_args(cpus_to_visit)); 211 212 cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus); /*related_cpus是online+offline cpus*/ 213 214 for_each_cpu(cpu, policy->related_cpus) { 215 raw_capacity[cpu] = topology_get_cpu_scale(cpu) * 216 policy->cpuinfo.max_freq / 1000UL; /*raw_capacity[cpu0~2]=544(capacity-dmips-mhz)*1820000/1000=990080(mips)*/ /*raw_capacity[cpu3]=1024(capacity-dmips-mhz)*2028000/1000=2076672(mips)*/ 217 capacity_scale = max(raw_capacity[cpu], capacity_scale); /*capacity_scale =2076672*/ 218 } 219 220 if (cpumask_empty(cpus_to_visit)) { 221 topology_normalize_cpu_scale(); 222 schedule_work(&update_topology_flags_work); 223 free_raw_capacity(); 224 pr_debug("cpu_capacity: parsing done\n"); 225 schedule_work(&parsing_done_work); 226 } 227 228 return 0; 229 }
drivers/base/arch_topology.c (CIE) 131 void topology_normalize_cpu_scale(void) 132 { 133 u64 capacity; 134 int cpu; 135 136 if (!raw_capacity) 137 return; 138 139 pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale); 140 for_each_possible_cpu(cpu) { 141 pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n", 142 cpu, raw_capacity[cpu]); 143 capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT) 144 / capacity_scale; /*把cpu_scale capacity歸一化到1024*/ 145 topology_set_cpu_scale(cpu, capacity); /*cpu_scale[0~2]=488; cpu_scale[3]=1024*/ 146 pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n", 147 cpu, topology_get_cpu_scale(cpu)); 148 } 149 } 150
- cpufreq_transition_notifier註冊到cpufreq_transition_notifier_list鏈表,在cpufreq 調頻時,通過調用註冊在cpufreq_transition_notifier_list上的callback函數,使用FIE(Frequency Invariant Engine)。
註冊notifier:__init register_cpufreq_notifier(void)->cpufreq_register_notifier(&cpufreq_notifier, CPUFREQ_TRANSITION_NOTIFIER)