閱讀618 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統70_路由緩存操作

//	刷新路由緩存
//	參數:
//		delay, 刷新操作的延遲時間

//	函數主要任務:
//		1.重新計算路由刷新定時器的到期時間
//		2.如果delay=0,則立即刷新緩存
//		3.激活定時器,使用最近的刷新延遲作為到期時間
1.1 void rt_cache_flush(int delay)
{
	unsigned long now = jiffies;
	//用戶態
	int user_mode = !in_softirq();

	if (delay < 0)
		delay = ip_rt_min_delay;

	spin_lock_bh(&rt_flush_lock);
	//刪除之前激活的定時器
	//重新計算定時器該到期的時間
	if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
		long tmo = (long)(rt_deadline - now);
		if (user_mode && tmo < ip_rt_max_delay-ip_rt_min_delay)
			tmo = 0;
		
		if (delay > tmo)
			delay = tmo;
	}
	//立即刷新路由緩存
	if (delay <= 0) {
		spin_unlock_bh(&rt_flush_lock);
		rt_run_flush(0);
		return;
	}
	//rt_deadline表示路由緩存必須被刷新的期限
	if (rt_deadline == 0)
		rt_deadline = now + ip_rt_max_delay;
	//激活緩存刷新定時器,最近到期的時間
	mod_timer(&rt_flush_timer, now+delay);
	spin_unlock_bh(&rt_flush_lock);
}
// 	刷新路由緩存
//		釋放所有路由緩存

//	調用路徑:rt_cache_flush->rt_run_flush
//	注:此函數同時為刷新定時器函數
1.2 static void rt_run_flush(unsigned long dummy)
{
	int i;
	struct rtable *rth, *next;
	rt_deadline = 0;

	get_random_bytes(&rt_hash_rnd, 4);
	//從緩存最後一個bucket開始遍曆
	for (i = rt_hash_mask; i >= 0; i--) {
		spin_lock_bh(&rt_hash_table[i].lock);
		//將衝突鏈表保存在本地,置鏈表null
		rth = rt_hash_table[i].chain;
		if (rth)
			rt_hash_table[i].chain = NULL;
		spin_unlock_bh(&rt_hash_table[i].lock);

		for (; rth; rth = next) {
			next = rth->u.rt_next;
			//釋放緩存
			rt_free(rth);
		}
	}
}
//	釋放緩存
//	調用路徑:rt_cache_flush->rt_run_flush->rt_free->dst_free
//	注:
//		dst->obsolete:
//			0,表示該結構有效而且可以被使用
//			2,表示該結構將被刪除因而不能被使用
//		   -1,被IPsec使用
1.3 static inline void dst_free(struct dst_entry * dst)
{	
	//表示dst已經在dst_garbage_list鏈表上
	if (dst->obsolete > 1)
		return;
	//引用計數為0
	if (!atomic_read(&dst->__refcnt)) {
		//釋放dst對l2幀頭緩存,鄰居項的引用
		dst = dst_destroy(dst);
		if (!dst)
			return;
	}
	//將dst加入到dst_garbage_list鏈表,通過垃圾回收機製進行回收
	__dst_free(dst);
}

//	插入新路由緩存
//	參數:
//		rt,新建的路由緩存
//		rp,導致創建新緩存的skb->dst
//		hash,新建緩存的hash值

//	函數主要任務:
//		1.遍曆緩存,如果緩存已經被添加,則將緩存移動到bucket的鏈表頭,退出
//		2.每次新插入路由緩存,都嚐試釋放一個合適的路由緩存,從而平衡路由緩存容量
//		3.綁定路由緩存到鄰居子係統
//			3.1 如果綁定失敗是由於鄰居子係統無法分配新的鄰居項
//			3.2 則對路由緩存進行同步垃圾回收
//			3.3 因為路由緩存會持有鄰居項的引用,通過釋放路由緩存,釋放鄰居項
//		4.將鄰居項緩存插入到緩存鏈表

