OSPF在计算SPF时,为了防止震荡以及连续收到LSA时多次计算SPF,某些代码里实现了避让算法。代码如下:
#define OSPF_SPF_INCREMENT_VALUE 2
#define ONE_SEC_MICROSECOND 1000000
void ospf_spf_calculate_timer_add (struct ospf_area *area)
{
struct ospf_master *om = area->top->om;
struct ospf *top = area->top;
timeval now, delta, delay, hold;
time_tzcurrent (&now, NULL);
delta = TV_SUB (now, area->tv_spf);
if ((TV_CMP (delta, area->tv_spf_curr)) < 0) {
delay = TV_SUB (area->tv_spf_curr, delta);
/* Change the value of tv_spf_curr to 5 times its value */
area->tv_spf_curr.tv_sec *= OSPF_SPF_INCREMENT_VALUE;
area->tv_spf_curr.tv_sec += ((OSPF_SPF_INCREMENT_VALUE *
area->tv_spf_curr.tv_usec) / ONE_SEC_MICROSECOND);
area->tv_spf_curr.tv_usec = (OSPF_SPF_INCREMENT_VALUE *
area->tv_spf_curr.tv_usec) % ONE_SEC_MICROSECOND;
}
else {
delay.tv_sec = delay.tv_usec = 0;
hold = area->tv_spf_curr;
hold.tv_sec *= OSPF_SPF_INCREMENT_VALUE;
hold.tv_sec += ((OSPF_SPF_INCREMENT_VALUE * hold.tv_usec) / ONE_SEC_MICROSECOND);
hold.tv_usec = (OSPF_SPF_INCREMENT_VALUE * hold.tv_usec) % ONE_SEC_MICROSECOND;
if (TV_CMP (top->spf_max_delay, hold) < 0)
hold = top->spf_max_delay;
if ((TV_CMP (hold, delta)) < 0) {
area->tv_spf_curr = top->spf_min_delay;
}
}
/* If delay < start delay , delay for another start_delay period */
if ((TV_CMP (top->spf_start_delay, delay)) > 0) {
delay = top->spf_start_delay;
}
if ((TV_CMP (top->spf_max_delay, area->tv_spf_curr)) < 0) {
/* Should not be more than max value */
area->tv_spf_curr = top->spf_max_delay;
}
/* Now schedule SPF timer. */
OSPF_TV_TIMER_ON (area->t_spf_calc, ospf_spf_calculate_timer, area, delay);
}
Tv_spf:SPF计算完毕后更新此变量
Tv_spf_cur:SPF本次需要延迟的时间,初始值为spf_min_delay
spf_max_delay:SPF最大延迟时间,设定为50s
spf_min_delay:SPF最小延迟时间,设定为500ms
spf_start_delay:SPF开始的延迟时间,设定为spf_min_delay,即500ms
1) 获取当前的系统时间,并计算当前时间和上一次SPF计算完的差值delta
2) 判断delta和spf_cur的差值,如果delta大于spf_cur,进入到a流程,否则进入到b流程
a) 设置delay时间为0,并计算hold值,hold=2*spf_cur,hold最大不能超过spf_max_delay,同时比较hold和delta,如果hold比delta小(即当前需要spf计算的时间超过了2倍的记录的延迟时间)就把spf_cur重新设置为spf_min_delay【即认为不再震荡,重新恢复spf_cur为初始值】
b) 设置delay时间为spf_cur和delta的差值,即认为还没有到设置的延迟时间,需要继续延迟delay的时间再触发。并且,认为发生了震荡,将spf_cur的时间设置为当前的2倍。
3) 判断spf_start_delay和delay的差值,如果大于零,进入a流程,否则b
a) 设置delay为spf_start_delay,即至少要延迟spf_start_delay的时间。
b) 不做任何设置
4) 判断spf_cur是否超过spf_max_delay,如果超过,进入(a),如果不超过进入(b)
a) Spf_cur = spf_max_delay
b) 不做任何处理
5) 开启定时器,超时时间为delay。
根据以上流程,可以看出,如果不是频繁震荡,那么流程总是1->2(a)->3(a)->4(b)->5。这个时候,SPF计算总是在spf_min_delay时间后触发,即500ms。
如果频繁震荡,那么流程会进入1->2(b)->3(b)->4(b)->5,此时设置的定时器将会是一个spf_cur-delta的差值,并且由于spf_cur会被更新为当前的2倍,这个延迟将会越来越大,直至50s。
如果震荡中恢复到稳态,就进入1->2(a),此时又会把spf_cur更新为最小值。
通过以上分析,可以看出无论是否震荡,系统总是延迟spf_min_delay才进行SPF计算,那么当某次接口down,需要快速收敛时,将会有很大的延迟。因此可以把spf_min_delay设置为更小的值来提高收敛速度。