网络子系统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