網絡子係統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