調度子系統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);
}


//參考 負載均衡(二)中的論文

                        


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章