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