]> git.puffer.fish Git - mirror/frr.git/commitdiff
ospf6d: schedule SPF to run on events rather than directly on each event.
authorDinesh Dutt <ddutt@cumulusnetworks.com>
Sat, 24 Aug 2013 07:54:09 +0000 (07:54 +0000)
committerDavid Lamparter <equinox@opensourcerouting.org>
Fri, 8 Nov 2013 02:15:30 +0000 (18:15 -0800)
OSPV3 SPF triggers on every SPF-able event instead of using timers the way
OSPFv2 does. This patch makes SPF be triggered/throttled similar to OSPFv2.
It adds a command to quagga identical to the OSPFv2 equivalent to configure
these timers.
Summary:

Signed-off-by: Dinesh Dutt <ddutt at cumulusnetworks.com>
Reviewed-by: Scott Feldman <sfeldma at cumulusnetworks.com>
[DL: removed reference to oa->ts_spf for rebase]
[DL: killed timeval_subtract]
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
doc/ospf6d.texi
ospf6d/ospf6_area.c
ospf6d/ospf6_interface.c
ospf6d/ospf6_spf.c
ospf6d/ospf6_spf.h
ospf6d/ospf6_top.c
ospf6d/ospf6_top.h

index 6667221cf787a72cfbf9bd3f429a2802e848f920..c01c0510d209c77b658f3f6e05557ed2088bd129 100644 (file)
@@ -28,6 +28,44 @@ Bind interface to specified area, and start sending OSPF packets.  @var{area} ca
 be specified as 0.
 @end deffn
 
+@deffn {OSPF6 Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {}
+@deffnx {OSPF6 Command} {no timers throttle spf} {}
+This command sets the initial @var{delay}, the @var{initial-holdtime}
+and the @var{maximum-holdtime} between when SPF is calculated and the
+event which triggered the calculation. The times are specified in
+milliseconds and must be in the range of 0 to 600000 milliseconds.
+
+The @var{delay} specifies the minimum amount of time to delay SPF
+calculation (hence it affects how long SPF calculation is delayed after
+an event which occurs outside of the holdtime of any previous SPF
+calculation, and also serves as a minimum holdtime).
+
+Consecutive SPF calculations will always be seperated by at least
+'hold-time' milliseconds. The hold-time is adaptive and initially is
+set to the @var{initial-holdtime} configured with the above command.
+Events which occur within the holdtime of the previous SPF calculation
+will cause the holdtime to be increased by @var{initial-holdtime}, bounded
+by the @var{maximum-holdtime} configured with this command. If the adaptive
+hold-time elapses without any SPF-triggering event occuring then
+the current holdtime is reset to the @var{initial-holdtime}.
+
+@example
+@group
+router ospf6
+ timers throttle spf 200 400 10000
+@end group
+@end example
+
+In this example, the @var{delay} is set to 200ms, the @var{initial
+holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence
+there will always be at least 200ms between an event which requires SPF
+calculation and the actual SPF calculation. Further consecutive SPF
+calculations will always be seperated by between 400ms to 10s, the
+hold-time increasing by 400ms each time an SPF-triggering event occurs
+within the hold-time of the previous SPF calculation.
+
+@end deffn
+
 @node OSPF6 area
 @section OSPF6 area
 
index 2d20e62517d23d7270b2fd2e9e80e7765637267c..1e07d8579482cd06bee5498675ff1ab41b103f46 100644 (file)
@@ -67,7 +67,7 @@ ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa)
           zlog_debug ("Schedule SPF Calculation for %s",
                      OSPF6_AREA (lsa->lsdb->data)->name);
         }
-      ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data));
+      ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6));
       break;
 
     case OSPF6_LSTYPE_INTRA_PREFIX:
@@ -97,7 +97,7 @@ ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa)
           zlog_debug ("Schedule SPF Calculation for %s",
                      OSPF6_AREA (lsa->lsdb->data)->name);
         }
