調度器11—調度相關trace匯總—MTK


基於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狀態的

調用傳參:

實參解析:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM