]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: allocate one nexthop id per-VTEP instead of one per-ES-VTEP
authorAnuradha Karuppiah <anuradhak@cumulusnetworks.com>
Wed, 27 May 2020 01:42:00 +0000 (18:42 -0700)
committerAnuradha Karuppiah <anuradhak@nvidia.com>
Tue, 1 Dec 2020 17:46:28 +0000 (09:46 -0800)
This is an optimization to reduce the number of L2 nexthops. A
l2 or fdb nexthop simply provides the dataplane with a nexthop ip-
torm-12:mgmt:~# ip nexthop
id 268435461 via 27.0.0.20 scope link fdb
id 268435463 via 27.0.0.20 scope link fdb
id 268435465 via 27.0.0.20 scope link fdb

So there is no need to allocate a nexthop per-ES/per-VTEP. There
can be 100+ ESs per-VTEP so this change cuts the scale down by a
factor of 100.

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
zebra/zebra_evpn_mh.c
zebra/zebra_evpn_mh.h
zebra/zebra_vty.c

index 33b2cd6a47bb90efc6a0f64842e491f18891c727..53412a434e7f1b76727a89b1c0ffb0b3c98c9472 100644 (file)
@@ -57,6 +57,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment");
 DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI");
 DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info");
 DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES");
+DEFINE_MTYPE_STATIC(ZEBRA, L2_NH, "L2 nexthop");
 
 static void zebra_evpn_es_get_one_base_evpn(void);
 static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
@@ -1005,6 +1006,27 @@ static void zebra_evpn_nhid_free(uint32_t nh_id, struct zebra_evpn_es *es)
        bf_release_index(zmh_info->nh_id_bitmap, id);
 }
 
+static unsigned int zebra_evpn_nh_ip_hash_keymake(const void *p)
+{
+       const struct zebra_evpn_l2_nh *nh = p;
+
+       return jhash_1word(nh->vtep_ip.s_addr, 0);
+}
+
+static bool zebra_evpn_nh_ip_cmp(const void *p1, const void *p2)
+{
+       const struct zebra_evpn_l2_nh *nh1 = p1;
+       const struct zebra_evpn_l2_nh *nh2 = p2;
+
+       if (nh1 == NULL && nh2 == NULL)
+               return true;
+
+       if (nh1 == NULL || nh2 == NULL)
+               return false;
+
+       return (nh1->vtep_ip.s_addr == nh2->vtep_ip.s_addr);
+}
+
 static unsigned int zebra_evpn_nhg_hash_keymake(const void *p)
 {
        const struct zebra_evpn_es *es = p;
@@ -1110,14 +1132,14 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
                return;
 
        for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
-               if (!es_vtep->nh_id)
+               if (!es_vtep->nh)
                        continue;
 
                if (nh_cnt >= ES_VTEP_MAX_CNT)
                        break;
 
                memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp));
-               nh_ids[nh_cnt].id = es_vtep->nh_id;
+               nh_ids[nh_cnt].id = es_vtep->nh->nh_id;
                ++nh_cnt;
        }
 
@@ -1163,44 +1185,154 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
 
 }
 