-      ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data));
+      ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6));
       break;
 
     case OSPF6_LSTYPE_INTRA_PREFIX:
index 467479b1cd3c3e6a064495d196a70b72cde9ca1a..7c45fe46a8255f0c4a4356d78c388007ca3d9b48 100644 (file)
@@ -75,12 +75,18 @@ ospf6_interface_lookup_by_ifindex (int ifindex)
 static void
 ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa)
 {
+  struct ospf6_interface *oi;
+
+  if (lsa == NULL)
+    return;
+
+  oi = lsa->lsdb->data;
   switch (ntohs (lsa->header->type))
     {
       case OSPF6_LSTYPE_LINK:
-        if (OSPF6_INTERFACE (lsa->lsdb->data)->state == OSPF6_INTERFACE_DR)
-          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (OSPF6_INTERFACE (lsa->lsdb->data));
-        ospf6_spf_schedule (OSPF6_INTERFACE (lsa->lsdb->data)->area);
+        if (oi->state == OSPF6_INTERFACE_DR)
+          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+        ospf6_spf_schedule (oi->area->ospf6);
         break;
 
       default:
index da0ee131b7fa3ea5c996d310d15dbf3a96702928..e4c424dbbb91c27df0a95183170c9d3532b9a41e 100644 (file)
@@ -506,39 +506,128 @@ static int
 ospf6_spf_calculation_thread (struct thread *t)
 {
   struct ospf6_area *oa;
+  struct ospf6 *ospf6;
   struct timeval start, end, runtime;
+  struct listnode *node;
+  struct ospf6_route *route;
 
-  oa = (struct ospf6_area *) THREAD_ARG (t);
-  oa->thread_spf_calculation = NULL;
-
-  if (IS_OSPF6_DEBUG_SPF (PROCESS))
-    zlog_debug ("SPF calculation for Area %s", oa->name);
-  if (IS_OSPF6_DEBUG_SPF (DATABASE))
-    ospf6_spf_log_database (oa);
+  ospf6 = (struct ospf6 *)THREAD_ARG (t);
+  ospf6->t_spf_calc = NULL;
 
   /* execute SPF calculation */
   quagga_gettime (QUAGGA_CLK_MONOTONIC, &start);
-  ospf6_spf_calculation (oa->ospf6->router_id, oa->spf_table, oa);
+
+  for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa))
+    {
+
+      if (oa == ospf6->backbone)
+       continue;
+
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+       zlog_debug ("SPF calculation for Area %s", oa->name);
+      if (IS_OSPF6_DEBUG_SPF (DATABASE))
+       ospf6_spf_log_database (oa);
+
+      ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa);
+      ospf6_intra_route_calculation (oa);
+      ospf6_intra_brouter_calculation (oa);
+    }
+
+  if (ospf6->backbone)
+    {
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+       zlog_debug ("SPF calculation for Backbone area %s",
+                   ospf6->backbone->name);
+      if (IS_OSPF6_DEBUG_SPF (DATABASE))
+       ospf6_spf_log_database(ospf6->backbone);
+
+      ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table,
+                           ospf6->backbone);
+      ospf6_intra_route_calculation(ospf6->backbone);
+      ospf6_intra_brouter_calculation(ospf6->backbone);
+    }
+
+  /* Redo summaries if required */
+  for (route = ospf6_route_head (ospf6->route_table); route;
+       route = ospf6_route_next (route))
+    ospf6_abr_originate_summary(route);
+
   quagga_gettime (QUAGGA_CLK_MONOTONIC, &end);
   timersub (&end, &start, &runtime);
 
+  ospf6->ts_spf_duration = runtime;
+
   if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
     zlog_debug ("SPF runtime: %ld sec %ld usec",
                runtime.tv_sec, runtime.tv_usec);
 
-  ospf6_intra_route_calculation (oa);
-  ospf6_intra_brouter_calculation (oa);
-
   return 0;
 }
 
+/* Add schedule for SPF calculation.  To avoid frequenst SPF calc, we
+   set timer for SPF calc. */
 void
