]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: Handle rib updates as a thread event
authorStephen Worley <sworley@cumulusnetworks.com>
Thu, 17 Oct 2019 19:38:54 +0000 (15:38 -0400)
committerStephen Worley <sworley@cumulusnetworks.com>
Fri, 18 Oct 2019 20:59:25 +0000 (16:59 -0400)
If we need to batch process the rib (all tables or specific
vrf), do so as a scheduled thread event rather than immediately
handling it. Further, add context to the events so that you
narrow down to certain route types you want to reprocess.

Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
zebra/rib.h
zebra/zebra_rib.c

index b82428e54c566a5bd5a63734e7107ab83fcc1f25..ee1df89c0e07d73b79f21eff082995e079fa5e17 100644 (file)
@@ -301,8 +301,10 @@ typedef struct rib_tables_iter_t_ {
 
 /* Events/reasons triggering a RIB update. */
 typedef enum {
+       RIB_UPDATE_KERNEL,
        RIB_UPDATE_RMAP_CHANGE,
-       RIB_UPDATE_OTHER
+       RIB_UPDATE_OTHER,
+       RIB_UPDATE_MAX
 } rib_update_event_t;
 
 extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
@@ -384,7 +386,8 @@ extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id,
 extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p,
                                           vrf_id_t vrf_id);
 
-extern void rib_update(vrf_id_t vrf_id, rib_update_event_t event);
+extern void rib_update(rib_update_event_t event);
+extern void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event);
 extern void rib_update_table(struct route_table *table,
                             rib_update_event_t event);
 extern int rib_sweep_route(struct thread *t);
index 4a726b3e07c690978ab6d42eb11f326bc80d0a13..c2fa33f57d788391e245ed848f15d58f5f43a0cb 100644 (file)
@@ -58,6 +58,8 @@
 #include "zebra/zebra_dplane.h"
 #include "zebra/zebra_nhg.h"
 
+DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object");
+
 /*
  * Event, list, and mutex for delivery of dataplane results
  */
@@ -2961,11 +2963,66 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
        return rib_add_multipath(afi, safi, p, src_p, re);
 }
 
+static const char *rib_update_event2str(rib_update_event_t event)
+{
+       const char *ret = "UNKNOWN";
+
+       switch (event) {
+       case RIB_UPDATE_KERNEL:
+               ret = "RIB_UPDATE_KERNEL";
+               break;
+       case RIB_UPDATE_RMAP_CHANGE:
+               ret = "RIB_UPDATE_RMAP_CHANGE";
+               break;
+       case RIB_UPDATE_OTHER:
+               ret = "RIB_UPDATE_OTHER";
+               break;
+       case RIB_UPDATE_MAX:
+               break;
+       }
+
+       return ret;
+}
+
+
+/* Schedule route nodes to be processed if they match the type */
+static void rib_update_route_node(struct route_node *rn, int type)
+{
+       struct route_entry *re, *next;
+       bool re_changed = false;
+
+       RNODE_FOREACH_RE_SAFE (rn, re, next) {
+               if (type == ZEBRA_ROUTE_ALL || type == re->type) {
+                       SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+                       re_changed = true;
+               }
+       }
+
+       if (re_changed)
+               rib_queue_add(rn);
+}
+
 /* Schedule routes of a particular table (address-family) based on event. */
 void rib_update_table(struct route_table *table, rib_update_event_t event)
 {
        struct route_node *rn;
-       struct route_entry *re, *next;
+
+       if (IS_ZEBRA_DEBUG_EVENT) {
+               struct zebra_vrf *zvrf;
+               struct vrf *vrf;
+
+               zvrf = table->info ? ((rib_table_info_t *)table->info)->zvrf
+                                  : NULL;
+               vrf = zvrf ? zvrf->vrf : NULL;
+
+               zlog_debug("%s: %s VRF %s Table %u event %s", __func__,
+                          table->info ? afi2str(
+                                  ((rib_table_info_t *)table->info)->afi)
+                                      : "Unknown",
+                          vrf ? vrf->name : "Unknown",
+                          zvrf ? zvrf->table_id : 0,
+                          rib_update_event2str(event));
+       }
 
        /* Walk all routes and queue for processing, if appropriate for
         * the trigger event.
@@ -2976,49 +3033,139 @@ void rib_update_table(struct route_table *table, rib_update_event_t event)
                 * has already been queued  we don't
                 * need to queue it up again
                 */
-               if (rn->info && CHECK_FLAG(rib_dest_from_rnode(rn)->flags,
-                                          RIB_ROUTE_ANY_QUEUED))
+               if (rn->info
+                   && CHECK_FLAG(rib_dest_from_rnode(rn)->flags,
+                                 RIB_ROUTE_ANY_QUEUED))
                        continue;
+
                switch (event) {
+               case RIB_UPDATE_KERNEL:
+                       rib_update_route_node(rn, ZEBRA_ROUTE_KERNEL);
+                       break;
                case RIB_UPDATE_RMAP_CHANGE:
                case RIB_UPDATE_OTHER:
-                       /* Right now, examine all routes. Can restrict to a
-                        * protocol in
-                        * some cases (TODO).
-                        */
-                       if (rnode_to_ribs(rn)) {
-                               RNODE_FOREACH_RE_SAFE (rn, re, next)
-                                       SET_FLAG(re->status,
-                                                ROUTE_ENTRY_CHANGED);
-                               rib_queue_add(rn);
-                       }
+                       rib_update_route_node(rn, ZEBRA_ROUTE_ALL);
                        break;
-
                default:
                        break;
                }
        }
 }
 