-static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep)
+static void zebra_evpn_es_l2_nh_show_entry(struct zebra_evpn_l2_nh *nh,
+                                          struct vty *vty,
+                                          json_object *json_array)
 {
-       if (es_vtep->nh_id)
+       if (json_array) {
+               json_object *json = NULL;
+               char ip_buf[INET6_ADDRSTRLEN];
+
+               json = json_object_new_object();
+               json_object_string_add(json, "vtep",
+                                      inet_ntop(AF_INET, &nh->vtep_ip, ip_buf,
+                                                sizeof(ip_buf)));
+               json_object_int_add(json, "nhId", nh->nh_id);
+               json_object_int_add(json, "refCnt", nh->ref_cnt);
+
+               json_object_array_add(json_array, json);
+       } else {
+               vty_out(vty, "%-16pI4 %-10u %u\n", &nh->vtep_ip, nh->nh_id,
+                       nh->ref_cnt);
+       }
+}
+
+static void zebra_evpn_l2_nh_show_cb(struct hash_bucket *bucket, void *ctxt)
+{
+       struct zebra_evpn_l2_nh *nh = (struct zebra_evpn_l2_nh *)bucket->data;
+       struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt;
+
+       zebra_evpn_es_l2_nh_show_entry(nh, wctx->vty, wctx->json);
+}
+
+void zebra_evpn_l2_nh_show(struct vty *vty, bool uj)
+{
+       struct evpn_mh_show_ctx wctx;
+       json_object *json_array = NULL;
+
+       if (uj) {
+               json_array = json_object_new_array();
+       } else {
+               vty_out(vty, "%-16s %-10s %s\n", "VTEP", "NH id", "#ES");
+       }
+
+       memset(&wctx, 0, sizeof(wctx));
+       wctx.vty = vty;
+       wctx.json = json_array;
+
+       hash_iterate(zmh_info->nh_ip_table, zebra_evpn_l2_nh_show_cb, &wctx);
+
+       if (uj) {
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               json_array, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json_array);
+       }
+}
+
+static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_find(struct in_addr vtep_ip)
+{
+       struct zebra_evpn_l2_nh *nh;
+       struct zebra_evpn_l2_nh tmp;
+
+       tmp.vtep_ip.s_addr = vtep_ip.s_addr;
+       nh = hash_lookup(zmh_info->nh_ip_table, &tmp);
+
+       return nh;
+}
+
+static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_alloc(struct in_addr vtep_ip)
+{
+       struct zebra_evpn_l2_nh *nh;
+
+       nh = XCALLOC(MTYPE_L2_NH, sizeof(*nh));
+       nh->vtep_ip = vtep_ip;
+       if (!hash_get(zmh_info->nh_ip_table, nh, hash_alloc_intern)) {
+               XFREE(MTYPE_L2_NH, nh);
+               return NULL;
+       }
+
+       nh->nh_id = zebra_evpn_nhid_alloc(NULL);
+       if (!nh->nh_id) {
+               hash_release(zmh_info->nh_ip_table, nh);
+               XFREE(MTYPE_L2_NH, nh);
+               return NULL;
+       }
+
+       /* install the NH in the dataplane */
+       kernel_upd_mac_nh(nh->nh_id, nh->vtep_ip);
+
+       return nh;
+}
+
+static void zebra_evpn_l2_nh_free(struct zebra_evpn_l2_nh *nh)
+{
+       /* delete the NH from the dataplane */
+       kernel_del_mac_nh(nh->nh_id);
+
+       zebra_evpn_nhid_free(nh->nh_id, NULL);
+       hash_release(zmh_info->nh_ip_table, nh);
+       XFREE(MTYPE_L2_NH, nh);
+}
+
+static void zebra_evpn_l2_nh_es_vtep_ref(struct zebra_evpn_es_vtep *es_vtep)
+{
+       if (es_vtep->nh)
                return;
 
-       es_vtep->nh_id = zebra_evpn_nhid_alloc(NULL /*NHG-es*/);
+       es_vtep->nh = zebra_evpn_l2_nh_find(es_vtep->vtep_ip);
+       if (!es_vtep->nh)
+               es_vtep->nh = zebra_evpn_l2_nh_alloc(es_vtep->vtep_ip);
 
-       if (!es_vtep->nh_id)
+       if (!es_vtep->nh) {
+               zlog_warn("es %s vtep %pI4 nh ref failed", es_vtep->es->esi_str,
+                         &es_vtep->vtep_ip);
                return;
+       }
+
+       ++es_vtep->nh->ref_cnt;
 
        if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
-               zlog_debug("es %s vtep %pI4 nh %u add", es_vtep->es->esi_str,
-                          &es_vtep->vtep_ip, es_vtep->nh_id);
-       /* install the NH */
-       kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip);
+               zlog_debug("es %s vtep %pI4 nh %u ref %u", es_vtep->es->esi_str,
+                          &es_vtep->vtep_ip, es_vtep->nh->nh_id,
+                          es_vtep->nh->ref_cnt);
+
        /* add the NH to the parent NHG */
        zebra_evpn_nhg_update(es_vtep->es);
 }
 