-ospf6_spf_schedule (struct ospf6_area *oa)
+ospf6_spf_schedule (struct ospf6 *ospf6)
 {
-  if (oa->thread_spf_calculation)
+  unsigned long delay, elapsed, ht;
+  struct timeval now, result;
+
+  if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+    zlog_debug ("SPF: calculation timer scheduled");
+
+  /* OSPF instance does not exist. */
+  if (ospf6 == NULL)
     return;
-  oa->thread_spf_calculation =
-    thread_add_event (master, ospf6_spf_calculation_thread, oa, 0);
+
+  /* SPF calculation timer is already scheduled. */
+  if (ospf6->t_spf_calc)
+    {
+      if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+        zlog_debug ("SPF: calculation timer is already scheduled: %p",
+                   ospf6->t_spf_calc);
+      return;
+    }
+
+  /* XXX Monotic timers: we only care about relative time here. */
+  now = recent_relative_time ();
+  timersub (&now, &ospf6->ts_spf, &result);
+
+  elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
+  ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier;
+
+  if (ht > ospf6->spf_max_holdtime)
+    ht = ospf6->spf_max_holdtime;
+
+  /* Get SPF calculation delay time. */
+  if (elapsed < ht)
+    {
+      /* 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 < ospf6->spf_max_holdtime)
+        ospf6->spf_hold_multiplier++;
+
+      /* always honour the SPF initial delay */
+      if ( (ht - elapsed) < ospf6->spf_delay)
+        delay = ospf6->spf_delay;
+      else
+        delay = ht - elapsed;
+    }
+  else
+    {
+      /* Event is past required hold-time of last SPF */
+      delay = ospf6->spf_delay;
+      ospf6->spf_hold_multiplier = 1;
+    }
+
+  if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+    zlog_debug ("SPF: calculation timer delay = %ld", delay);
+
+  zlog_info ("SPF: Scheduled in %ld msec", delay);
+
+  ospf6->t_spf_calc =
+    thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay);
 }
 
 void
@@ -666,6 +755,59 @@ DEFUN (no_debug_ospf6_spf_database,
   return CMD_SUCCESS;
 }
 
