Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Day 14:

如果覺得文章對你有所啟發,可以考慮用 🌟 支持 Gthulhu 專案,短期目標是集齊 300 個 🌟 藉此被 CNCF Landscape 採納 [ref]

scx_simple 的原始程式碼收錄於 scx,採用 GPL 授權。本篇文章會講解該排程器的實作細節,方便大家近一步理解 sched_ext。

參考昨天的文章可以得知,當一個任務被喚醒會進入到 select cpu 環節,所以 simple_select_cpu 會被呼叫:

s32 BPF_STRUCT_OPS(simple_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags)
{
    bool is_idle = false;
    s32 cpu;

    cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &is_idle);
    if (is_idle) {
        stat_inc(0);    /* count local queueing */
        scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0);
    }

    return cpu;
}

如果有找到處於 IDLE 狀態的 CPU,直接把 TASK 放到該 CPU 的 LOCAL DSQ。
否則,傳入 busy cpu 的代號,準備進入 enqueue:

void BPF_STRUCT_OPS(simple_enqueue, struct task_struct *p, u64 enq_flags)
{
    stat_inc(1);    /* count global queueing */

    if (fifo_sched) {
        scx_bpf_dsq_insert(p, SHARED_DSQ, SCX_SLICE_DFL, enq_flags);
    } else {
        u64 vtime = p->scx.dsq_vtime;

        /*
         * Limit the amount of budget that an idling task can accumulate
         * to one slice.
         */
        if (time_before(vtime, vtime_now - SCX_SLICE_DFL))
            vtime = vtime_now - SCX_SLICE_DFL;

        scx_bpf_dsq_insert_vtime(p, SHARED_DSQ, SCX_SLICE_DFL, vtime,
                     enq_flags);
    }
}

進入 simple_enqueue,如果 FIFO(First In First Out)功能關閉,系統會更新 vtime(可以把它當成 virtual deadline),使用 scx_bpf_dsq_insert_vtime() 會根據 vtime 將任務插入到合適的位置,也就是屬於 priority queue DSQ 的操作。

假設 CPU 已經處理完當前的任務(假設是 cpu0),cpu0 進入到 kernel mode 準備處理下一個任務,首先 cpu0 會檢查自己持有的 Local DSQ,若有任務存在於 DSQ 將任務從 DSQ 取出並執行。若否,則檢查 Global DSQ,將任務取出並執行。

如果仍沒有任務,會進入 dispatch 環節:

void BPF_STRUCT_OPS(simple_dispatch, s32 cpu, struct task_struct *prev)
{
    scx_bpf_dsq_move_to_local(SHARED_DSQ);
}

嘗試將 SHARED_DSQ 的任務移至 cpu0 的 Local DSQ,如果順利則執行該任務。否則 cpu0 進入 idle 狀態。

假設有任務成功被執行,這時候會進入到 running 環節:

void BPF_STRUCT_OPS(simple_running, struct task_struct *p)
{
    if (fifo_sched)
        return;

    /*
     * Global vtime always progresses forward as tasks start executing. The
     * test and update can be performed concurrently from multiple CPUs and
     * thus racy. Any error should be contained and temporary. Let's just
     * live with it.
     */
    if (time_before(vtime_now, p->scx.dsq_vtime))
        vtime_now = p->scx.dsq_vtime;
}

用於更新 global deadline vtime_now

如果有新的任務被啟動(不是喚醒,是啟動),會進入 enable 環節:

void BPF_STRUCT_OPS(simple_enable, struct task_struct *p)
{
    p->scx.dsq_vtime = vtime_now;
}

將該任務的 vtime 指派為當下的 global deadline。

總結

scx_simple 利用不到 150 行的程式碼就實作了一款排程器,藉由追蹤原始程式碼的方式我們也更加的了解 scx 的 scheduling cycle 如何在 kernel 實際的運作。