調度子係統7_負載均衡(四)
// 尋找sched domain中最忙的group // 函數參數: // sd:待查找的sched domain // this_cpu:當前正在對其執行負載均衡的cpu // imbalance:為達到平衡需要移動的權重 // idle:this_cpu當前的狀態 // sd_idle: sd空閑狀態 // cpus:可作為源cpu的集合 // balance:指示this_cpu是否適合負載均衡 // 返回值: // 如果存在不均衡,返回最忙的group // 否則,如果用戶建議power-savings balance,返回最不忙的group, // 通過將其中cpus的進程移動到本group,使其idle // 函數任務: // 1.計算sd的負載信息 // 2.根據統計信息,決定是否進行負載均衡 // 2.1 this_cpu不適合在sd中進行均衡,則返回 // 2.2 沒有最忙的group,或者最忙group可運行進程數為0,則返回 // 2.3 this_cpu所在group的負載大於最忙group的負載,則返回 // 2.4 計算sched domain的平均負載 // 公式:(SCHED_LOAD_SCALE * sds.total_load) / sds.total_pwr // 2.4.1 如果this_group的負載大於等於平均負載,則返回 // 2.5 this_cpu所在group的負載閾值超過了最忙group的負載閾值,則返回 // this_cpu所在group的負載閾值計算公式:sd->imbalance_pct * sds.this_load // 2.6 運行到此處,說明存在失衡,計算失衡的負載量(即需要移動的負載數) // 2.7 返回最忙的group // 3.如果sd負載沒有失衡,計算是否可以通過負載均衡來省電 // 3.1 返回最不忙的group 1.1 static struct sched_group *find_busiest_group(struct sched_domain *sd, int this_cpu, unsigned long *imbalance, enum cpu_idle_type idle, int *sd_idle, const struct cpumask *cpus, int *balance) { struct sd_lb_stats sds; memset(&sds, 0, sizeof(sds)); //計算sd的負載 update_sd_lb_stats(sd, this_cpu, idle, sd_idle, cpus, balance, &sds); //this_cpu不適合在sd中進行均衡,則返回 if (!(*balance)) goto ret; //沒有最忙的group,或者最忙group可運行進程數為0,則返回 if (!sds.busiest || sds.busiest_nr_running == 0) goto out_balanced; //this_cpu所在group的負載大於最忙group的負載,則返回 if (sds.this_load >= sds.max_load) goto out_balanced; //sd的平均權重 sds.avg_load = (SCHED_LOAD_SCALE * sds.total_load) / sds.total_pwr; //this_cpu所在group負載大於sd的平均負載,則返回 if (sds.this_load >= sds.avg_load) goto out_balanced; //imbalance_pct,進行負載均衡的閾值 if (100 * sds.max_load <= sd->imbalance_pct * sds.this_load) goto out_balanced; //存在失衡,計算需要均衡的負載量 calculate_imbalance(&sds, this_cpu, imbalance); //返回最忙的group return sds.busiest; out_balanced: //沒有明顯的失衡,檢查是否可以進行通過負載均衡省電 if (check_power_save_busiest_group(&sds, this_cpu, imbalance)) return sds.busiest; ret: *imbalance = 0; return NULL; } // 計算sched domain負載均衡統計信息 // 函數參數: // sd:待計算負載統計信息的sd // this_cpu:當前正在對其執行負載均衡的cpu // idle:this_cpu的idle狀態 // sd_idle:sd的idle狀態 // cpu:可作為源cpu的掩碼 // balance:指示是否應該進行負載均衡 // sds:保存統計信息的變量 // 函數任務: // 1.遍曆sched domain中所有的group // 1.1 計算當前group的負載信息 // 1.2 如果this_cpu在當前group,並且當前group已經均衡,則退出 // 1.3 更新sched domain的負載統計 // 1.3.1 sds->total_load統計所有group的負載 // 1.3.2 sds->total_pwr統計所有group的cpu power // 1.4 如果sched domain的子domain設置了SD_PREFER_SIBLING標誌 // 1.4.1 說明sched domain的sibling之間移動進程 // 1.4.2 降低本group的group_capacity,之後將所有多餘進程移動到其他sibling // 1.5 如果this_cpu屬於當前group,更新sched domain中關於this_cpu所在group的記錄信息 // 1.6 如果當前group是sched domain中負載最重的group,記錄group的負載信息 // 1.6.1 sds->max_load,記錄sched domain內負載最重group的負載量 // 1.6.2 sds->busiest,記錄sched domain內負載最重group的編號 // 1.7 更新sched domain power saving的信息 // 注: // sched domain下所有sched group組織成環形鏈表的形式。 1.2 static inline void update_sd_lb_stats(struct sched_domain *sd, int this_cpu, enum cpu_idle_type idle, int *sd_idle, const struct cpumask *cpus, int *balance, struct sd_lb_stats *sds) { struct sched_domain *child = sd->child; struct sched_group *group = sd->groups; struct sg_lb_stats sgs; int load_idx, prefer_sibling = 0; if (child && child->flags & SD_PREFER_SIBLING) prefer_sibling = 1; //初始化power saving的信息 init_sd_power_savings_stats(sd, sds, idle); load_idx = get_sd_load_idx(sd, idle); //遍曆sched domain中所有的group do { int local_group; //判斷this_cpu是否當前group中 local_group = cpumask_test_cpu(this_cpu, sched_group_cpus(group)); memset(&sgs, 0, sizeof(sgs)); //計算當前group的負載信息 update_sg_lb_stats(sd, group, this_cpu, idle, load_idx, sd_idle, local_group, cpus, balance, &sgs); //this_cpu在本group內,並且group已經均衡,則返回 if (local_group && !(*balance)) return; //更新sched domain的負載統計信息 sds->total_load += sgs.group_load; sds->total_pwr += group->cpu_power; //sched domain中的子sched domain設置SD_PREFER_SIBLING,標識sched domain的sibling之間移動進程 //降低本group的group_capacity,將所有多餘進程移動到其他sibling if (prefer_sibling) sgs.group_capacity = min(sgs.group_capacity, 1UL); //this_cpu屬於group,更新sched domain中關於this_cpu所在group的記錄信息 if (local_group) { //sds->this_load,this_cpu所在group的負載 sds->this_load = sgs.avg_load; //sds->this,this_cpu所在的group sds->this = group; sds->this_nr_running = sgs.sum_nr_running; sds->this_load_per_task = sgs.sum_weighted_load; //當前group的平均負載大於sched domain中已遍曆group的最大的負載 //當前group就緒進程的個數大於group的容量,或者group設置了imb標識 } else if (sgs.avg_load > sds->max_load && (sgs.sum_nr_running > sgs.group_capacity || sgs.group_imb)) { //更新sched domain中用於記錄具有最大負載group的信息 sds->max_load = sgs.avg_load; sds->busiest = group; sds->busiest_nr_running = sgs.sum_nr_running; sds->busiest_group_capacity = sgs.group_capacity; sds->busiest_load_per_task = sgs.sum_weighted_load; sds->group_imb = sgs.group_imb; } //更新sched domain power saving的信息 update_sd_power_savings_stats(group, sds, local_group, &sgs); //繼續遍曆下一個group group = group->next; } while (group != sd->groups); } // 計算sched group的負載信息 // 函數參數: // sd,group所在的sched domain // group,當前要計算的group // this_cpu,當前對其進行負載均衡的cpu // idle,this_cpu的idle狀態 // load_idx,Load index of sched_domain of this_cpu for load calc // sd_idle,group所在sched domain的idle狀態 // local_group,指示當前group是否包含this_cpu // cpus,可選為源cpu的掩碼集合 // balance:指示是否應該進行負載均衡 // sgs,收集統計信息的變量 // 函數任務: // 1.遍曆group中的候選cpu // 1.1 如果cpu有進程運行,更新sched domain為非idle狀態 // 1.2 獲取cpu的曆史負載load // 1.2.1 如果this_cpu在group內,返回max(cpu->cpu_load[load_idx], rq->load.weight) // 1.2.2 如果this_cpu不在group內,返回min(cpu->cpu_load[load_idx], rq->load.weight) // 1.3 更新group的統計信息 // 1.3.1 group->group_load, group的曆史負載 // 1.3.2 group->sum_nr_running, group中進程總數 // 1.3.3 group->sum_weighted_load, group的當前負載 // 2.更新group的cpu power // 2.1 sched domain的cpu power保存在其sd->groups->cpu_power中(即domain包含的第一個group的cpu_power字段) // 2.2 sd->groups->cpu_power等於子domain的cpu power總和 // 3.計算group的平均負載 // 3.1 公式 avg_load = group_load/group->cpu_power // 4.計算group每進程負載 // 4.1 公式 avg_load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running // 5.如果group內cpu最大、最小負載懸殊,設置標識標識group內不均衡 // 5.1 公式 (max_cpu_load - min_cpu_load) > 2*avg_load_per_task // 6.更新sched group的group_capacity,即能接納的進程容量 // 注: // cpu power用於表示cpu group的能力,不同層次的cpu group具有不同的計算公式: // cpu domain: cpu_power = SCHED_LOAD_SCALE // physical domain:SCHED_LOAD_SCALE+SCHED_LOAD_SCALE*(cpus_weight(cpumask)-1)/10 // 其中cpus_weight計算物理cpu裏的邏輯核個數(超線程) // node domain:在相同node domain下所有physical domain的cpu power的總和 1.3 static inline void update_sg_lb_stats(struct sched_domain *sd, struct sched_group *group, int this_cpu, enum cpu_idle_type idle, int load_idx, int *sd_idle, int local_group, const struct cpumask *cpus, int *balance, struct sg_lb_stats *sgs) { unsigned long load, max_cpu_load, min_cpu_load; int i; unsigned int balance_cpu = -1, first_idle_cpu = 0; unsigned long avg_load_per_task = 0; //this_cpu在當前group if (local_group) balance_cpu = group_first_cpu(group); //最大、最小負載 max_cpu_load = 0; min_cpu_load = ~0UL; //遍曆group中的候選cpu for_each_cpu_and(i, sched_group_cpus(group), cpus) { struct rq *rq = cpu_rq(i); //非idle狀態,並且有進程 if (*sd_idle && rq->nr_running) *sd_idle = 0; //this_cpu在group內 if (local_group) { //當前cpu為idle,並且為發現的第一個idle cpu if (idle_cpu(i) && !first_idle_cpu) { first_idle_cpu = 1; //balance_cpu記錄this_cpu所在group內的第一個idle cpu balance_cpu = i; } //返回cpu i當前負載和load_idx曆史負載記錄兩者最大的 load = target_load(i, load_idx); } else //this_cpu不在group內 { //返回cpu i當前負載和load_idx曆史負載記錄兩者最大的 load = source_load(i, load_idx); //max_cpu_load,min_cpu_load記錄最大、最小負載 if (load > max_cpu_load) max_cpu_load = load; if (min_cpu_load > load) min_cpu_load = load; } //更新group的統計量 //load_idx,曆史負載統計 sgs->group_load += load; //sched group中進程總數 sgs->sum_nr_running += rq->nr_running; //cpu_rq(cpu)->load.weight,當前tick的負載統計 sgs->sum_weighted_load += weighted_cpuload(i); } //隻有當前domain的first idle cpu和first cpu(busiest)合適做load balance //CPU_NEWLY_IDLE類型的load balance將總是被允許 if (idle != CPU_NEWLY_IDLE && local_group && balance_cpu != this_cpu) { //不需要做負載均衡 *balance = 0; return; } //更新group的cpu power update_group_power(sd, this_cpu); //計算group的平均負載 sgs->avg_load = (sgs->group_load * SCHED_LOAD_SCALE) / group->cpu_power; //更新group內進程平均負載 // sgs->sum_nr_running記錄group內進程總數 if (sgs->sum_nr_running) avg_load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running; //如果group內最大負載、最小負載懸殊,表示組內不均衡 if ((max_cpu_load - min_cpu_load) > 2*avg_load_per_task) sgs->group_imb = 1; //更新sched group的group_capacity,即能接納的進程容量 sgs->group_capacity = DIV_ROUND_CLOSEST(group->cpu_power, SCHED_LOAD_SCALE); }
//參考 負載均衡(二)中的論文

最後更新:2017-04-03 12:53:49