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

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