調度子係統4_負載均衡(一)
// 參考:https://blog.csdn.net/dog250/article/details/5303561 // 負載均衡 // 當rq->next_balance到時,觸發負載均衡 // 調用路徑:scheduler_tick->trigger_load_balance // 注: // nohz.cpu_mask中的cpu表示停用了周期時鍾 // 函數任務: // 1.如果進入tick的時候rq變得有事可做,並且之前由本cpu執行idle load balance // 1.1 不再做idle load balance // 1.2 nohz.cpu_mask中選擇一個cpu負責idle load balance // 1.3 通過ipi通知彼cpu負責ilb // 2.如果所有cpu均處於idle狀態,之前由本cpu做idle load balance // 2.1 沒有必要再做idb,通知本cpu停止idle load balance // 3.如果本cpu處於idle狀態,其他cpu做idle load balance // 3.1 ilb的cpu會代此cpu執行load balance,不需要raise SCHED_SOFTIRQ // 4.本cpu沒有加入到任何domain,則不需要在domain間load balance // 5.如果到達執行load balance時間點 // 5.1 raise SCHED_SOFTIRQ 1.1 static inline void trigger_load_balance(struct rq *rq, int cpu) { #ifdef CONFIG_NO_HZ // rq->idle_at_tick = 0表示rq上運行的非idle進程 // rq->in_nohz_recently表示最近關閉了周期時鍾 if (rq->in_nohz_recently && !rq->idle_at_tick) { rq->in_nohz_recently = 0; //如果之前由本cpu執行idle load balance if (atomic_read(&nohz.load_balancer) == cpu) { //nohz.cpu_mask中的cpu表示停用了周期時鍾,在select_nohz_load_balancer中被加入 cpumask_clear_cpu(cpu, nohz.cpu_mask); //進入tick的時候rq變得有事可做,則不再做idle load balance atomic_set(&nohz.load_balancer, -1); } //從nohz.cpu_mask中選擇一個cpu做idle load balance if (atomic_read(&nohz.load_balancer) == -1) { int ilb = find_new_ilb(cpu); if (ilb < nr_cpu_ids) { //向該cpu發送ipi resched_cpu(ilb); } } } //如果所有cpu均處於idle狀態,本cpu做idle load balance,則通知本cpu停止idle load balance if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) == cpu && cpumask_weight(nohz.cpu_mask) == num_online_cpus()) { resched_cpu(cpu); return; } //如果本cpu處於idle狀態,其他cpu做idle load balance,則不需要raise其SCHED_SOFTIRQ if (rq->idle_at_tick && atomic_read(&nohz.load_balancer) != cpu && cpumask_test_cpu(cpu, nohz.cpu_mask)) return; #endif //本cpu沒有加入到任何domain,則不需要raise其SCHED_SOFTIRQ if (time_after_eq(jiffies, rq->next_balance) && likely(!on_null_domain(cpu))) { //raise其SCHED_SOFTIRQ raise_softirq(SCHED_SOFTIRQ); } } // 負載均衡軟中斷 // 由trigger_load_balance函數觸發 // 函數任務: // 1.自下而上遍曆cpu所屬的sched domain,對其進行負載均衡 // 2.如果本cpu負責idle load balance,代停用周期時鍾的cpu執行load balance // 2.1 遍曆nohz.cpu_mask中所有的idle cpu // 2.2.1 代其執行步驟1 // 2.2.2 如果這這段時間內本cpu有非idle進程就緒,退出ilb,下一次負載均衡時發生時再ilb // 2.2.3 如果idle cpu下一次進行負載均衡的時間戳大於本cpu // 2.2.3.1 更新idle cpu下一次負載均衡的時間為本cpu進行負載均衡的時間戳 2.1 static void run_rebalance_domains(struct softirq_action *h) { int this_cpu = smp_processor_id(); struct rq *this_rq = cpu_rq(this_cpu); //cpu當前狀態 // 如果rq上當前運行的為idle task則cpu為idle狀態 enum cpu_idle_type idle = this_rq->idle_at_tick ? CPU_IDLE : CPU_NOT_IDLE; //為cpu在同一個domain內執行load balance rebalance_domains(this_cpu, idle); #ifdef CONFIG_NO_HZ //如果本cpu負責idle load balance,代停用周期時鍾的cpu執行load balance if (this_rq->idle_at_tick && atomic_read(&nohz.load_balancer) == this_cpu) { struct rq *rq; int balance_cpu; for_each_cpu(balance_cpu, nohz.cpu_mask) { if (balance_cpu == this_cpu) continue; //非idle進程就緒,不在執行idle load balance,下一次load balance發生時再ilb if (need_resched()) break; //代idle的cpu執行load balance rebalance_domains(balance_cpu, CPU_IDLE); //更新本rq下一次load balance的時間為所有被代理rq執行load balance中最早的 rq = cpu_rq(balance_cpu); if (time_after(this_rq->next_balance, rq->next_balance)) this_rq->next_balance = rq->next_balance; } } #endif } // 負載均衡 // 在cpu所屬的sched domain層次結構上執行load balance // 調用路徑:run_rebalance_domains->rebalance_domains // 函數參數: // idle,cpu處於的狀態 // CPU_IDLE,cpu上運行的idle task // CPU_NOT_IDLE,cpu上運行的非idle task // 函數任務: // 1.自上而下遍曆rq所屬的所有sched domain // 1.1 如果此domain不需要執行load balance(沒有設置SD_LOAD_BALANCE),則跳過 // 1.2 計算domain執行load balance的時間間隔 // 1.2.1 由domain的balance_interval指定load balance在domain上執行的時間間隔 // 1.2.2 降低非idle狀態的cpu通過放大時間間隔降低load balance執行的頻率 // 1.2.3 在HZ*NR_CPUS/10時間內,必須對domain執行一次load balance // 1.3 如果domain的load balance需要串行執行,則獲取balance鎖 // 1.4 如果當前時間到達domain load balance執行的時間點 // 1.4.1 從同一個domain的其他cpu拉進程到本cpu執行 // 1.4.2 如果成功從其他cpu上拉進程到本cpu,則設置cpu不再為idle狀態 // 1.5 如果domain的load balance需要串行執行,則釋放balance鎖 // 1.6 通過next_balance記錄rq下一次執行load balance的時間為其所屬domain中 // 最近的load balance時間 // 1.7 如果返回值balance=0,說明已經完成了負載均衡,退出遍曆,否則繼續1.1 // 2.更新rq下一次執行load balance的時間為其所屬domain中最近的load balance時間 3.1 static void rebalance_domains(int cpu, enum cpu_idle_type idle) { int balance = 1; struct rq *rq = cpu_rq(cpu); unsigned long interval; struct sched_domain *sd; //rebalance執行最近的時間 unsigned long next_balance = jiffies + 60*HZ; int update_next_balance = 0; int need_serialize; //遍曆cpu所屬的所有sched domain for_each_domain(cpu, sd) { //此domain不需要load balance if (!(sd->flags & SD_LOAD_BALANCE)) continue; //domain執行load balance的時間間隔 interval = sd->balance_interval; //降低非idle狀態cpu的load balance的執行頻率 // 通過放大load balance執行的最小間隔達到降低頻率的目的 if (idle != CPU_IDLE) interval *= sd->busy_factor; interval = msecs_to_jiffies(interval); if (unlikely(!interval)) interval = 1; //最大間隔為HZ*NR_CPUS/10 if (interval > HZ*NR_CPUS/10) interval = HZ*NR_CPUS/10; //此domain的load balance需要串行執行 need_serialize = sd->flags & SD_SERIALIZE; if (need_serialize) { //試圖獲取串行load balance失敗,則放棄此次load balance if (!spin_trylock(&balancing)) goto out; } //如果當前時間到達domain load balance執行的時間點 if (time_after_eq(jiffies, sd->last_balance + interval)) { //從同一個domain的其他cpu拉進程到本cpu執行 if (load_balance(cpu, rq, sd, idle, &balance)) { //由於已經從其他cpu上拉來了進程,因此本cpu不在是idle狀態 idle = CPU_NOT_IDLE; } //更新domain上一次load balance的時間為當前jiffies sd->last_balance = jiffies; } if (need_serialize) spin_unlock(&balancing); out: if (time_after(next_balance, sd->last_balance + interval)) { //next_balance記錄下一次執行load balance最近的時間 next_balance = sd->last_balance + interval; update_next_balance = 1; } //完成均衡 if (!balance) break; } //更新rq下一次load balance執行時間為所屬domain中下一次load balance最早的時間 if (likely(update_next_balance)) rq->next_balance = next_balance; }
最後更新:2017-04-03 12:53:43