基於MTK Linux-4.14
1. sched_util ——負載調頻
打印:
RenderThread-2837 [006] d.h2 8163.520041: sched_util: cid=1 next=1624000 last_freq_update_time=8163524121923
形參:
TP_PROTO(int cid, unsigned int next_freq, u64 time),
打印解析:
分別為三個傳入參數
調用路徑:
enqueue_entity //fair.c 無條件條調用 dequeue_entity //fair.c 無條件條調用 set_next_entity //fair.c 只有se->on_rq才調用,選一個cfs任務去運行 put_prev_entity //fair.c 只有se->on_rq才調用,取消一個在運行的cfs任務 entity_tick //fair.c 在scheduler_tick()中對正在運行的任務以Hz為頻度更新負載 enqueue_task_fair //fair.c 無條件調用 dequeue_task_fair //fair.c 無條件調用 update_blocked_averages //fair.c 負載均衡路徑 propagate_entity_cfs_rq //fair.c fair group sched里面的 detach_entity_cfs_rq //fair.c 無條件條調用 attach_entity_cfs_rq //fair.c 無條件條調用 sched_group_set_shares //fair.c fair group sched cgroup分組 update_load_avg //fair.c update_sd_lb_stats //fair.c 負載均衡路徑 idle_balance //fair.c nohz_idle_balance //fair.c run_rebalance_domains //fair.c update_blocked_averages //fair.c update_cfs_rq_load_avg //fair.c 更新負載后會觸發調頻 attach_entity_load_avg //fair.c 將一個se的load添加到cfs_rq上后會觸發調頻 detach_entity_load_avg //fair.c 將一個se的load從cfs_rq上移除后會觸發調頻 enqueue_task_fair //fair.c 向一個idle的cpu中掛入一個任務會觸發提頻 cfs_rq_util_change //fair.c 傳參(rq, 0) update_curr_dl //deadline.c DL任務也會觸發調頻,傳參(rq, SCHED_CPUFREQ_DL) 原語上說只在CFS任務中觸發調頻,擔心CFS任務不能及時運行而不能及時提頻。 update_curr_rt //rt.c rt任務無條件觸發提頻,傳參(rq, SCHED_CPUFREQ_RT) enqueue_task_fair //fair.c enqueue一個從iowait退出的任務會觸發提頻,傳參(rq, SCHED_CPUFREQ_IOWAIT) cpufreq_update_util //sched.h sugov_start()中將下面兩個函數的指針放到per-cpu的cpufreq_update_util_data中,若policy中只有一個cpu放的才是sugov_update_single sugov_update_single //cpufreq_schedutil.c 只有單核的cluster使用這個 sugov_update_shared //cpufreq_schedutil.c 兩者傳參一樣 sugov_update_commit trace_sched_util(cid, next_freq, time) //mtk加的
說明:
看名字是trace util的,但是實際上是trace cluster 頻點設置的。
調用傳參:
見 調用路徑
實參解析:
cid:
為 cpu_topology.cluster_id 表示對哪個cluster調頻
next_freq:
先賦值給 cpufreq_policy.cur,,表示要設置的頻率
time:
頻點最后一次更新的時間戳。TODO: 應該可以和update limit限制配合來看
總共有三個,另兩個是 sched_util_est_task 和 sched_util_est_cpu
2. sched_util_est_cpu ——cfs_rq的預估負載
打印:
<idle>-0 [005] d.H2 8163.520044: sched_util_est_cpu: cpu=5 util_avg=364 util_est_enqueued=359
形參:
TP_PROTO(int cpu, struct cfs_rq *cfs_rq),
打印解析:
cpu:參數cpu util_avg:取自 cfs_rq->avg.util_avg util_est_enqueued:取自 cfs_rq->avg.util_est.enqueued
調用路徑:
//<1> update_load_avg //fair.c 調用路徑見對 sched_util 的解析 __update_load_avg_se //若是定義了 UTIL_EST_DEBUG 且 se 是task才會調用 trace_sched_util_est_cpu(cpu, cfs_rq); //<2> enqueue_task_fair //fair.c util_est_enqueue //fair.c 在更新schedutil freq前,簡潔的增加util trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); //<3> dequeue_task_fair //fair.c util_est_dequeue trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq);
說明:
在任務enqueue、dequeue、和負載更新 中 trace cpu 的 util_avg 和 util_est.enqueued,
調用傳參:
cpu和其上的cfs_rq
實參解析:
見調用路徑
補充:
struct util_est 注釋: enqueued 屬性對於 tasks 和 cpus 的含義略有不同: - task:上次任務出隊時任務的 util_avg - cfs_rq:該 CPU 上每個 RUNNABLE 任務的 util_est.enqueued 總和。因此,任務(非cfs_rq)的 util_est.enqueued 表示該任務對當前排隊的 CPU 估計利用率的貢獻。
一個任務的util很大的話,由於pelt算法下util增加的比較慢,util_est 就是對pelt的這一缺陷進行的補救。
3. sched_util_est_task ——任務的預估負載
打印:
<idle>-0 [005] d.H2 8163.520044: sched_util_est_task: comm=cat pid=15061 cpu=5 util_avg=359 util_est_ewma=350 util_est_enqueued=359
形參:
TP_PROTO(struct task_struct *tsk, struct sched_avg *avg),
打印解析:
comm:取自 tsk->comm pid:取自 tsk->pid cpu:取自 task_cpu(tsk) util_avg:取自 avg->util_avg util_est_ewma:取自 avg->util_est.ewma util_est_enqueued:取自 avg->util_est.enqueued
調用路徑:
<1> __update_load_avg_se //fair.c trace_sched_util_est_task(tsk, &se->avg); //若 se 是 task 調用,和 trace_sched_util_est_cpu 一起調用 <2> util_est_enqueue trace_sched_util_est_task(p, &p->se.avg); //和 trace_sched_util_est_cpu 一起調用 <3> util_est_dequeue trace_sched_util_est_task(p, &p->se.avg); //和 trace_sched_util_est_cpu 一起調用
說明:
tarce se 的預估負載,util_est.enqueued 和 util_est.ewma
調用傳參:
見 調用路徑
實參解析:
見調用路徑
4. sched_select_task_rq ——任務選核
打印:
MDP-0-17450 [006] d..2 790259.891744: sched_select_task_rq: pid= 598 policy=0x00880002 pre-cpu=3 target=2 util=36 boost=36 mask=0xf prefer=1 cpu_prefer=0 flags=1
形參:
TP_PROTO(struct task_struct *tsk, int policy, int prev_cpu, int target_cpu, int task_util, int boost, bool prefer, int wake_flags),
打印解析:
pid: 取自 tsk->pid policy: 取自參數 policy pre-cpu: 取自參數 prev_cpu target: 取自參數 target_cpu util: 取自參數 task_util boost: 取自參數 boost mask: 取自 tsk->cpus_allowed.bits[0],也就是任務task的cpu親和性掩碼 prefer: 取自參數 prefer cpu_prefer: 取自 tsk->cpu_prefer,配置文件/sys/devices/system/cpu/sched/cpu_prefer,設置<pid, prefer_value>后者取 SCHED_PREFER_NONE=0,BIG=1,LITTLE=2,MEDIUM=3,END=4,目前看主要在負載均衡中使用 flags: 取自參數 wake_flags
調用路徑:
scheduler_tick //core.c 只有當前任務是CFS任務時才調用 check_for_migration //fair.c 任務遷移路徑中的選核,傳參 (p, cpu, SD_BALANCE_WAKE, 0, 1) wake_up_q //core.c wake_up_process //core.c wake_up_state //core.c 只喚醒 p->state & state 的交集不為空的任務 default_wake_function //core.c 內核等待隊列默認的喚醒函數,很多機制都是基於等待機制實現的 try_to_wake_up //core.c 任務喚醒,當任務的cpu親和性大於一個cpu時才調用,傳參 (p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags, sibling_count_hint) _do_fork wake_up_new_task //core.c 為新創建的任務選核,傳參 (p, task_cpu(p), SD_BALANCE_FORK, 0, 1) fair_sched_class.select_task_rq select_task_rq_fair //fair.c trace_sched_select_task_rq
說明:
在為任務tsk的選核路徑中trace.
調用傳參:
trace_sched_select_task_rq(p, result, prev_cpu, cpu, task_util_est(p), boosted_task_util(p), (schedtune_prefer_idle(p) > 0), wake_flags)
實參解析:
p: 待選核的任務 result: 是policy,是SELECT_TASK_RQ_FAIR()的返回值, prev_cpu: 不同的執行路徑表示不同含義,check_for_migration 路徑和 wake_up_new_task 路徑為 task_cpu(p),表示任務先前運行的cpu。try_to_wake_up路徑傳參為 p->wake_cpu,其取值如下描述 cpu: target 字段打印的是這個,為 SELECT_TASK_RQ_FAIR() 選出來的 target_cpu task_util_est(p): util 字段打印的是這個,表示任務的 util,不使用walt算法情況下是 max(p->se.avg.util_avg, p->se.avg.util_est),取任務的util_avg和預估的util_est的兩者之間的較大值。 boosted_task_util(p): boost 字段打印的是這個,其獲取方法見下面對trace sched_boost_task的解析。 schedtune_prefer_idle(p) > 0: prefer 字段打印的是這個,取值 st->prefer_idle 來自 /dev/stune/[group/]schedtune.prefer_idle,若是沒有分組就是來自根目錄下的這個文件。 wake_flags: flag 字段打印的是這個,取值可為 WF_SYNC(1) WF_FORK(2) WF_MIGRATED(4),但是選核函數中只對 WF_SYNC 標志進行了判斷,此表示表示喚醒者將要睡眠,被喚醒者更傾向於允許在喚醒者所在的cpu上。
p->wake_cpu 的賦值路徑:
__set_cpus_allowed_ptr //core.c 用戶設置親和性時,傳cpu為新設置的親和性里面的cpu(1) __migrate_task //core.c 傳的cpu為遷移時的目的cpu(2) move_queued_task //core.c 參數來自上層 migrate_swap_stop //core.c 調用兩次,分別傳(src_task,dst_cpu)和(dst_task,src_cpu),這是stop調度類的,先不看 __migrate_swap_task //core.c 參數來自上層 try_to_wake_up //core.c 傳參 cpu=select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0, 1)選出的cpu(3) idle_balance //fair.c 傳參cpu為目前處於idle的cpu(4) nohz_idle_balance //fair.c 除了當前cpu外,對每個idle的cpu都調用,依次傳每個idle cpu(5) rebalance_domains //fair.c 來自其第一個參數rq load_balance //fair.c env->dst_cpu 為參數 this_cpu detach_task //傳參 cpu=env->dst_cpu set_task_cpu //core.c 參數來自上層 sched_fork //core.c 傳參 cpu=smp_processor_id() 就是執行 fork() 系統調用的父任務運行的cpu(6) wake_up_new_task //core.c 傳參 cpu=select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0, 1)選出的cpu,選核在后,選核中的 p->wake_cpu(7) init_idle //core.c 參數來自上層 __set_task_cpu(struct task_struct *p, unsigned int cpu) set_task_rq(p, cpu) //使能組調度時賦值 p->cpu = cpu; p->wake_cpu = cpu //這里
5. sched_boost_task ——任務的boost負載
打印:
UsbFfs-worker-810 [000] d..3 800865.820424: sched_boost_task: comm=UsbFfs-worker pid=810 util=17 margin=503 util_min=102
形參:
TP_PROTO(struct task_struct *tsk, unsigned long util, long margin, unsigned int util_min),
打印解析:
comm: 取自 tsk->comm pid: 取自 tsk->pid util: 取自參數 util margin: 取自參數 margin util_min: 取自參數 util_min
調用路徑:
update_load_avg //fair.c pelt的負載更新函數 enqueue_task_fair dequeue_task_fair inc_nr_heavy_running //rq_stats.c sched_update_nr_heavy_prod //drivers\misc\mediatek\sched\sched_avg.c store_btask_down_thresh //core_ctl.c 來自/sys/devices/system/cpu/cpuX/core_ctl/btask_down_thresh core_ctl_set_btask_up_thresh //沒有調用 store_btask_up_thresh //core_ctl.c 來自/sys/devices/system/cpu/cpuX/core_ctl/btask_up_thresh set_btask_up_thresh set_overutil_threshold //drivers\misc\mediatek\sched\rq_stats.c store_overutil //rq_stats.c 對應/sys/devices/system/cpu/rq-stats/over_util overutil_thresh_chg_notify //sched_avg.c is_task_overutil //fair.c 唯一調用路徑 get_task_util task_numa_fault //fair.c task_numa_placement //fair.c do_huge_pmd_numa_page //huge_memory.c 最終還和內存掛上勾了 do_numa_page //memory.c task_numa_fault //fair.c numa_migrate_preferred //fair.c task_numa_migrate //fair.c task_numa_find_cpu //fair.c task_numa_compare //fair.c select_idle_sibling //fair.c select_idle_sibling_cstate_aware pick_next_task_fair //fair.c 選到cfs任務了更新,若cpu idle了將rq->misfit_task_load = 0 hrtick //core.c scheduler_tick //core.c 在tick中斷中更新task的misfit信息 task_tick_fair //fair.c fair_sched_class.task_tick回調 update_misfit_status //fair.c SELECT_TASK_RQ_FAIR //fair.c 判斷算力是否容得下任務,賦值給want_affine wake_cap //fair.c scheduler_tick //core.c 若curr是CFS任務才調用 check_for_migration //fair.c task_fits_capacity find_energy_efficient_cpu get_eenv //fair.c find_energy_efficient_cpu //fair.c 若返回的util是0直接退出,一般不會發生 select_task_rq_fair //fair.c 作為trace_sched_select_task_rq的一個實參 boosted_task_util //fair.c trace_sched_boost_task
boosted_task_util 函數:
/* * 此函數可實現只對指定組內的heavy_task進行boot,並且可以對util進行鉗位 */ static inline unsigned long boosted_task_util(struct task_struct *task) { unsigned long util = task_util_est(task); //返回任務的利用率和預估利用率兩者的較大值 /* st->boost是來自/dev/stune/[group/]schedtune.boost,大多數任務是沒有分組的,因此根 * 目錄下的schedtune.boost配置影響大多數任務。boost_write()中只允許寫入[0,100]的數, * 也就是說只能boost util,不能削減util,但是可通過下面的uclamp鉗位實現。 * 公式:margin = st->boost% * (SCHED_CAPACITY_SCALE - util) */ long margin = schedtune_task_margin(task); /* * 值是來自/dev/stune/[group/]schedtune.util.min,若任務沒有在任何組中,則自根目錄下 * 的/dev/stune/schedtune.util.min。往里面echo值的時候,會從opp列表中找一個不小於echo * 的值寫進去。 */ unsigned long util_min = uclamp_task_effective_util(task, UCLAMP_MIN); /* 同理值來自 /dev/stune/[group/]/schedtune.util.max */ unsigned long util_max = uclamp_task_effective_util(task, UCLAMP_MAX); trace_sched_boost_task(task, util, margin, util_min); //注意:trace這里還是沒有過濾小util任務時的 /* * 只對heavy任務進行boost,判斷是否heavy的閾值stune_task_threshold來自 * /proc/sys/kernel/sched_stune_task_threshold */ if (util >= stune_task_threshold) { util = util + margin; return clamp(util, util_min, util_max); //boost后還要被uclamp限制一下 } else { return clamp(util, util_min, util_max); } }
說明:
trace獲取boost后的util,如上函數還有過濾邏輯,trace打印的不表示返回的值。由調用路徑可見,在選核、遷移、tick、用戶sysfs接口配置,都有涉及。
調用傳參:
見 boosted_task_util 函數
實參解析:
見上面 boosted_task_util() 中的注釋。
6. schedutil_uclamp_util
打印:
<idle>-0 [000] d.h3 9895.552700: schedutil_uclamp_util: cpu=0 util=145 util_min=0 util_max=1024
形參:
TP_PROTO(int cpu, unsigned long util),
打印解析:
cpu:參數cpu util:參數util util_min:取自 uclamp_value(cpu, UCLAMP_MIN) 展開為 cpu_rq(cpu)->uclamp.value[UCLAMP_MIN] util_max:取自 uclamp_value(cpu, UCLAMP_MAX) 展開為 cpu_rq(cpu)->uclamp.value[UCLAMP_MAX]
調用路徑:
//見 trace sched_util 的調用路徑 cpufreq_update_util //sched.h sugov_update_shared //cpufreq_schedutil.c sugov_next_freq_shared //cpufreq_schedutil.c trace_schedutil_uclamp_util(j, j_util); //對policy->cpus中的每個cpu j 都進行trace
說明:
應該是trace cpu cgroup 設置下來的 util 的max和min值。util_min/util_max 的值應該是來自 /dev/cpuctl/<group_name>/cpu.uclamp.min 和 /dev/cpuctl/<group_name>/cpu.uclamp.max,通過cpu_util_max_write_u64(core.c)寫到變量中。但是MTK BSP 里面沒有這些文件,Qcom 5.4 BSP 有這些文件。
調用傳參:
見 調用路徑
實參解析:
見 調用路徑
注:看來任務和 cfs_rq 都有 uclamp 成員,但是分布在不同的cgroup中,任務的是 stune cgroup,對應 /dev/stune 目錄,而rq的是 cpu cgroup,對應的是 /dev/cpuctl 目錄。
7. uclamp_util_se
打印:
kworker/u16:6-15247 [000] d..2 9895.551953: uclamp_util_se: pid=15247 comm=kworker/u16:6 cpu=0 active=0 util_avg=82 uclamp_avg=82 uclamp_min=0 uclamp_max=1024 uclamp_min_eff=0 uclamp_max_eff=1024
形參:
TP_PROTO(bool is_task, struct task_struct *p, struct rq *rq),
打印解析:
pid:取自 p->pid comm:取自 p->comm cpu:取自 rq->cpu active:取自 p->uclamp[UCLAMP_MIN].active 其中UCLAMP_MIN=0,UCLAMP_MAX=1 util_avg:取自 p->se.avg.util_avg uclamp_avg:取自 uclamp_util(rq, p->se.avg.util_avg) 將 p->se.avg.util_avg 鉗位在 uclamp 的 min 和 max 之間。 uclamp_min:取自 p->uclamp[UCLAMP_MIN].value 應該來自 /dev/stune/schedtune.util.min,但是MTK BSP上不生效 uclamp_max:取自 p->uclamp[UCLAMP_MAX].value uclamp_min_eff:取自 p->uclamp[UCLAMP_MIN].effective.value 來自 /dev/stune/schedtune.util.min.effective uclamp_max_eff:取自 p->uclamp[UCLAMP_MAX].effective.value TODO: 什么意思?
說明:
這個是在負載統計的時候進行trace.
調用傳參:
trace_uclamp_util_se(entity_is_task(se), container_of(se, struct task_struct, se), cpu_rq(cpu));
實參解析:
略
注:此trace看以看出 uclamp_avg 是否是被 uclamp 鉗位。測試發現改/proc/sys/kernel/sched_uclamp_util_min和max,/dev/stune/<group>/schedtune.util.max和min這里的uclamp_min和max
始終都是0和1024
8. uclamp_util_cfs
打印:
<idle>-0 [000] d.h2 9895.552814: uclamp_util_cfs: cpu=0 util_avg=145 uclamp_avg=145 uclamp_min=0 uclamp_max=1024
形參:
TP_PROTO(bool is_root, int cpu, struct cfs_rq *cfs_rq),
打印解析:
cpu:參數 cpu
util_avg:取自 cfs_rq->avg.util_avg
uclamp_avg:取自 uclamp_util(cpu_rq(cpu), cfs_rq->avg.util_avg); 將 cfs_rq->avg.util_avg 鉗位在 uclamp 的 min 和 max 之間。
uclamp_min:取自 cpu_rq(cpu)->uclamp.value[UCLAMP_MIN]; 應該是來自 /dev/cpuctl/<cgroup>/cpu.uclamp.min,但是MTK BSP沒有導出這個文件
uclamp_max:取自 cpu_rq(cpu)->uclamp.value[UCLAMP_MAX];
調用路徑:
//調用路徑見trace sched_util update_cfs_rq_load_avg __update_load_avg_cfs_rq trace_uclamp_util_cfs
說明:
也是在PELT負載更新時進行trace
調用傳參:
trace_uclamp_util_cfs(is_root_rq, cpu, cfs_rq)
實參解析:
is_root_rq:
判斷方法 is_root_rq = (&cpu_rq(cpu)->cfs == cfs_rq)
9. sched_waking
打印:
cat-1843 [003] d..3 18706.110171: sched_waking: comm=kworker/u16:1 pid=30684 prio=120 success=1 target_cpu=002 state=D|N
形參:
TP_PROTO(struct task_struct *p),
打印解析:
comm: 取自 p->comm
pid: 取自 p->pid
prio: 取自 p->prio
success: 常量賦值,恆為1,沒有意義
target_cpu:取自 task_cpu(p),表示任務p喚醒之前是運行在哪個cpu上
state: 取自對 p->state 的解析后的,喚醒時任務的狀態
調用路徑:
//對應各種喚醒路徑 default_wake_function wake_up_state wake_up_process wake_up_q try_to_wake_up //core.c trace_sched_waking(p); //如果worker進入睡眠狀態,通知並詢問workqueue是否要喚醒其上的其它任務以保持連續運行,這會抬高workerqueue上任務的優先級。 __schedule //core.c try_to_wake_up_local //core.c Qcom的5.4內核沒有這個執行路徑,最新內核目前已從這里移除,改放在 sched_submit_work 中 trace_sched_waking(p);
說明:
開始喚醒任務時的trace, 可以用來看任務喚醒早期的信息。
調用傳參:
略
實參解析:
略
10. sched_wakeup
打印:
kworker/u16:4-31213 [003] d.h2 20499.768856: sched_wakeup: comm=kworker/u16:2 pid=2048 prio=120 success=1 target_cpu=003 state=R
形參:
TP_PROTO(struct task_struct *p),
打印解析:
comm: 取自 p->comm
pid: 取自 p->pid
prio: 取自 p->prio
success: 常量賦值,恆為1,沒有意義
target_cpu:取自 task_cpu(p),結合調用路徑可知,此時就表示任務p喚醒之后是運行在哪個cpu上了
state: 取自對 p->state 的解析后的,喚醒時任務的狀態
調用路徑:
try_to_wake_up ttwu_queue ttwu_do_activate try_to_wake_up ttwu_remote __schedule //最新內核這個喚醒worker保持連續的分支已經刪除 try_to_wake_up_local ttwu_do_wakeup //core.c p->state = TASK_RUNNING; trace_sched_wakeup(p)
說明:
可以用來trace任務喚醒后是在哪個cpu上運行。由於trace前設置了p->state = TASK_RUNNING,所以trace上state恆等於R。
調用傳參:
略
實參解析:
略
11. sched_overutilized
打印:
logd.writer-583 [000] d..2 27589.723001: sched_overutilized: overutilized=1 sd_span=0-3
形參:
TP_PROTO(struct sched_domain *sd, bool was_overutilized, bool overutilized)
打印解析:
overutilized: 取自參數 overutilized
sd_span: 是以 "%*pbl" 格式打印出來的 cpumask_pr_args(sched_domain_span(sd)),表示此調度域中的cpu,可以從cpu分布上看出是哪個調度域
調用路徑:
(1)設置overutilized
load_balance //fair.c find_busiest_group //負載均衡路徑更新 update_sd_lb_stats //fair.c MTK的BSP通過對SCHED_MTK_EAS的判斷將這個函數中的設置給關閉了 enqueue_task_fair //插入cfs任務時調用更新 task_tick_fair //tick周期更新 update_overutilized_status //fair.c 只是在MC層級的判斷cpu_overutilized后設置sd_overutilized load_balance find_busiest_group //fair.c 入口位置無條件調用 update_system_overutilized //eas_plus.c 這里是實際更新位置,此cluster的所有核需要的算力之和超過了此cluster的cpu算力之和設置為true set_sd_overutilized trace_sched_overutilized(sd, sd->shared->overutilized, true); sd->shared->overutilized = true; (2)清理overutilized load_balance find_busiest_group update_sd_lb_stats update_system_overutilized //eas_plus.c 調用路徑見<1> clear_sd_overutilized trace_sched_overutilized(sd, sd->shared->overutilized, false); sd->shared->overutilized = false;
sd->shared->overutilized 的使用位置:
static inline int wake_energy(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) //fair.c { sd = rcu_dereference_sched(cpu_rq(prev_cpu)->sd); ... if (sd_overutilized(sd)) //prev_cpu已經過載了,MC層級 return false; //false:we shouldn't attempt energy-aware wakeup ... } //調用路徑: SELECT_TASK_RQ_FAIR wake_energy //返回false更容易進入根據親和性進行選核的路徑 <2> 若是cpu的調度域沒有超載,更偏向於EAS選核 static int SELECT_TASK_RQ_FAIR(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags, int sibling_count_hint)//fair.c { /*以waker執行的cpu調度域逐步向上 MC-->DIE */ for_each_domain(cpu, tmp) { ... //此domain沒有超載,並且perv_cpu也在此cluster中 if (want_energy && !sd_overutilized(tmp) && cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) energy_sd = tmp; } ... if (energy_sd) { new_cpu = __find_energy_efficient_cpu(energy_sd, p, cpu, prev_cpu, sync); //TODO: select_reason = LB_EAS; } ... } <3> static struct sched_group *find_busiest_group(struct lb_env *env) //fair.c { ... intra = is_intra_domain(local_cpu, busiest_cpu); //兩個cpu同一個cluster就認為是domain內部 if (energy_aware() && !sd_overutilized(env->sd) && !intra) //滿足條件認為cluster之間是均衡的,退出 goto out_balanced; ... out_balanced: env->imbalance = 0; return NULL; } //調用路徑: load_balance find_busiest_group <4> static inline unsigned long get_sd_balance_interval(struct sched_domain *sd, int cpu_busy) //fair.c { ... if (energy_aware() && sd_overutilized(sd)) { /* we know the root is overutilized, let's check for a misfit task */ for_each_cpu(cpu, sched_domain_span(sd)) { if (cpu_rq(cpu)->misfit_task_load) return 1; //希望立即觸發balance } } return interval; } //調用路徑: rebalance_domains //在interval時間之后才觸發負載均衡 update_next_balance //只是更新 next_balance 的值 get_sd_balance_interval <5> static void rebalance_domains(struct rq *rq, enum cpu_idle_type idle) { ... for_each_domain(cpu, sd) { //MC --> DIE if (energy_aware() && !sd_overutilized(sd)) continue; //跳過沒有超載的調度域 } ... } //調用路徑: run_rebalance_domains //也是SCHED_SOFTIRQ軟中斷相應函數 nohz_idle_balance handle_IPI //arch/arm64/kernel/smp.c case IPI_RESCHEDULE:才調用,對應cluster間喚醒路徑 scheduler_ipi raise_softirq_irqoff(SCHED_SOFTIRQ); //core.c if (unlikely(got_nohz_idle_kick()) && !cpu_isolated(cpu))為真才執行 scheduler_tick //core.c trigger_load_balance raise_softirq(SCHED_SOFTIRQ)//if (time_after_eq(jiffies, rq->next_balance)) 到期后才觸發balance run_rebalance_domains //open_softirq(SCHED_SOFTIRQ, run_rebalance_domains) //fair.c rebalance_domains IPI_RESCHEDULE ipi中斷在任務cluster間喚醒時觸發: try_to_wake_up //core.c ttwu_queue //喚醒后的cpu和當前cpu不共享cache,說明不是同一個cluster ttwu_queue_remote smp_send_reschedule smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
fair.c中除了有 sd_overutilized(sd) 還有 cpu_overutilized(rq->cpu) 和 group_is_overloaded():
static int need_active_balance(struct lb_env *env) //fair.c { ... if (capacity_of(env->src_cpu) < capacity_of(env->dst_cpu) && (capacity_orig_of(env->src_cpu) < capacity_orig_of(env->dst_cpu)) && env->src_rq->cfs.h_nr_running == 1 && cpu_overutilized(env->src_cpu) && !cpu_overutilized(env->dst_cpu)) { return 1; } ... } static inline bool group_is_overloaded(struct lb_env *env, struct sg_lb_stats *sgs) { if (sgs->sum_nr_running <= sgs->group_weight) return false; /* 整理:sgs->group_util > sgs->group_capacity * 100/env->sd->imbalance_pct */ if ((sgs->group_capacity * 100) < (sgs->group_util * env->sd->imbalance_pct)) return true; return false; }
說明:
這個是trace sd->shared->overutilized 的設置和清除的trace,參數2沒有打印。主要在負載均衡路徑和選核路徑中使用,對是否要均衡和均衡時機都有影響,選核路徑中會影響是否通過wake_affinity選核。
調用傳參:
略
實參解析:
略
13. sched_update_lb_sg
打印:
kworker/u16:13-24358 [006] d..1 49578.126201: sched_update_lb_sg: avg_load=3821 group_load=3650 group_capacity=978 group_no_capacity=1 group_type=3
形參:
TP_PROTO(unsigned long avg_load, unsigned long group_load, unsigned long group_capacity, int group_no_capacity, int group_type),
打印解析:
全部打印都是取自參數
調用路徑:
load_balance //fair.c find_busiest_group //fair.c update_sd_lb_stats update_sg_lb_stats trace_sched_update_lb_sg
說明:
負載均衡路徑中trace struct sg_lb_stats 的成員的值。
調用傳參:
trace_sched_update_lb_sg(sgs->avg_load, sgs->group_load, sgs->group_capacity, sgs->group_no_capacity, sgs->group_type);
實參解析:
sgs->avg_load: 都是 struct sg_lb_stats 的成員,賦值為 (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity
sgs->group_load: 對於 group->span 和 env->cpus 里面的每一個cpu,加上其對應的負載 target_load(i, load_idx)
sgs->group_capacity: 賦值為 group->sgc->capacity,約等於一個cluster的所有能用的cpu的算力之和。
sgs->group_no_capacity: 賦值為 group_is_overloaded(env, sgs)的返回值,整理后為 sgs->group_util > sgs->group_capacity * 100/env->sd->imbalance_pct 為真,表示group沒有空余算力了。
sgs->group_type: 可以取值 group_other=0、group_misfit_task=1、group_imbalanced=2、group_overloaded=3
14. sched_stat_template
模板的三個使用 sched_stat_wait、sched_stat_sleep、sched_stat_iowait、sched_stat_blocked
打印:
<idle>-0 [003] dn.2 51863.925458: sched_stat_wait: comm=kworker/u16:2 pid=23411 delay=0 [ns]
<idle>-0 [005] d.h2 51863.925523: sched_stat_sleep: comm=cat pid=29224 delay=19385 [ns]
RenderThread-5572 [003] d.s3 51958.206163: sched_stat_iowait: comm=SettingsProvide pid=2006 delay=76846 [ns]
<idle>-0 [005] d.s4 52145.249923: sched_stat_blocked: comm=kworker/5:1 pid=25088 delay=1983674774 [ns]
形參:
略
打印解析:
略
調用路徑:
static inline void update_stats_wait_end(struct cfs_rq *cfs_rq, struct sched_entity *se) //fair.c { delta = rq_clock(rq_of(cfs_rq)) - schedstat_val(se->statistics.wait_start); trace_sched_stat_wait(p, delta); }
在 se 入隊列進入 runable 狀態開始等待時在 update_stats_wait_start 中對 se->statistics.wait_start 進行賦值,此trace表示在cfs_rq上等待的時間,也就是runnable的時間。
static inline void update_stats_enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) //fair.c { sleep_start = schedstat_val(se->statistics.sleep_start); block_start = schedstat_val(se->statistics.block_start); if (sleep_start) { u64 delta = rq_clock(rq_of(cfs_rq)) - sleep_start; trace_sched_stat_sleep(tsk, delta); } if (block_start) { u64 delta = rq_clock(rq_of(cfs_rq)) - block_start; if (tsk->in_iowait) { trace_sched_stat_iowait(tsk, delta); } trace_sched_stat_blocked(tsk, delta); trace_sched_blocked_reason(tsk); } } 調用路徑: enqueue_entity update_stats_enqueue //if (flags & ENQUEUE_WAKEUP) 才執行 update_stats_enqueue_sleeper /* dequeue_entity -> update_stats_dequeue */ static inline void update_stats_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) //fair.c { //任務切換時主動放棄CPU的話有DEQUEUE_SLEEP標志 if ((flags & DEQUEUE_SLEEP) && entity_is_task(se)) { struct task_struct *tsk = task_of(se); if (tsk->state & TASK_INTERRUPTIBLE) schedstat_set(se->statistics.sleep_start, rq_clock(rq_of(cfs_rq))); if (tsk->state & TASK_UNINTERRUPTIBLE) schedstat_set(se->statistics.block_start, rq_clock(rq_of(cfs_rq))); } }
在 __schedule 中若任務是主動放棄CPU的,則根據任務的狀態初始化 sleep_start 或 block_start,在任務喚醒入隊列時打印經歷的時間,就是任務的睡眠時間或D狀態的持續時間。若任務是由於 iowait 而進入的休眠,還會打印 iowait 導致的休眠的時間。
說明:
略
調用傳參:
略
實參解析:
略
15. sched_blocked_reason
打印:
kworker/u16:6-30772 [005] d..3 61768.396767: sched_blocked_reason: pid=9852 iowait=0 caller=flush_work+0x1ac/0x204
形參:
TP_PROTO(struct task_struct *tsk),
打印解析:
pid: 取自 tsk->pid
iowait: 取自 tsk->in_iowait
caller: 打印格式控制為"%pS",取自 (void*)get_wchan(tsk),通過 frame->fp 指針進行rewind遍歷,然后通過 %pF 格式打印 frame->pc 就是函數名,這里只打印了一級函數名
調用路徑:
enqueue_entity //fair.c update_stats_enqueue //if (flags & ENQUEUE_WAKEUP) 才執行 update_stats_enqueue_sleeper trace_sched_blocked_reason(tsk);
說明:
對於cfs任務,喚醒時發現是從D狀態喚醒的,就會trace其在哪個函數中進入D狀態的。
調用傳參:
略
實參解析:
略