調度子係統6_負載均衡(三)
// 負載均衡 // 在sched_domain中進行負載均衡,檢查是否可以通過最繁忙的組中遷移一些進程到本cpu // 函數參數: // this_cpu, 其上執行負載均衡的cpu // this_rq, 其上執行負載均衡的rq // sd, 其上執行負載均衡的sched domain // idle, this_cpu的狀態 // CPU_SCHED_IDLE,this_cpu空閑 // CPU_NOT_IDLE,this_cpu不空閑 // balance,sd中是否均衡 // 函數任務: // 1.獲取當前在線的cpu到本地 // 2.sched domain中尋找最忙的sched group // 3.sched group中尋找最忙的cpu // 4.最忙cpu就緒進程數>=2,從最忙cpu移動進程到this_cpu // 4.1 關本cpu中斷 // 4.2 獲取this_cpu, 最忙cpu的rq的鎖 // 4.3 移動進程到this_cpu // 4.4 如果最忙cpu中所有進程均設置親和性,移動進程失敗 // 4.4.1 從掩碼中清除最忙cpu,如果掩碼不空,則重複步驟2進行負載均衡 // 4.5 如果移動進程成功 // 4.5.1 如果本cpu不是this_cpu,通過ipi喚醒this_cpu重調度 // 4.5.2 更新domain負載均衡失敗次數計數器為 0 // 5.最忙cpu就緒進程數<=1,或從最忙cpu移動進程失敗 // 5.1 檢查是否可以進行主動負載均衡(負載均衡失敗次數上限) // 5.1.1 設置最忙cpu的active_balance標誌 // 5.1.2 設置最忙cpu的push_cpu為this_cpu,表示是this_cpu向其發起了主動負載均衡 // 5.1.3 喚醒最忙cpu的migration_thread進程 // 5.1.4 更新domain負載均衡失敗次數計數器為 上限-1 // 6.調整負載均衡時間間隔 // 6.1 如果沒有發起主動負載均衡,下次盡早到期 // 6.2 否則,推遲下次負載均衡的時間 1.1 static int load_balance(int this_cpu, struct rq *this_rq, struct sched_domain *sd, enum cpu_idle_type idle, int *balance) { int ld_moved, all_pinned = 0, active_balance = 0, sd_idle = 0; struct sched_group *group; unsigned long imbalance; struct rq *busiest; unsigned long flags; struct cpumask *cpus = __get_cpu_var(load_balance_tmpmask); //在線的cpu cpumask_copy(cpus, cpu_active_mask); //SD_SHARE_CPUPOWER,Domain members share cpu power //SD_POWERSAVINGS_BALANCE, Balance for power savings //cpu空閑,共享cpu power,power saving不進行balance if (idle != CPU_NOT_IDLE && sd->flags & SD_SHARE_CPUPOWER && !test_sd_parent(sd, SD_POWERSAVINGS_BALANCE)) sd_idle = 1; redo: //在sched domain中尋找最忙的sched group group = find_busiest_group(sd, this_cpu, &imbalance, idle, &sd_idle, cpus, balance); //sched group之間已經均衡 if (*balance == 0) goto out_balanced; //在sched group中尋找最忙的rq busiest = find_busiest_queue(group, idle, imbalance, cpus); //rq之間已經均衡 if (!busiest) { goto out_balanced; } ld_moved = 0; //最忙cpu可運行的進程>=2,從最忙cpu移動進程到本cpu if (busiest->nr_running > 1) { //關本cpu中斷 local_irq_save(flags); //同時獲取本rq和最忙rq的鎖 double_rq_lock(this_rq, busiest); //從最忙rq移動進程到本rq ld_moved = move_tasks(this_rq, this_cpu, busiest, imbalance, sd, idle, &all_pinned); double_rq_unlock(this_rq, busiest); local_irq_restore(flags); //成功移動進程到this_cpu,但運行負載均衡的cpu非當前cpu,喚醒this_cpu, if (ld_moved && this_cpu != smp_processor_id()) resched_cpu(this_cpu); //最忙cpu中的進程全部設置親和性被綁定 if (unlikely(all_pinned)) { //不在考慮此最忙的cpu cpumask_clear_cpu(cpu_of(busiest), cpus); //繼續在sched domain中尋找忙碌的cpu if (!cpumask_empty(cpus)) goto redo; //domain中的所有cpu都不能進行負載均衡,退出 goto out_balanced; } } //最忙cpu可運行的進程<=1或者移動進程失敗 if (!ld_moved) { //sd->nr_balance_failed > sd->cache_nice_tries+2時,啟動主動負載均衡 // 主動負載均衡: // 最忙的cpu主動向空閑cpu搬移進程 // 步驟: // 1.從lowest-level scheduling domain遍曆每一個CPU GROUP中的cpu // 1.1 如果cpu idle,則向其遷移一個進程 // 1.2 繼續尋找idle的cpu // 2.當最忙的cpu遍曆完該scheduling domain中的每一個CPU GROUP的每一個cpu // 2.1 向higher-level scheduling domain,直到其隻剩兩個進程或者遇到 // 沒有設置SD_LOAD_BALANCE的scheduling domain if (need_active_balance(sd, sd_idle, idle)) { //關中斷,獲取最忙cpu rq的鎖 raw_spin_lock_irqsave(&busiest->lock, flags); //this_cpu不在最忙cpu允許的域中 if (!cpumask_test_cpu(this_cpu, &busiest->curr->cpus_allowed)) { raw_spin_unlock_irqrestore(&busiest->lock, flags); //等價於全部設置了親和性 all_pinned = 1; goto out_one_pinned; } //發起主動負載均衡 if (!busiest->active_balance) { //設置最忙cpu的active_balance標誌 busiest->active_balance = 1; //向其發起主動負載均衡的cpu busiest->push_cpu = this_cpu; active_balance = 1; } raw_spin_unlock_irqrestdore(&busiest->lock, flags); //喚醒最忙cpu的migration_thread進程 if (active_balance) wake_up_process(busiest->migration_thread); //重置負載均衡失敗次數 sd->nr_balance_failed = sd->cache_nice_tries+1; } } else { //成功進行了負載均衡,nr_balance_failed設置為0 sd->nr_balance_failed = 0; } //調整負載均衡時間間隔 if (likely(!active_balance)) { //沒有發起主動負載均衡,下次盡早到期 sd->balance_interval = sd->min_interval; } else { //推遲下一次負載均衡的時間 if (sd->balance_interval < sd->max_interval) sd->balance_interval *= 2; } if (!ld_moved && !sd_idle && sd->flags & SD_SHARE_CPUPOWER && !test_sd_parent(sd, SD_POWERSAVINGS_BALANCE)) ld_moved = -1; goto out; out_balanced: schedstat_inc(sd, lb_balanced[idle]); sd->nr_balance_failed = 0; out_one_pinned: if ((all_pinned && sd->balance_interval < MAX_PINNED_INTERVAL) || (sd->balance_interval < sd->max_interval)) sd->balance_interval *= 2; if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER && !test_sd_parent(sd, SD_POWERSAVINGS_BALANCE)) ld_moved = -1; else ld_moved = 0; out: if (ld_moved) update_shares(sd); return ld_moved; }
最後更新:2017-04-03 12:53:47