-/* RIB update function. */
-void rib_update(vrf_id_t vrf_id, rib_update_event_t event)
+static void rib_update_handle_vrf(vrf_id_t vrf_id, rib_update_event_t event)
 {
        struct route_table *table;
 
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_debug("%s: Handling VRF %s event %s", __func__,
+                          vrf_id_to_name(vrf_id), rib_update_event2str(event));
+
        /* Process routes of interested address-families. */
        table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id);
-       if (table) {
-               if (IS_ZEBRA_DEBUG_EVENT)
-                       zlog_debug("%s : AFI_IP event %d", __func__, event);
+       if (table)
                rib_update_table(table, event);
-       }
 
        table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, vrf_id);
-       if (table) {
-               if (IS_ZEBRA_DEBUG_EVENT)
-                       zlog_debug("%s : AFI_IP6 event %d", __func__, event);
+       if (table)
                rib_update_table(table, event);
-       }
+}
+
+static void rib_update_handle_vrf_all(rib_update_event_t event)
+{
+       struct zebra_router_table *zrt;
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_debug("%s: Handling VRF (ALL) event %s", __func__,
+                          rib_update_event2str(event));
+
+       /* Just iterate over all the route tables, rather than vrf lookups */
+       RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables)
+               rib_update_table(zrt->table, event);
+}
+
+struct rib_update_ctx {
+       rib_update_event_t event;
+       bool vrf_all;
+       vrf_id_t vrf_id;
+};
+
+static struct rib_update_ctx *rib_update_ctx_init(vrf_id_t vrf_id,
+                                                 rib_update_event_t event)
+{
+       struct rib_update_ctx *ctx;
+
+       ctx = XCALLOC(MTYPE_RIB_UPDATE_CTX, sizeof(struct rib_update_ctx));
+
+       ctx->event = event;
+       ctx->vrf_id = vrf_id;
+
+       return ctx;
+}
+
+static void rib_update_ctx_fini(struct rib_update_ctx **ctx)
+{
+       XFREE(MTYPE_RIB_UPDATE_CTX, *ctx);
+
+       *ctx = NULL;
+}
+
+static int rib_update_handler(struct thread *thread)
+{
+       struct rib_update_ctx *ctx;
+
+       ctx = THREAD_ARG(thread);
+
+       if (ctx->vrf_all)
+               rib_update_handle_vrf_all(ctx->event);
+       else
+               rib_update_handle_vrf(ctx->vrf_id, ctx->event);
+
+       rib_update_ctx_fini(&ctx);
+
+       return 0;
+}
+
+/*
+ * Thread list to ensure we don't schedule a ton of events
+ * if interfaces are flapping for instance.
+ */
+static struct thread *t_rib_update_threads[RIB_UPDATE_MAX];
+
+/* Schedule a RIB update event for specific vrf */
+void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event)
+{
+       struct rib_update_ctx *ctx;
+
+       ctx = rib_update_ctx_init(vrf_id, event);
+
+       /* Don't worry about making sure multiple rib updates for specific vrf
+        * are scheduled at once for now. If it becomes a problem, we can use a
+        * lookup of some sort to keep track of running threads via t_vrf_id
+        * like how we are doing it in t_rib_update_threads[].
+        */
+       thread_add_event(zrouter.master, rib_update_handler, ctx, 0, NULL);
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_debug("%s: Scheduled VRF %s, event %s", __func__,
+                          vrf_id_to_name(ctx->vrf_id),
+                          rib_update_event2str(event));
+}
+
+/* Schedule a RIB update event for all vrfs */
+void rib_update(rib_update_event_t event)
+{
+       struct rib_update_ctx *ctx;
+
+       ctx = rib_update_ctx_init(0, event);
+
+       ctx->vrf_all = true;
+
+       if (!thread_add_event(zrouter.master, rib_update_handler, ctx, 0,
+                             &t_rib_update_threads[event]))
+               rib_update_ctx_fini(&ctx); /* Already scheduled */
+       else if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_debug("%s: Schedued VRF (ALL), event %s", __func__,
+                          rib_update_event2str(event));
 }
 
 /* Delete self installed routes after zebra is relaunched.  */