// 刷新路由缓存
// 参数:
// 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;
}
网络子系统70_路由缓存操作
最新推荐文章于 2022-07-23 17:44:18 发布