-static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep)
+static void zebra_evpn_l2_nh_es_vtep_deref(struct zebra_evpn_es_vtep *es_vtep)
 {
-       uint32_t nh_id;
+       struct zebra_evpn_l2_nh *nh = es_vtep->nh;
 
-       if (!es_vtep->nh_id)
+       if (!nh)
                return;
 
-       if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
-               zlog_debug("es %s vtep %pI4 nh %u del", es_vtep->es->esi_str,
-                          &es_vtep->vtep_ip, es_vtep->nh_id);
+       es_vtep->nh = NULL;
+       if (nh->ref_cnt)
+               --nh->ref_cnt;
 
-       nh_id = es_vtep->nh_id;
-       es_vtep->nh_id = 0;
+       if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
+               zlog_debug("es %s vtep %pI4 nh %u deref %u",
+                          es_vtep->es->esi_str, &es_vtep->vtep_ip, nh->nh_id,
+                          nh->ref_cnt);
 
        /* remove the NH from the parent NHG */
        zebra_evpn_nhg_update(es_vtep->es);
+
        /* uninstall the NH */
-       kernel_del_mac_nh(nh_id);
-       zebra_evpn_nhid_free(nh_id, NULL /*NHG-es*/);
+       if (!nh->ref_cnt)
+               zebra_evpn_l2_nh_free(nh);
 }
 
 /*****************************************************************************/
@@ -1248,7 +1380,7 @@ static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep)
 
        list_delete_node(es->es_vtep_list, &es_vtep->es_listnode);
        /* update the L2-NHG associated with the ES */
-       zebra_evpn_nh_del(es_vtep);
+       zebra_evpn_l2_nh_es_vtep_deref(es_vtep);
        XFREE(MTYPE_ZES_VTEP, es_vtep);
 }
 
@@ -1433,7 +1565,7 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
                                        es->esi_str, &vtep_ip);
                es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip);
                /* update the L2-NHG associated with the ES */
-               zebra_evpn_nh_add(es_vtep);
+               zebra_evpn_l2_nh_es_vtep_ref(es_vtep);
        }
 
        old_esr_rxed = !!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR);
@@ -2458,8 +2590,9 @@ static void zebra_evpn_es_json_vtep_fill(struct zebra_evpn_es *es,
                        json_object_int_add(json_vtep_entry, "dfPreference",
                                            es_vtep->df_pref);
                }
-               json_object_int_add(json_vtep_entry, "nexthopId",
-                                   es_vtep->nh_id);
+               if (es_vtep->nh)
+                       json_object_int_add(json_vtep_entry, "nexthopId",
+                                           es_vtep->nh->nh_id);
                json_object_array_add(json_vteps, json_vtep_entry);
        }
 }
@@ -2610,7 +2743,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
                                                           alg_buf,
                                                           sizeof(alg_buf)),
                                        es_vtep->df_pref);
-                       vty_out(vty, " nh: %u\n", es_vtep->nh_id);
+                       vty_out(vty, " nh: %u\n",
+                               es_vtep->nh ? es_vtep->nh->nh_id : 0);
                }
 
                vty_out(vty, "\n");
@@ -3383,6 +3517,9 @@ void zebra_evpn_mh_init(void)
        bf_assign_zero_index(zmh_info->nh_id_bitmap);
        zmh_info->nhg_table = hash_create(zebra_evpn_nhg_hash_keymake,
                                          zebra_evpn_nhg_cmp, "l2 NHG table");
+       zmh_info->nh_ip_table =
+               hash_create(zebra_evpn_nh_ip_hash_keymake, zebra_evpn_nh_ip_cmp,
+                           "l2 NH IP table");
 
        /* setup broadcast domain tables */
        zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake,
@@ -3399,5 +3536,7 @@ void zebra_evpn_mh_terminate(void)
        hash_iterate(zmh_info->evpn_vlan_table,
                        zebra_evpn_acc_vl_cleanup_all, NULL);
        hash_free(zmh_info->evpn_vlan_table);
+       hash_free(zmh_info->nhg_table);
+       hash_free(zmh_info->nh_ip_table);
        bf_free(zmh_info->nh_id_bitmap);
 }
index 701e5faa956ac2f0c00b74bca06df613602016dc..acdd2862b0909c701ba074a42f9b08ca6ed4452a 100644 (file)
@@ -123,6 +123,19 @@ struct zebra_evpn_es_evi {
        struct listnode es_listnode;
 };
 
