done
结束部分:【WALT】WALT入口 update_task_ravg() 代码详解代码版本:Linux4.9 android-msm-crosshatch-4.9-android12
(资料图片)
代码展示void update_task_ravg(struct task_struct *p, struct rq *rq, int event,u64 wallclock, u64 irqtime){u64 old_window_start;// ⑴ 判断是否进入 WALT 算法if (!rq->window_start || sched_disable_window_stats || p->ravg.mark_start == wallclock)return;lockdep_assert_held(&rq->lock);// ⑵ 获取 WALT 算法中上一个窗口的开始时间old_window_start = update_window_start(rq, wallclock, event);// ⑶ 如果任务刚初始化结束,不进入 WALT 算法,进入 `done`if (!p->ravg.mark_start) {update_task_cpu_cycles(p, cpu_of(rq), wallclock);goto done;}// ⑷ 更新任务及 CPU 的 cyclesupdate_task_rq_cpu_cycles(p, rq, event, wallclock, irqtime);// ⑸ 更新任务及 CPU 的 demand 及 pred_demandupdate_task_demand(p, rq, event, wallclock);// ⑹ 更新 CPU 的 busy timeupdate_cpu_busy_time(p, rq, event, wallclock, irqtime);// ⑺ 更新任务的 pred_demandupdate_task_pred_demand(rq, p, event);// ⑻ 如果任务正在退出,进入 `done`if (exiting_task(p))goto done;// 两个系统自带的 tracepointtrace_sched_update_task_ravg(p, rq, event, wallclock, irqtime,rq->cc.cycles, rq->cc.time, &rq->grp_time);trace_sched_update_task_ravg_mini(p, rq, event, wallclock, irqtime,rq->cc.cycles, rq->cc.time, &rq->grp_time);done:p->ravg.mark_start = wallclock;run_walt_irq_work(old_window_start, rq);}
代码逻辑WALT 算法以任务为主,当任务被唤醒、任务开始执行、任务停止执行、任务退出、窗口滚动、频率变化、任务迁移、经过一个调度tick、在中断结束时会调用update_task_ravg()
。
其中,窗口是 WALT 算法中的一个特殊的设定,将在 update_task_demand()
与 update_cpu_busy_time()
中详细解释。
在进入 WALT 算法后首先会判断当前任务所在的运行队列(runqueue)是否进行初始化,以及是否禁用 CPU 的窗口统计:if(!rq->window_start || sched_disable_window_stats...)
。如果没有初始化,就不会记录窗口的开始时间,任务负载就无法进行计算。有几点需要注意:
然后会判断窗口开始时间是否更新:if(...p->ravg.mark_start == wallclock)
。如果运行队列没有初始化,或禁用了 CPU 的窗口统计,或窗口开始时间没有更新,就会直接结束 WALT 算法。
然后通过函数update_window_start()
获取上一个窗口的开始时间,存在变量old_window_start
中。
点击此处查看 update_window_start() 代码详解。
⑶ 如果任务刚初始化结束如果任务刚初始化结束:if(!p->ravg.mark_start)
,还没有标记过任务的开始时间,就先通过函数 update_task_cpu_cycles()
更新一下该任务的 cycles 值(p->cpu_cycles
),然后进入 done
。
点击此处查看 update_task_cpu_cycles() 代码详解。
⑷更新任务及 CPU 的 cycles和 update_task_cpu_cycles()
相似,但比其多更新了 CPU 的 cycles 值(rq->cc.cycles
)。
点击此处查看 update_task_rq_cpu_cycles() 代码详解。
⑸更新任务及 CPU 的 demand 及 pred_demand在任务满足条件后,在不同情况下根据任务的开始时间、窗口的开始时间以及当前时间来计算任务在当前及之前M个窗口中的运行时间。在窗口结束时将运行时间进行归一化,并统计进任务的历史窗口中(sum_history[RAVG_HIST_SIZE]
)。
WALT 算法根据历史窗口中的值计算任务的 demand,根据桶算法计算任务的 pred_demand,并将 demand 与 pred_demand 统计进任务所在 CPU 的 rq(runqueue)中。
注意:以上说的 demand 与 pred_demand 都是预测值。
点击此处查看 update_task_demand() 代码详解。
⑹更新 CPU 的 busy time在任务满足条件后,在不同情况下根据任务的开始时间、窗口的开始时间以及当前时间来计算任务在当前及上一个窗口中的运行时间,将不同窗口内的运行时间进行归一化,并根据任务的状态统计进任务的 curr_window
和 prev_window
中,以及任务所在 rq 的 curr_runnable_sum
和 prev_runnable_sum
中。
在窗口翻滚的时候更新任务的 window 值 以及 rq 的 runnable_sum 的值。
注意:以上说的 window 以及 runnable_sum 都是真实值。
点击此处查看 update_cpu_busy_time() 代码详解。
⑺更新任务的 pred_demand如果符合条件的任务在当前窗口中预测出来的 demand 值小于 curr_window
,则再次使用桶算法计算 pred_demand。
点击此处查看 update_task_pred_demand() 代码详解。
⑻如果任务正在退出#define EXITING_TASK_MARKER0xdeaddeadstatic inline int exiting_task(struct task_struct *p){return (p->ravg.sum_history[0] == EXITING_TASK_MARKER);}
当任务最近一个窗口的值为 0xdeaddead 时,意味着任务正在退出,进入 done
。
done
结束部分:更新一下任务的开始时间:p->ravg.mark_start = wallclock
。通过函数 irq_work_queue()
处理没有 tick 的情况,循环调用 update_task_ravg()
。