目录
group[idx].boost和group[idx].ts在什么时候更新?
kernel4.14
-
关键数据结构
-
CPU boost
更新cpu的boost和ts
调用 schedtune_cpu_update更新cpu的boost和ts
140 static void
141 schedtune_cpu_update(int cpu, u64 now)
142 {
143 struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu);
144 int boost_max;
145 u64 boost_ts;
146 int idx;
147
148 /* The root boost group is always active */
149 boost_max = bg->group[0].boost;
150 boost_ts = now;
151 for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
152 /*
153 * A boost group affects a CPU only if it has
154 * RUNNABLE tasks on that CPU or it has hold
155 * in effect from a previous task.
156 */
157 if (!schedtune_boost_group_active(idx, bg, now))
158 continue;
159
160 /* This boost group is active */
161 if (boost_max > bg->group[idx].boost)
162 continue;
163
164 boost_max = bg->group[idx].boost;
165 boost_ts = bg->group[idx].ts;
166 }
167 bg->boost_max = boost_max;
168 bg->boost_ts = boost_ts;
169 }
即更新cpu的boost_max和boost_ts。方法是找到cpu上当前active group中boost值最大的group,把这个group的boost和ts作为cpu的boost_max和boost_ts。
--什么是active的group?即group中有task,或者group中之前task的boost没超时。137行,因为bg->group[idx].ts只有在rt task入队时才会被更新,所以对于其它非rt task,基本都是超时的。可以理解如果group中没有task,group就是非active的。除非此刻前50ms内有rt task曾入队运行,更新过ts。也就是说rt task入队后,其所在group的boost会持续active 50ms,如果rt task在top-app中,top-app 的boost值最大,就会在50ms内影响cpu的boost util。
131 static inline bool
132 schedtune_boost_group_active(int idx, struct boost_groups* bg, u64 now)
133 {
134 if (bg->group[idx].tasks)
135 return true;
136
137 return !schedtune_boost_timeout(now, bg->group[idx].ts);
138 }
----什么是“group中之前task的boost没超时”?即task enqueue时boost开始生效,boost有效时间50ms,task在20ms时dequeue了,20ms后group中虽然没有task,但group的boost值仍然对cpu有效。
cpu的boost和ts在什么时候更新?
1. 调用函数cpufreq_update_util更新cpu util时,使用、检查更新 cpu的boost和ts。cfs/rt/deadline都会调用cpufreq_update_util函数。以cfs为例:
enqueue_task_fair->schedtune_enqueue_task->schedtune_tasks_update;
->enqueue_entity->...->cpufreq_update_util->sugov_update_single{share}->sugov_get_util->boosted_cpu_util->schedtune_cpu_margin->schedtune_cpu_boost->schedtune_cpu_update
当cpu的util发生变化,更新cpu的util时,会重新计算boost util,此时:
如果cpu当前的boost超时,(now - bg->boost_ts) > SCHEDTUNE_BOOST_HOLD_NS(50ms),则update cpu的boost和ts;
如果cpu当前boost没超时,返回bg->boost_max。
2. enqueue_task_fair{rt}->schedtune_enqueue_task->schedtune_tasks_update
247~249行,task入队被加入cpu上一个非active的group时,此时因为task的加入,active了一个group,同时也会对cpu的boost_max产生影响,需要更新。
task_count={ENQUEUE_TASK 1
DEQUEUE_TASK -1}
31 static inline void
232 schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count)
233 {
234 struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu);
235 int tasks = bg->group[idx].tasks + task_count;
236
237 /* Update boosted tasks count while avoiding to make it negative */
238 bg->group[idx].tasks = max(0, tasks);
239
240 /* Update timeout on enqueue */
241 if (task_count > 0) {
242 u64 now = sched_clock_cpu(cpu);
243
244 if (schedtune_update_timestamp(p))
245 bg->group[idx].ts = now;
246
247 /* Boost group activation or deactivation on that RQ */
248 if (bg->group[idx].tasks == 1)
249 schedtune_cpu_update(cpu, now);
250 }
251
252 trace_sched_tune_tasks_update(p, cpu, tasks, idx,
253 bg->group[idx].boost, bg->boost_max,
254 bg->group[idx].ts);
255 }
241,行仅处理 task入队时的情况;248行表示active一个group。那如果一个group中唯一的task dequeue了,group被deactive了,需要调用schedtune_cpu_update更新cpu的boost和ts吗?
答案是不需要。使用cpu boost的地方只有一处,即在计算cpu util的时候。而在return bg->boost_max前,已经做了schedtune_cpu_update。
而条件schedtune_boost_timeout一般都是true,除了计算util时,只有在active group的时候会更新bg->boost_ts,而bg->boost_ts取决于group[idx].boost最大的ts,这个ts只有rt入队时才更新,在计算util前已经调用schedtune_enqueue_task进行了更新。如果前50ms内有rt task,则此处直接返回bg->boost_max,是受益rt task;如果50ms内没有rt task,计算util时超时,调用了schedtune_cpu_update更新了deactive group后的cpu groups[idx].boost。
| | | \--> boost = schedtune_cpu_boost(cpu)
| | | | \-->if (schedtune_boost_timeout(now, bg->boost_ts)) /*更新bg->boost_max和bg->boost_ts*/
| | | | schedtune_cpu_update(cpu, now);
| | | | return bg->boost_max
| | | return schedtune_margin(util, boost) /*根据boost值计算margin*/
3. schedtune_boostgroup_release
boost_write->schedtune_boostgroup_update(int idx, int boost)->schedtune_cpu_update
通过sys节点修改group的boost值,或release 一个group时,更新cpu的boost。
schedtune_boostgroup_update为每个cpu的idx group(如top-app)更新boost值,并检查新的group[idx].boost是否影响cpu当前的boost_max,如果影响则更新。
171 static int
172 schedtune_boostgroup_update(int idx, int boost)
173 {
174 struct boost_groups *bg;
175 int cur_boost_max;
176 int old_boost;
177 int cpu;
178 u64 now;
179
180 /* Update per CPU boost groups */
181 for_each_possible_cpu(cpu) {
182 bg = &per_cpu(cpu_boost_groups, cpu);
183
184 /*
185 * Keep track of current boost values to compute the per CPU
186 * maximum only when it has been affected by the new value of
187 * the updated boost group
188 */
189 cur_boost_max = bg->boost_max;
190 old_boost = bg->group[idx].boost;
191
192 /* Update the boost value of this boost group */
193 bg->group[idx].boost = boost;
194
195 /* Check if this update increase current max */
196 now = sched_clock_cpu(cpu);
197 if (boost > cur_boost_max &&
198 schedtune_boost_group_active(idx, bg, now)) {
199 bg->boost_max = boost;
200 bg->boost_ts = bg->group[idx].ts;
201
202 trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max);
203 continue;
204 }
205
206 /* Check if this update has decreased current max */
207 if (cur_boost_max == old_boost && old_boost > boost) {
208 schedtune_cpu_update(cpu, now);
209 trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max);
210 continue;
211 }
212
213 trace_sched_tune_boostgroup_update(cpu, 0, bg->boost_max);
214 }
215
216 return 0;
217 }
cpu的boost和ts在什么时候使用?
当cpu的util发生变化,更新cpu的util时,会重新计算boost util,此时会使用cpu的bg->boost_max,是否需要更新参考上边解释。
sugov_get_util
\--> util = cpu_util_freq(cpu)
util = boosted_cpu_util(cpu, util)
| \--> margin = schedtune_cpu_margin(util, cpu)
| | | \--> boost = schedtune_cpu_boost(cpu)
| | | | \-->if (schedtune_boost_timeout(now, bg->boost_ts)) /*更新bg->boost_max和bg->boost_ts*/
| | | | schedtune_cpu_update(cpu, now);
| | | | return bg->boost_max
| | | return schedtune_margin(util, boost) /*根据boost值计算margin*/
| | trace_sched_boost_cpu(cpu, util, margin)
| | return util + margin
return min(util, max_cap)
group[idx].boost和group[idx].ts在什么时候更新?
enqueue_task_fair{rt}->schedtune_enqueue_task->schedtune_tasks_update
244~245行,rt task入队,或配置SCHEDTUNE_BOOST_HOLD_ALL sched feature时,更新bg->group[idx].ts = now。
190行,通过sys节点修改boost值,或release一个group时(boost=0),schedtune_boostgroup_update会更新bg->group[idx].ts = boost。
222 static inline bool
223 schedtune_update_timestamp(struct task_struct *p)
224 {
225 if (sched_feat(SCHEDTUNE_BOOST_HOLD_ALL))
226 return true;
227
228 return task_has_rt_policy(p);
229 }
-
task boost
select_idle_sibling_cstate_aware
task_fits_capacity
find_best_target
get_eenv
\--> boosted_task_util
\--> util = task_util_est(task)
margin = schedtune_task_margin(task)
| \--> boost = schedtune_task_boost(task)
| | \-->st = task_schedtune(p)
| | task_boost = st->boost
| util = task_util_est(task)
| margin = schedtune_margin(util, boost)
trace_sched_boost_task(task, util, margin)
return util + margin
-
cpu和task 通过boost值计算margin
/*根据计算出的boost值,分为两种情况计算: margin=util*boost (boost<spc); margin=(1024-util)*boost (boost≥spc) */
6756 unsigned int spc_threshold = 100;
6757 static long
6758 schedtune_margin(unsigned long signal, long boost)
6759 {
6760 long long margin = 0;
6761
6762 /*
6763 * Signal proportional compensation (SPC)
6764 *
6765 * The Boost (B) value is used to compute a Margin (M) which is
6766 * proportional to the complement of the original Signal (S):
6767 * M = B * (SCHED_CAPACITY_SCALE - S)
6768 * The obtained M could be used by the caller to "boost" S.
6769 */
6770 if (boost >= 0) {
6771 if (signal < spc_threshold)
6772 margin = signal * boost;
6773 else {
6774 margin = SCHED_CAPACITY_SCALE - signal;
6775 margin *= boost;
6776 }
6777 } else
6778 margin = -signal * boost;
6779
6780 margin = reciprocal_divide(margin, schedtune_spc_rdiv);
6781
6782 if (boost < 0)
6783 margin *= -1;
6784 return margin;
6785 }
Question?
- 具体某一个group的ts是在什么时候更新的,又是怎么判断失效的?
- Signal Proportional Compensation' (SPC)原理是什么?
SPC补偿机制的原理是:
b的意义是重定义信号x的基线值:
1)当b=0时,margin=0,信号x的范围是:0 ≤ x ≤ 1024;
2)当b=50时,信号x的基线是:
信号x的范围是:512 ≤ x ≤ 1024
从以上可知,b定义了信号x的基线:base=(b/100)*1024,把信号x从(0 ≤ x ≤ 1024)线性映射到(base ≤ x ≤ 1024) - 关于SPC的缺陷
应用程序往往由不同属组的task构成,比如MP3的主线程如果被放到top-app中,top-app又设置了boost=50,此线程的util很小,却被boost的很大,影响功耗。对小task的boost,我们做了优化(s=100):
函数图形参考:STUNE - 总结
schedtune 作用于某一个cpu 的util,rt task会持续boost 50ms,其它task只在running时间boost有效,cpu的boost值取决于当前cpu所有active groups中boost的最大值。