//	注:
//		1.當插入一個新的路由緩存時,嚐試釋放一個舊的路由緩存來平衡容量。
//		2.候選項:
//			2.1 引用計數=0
//			2.2 得分在同一個bucket中最小
//		3.釋放條件:
//			3.1 有候選項
//			3.2 當前bucket鏈表長度大於平均bucket長度

//		4.ip_rt_gc_elasticity用於描述bucket的平均長度
2.1 static int rt_intern_hash(unsigned hash, struct rtable *rt, struct rtable **rp)
{
	struct rtable	*rth, **rthp;
	unsigned long	now;
	struct rtable *cand, **candp;
	u32 		min_score;
	int		chain_length;
	//當前上下文環境
	int attempts = !in_softirq();

restart:
	chain_length = 0;
	//最小的分
	min_score = ~(u32)0;
	cand = NULL;
	candp = NULL;
	now = jiffies;

	rthp = &rt_hash_table[hash].chain;
	//關下半部,獲取鎖
	spin_lock_bh(&rt_hash_table[hash].lock);
	while ((rth = *rthp) != NULL) {
		//該緩存已經被添加
		//移動該緩存到bucket鏈表頭
		if (compare_keys(&rth->fl, &rt->fl)) {
			//將緩存移動到bucket鏈表頭
			*rthp = rth->u.rt_next;
			rcu_assign_pointer(rth->u.rt_next,
					   rt_hash_table[hash].chain);
			rcu_assign_pointer(rt_hash_table[hash].chain, rth);

			rth->u.dst.__use++;
			dst_hold(&rth->u.dst);
			rth->u.dst.lastuse = now;
			spin_unlock_bh(&rt_hash_table[hash].lock);

			rt_drop(rt);
			*rp = rth;
			return 0;
		}
		//在同一個bucket中選擇刪除候選項
		//刪除候選項隻考慮引用計數=0的選項
		if (!atomic_read(&rth->u.dst.__refcnt)) {
			//按照緩存是否適合刪除,給緩存評分
			u32 score = rt_score(rth);
			//尋找最小得分				
			if (score <= min_score) {
				cand = rth;
				candp = rthp;
				min_score = score;
			}
		}
		//當前衝突鏈的長度
		chain_length++;
		rthp = &rth->u.rt_next;
	}

	if (cand) {
		//在bucket鏈表超過平均長度,釋放適合刪除的緩存
		if (chain_length > ip_rt_gc_elasticity) {
			*candp = cand->u.rt_next;
			rt_free(cand);
		}
	}
	//綁定單播輸出路由到鄰居子係統
	if (rt->rt_type == RTN_UNICAST || rt->fl.iif == 0) {
		//綁定路由到鄰居子係統,由鄰居子係統負責完成l3->l2地址的映射
		int err = arp_bind_neighbour(&rt->u.dst);
		if (err) {
			spin_unlock_bh(&rt_hash_table[hash].lock);
			//綁定路由緩存可能會觸發創建新的鄰居項
			//當鄰居子係統無法釋放內存時,嚐試釋放路由緩存
			//因為路由緩存會持有鄰居子係統的引用
			if (attempts-- > 0) {
				int saved_elasticity = ip_rt_gc_elasticity;
				int saved_int = ip_rt_gc_min_interval;
				ip_rt_gc_elasticity	= 1;
				ip_rt_gc_min_interval	= 0;
				//同步回收路由緩存
				rt_garbage_collect();
				ip_rt_gc_min_interval	= saved_int;
				ip_rt_gc_elasticity	= saved_elasticity;
				goto restart;
			}

			rt_drop(rt);
			return -ENOBUFS;
		}
	}
	//將路由項插入到路由緩存hash表
	rt->u.rt_next = rt_hash_table[hash].chain;
	rt_hash_table[hash].chain = rt;
	spin_unlock_bh(&rt_hash_table[hash].lock);
	*rp = rt;
	return 0;
}

最後更新:2017-04-03 14:53:50

  上一篇:go 基於中間件/構件的開發
  下一篇:go 【轉載】第一個GNURadio應用程序心得