調度子系統4_負載均衡(一)

//	參考:http://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;
}

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