+/* A single L2 nexthop is allocated across all ESs with the same PE/VTEP
+ * nexthop
+ */
+struct zebra_evpn_l2_nh {
+       struct in_addr vtep_ip;
+
+       /* MAC nexthop id */
+       uint32_t nh_id;
+
+       /* es_vtep entries using this nexthop */
+       uint32_t ref_cnt;
+};
+
 /* PE attached to an ES */
 struct zebra_evpn_es_vtep {
        struct zebra_evpn_es *es; /* parent ES */
@@ -133,12 +146,12 @@ struct zebra_evpn_es_vtep {
 #define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0)
 #define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1)
 
+       /* MAC nexthop info */
+       struct zebra_evpn_l2_nh *nh;
+
        /* memory used for adding the entry to es->es_vtep_list */
        struct listnode es_listnode;
 
-       /* MAC nexthop */
-       uint32_t nh_id;
-
        /* Parameters for DF election */
        uint8_t df_alg;
        uint32_t df_pref;
@@ -206,6 +219,8 @@ struct zebra_evpn_mh_info {
 #define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS)
        /* L2-NHG table - key: nhg_id, data: zebra_evpn_es */
        struct hash *nhg_table;
+       /* L2-NH table - key: vtep_up, data: zebra_evpn_nh */
+       struct hash *nh_ip_table;
 
        /* XXX - re-visit the default hold timer value */
        int mac_hold_time;
@@ -312,5 +327,17 @@ extern void zebra_evpn_mh_json(json_object *json);
 extern bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id,
                                       struct zebra_evpn_es **local_es);
 extern int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off);
+extern int zebra_evpn_mh_startup_delay_update(struct vty *vty,
+                                             uint32_t duration,
+                                             bool set_default);
+extern void zebra_evpn_mh_uplink_oper_update(struct zebra_if *zif);
+extern void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif,
+                                                   bool clear,
+                                                   const char *caller);
+extern bool zebra_evpn_is_es_bond(struct interface *ifp);
+extern bool zebra_evpn_is_es_bond_member(struct interface *ifp);
+extern void zebra_evpn_mh_print(struct vty *vty);
+extern void zebra_evpn_mh_json(json_object *json);
+extern void zebra_evpn_l2_nh_show(struct vty *vty, bool uj);
 
 #endif /* _ZEBRA_EVPN_MH_H */
index b209da3307a51506276b2477704db1570da74b82..5131a7d867112a28ac282bd3a3d1e66db3871a31 100644 (file)
@@ -2690,6 +2690,21 @@ DEFUN (show_evpn_global,
        return CMD_SUCCESS;
 }
 
+DEFPY(show_evpn_l2_nh,
+      show_evpn_l2_nh_cmd,
+      "show evpn l2-nh [json$json]",
+      SHOW_STR
+      "EVPN\n"
+      "Layer2 nexthops\n"
+      JSON_STR)
+{
+       bool uj = !!json;
+
+       zebra_evpn_l2_nh_show(vty, uj);
+
+       return CMD_SUCCESS;
+}
+
 DEFPY(show_evpn_es,
       show_evpn_es_cmd,
       "show evpn es [NAME$esi_str|detail$detail] [json$json]",
@@ -2697,8 +2712,8 @@ DEFPY(show_evpn_es,
       "EVPN\n"
       "Ethernet Segment\n"
       "ES ID\n"
-      JSON_STR
-      "Detailed information\n")
+      "Detailed information\n"
+      JSON_STR)
 {
        esi_t esi;
        bool uj = !!json;
@@ -4037,6 +4052,7 @@ void zebra_vty_init(void)
        install_element(VIEW_NODE, &show_evpn_vni_cmd);
        install_element(VIEW_NODE, &show_evpn_vni_detail_cmd);
        install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
+       install_element(VIEW_NODE, &show_evpn_l2_nh_cmd);
        install_element(VIEW_NODE, &show_evpn_es_cmd);
        install_element(VIEW_NODE, &show_evpn_es_evi_cmd);
        install_element(VIEW_NODE, &show_evpn_access_vlan_cmd);