+static int
+ospf6_timers_spf_set (struct vty *vty, unsigned int delay,
+                     unsigned int hold,
+                     unsigned int max)
+{
+  struct ospf6 *ospf = vty->index;
+
+  ospf->spf_delay = delay;
+  ospf->spf_holdtime = hold;
+  ospf->spf_max_holdtime = max;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_timers_throttle_spf,
+       ospf6_timers_throttle_spf_cmd,
+       "timers throttle spf <0-600000> <0-600000> <0-600000>",
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF6 SPF timers\n"
+       "Delay (msec) from first change received till SPF calculation\n"
+       "Initial hold time (msec) between consecutive SPF calculations\n"
+       "Maximum hold time (msec)\n")
+{
+  unsigned int delay, hold, max;
+
+  if (argc != 3)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000);
+
+  return ospf6_timers_spf_set (vty, delay, hold, max);
+}
+
+DEFUN (no_ospf6_timers_throttle_spf,
+       no_ospf6_timers_throttle_spf_cmd,
+       "no timers throttle spf",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF6 SPF timers\n")
+{
+  return ospf6_timers_spf_set (vty,
+                              OSPF_SPF_DELAY_DEFAULT,
+                              OSPF_SPF_HOLDTIME_DEFAULT,
+                              OSPF_SPF_MAX_HOLDTIME_DEFAULT);
+}
+
 int
 config_write_ospf6_debug_spf (struct vty *vty)
 {
@@ -678,6 +820,19 @@ config_write_ospf6_debug_spf (struct vty *vty)
   return 0;
 }
 
+void
+ospf6_spf_config_write (struct vty *vty)
+{
+
+  if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
+      ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
+      ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT)
+    vty_out (vty, " timers throttle spf %d %d %d%s",
+            ospf6->spf_delay, ospf6->spf_holdtime,
+            ospf6->spf_max_holdtime, VTY_NEWLINE);
+
+}
+
 void
 install_element_ospf6_debug_spf (void)
 {
@@ -698,6 +853,6 @@ install_element_ospf6_debug_spf (void)
 void
 ospf6_spf_init (void)
 {
+  install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
 }
-
-
index c7069c2581b57f3a4f64d9e86cce812e06702438..6c40424fdbd22244254b46e582f553fc9bd85279 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef OSPF6_SPF_H
 #define OSPF6_SPF_H
 
+#include "ospf6_top.h"
+
 /* Debug option */
 extern unsigned char conf_debug_ospf6_spf;
 #define OSPF6_DEBUG_SPF_PROCESS   0x01
@@ -81,11 +83,12 @@ extern void ospf6_spf_table_finish (struct ospf6_route_table *result_table);
 extern void ospf6_spf_calculation (u_int32_t router_id,
                                    struct ospf6_route_table *result_table,
                                    struct ospf6_area *oa);
-extern void ospf6_spf_schedule (struct ospf6_area *oa);
+extern void ospf6_spf_schedule (struct ospf6 *ospf);
 
 extern void ospf6_spf_display_subtree (struct vty *vty, const char *prefix,
                                        int rest, struct ospf6_vertex *v);
 
+extern void ospf6_spf_config_write (struct vty *vty);
 extern int config_write_ospf6_debug_spf (struct vty *vty);
 extern void install_element_ospf6_debug_spf (void);
 extern void ospf6_spf_init (void);
index df856b4cf2b69eb63d77dd8582cdc0ec65ebcba0..540ef382ee90ced77a28a71aed81d67b224ed689 100644 (file)
@@ -46,6 +46,7 @@
 #include "ospf6_asbr.h"
 #include "ospf6_abr.h"
 #include "ospf6_intra.h"
+#include "ospf6_spf.h"
 #include "ospf6d.h"
 
 /* global ospf6d variable */
@@ -127,6 +128,11 @@ ospf6_create (void)
   o->lsdb->hook_add = ospf6_top_lsdb_hook_add;
   o->lsdb->hook_remove = ospf6_top_lsdb_hook_remove;
 
+  o->spf_delay = OSPF_SPF_DELAY_DEFAULT;
+  o->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT;
+  o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT;
+  o->spf_hold_multiplier = 1;
+
   o->route_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, ROUTES);
   o->route_table->scope = o;
   o->route_table->hook_add = ospf6_top_route_hook_add;
@@ -650,6 +656,7 @@ config_write_ospf6 (struct vty *vty)
 
   ospf6_redistribute_config_write (vty);
   ospf6_area_config_write (vty);
+  ospf6_spf_config_write (vty);
 
   for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, j, oa))
     {
index 4b2d2c3efce4df2f479ecddbaebbb9f5f7034223..27eb18cddf9f41ca0dfc797a68415f95882ccf47 100644 (file)
@@ -38,6 +38,7 @@ struct ospf6
 
   /* list of areas */
   struct list *area_list;
+  struct ospf6_area *backbone;
 
   /* AS scope link state database */
   struct ospf6_lsdb *lsdb;
@@ -59,6 +60,18 @@ struct ospf6
 
   u_char flag;
 
+  /* SPF parameters */
+  unsigned int spf_delay;              /* SPF delay time. */
+  unsigned int spf_holdtime;           /* SPF hold time. */
+  unsigned int spf_max_holdtime;       /* SPF maximum-holdtime */
+  unsigned int spf_hold_multiplier;    /* Adaptive multiplier for hold time */
+
+  struct timeval ts_spf;               /* SPF calculation time stamp. */
+  struct timeval ts_spf_duration;      /* Execution time of last SPF */
+
+  /* Threads */
+  struct thread *t_spf_calc;           /* SPF calculation timer. */
+  struct thread *t_ase_calc;           /* ASE calculation timer. */
   struct thread *maxage_remover;
 };