对于通用邻居层,我认为主要可以分为三个方面:
1、邻居项处理函数,包括邻居项创建、更新、删除等
2、邻居项的状态机机制,主要是处理邻居项中状态的改变,其中包括几个邻居状态的定时器机制,也包括发送solicit请求等
3、邻居项的垃圾回收机制,主要是负责回收一个邻居表里长时间不用的邻居项,已节省邻居缓存空间。这三个方面需要相互协调工作,才能完成通用邻居层的功能,这一节就分析邻居项的垃圾回收机制。
对于垃圾回收,主要分为2个方面:同步垃圾回收和异步垃圾回收。
异步垃圾回收就是周期的进行垃圾回收,这个是周期性的。而同步垃圾回收机制主要是在创建邻居项而又没有缓存空间时,会调用同步垃圾回收,强制回收无效的邻居缓存。下面介绍
同步垃圾回收处理函数neigh_forced_gc。
这个函数的逻辑流程还是比较简单的。
该函数的功能是遍历邻接表中hash散列数组中的每一个hash表项中的所有邻接表,
对于邻居项的引用计数为1且状态不是NUD_PERMANENT时,则设置neigh->dead为1,并调用neigh_cleanup_and_release释放
该邻居项占用的缓存。
static int neigh_forced_gc(structneigh_table *tbl)
{
intshrunk = 0;
inti;
NEIGH_CACHE_STAT_INC(tbl,forced_gc_runs);
write_lock_bh(&tbl->lock);
for(i = 0; i <= tbl->hash_mask; i++) {
structneighbour *n, **np;
np= &tbl->hash_buckets[i];
while((n = *np) != NULL) {
/*Neighbour record may be discarded if:
* - nobody refers to it.
* - it is not permanent
*/
write_lock(&n->lock);
if(atomic_read(&n->refcnt) == 1 &&
!(n->nud_state & NUD_PERMANENT)) {
*np = n->next;
n->dead= 1;
shrunk = 1;
write_unlock(&n->lock);
neigh_cleanup_and_release(n);
continue;
}
write_unlock(&n->lock);
np= &n->next;
}
}
tbl->last_flush= jiffies;
write_unlock_bh(&tbl->lock);
returnshrunk;
}
对于异步垃圾回收,在2.6.34里,对于异步垃圾回收的函数实现机制上,进行了调整,
在2.6.21里,是直接使用定时器来实现异步清理的,而在2.6.34里,则是
使用带有定时器功能的工作队列来实现邻居项的内存异步清理的。
在2.6.1里,对于异步清理函数,每次定时器到期后,即会扫描邻居表项
中的邻居项hash数组中的一个hash表中的所有邻居表项,对于符合删除条件
的邻居项,则会调用函数neigh_release,释放该邻居项。
符合删除的条件为:
1、neigh->dead == 1
2、neigh->state == NUD_FAILED或者闲置时间超过了指定上限gc_staletime
而在2.6.34里,异步清理函数neigh_periodic_work每次清理时,会清理邻居表里的邻居
hash数组里所有hash表里的所有邻居项,而不是仅搜索一个hash表里的所有邻居项。
下面介绍同步清理的实际处理函数neigh_periodic_work
1、每隔5分钟,重置一次reachable_time的值,并更新邻居表的last_rand,以便于下次执行更新reach_time操作(而reachable_time用于处于reach状态的邻居项的超时
时间,这个就是用于NUD_REACHABLE状态下的定时器超时时间,是邻居项状态转换中的定时器处理)
2、遍历邻居表里的邻居hash数组里的每一个hash表里的所有邻居项,执行以下操作:
a)对于邻居项状态为NUD_PERMANENT或者NUD_IN_TIMER,则遍历下一个邻居项
b)当邻居项的引用计数为1且状态为NUD_FAILED时,则调用
neigh_cleanup_and_release释放该邻居项占用的缓存
c)当邻居项的引用计数为1且闲置时间超过gc_staletime时,调用neigh_cleanup_and_release释放该邻居项所占用的缓存
3、调用schedule_delayed_work,重启定时器,待定时器超时后则调用queue_work,调用
neigh_periodic_work进行新一轮的异步垃圾回收。
static void neigh_periodic_work(structwork_struct *work)
{
structneigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
structneighbour *n, **np;
unsignedint i;
NEIGH_CACHE_STAT_INC(tbl,periodic_gc_runs);
write_lock_bh(&tbl->lock);
/*
* periodicallyrecompute ReachableTime from random function
*/
if(time_after(jiffies, tbl->last_rand + 300 * HZ)) {
structneigh_parms *p;
tbl->last_rand= jiffies;
for(p = &tbl->parms; p; p = p->next)
p->reachable_time=
neigh_rand_reach_time(p->base_reachable_time);
}
for(i = 0 ; i <= tbl->hash_mask; i++) {
np= &tbl->hash_buckets[i];
while((n = *np) != NULL) {
unsignedint state;
write_lock(&n->lock);
state= n->nud_state;
if(state & (NUD_PERMANENT | NUD_IN_TIMER)) {
write_unlock(&n->lock);
gotonext_elt;
}
if(time_before(n->used, n->confirmed))
n->used= n->confirmed;
if(atomic_read(&n->refcnt) == 1 &&
(state == NUD_FAILED ||
time_after(jiffies, n->used +n->parms->gc_staletime))) {
*np= n->next;
n->dead= 1;
write_unlock(&n->lock);
neigh_cleanup_and_release(n);
continue;
}
write_unlock(&n->lock);
next_elt:
np= &n->next;
}
/*
* It's fine to release lock here, even if hashtable
* grows while we are preempted.
*/
write_unlock_bh(&tbl->lock);
cond_resched();
write_lock_bh(&tbl->lock);
}
/*Cycle through all hash buckets every base_reachable_time/2 ticks.
* ARP entry timeouts range from 1/2base_reachable_time to 3/2
* base_reachable_time.
*/
schedule_delayed_work(&tbl->gc_work,
tbl->parms.base_reachable_time>> 1);
write_unlock_bh(&tbl->lock);
}
至此,完成了通用邻居层的垃圾回收机制的处理功能。