2005-10-21 Paul Jakma <paul.jakma@sun.com>

	* (general) SPF millisecond resolution timer with adaptive,
	  linear back-off holdtime. Prettification of ospf_timer_dump.
	* ospf_dump.c: (ospf_timeval_dump) new function. The guts of
	  ospf_timer_dump, but made to be more dynamic in printing out
	  the relative timeval, sliding the precision printed out
	  according to the value.
	  (ospf_timer_dump) guts moved to ospf_timeval_dump.
	* ospf_dump.h: export ospf_timeval_dump.
	* ospf_flood.c: (ospf_flood) remove gettimeofday, use
	  the libzebra exported recent_time instead, as it's not
	  terribly critical to have time exactly right - the dropped
	  LSA will be retransmited to us if we don't ACK it.
	* ospf_packet.c: (ospf_ls_upd_timer) Ditto, but here we're
	  not transmitting, just putting LSA back on update transmit list.
	* ospfd.h: delay and holdtimes should be unsigned.
	  Add spf_max_holdtime and spf_hold_multiplier.
	  Update default defines for delay and hold time to be in msec.
	  (struct ospf) change the SPF timestamp to a struct timeval.
	  Remove ospf_timers_spf_(un)?set.
	* ospfd.c: (ospf_timers_spf_{set,unset}) removed.
	  (ospf_new) initialise spf_max_holdtime and spf_hold_multiplier
	* ospf_spf.c: (ospf_spf_calculate) SPF timestamp is a timeval
	  now, update with gettimeofday.
	  (ospf_spf_calculate_schedule) Change SPF timers to millisecond
	  resolution.
	  Make the holdtime be adaptive, with a linear increase in
	  holdtime ever consecutive SPF run which occurs within holdtime
	  of previous SPF, bounded by spf_max_holdtime.
	* ospf_vty.c: Update spf timers commands.
	  (ospf_timers_spf_set) trivial helper.
	  (ospf_timers_throttle_spf_cmd) new command to set SPF delay,
	  initial hold and max hold times with millisecond resolution.
	  (ospf_timers_spf_cmd) Deprecated. Accept the old values,
	  convert to msec, truncate to new limits.
	  (no_ospf_timers_throttle_spf_cmd) set timers to defaults.
	  (no_ospf_timers_spf_cmd) deprecated form, same as previous.
	  (show_ip_ospf_cmd) Display SPF parameters and times.
	  (show_ip_ospf_neighbour_header) Centralise the 'sh ip os ne'
	  header.
	  (show_ip_ospf_neighbor_sub) Fix the field widths. Get rid of
	  the multiple spaces which were making the lines even longer.
	  (show_ip_ospf_neighbor_cmd) Use show_ip_ospf_neighbour_header
	  (show_ip_ospf_neighbor_all_cmd) ditto and fix the field
	  widths for NBMA neighbours.
	  (show_ip_ospf_neighbor_int) Use header function.
	  (show_ip_ospf_nbr_nbma_detail_sub) use sizeof for timebuf,
	  local array - safer.
	  (show_ip_ospf_neighbor_detail_sub) ditto
	  (ospf_vty_init) install the new SPF throttle timer commands.
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index b05117d..f6260fb 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1077,7 +1077,7 @@
   /* Increment SPF Calculation Counter. */
   area->spf_calculation++;
 
-  area->ospf->ts_spf = time (NULL);
+  gettimeofday (&area->ospf->ts_spf, NULL);
 
   if (IS_DEBUG_OSPF_EVENT)
     zlog_debug ("ospf_spf_calculate: Stop");
@@ -1151,7 +1151,8 @@
 void
 ospf_spf_calculate_schedule (struct ospf *ospf)
 {
-  time_t ht, delay;
+  unsigned long delay, elapsed, ht;
+  struct timeval result;
 
   if (IS_DEBUG_OSPF_EVENT)
     zlog_debug ("SPF: calculation timer scheduled");
@@ -1159,7 +1160,7 @@
   /* OSPF instance does not exist. */
   if (ospf == NULL)
     return;
-
+  
   /* SPF calculation timer is already scheduled. */
   if (ospf->t_spf_calc)
     {
@@ -1168,22 +1169,42 @@
                    ospf->t_spf_calc);
       return;
     }
-
-  ht = time (NULL) - ospf->ts_spf;
-
+  
+  /* XXX Monotic timers: we only care about relative time here. */
+  timersub (&recent_time, &ospf->ts_spf, &result);
+  
+  elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
+  ht = ospf->spf_holdtime * ospf->spf_hold_multiplier;
+  
+  if (ht > ospf->spf_max_holdtime)
+    ht = ospf->spf_max_holdtime;
+  
   /* Get SPF calculation delay time. */
-  if (ht < ospf->spf_holdtime)
+  if (elapsed < ht)
     {
-      if (ospf->spf_holdtime - ht < ospf->spf_delay)
+      /* Got an event within the hold time of last SPF. We need to
+       * increase the hold_multiplier, if it's not already at/past
+       * maximum value, and wasn't already increased..
+       */
+      if (ht < ospf->spf_max_holdtime)
+        ospf->spf_hold_multiplier++;
+      
+      /* always honour the SPF initial delay */
+      if ( (ht - elapsed) < ospf->spf_delay)
         delay = ospf->spf_delay;
       else
-        delay = ospf->spf_holdtime - ht;
+        delay = ht - elapsed;
     }
   else
-    delay = ospf->spf_delay;
-
+    {
+      /* Event is past required hold-time of last SPF */
+      delay = ospf->spf_delay;
+      ospf->spf_hold_multiplier = 1;
+    }
+  
   if (IS_DEBUG_OSPF_EVENT)
-    zlog_debug ("SPF: calculation timer delay = %ld", (long)delay);
+    zlog_debug ("SPF: calculation timer delay = %ld", delay);
+
   ospf->t_spf_calc =
-    thread_add_timer (master, ospf_spf_calculate_timer, ospf, delay);
+    thread_add_timer_msec (master, ospf_spf_calculate_timer, ospf, delay);
 }