From 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 19 Dec 2016 23:05:39 +0100 Subject: Reorganize source tree to use single autotools instance Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion --- src/vnet/l2/l2_learn.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/vnet/l2/l2_learn.h (limited to 'src/vnet/l2/l2_learn.h') diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h new file mode 100644 index 00000000..5bb1130b --- /dev/null +++ b/src/vnet/l2/l2_learn.h @@ -0,0 +1,64 @@ +/* + * l2_learn.c : layer 2 learning using l2fib + * + * Copyright (c) 2014 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef included_l2learn_h +#define included_l2learn_h + +#include +#include + + +typedef struct +{ + + /* Hash table */ + BVT (clib_bihash) * mac_table; + + /* number of dynamically learned mac entries */ + u32 global_learn_count; + + /* maximum number of dynamically learned mac entries */ + u32 global_learn_limit; + + /* Next nodes for each feature */ + u32 feat_next_node_index[32]; + + /* convenience variables */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; +} l2learn_main_t; + + +l2learn_main_t l2learn_main; + +extern vlib_node_registration_t l2fib_mac_age_scanner_process_node; + +enum +{ + L2_MAC_AGE_PROCESS_EVENT_START = 1, + L2_MAC_AGE_PROCESS_EVENT_STOP = 2, +} l2_mac_age_process_event_t; + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg From da1f2c7cffb0de4ef05a48ffd107214eb11fa45f Mon Sep 17 00:00:00 2001 From: John Lo Date: Fri, 24 Mar 2017 20:11:15 -0400 Subject: Implement MAC Flush for BD or Interface from the L2FIB Allow non-static MACs in the L2FIB which is associated with an interface or a bridge domain (BD) be flushed. MAC flush are initiated automatically when an interface is removed from a BD or when a BD is deleted. MAC flush can also be invoked manually via the following CLI: l2fib mac-flush interface l2fib mac-flush bridge-domain Change-Id: Ie33243622834810a765f48ebcd22bdb8e8fc87a4 Signed-off-by: John Lo --- src/vnet/buffer.h | 8 ++- src/vnet/l2/l2_bd.c | 41 +++++++---- src/vnet/l2/l2_bd.h | 3 + src/vnet/l2/l2_fib.c | 188 +++++++++++++++++++++++++++++++++++++++++++++---- src/vnet/l2/l2_fib.h | 39 +++++----- src/vnet/l2/l2_input.c | 14 ++++ src/vnet/l2/l2_input.h | 3 + src/vnet/l2/l2_learn.c | 10 ++- src/vnet/l2/l2_learn.h | 1 + 9 files changed, 258 insertions(+), 49 deletions(-) (limited to 'src/vnet/l2/l2_learn.h') diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h index f08b4fc1..ea3ce093 100644 --- a/src/vnet/buffer.h +++ b/src/vnet/buffer.h @@ -169,9 +169,11 @@ typedef struct struct { u32 feature_bitmap; - u16 bd_index; // bridge-domain index - u8 l2_len; // ethernet header length - u8 shg; // split-horizon group + u16 bd_index; /* bridge-domain index */ + u8 l2_len; /* ethernet header length */ + u8 shg; /* split-horizon group */ + u8 bd_sn; /* bridge domain seq# */ + u8 int_sn; /* interface seq# */ } l2; /* l2tpv3 softwire encap, only valid there */ diff --git a/src/vnet/l2/l2_bd.c b/src/vnet/l2/l2_bd.c index f741b643..6c01368b 100644 --- a/src/vnet/l2/l2_bd.c +++ b/src/vnet/l2/l2_bd.c @@ -115,6 +115,8 @@ bd_delete_bd_index (bd_main_t * bdm, u32 bd_id) l2input_main.bd_configs[bd_index].bd_id = ~0; l2input_main.bd_configs[bd_index].feature_bitmap = 0; + l2fib_flush_bd_mac (vlib_get_main (), bd_index); + return 0; } @@ -900,7 +902,6 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) u32 bd_index = ~0; l2_bridge_domain_t *bd_config; u32 start, end; - u32 printed; u32 detail = 0; u32 intf = 0; u32 arp = 0; @@ -942,7 +943,8 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) } /* Show all bridge-domains that have been initialized */ - printed = 0; + u32 printed = 0; + u8 *as = 0; for (bd_index = start; bd_index < end; bd_index++) { bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); @@ -952,26 +954,32 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { printed = 1; vlib_cli_output (vm, - "%=5s %=7s %=10s %=10s %=10s %=10s %=10s %=14s", - "ID", "Index", "Learning", "U-Forwrd", - "UU-Flood", "Flooding", "ARP-Term", + "%=5s %=7s %=4s %=9s %=9s %=9s %=9s %=9s %=9s %=9s", + "ID", "Index", "BSN", "Age(min)", "Learning", + "U-Forwrd", "UU-Flood", "Flooding", "ARP-Term", "BVI-Intf"); } + if (bd_config->mac_age) + as = format (as, "%d", bd_config->mac_age); + else + as = format (as, "off"); vlib_cli_output (vm, - "%=5d %=7d %=10s %=10s %=10s %=10s %=10s %=14U", - bd_config->bd_id, bd_index, + "%=5d %=7d %=4d %=9v %=9s %=9s %=9s %=9s %=9s %=9U", + bd_config->bd_id, bd_index, bd_config->seq_num, as, bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off", - bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" - : "off", + bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? + "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? - "on" : "off", format_vnet_sw_if_index_name_with_NA, + "on" : "off", + format_vnet_sw_if_index_name_with_NA, vnm, bd_config->bvi_sw_if_index); + vec_reset_length (as); if (detail || intf) { @@ -981,19 +989,21 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { l2_flood_member_t *member = vec_elt_at_index (bd_config->members, i); + l2_input_config_t *int_config = + l2input_intf_config (member->sw_if_index); u32 vtr_opr, dot1q, tag1, tag2; if (i == 0) { - vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=9s%=30s", - "Interface", "Index", "SHG", "BVI", - "TxFlood", "VLAN-Tag-Rewrite"); + vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=5s%=9s%=30s", + "Interface", "If-idx", "ISN", "SHG", + "BVI", "TxFlood", "VLAN-Tag-Rewrite"); } l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q, &tag1, &tag2); - vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=9s%=30U", + vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30U", format_vnet_sw_if_index_name, vnm, member->sw_if_index, member->sw_if_index, - member->shg, + int_config->seq_num, member->shg, member->flags & L2_FLOOD_MEMBER_BVI ? "*" : "-", i < bd_config->flood_count ? "*" : "-", format_vtr, vtr_opr, dot1q, tag1, tag2); @@ -1027,6 +1037,7 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) } } } + vec_free (as); if (!printed) { diff --git a/src/vnet/l2/l2_bd.h b/src/vnet/l2/l2_bd.h index 4bb9bc9b..5c2502d9 100644 --- a/src/vnet/l2/l2_bd.h +++ b/src/vnet/l2/l2_bd.h @@ -86,6 +86,9 @@ typedef struct /* mac aging */ u8 mac_age; + /* sequence number for bridge domain based flush of MACs */ + u8 seq_num; + } l2_bridge_domain_t; /* Return 1 if bridge domain has been initialized */ diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c index d34836e3..fadd79eb 100644 --- a/src/vnet/l2/l2_fib.c +++ b/src/vnet/l2/l2_fib.c @@ -168,10 +168,10 @@ show_l2fib (vlib_main_t * vm, { first_entry = 0; vlib_cli_output (vm, - "%=19s%=7s%=30s%=7s%=8s%=8s%=5s%=16s", - "Mac Address", "BD Idx", "Interface", - "Index", "static", "filter", "bvi", - "Mac Age (min)"); + "%=19s%=7s%=7s%=8s%=9s%=7s%=7s%=5s%=30s", + "Mac-Address", "BD-Idx", "If-Idx", + "BSN-ISN", "Age(min)", "static", "filter", + "bvi", "Interface-Name"); } key.raw = v->kvp[k].key; @@ -183,26 +183,27 @@ show_l2fib (vlib_main_t * vm, bd_config = vec_elt_at_index (l2input_main.bd_configs, key.fields.bd_index); - if (bd_config->mac_age) + if (bd_config->mac_age && !result.fields.static_mac) { i16 delta = now - result.fields.timestamp; delta += delta < 0 ? 256 : 0; s = format (s, "%d", delta); } else - s = format (s, "disabled"); + s = format (s, "-"); vlib_cli_output (vm, - "%=19U%=7d%=30U%=7d%=8d%=8d%=5d%=16v", + "%=19U%=7d%=7d %3d/%-3d%=9v%=7s%=7s%=5s%=30U", format_ethernet_address, key.fields.mac, key.fields.bd_index, - format_vnet_sw_if_index_name_with_NA, - msm->vnet_main, result.fields.sw_if_index, result.fields.sw_if_index == ~0 ? -1 : result.fields.sw_if_index, - result.fields.static_mac, - result.fields.filter, - result.fields.bvi, s); + result.fields.bd_sn, result.fields.int_sn, + s, result.fields.static_mac ? "*" : "-", + result.fields.filter ? "*" : "-", + result.fields.bvi ? "*" : "-", + format_vnet_sw_if_index_name_with_NA, + msm->vnet_main, result.fields.sw_if_index); vec_reset_length (s); } total_entries++; @@ -330,6 +331,15 @@ l2fib_add_entry (u64 mac, result.fields.static_mac = static_mac; result.fields.filter = filter_mac; result.fields.bvi = bvi_mac; + if (!static_mac) + { + l2_input_config_t *int_config = l2input_intf_config (sw_if_index); + l2_bridge_domain_t *bd_config = + vec_elt_at_index (l2input_main.bd_configs, + bd_index); + result.fields.int_sn = int_config->seq_num; + result.fields.bd_sn = bd_config->seq_num; + } kv.key = key.raw; kv.value = result.raw; @@ -703,6 +713,141 @@ VLIB_CLI_COMMAND (l2fib_del_cli, static) = { }; /* *INDENT-ON* */ +/** + Kick off ager to scan MACs to age/delete MAC entries +*/ +void +l2fib_start_ager_scan (vlib_main_t * vm) +{ + l2_bridge_domain_t *bd_config; + int enable = 0; + + /* check if there is at least one bd with mac aging enabled */ + vec_foreach (bd_config, l2input_main.bd_configs) + if (bd_config->bd_id != ~0 && bd_config->mac_age != 0) + enable = 1; + + vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index, + enable ? L2_MAC_AGE_PROCESS_EVENT_START : + L2_MAC_AGE_PROCESS_EVENT_ONE_PASS, 0); +} + +/** + Flush all learned MACs from an interface +*/ +void +l2fib_flush_int_mac (vlib_main_t * vm, u32 sw_if_index) +{ + l2_input_config_t *int_config; + int_config = l2input_intf_config (sw_if_index); + int_config->seq_num += 1; + l2fib_start_ager_scan (vm); +} + +/** + Flush all learned MACs in a bridge domain +*/ +void +l2fib_flush_bd_mac (vlib_main_t * vm, u32 bd_index) +{ + l2_bridge_domain_t *bd_config; + vec_validate (l2input_main.bd_configs, bd_index); + bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); + bd_config->seq_num += 1; + l2fib_start_ager_scan (vm); +} + +/** + Flush MACs, except static ones, associated with an interface + The CLI format is: + l2fib flush-mac interface +*/ +static clib_error_t * +l2fib_flush_mac_int (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index; + + if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, input); + goto done; + } + + l2fib_flush_int_mac (vm, sw_if_index); + +done: + return error; +} + +/*? + * This command kick off ager to delete all existing MAC Address entries, + * except static ones, associated with an interface from the L2 FIB table. + * + * @cliexpar + * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table: + * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2fib_flush_mac_int_cli, static) = { + .path = "l2fib flush-mac interface", + .short_help = "l2fib flush-mac interface ", + .function = l2fib_flush_mac_int, +}; +/* *INDENT-ON* */ + +/** + Flush bridge-domain MACs except static ones. + The CLI format is: + l2fib flush-mac bridge-domain +*/ +static clib_error_t * +l2fib_flush_mac_bd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + bd_main_t *bdm = &bd_main; + clib_error_t *error = 0; + u32 bd_index, bd_id; + uword *p; + + if (!unformat (input, "%d", &bd_id)) + { + error = clib_error_return (0, "expecting bridge-domain id but got `%U'", + format_unformat_error, input); + goto done; + } + + p = hash_get (bdm->bd_index_by_bd_id, bd_id); + if (p) + bd_index = *p; + else + return clib_error_return (0, "No such bridge domain %d", bd_id); + + l2fib_flush_bd_mac (vm, bd_index); + +done: + return error; +} + +/*? + * This command kick off ager to delete all existing MAC Address entries, + * except static ones, in a bridge domain from the L2 FIB table. + * + * @cliexpar + * Example of how to flush MAC Address entries learned in a bridge domain from the L2 FIB table: + * @cliexcmd{l2fib flush-mac bridge-domain 1000} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2fib_flush_mac_bd_cli, static) = { + .path = "l2fib flush-mac bridge-domain", + .short_help = "l2fib flush-mac bridge-domain ", + .function = l2fib_flush_mac_bd, +}; +/* *INDENT-ON* */ + BVT (clib_bihash) * get_mac_table (void) { @@ -716,6 +861,7 @@ l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt, { uword event_type, *event_data = 0; l2fib_main_t *msm = &l2fib_main; + l2_input_config_t *int_config; l2_bridge_domain_t *bd_config; BVT (clib_bihash) * h = &msm->mac_table; clib_bihash_bucket_t *b; @@ -747,6 +893,9 @@ l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt, case L2_MAC_AGE_PROCESS_EVENT_STOP: enabled = 0; continue; + case L2_MAC_AGE_PROCESS_EVENT_ONE_PASS: + enabled = 0; + break; default: ASSERT (0); } @@ -790,8 +939,19 @@ l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt, if (result.fields.static_mac) continue; - bd_config = vec_elt_at_index (l2input_main.bd_configs, - key.fields.bd_index); + int_config = + l2input_intf_config (result.fields.sw_if_index); + bd_config = + vec_elt_at_index (l2input_main.bd_configs, + key.fields.bd_index); + + if ((result.fields.int_sn != int_config->seq_num) || + (result.fields.bd_sn != bd_config->seq_num)) + { + void *p = &key.fields.mac; + l2fib_del_entry (*(u64 *) p, key.fields.bd_index); + continue; + } if (bd_config->mac_age == 0) continue; diff --git a/src/vnet/l2/l2_fib.h b/src/vnet/l2/l2_fib.h index 4a2da59b..7e49d74b 100644 --- a/src/vnet/l2/l2_fib.h +++ b/src/vnet/l2/l2_fib.h @@ -66,7 +66,8 @@ typedef struct u8 filter:1; /* drop packets to/from this mac */ u8 unused1:5; u8 timestamp; /* timestamp for aging */ - u16 unused2; + u8 int_sn; /* interface seq num */ + u8 bd_sn; /* bridge domain seq num */ } fields; u64 raw; }; @@ -313,22 +314,28 @@ l2fib_lookup_4 (BVT (clib_bihash) * mac_table, } } +void l2fib_clear_table (uint keep_static); + +void +l2fib_add_entry (u64 mac, + u32 bd_index, + u32 sw_if_index, u32 static_mac, u32 drop_mac, u32 bvi_mac); + +u32 l2fib_del_entry (u64 mac, u32 bd_index); + +void l2fib_start_ager_scan (vlib_main_t * vm); + +void l2fib_flush_int_mac (vlib_main_t * vm, u32 sw_if_index); + +void l2fib_flush_bd_mac (vlib_main_t * vm, u32 bd_index); + +void +l2fib_table_dump (u32 bd_index, l2fib_entry_key_t ** l2fe_key, + l2fib_entry_result_t ** l2fe_res); + +u8 *format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args); + BVT (clib_bihash) * get_mac_table (void); - void - l2fib_clear_table (uint keep_static); - void - l2fib_add_entry (u64 mac, - u32 bd_index, - u32 sw_if_index, - u32 static_mac, u32 drop_mac, u32 bvi_mac); -u32 -l2fib_del_entry (u64 mac, u32 bd_index); - - void - l2fib_table_dump (u32 bd_index, l2fib_entry_key_t ** l2fe_key, - l2fib_entry_result_t ** l2fe_res); - - u8 *format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args); #endif diff --git a/src/vnet/l2/l2_input.c b/src/vnet/l2/l2_input.c index 3aa5331b..041ff38d 100644 --- a/src/vnet/l2/l2_input.c +++ b/src/vnet/l2/l2_input.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -201,6 +202,9 @@ classify_and_dispatch (vlib_main_t * vm, /* Get config for the bridge domain interface */ bd_config = vec_elt_at_index (msm->bd_configs, bd_index0); + /* Save bridge domain seq_num */ + vnet_buffer (b0)->l2.bd_sn = bd_config->seq_num; + /* * Process bridge domain feature enables. * To perform learning/flooding/forwarding, the corresponding bit @@ -214,6 +218,9 @@ classify_and_dispatch (vlib_main_t * vm, /* mask out features from bitmap using packet type and bd config */ feature_bitmap = config->feature_bitmap & feat_mask; + /* Save interface seq_num */ + vnet_buffer (b0)->l2.int_sn = config->seq_num; + /* save for next feature graph nodes */ vnet_buffer (b0)->l2.feature_bitmap = feature_bitmap; @@ -561,6 +568,12 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT); ASSERT (slot == VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT); } + + /* Clear MACs learned on the interface */ + if ((config->feature_bitmap | L2INPUT_FEAT_LEARN) || + (bd_config->feature_bitmap | L2INPUT_FEAT_LEARN)) + l2fib_flush_int_mac (vm, sw_if_index); + l2_if_adjust--; } else if (config->xconnect) @@ -632,6 +645,7 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ config->xconnect = 0; config->bridge = 1; config->bd_index = bd_index; + config->seq_num += 1; /* * Enable forwarding, flooding, learning and ARP termination by default diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h index f3fada6a..262f75c7 100644 --- a/src/vnet/l2/l2_input.h +++ b/src/vnet/l2/l2_input.h @@ -53,6 +53,9 @@ typedef struct /* split horizon group */ u8 shg; + /* sequence number for interface based flush of MACs */ + u8 seq_num; + } l2_input_config_t; diff --git a/src/vnet/l2/l2_learn.c b/src/vnet/l2/l2_learn.c index afe7f478..faed0d66 100644 --- a/src/vnet/l2/l2_learn.c +++ b/src/vnet/l2/l2_learn.c @@ -140,7 +140,11 @@ l2learn_process (vlib_node_runtime_t * node, counter_base[L2LEARN_ERROR_HIT] += 1; if (PREDICT_FALSE (result0->fields.timestamp != timestamp)) result0->fields.timestamp = timestamp; - + if (PREDICT_FALSE + (result0->fields.int_sn != vnet_buffer (b0)->l2.int_sn)) + result0->fields.int_sn = vnet_buffer (b0)->l2.int_sn; + if (PREDICT_FALSE (result0->fields.bd_sn != vnet_buffer (b0)->l2.bd_sn)) + result0->fields.bd_sn = vnet_buffer (b0)->l2.bd_sn; } else if (result0->raw == ~0) { @@ -167,6 +171,8 @@ l2learn_process (vlib_node_runtime_t * node, result0->raw = 0; /* clear all fields */ result0->fields.sw_if_index = sw_if_index0; result0->fields.timestamp = timestamp; + result0->fields.bd_sn = vnet_buffer (b0)->l2.bd_sn; + result0->fields.int_sn = vnet_buffer (b0)->l2.int_sn; kv.key = key0->raw; kv.value = result0->raw; @@ -204,6 +210,8 @@ l2learn_process (vlib_node_runtime_t * node, result0->raw = 0; /* clear all fields */ result0->fields.sw_if_index = sw_if_index0; result0->fields.timestamp = timestamp; + result0->fields.bd_sn = vnet_buffer (b0)->l2.bd_sn; + result0->fields.int_sn = vnet_buffer (b0)->l2.int_sn; kv.key = key0->raw; kv.value = result0->raw; diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h index 5bb1130b..0d95de04 100644 --- a/src/vnet/l2/l2_learn.h +++ b/src/vnet/l2/l2_learn.h @@ -51,6 +51,7 @@ enum { L2_MAC_AGE_PROCESS_EVENT_START = 1, L2_MAC_AGE_PROCESS_EVENT_STOP = 2, + L2_MAC_AGE_PROCESS_EVENT_ONE_PASS = 3, } l2_mac_age_process_event_t; #endif -- cgit 1.2.3-korg From 8d00fff8dff4e449767601645422e03df92a83af Mon Sep 17 00:00:00 2001 From: John Lo Date: Thu, 3 Aug 2017 00:35:36 -0400 Subject: Add support for API client to receive L2 MAC events Added APIs want_l2_macs_events and l2_macs_event to allow an API client to receive notification events from VPP for MAC learned or aged in L2FIB. Only one API client is allowed for L2 MAC events. The want_l2_macs_events API allow caller to specify MAC learn limit, event scan delay and max number of MACs that can be included in a event message. These parameters should be choosen properly as to not have too many MAC events sent by VPP and overwhelm the API share memory. They can all be left as 0's so VPP will setup reasonable defaults which are: 1000 learn limit, 100 msec scan delay and 100 MACs per event message. If want_l2_macs_events is never called, VPP learning and aging should behave as before except that MAC entries provisioned by API or CLI will not be aged, even if it is not set as static_mac. These non static MACs, however, can be overwritten by MAC learning on a MAC move as a leared MAC. Only learned MACs are subject to aging. Change-Id: Ia3757a80cf8adb2811a089d2eafbd6439461285c Signed-off-by: John Lo --- src/vat/api_format.c | 86 +++++++++- src/vnet/api_errno.h | 5 +- src/vnet/l2/l2.api | 65 +++++++- src/vnet/l2/l2_api.c | 83 +++++++++- src/vnet/l2/l2_bd.c | 37 +---- src/vnet/l2/l2_fib.c | 394 ++++++++++++++++++++++++++++++++++------------ src/vnet/l2/l2_fib.h | 37 ++++- src/vnet/l2/l2_fwd.c | 3 +- src/vnet/l2/l2_learn.c | 30 ++-- src/vnet/l2/l2_learn.h | 5 + src/vpp/api/custom_dump.c | 33 +++- 11 files changed, 605 insertions(+), 173 deletions(-) (limited to 'src/vnet/l2/l2_learn.h') diff --git a/src/vat/api_format.c b/src/vat/api_format.c index bbd97ba1..27286686 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -1283,6 +1283,30 @@ vl_api_ip6_nd_event_t_handler_json (vl_api_ip6_nd_event_t * mp) /* JSON output not supported */ } +static void +vl_api_l2_macs_event_t_handler (vl_api_l2_macs_event_t * mp) +{ + u32 n_macs = ntohl (mp->n_macs); + errmsg ("L2MAC event recived with pid %d cl-idx %d for %d macs: \n", + ntohl (mp->pid), mp->client_index, n_macs); + int i; + for (i = 0; i < n_macs; i++) + { + vl_api_mac_entry_t *mac = &mp->mac[i]; + errmsg (" [%d] sw_if_index %d mac_addr %U is_del %d \n", + i + 1, ntohl (mac->sw_if_index), + format_ethernet_address, mac->mac_addr, mac->is_del); + if (i == 1000) + break; + } +} + +static void +vl_api_l2_macs_event_t_handler_json (vl_api_l2_macs_event_t * mp) +{ + /* JSON output not supported */ +} + #define vl_api_bridge_domain_details_t_endian vl_noop_handler #define vl_api_bridge_domain_details_t_print vl_noop_handler @@ -4597,6 +4621,7 @@ _(modify_vhost_user_if_reply) \ _(delete_vhost_user_if_reply) \ _(want_ip4_arp_events_reply) \ _(want_ip6_nd_events_reply) \ +_(want_l2_macs_events_reply) \ _(input_acl_set_interface_reply) \ _(ipsec_spd_add_del_reply) \ _(ipsec_interface_add_del_spd_reply) \ @@ -4813,6 +4838,8 @@ _(WANT_IP4_ARP_EVENTS_REPLY, want_ip4_arp_events_reply) \ _(IP4_ARP_EVENT, ip4_arp_event) \ _(WANT_IP6_ND_EVENTS_REPLY, want_ip6_nd_events_reply) \ _(IP6_ND_EVENT, ip6_nd_event) \ +_(WANT_L2_MACS_EVENTS_REPLY, want_l2_macs_events_reply) \ +_(L2_MACS_EVENT, l2_macs_event) \ _(INPUT_ACL_SET_INTERFACE_REPLY, input_acl_set_interface_reply) \ _(IP_ADDRESS_DETAILS, ip_address_details) \ _(IP_DETAILS, ip_details) \ @@ -6607,8 +6634,9 @@ api_l2_flags (vat_main_t * vam) unformat_input_t *i = vam->input; vl_api_l2_flags_t *mp; u32 sw_if_index; - u32 feature_bitmap = 0; + u32 flags = 0; u8 sw_if_index_set = 0; + u8 is_set = 0; int ret; /* Parse args required to build the message */ @@ -6628,13 +6656,19 @@ api_l2_flags (vat_main_t * vam) break; } else if (unformat (i, "learn")) - feature_bitmap |= L2INPUT_FEAT_LEARN; + flags |= L2_LEARN; else if (unformat (i, "forward")) - feature_bitmap |= L2INPUT_FEAT_FWD; + flags |= L2_FWD; else if (unformat (i, "flood")) - feature_bitmap |= L2INPUT_FEAT_FLOOD; + flags |= L2_FLOOD; else if (unformat (i, "uu-flood")) - feature_bitmap |= L2INPUT_FEAT_UU_FLOOD; + flags |= L2_UU_FLOOD; + else if (unformat (i, "arp-term")) + flags |= L2_ARP_TERM; + else if (unformat (i, "off")) + is_set = 0; + else if (unformat (i, "disable")) + is_set = 0; else break; } @@ -6648,7 +6682,8 @@ api_l2_flags (vat_main_t * vam) M (L2_FLAGS, mp); mp->sw_if_index = ntohl (sw_if_index); - mp->feature_bitmap = ntohl (feature_bitmap); + mp->feature_bitmap = ntohl (flags); + mp->is_set = is_set; S (mp); W (ret); @@ -12534,6 +12569,42 @@ api_want_ip6_nd_events (vat_main_t * vam) return ret; } +static int +api_want_l2_macs_events (vat_main_t * vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_want_l2_macs_events_t *mp; + u8 enable_disable = 1; + u32 scan_delay = 0; + u32 max_macs_in_event = 0; + u32 learn_limit = 0; + int ret; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "learn-limit %d", &learn_limit)) + ; + else if (unformat (line_input, "scan-delay %d", &scan_delay)) + ; + else if (unformat (line_input, "max-entries %d", &max_macs_in_event)) + ; + else if (unformat (line_input, "disable")) + enable_disable = 0; + else + break; + } + + M (WANT_L2_MACS_EVENTS, mp); + mp->enable_disable = enable_disable; + mp->pid = htonl (getpid ()); + mp->learn_limit = htonl (learn_limit); + mp->scan_delay = (u8) scan_delay; + mp->max_macs_in_event = (u8) (max_macs_in_event / 10); + S (mp); + W (ret); + return ret; +} + static int api_input_acl_set_interface (vat_main_t * vam) { @@ -19831,7 +19902,7 @@ _(l2fib_add_del, \ _(l2fib_flush_bd, "bd_id ") \ _(l2fib_flush_int, " | sw_if_index ") \ _(l2_flags, \ - "sw_if | sw_if_index [learn] [forward] [uu-flood] [flood]\n") \ + "sw_if | sw_if_index [learn] [forward] [uu-flood] [flood] [arp-term] [disable]\n") \ _(bridge_flags, \ "bd_id [learn] [forward] [uu-flood] [flood] [arp-term] [disable]\n") \ _(tap_connect, \ @@ -19974,6 +20045,7 @@ _(input_acl_set_interface, \ " [l2-table ] [del]") \ _(want_ip4_arp_events, "address [del]") \ _(want_ip6_nd_events, "address [del]") \ +_(want_l2_macs_events, "[disable] [learn-limit ] [scan-delay ] [max-entries ]") \ _(ip_address_dump, "(ipv4 | ipv6) ( | sw_if_index )") \ _(ip_dump, "ipv4 | ipv6") \ _(ipsec_spd_add_del, "spd_id [del]") \ diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index 747c65e7..22522f34 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -112,8 +112,9 @@ _(BD_ALREADY_EXISTS, -119, "Bridge domain already exists") \ _(BD_IN_USE, -120, "Bridge domain has member interfaces") \ _(BD_NOT_MODIFIABLE, -121, "Bridge domain 0 can't be deleted/modified") \ _(BD_ID_EXCEED_MAX, -122, "Bridge domain ID exceed 16M limit") \ -_(UNSUPPORTED, -123, "Unsupported") \ -_(SUBIF_DOESNT_EXIST, -124, "Subinterface doesn't exist") +_(SUBIF_DOESNT_EXIST, -123, "Subinterface doesn't exist") \ +_(L2_MACS_EVENT_CLINET_PRESENT, -124, "Client already exist for L2 MACs events") \ +_(UNSUPPORTED, -125, "Unsupported") typedef enum { diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index bb3990c6..e508bfb5 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -133,12 +133,64 @@ autoreply define l2fib_add_del u8 bvi_mac; }; -/** \brief Set L2 flags request !!! TODO - need more info, feature bits in l2_input.h +/** \brief Register to recive L2 MAC events for leanred and aged MAC + Will also change MAC learn limit to L2LEARN_INFORM_LIMIT + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param learn_limit - MAC learn limit, 0 => default to 1000 + @param scan_delay - event scan delay in 10 msec unit, 0 => default to 100 msec + @param max_macs_in_event - in units of 10 mac entries, 0 => default to 100 entries + @param enable_disable - 1 => register for MAC events, 0 => cancel registration + @param pid - sender's pid +*/ +autoreply define want_l2_macs_events +{ + u32 client_index; + u32 context; + u32 learn_limit; + u8 scan_delay; + u8 max_macs_in_event; + u8 enable_disable; + u32 pid; +}; + +/** \brief Entry for learned or aged MAC in L2 MAC Events + @param sw_if_index - sw_if_index in the domain + @param mac_addr - mac_address + @is_del - 0 => newly learned MAC, 1 => aged out MAC +*/ +typeonly define mac_entry +{ + u32 sw_if_index; + u8 mac_addr[6]; + u8 is_del; + u8 spare; +}; + +/** \brief L2 MAC event for a list of learned or aged MACs + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param n_macs - number of learned/aged MAC enntries + @param mac - array of learned/aged MAC entries +*/ +define l2_macs_event +{ + u32 client_index; + u32 pid; + u32 n_macs; + vl_api_mac_entry_t mac[n_macs]; +}; + +/** \brief Set interface L2 flags (such as L2_LEARN, L2_FWD, + L2_FLOOD, L2_UU_FLOOD, or L2_ARP_TERM bits). This can be used + to disable one or more of the features represented by the + flag bits on an interface to override what is set as default + for all interfaces in the bridge domain @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param sw_if_index - interface @param is_set - if non-zero, set the bits, else clear them - @param feature_bitmap - non-zero bits to set or clear + @param feature_bitmap - non-zero bits (as above) to set or clear */ define l2_flags { @@ -149,9 +201,10 @@ define l2_flags u32 feature_bitmap; }; -/** \brief Set L2 bits response +/** \brief Set interface L2 flags response @param context - sender context, to match reply w/ request @param retval - return code for the set l2 bits request + @param resulting_feature_bitmap - the internal l2 feature bitmap after the request is implemented */ define l2_flags_reply { @@ -250,12 +303,12 @@ manual_print manual_endian define bridge_domain_details }; /** \brief Set bridge flags (such as L2_LEARN, L2_FWD, L2_FLOOD, - L2_UU_FLOOD, or L2_ARP_TERM) request + L2_UU_FLOOD, or L2_ARP_TERM bits) request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param bd_id - the bridge domain to set the flags for @param is_set - if non-zero, set the flags, else clear them - @param feature_bitmap - bits that are non-zero to set or clear + @param feature_bitmap - bits (as above) that are non-zero to set or clear */ define bridge_flags { @@ -269,7 +322,7 @@ define bridge_flags /** \brief Set bridge flags response @param context - sender context, to match reply w/ request @param retval - return code for the set bridge flags request - @param resulting_feature_bitmap - the feature bitmap value after the request is implemented + @param resulting_feature_bitmap - the internal L2 feature bitmap after the request is implemented */ define bridge_flags_reply { diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index a0b40d6d..c81cbad7 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -55,6 +56,7 @@ _(L2FIB_FLUSH_ALL, l2fib_flush_all) \ _(L2FIB_FLUSH_INT, l2fib_flush_int) \ _(L2FIB_FLUSH_BD, l2fib_flush_bd) \ _(L2FIB_ADD_DEL, l2fib_add_del) \ +_(WANT_L2_MACS_EVENTS, want_l2_macs_events) \ _(L2_FLAGS, l2_flags) \ _(BRIDGE_DOMAIN_ADD_DEL, bridge_domain_add_del) \ _(BRIDGE_DOMAIN_DUMP, bridge_domain_dump) \ @@ -221,8 +223,8 @@ vl_api_l2fib_add_del_t_handler (vl_api_l2fib_add_del_t * mp) goto bad_sw_if_index; } } - u32 static_mac = mp->static_mac ? 1 : 0; - u32 bvi_mac = mp->bvi_mac ? 1 : 0; + u8 static_mac = mp->static_mac ? 1 : 0; + u8 bvi_mac = mp->bvi_mac ? 1 : 0; l2fib_add_fwd_entry (mac, bd_index, sw_if_index, static_mac, bvi_mac); } @@ -237,6 +239,58 @@ vl_api_l2fib_add_del_t_handler (vl_api_l2fib_add_del_t * mp) REPLY_MACRO (VL_API_L2FIB_ADD_DEL_REPLY); } +static void +vl_api_want_l2_macs_events_t_handler (vl_api_want_l2_macs_events_t * mp) +{ + int rv = 0; + vl_api_want_l2_macs_events_reply_t *rmp; + l2learn_main_t *lm = &l2learn_main; + l2fib_main_t *fm = &l2fib_main; + u32 pid = ntohl (mp->pid); + u32 learn_limit = ntohl (mp->learn_limit); + + if (mp->enable_disable) + { + if (lm->client_pid == 0) + { + lm->client_pid = pid; + lm->client_index = mp->client_index; + + if (mp->max_macs_in_event) + fm->max_macs_in_event = mp->max_macs_in_event * 10; + else + fm->max_macs_in_event = L2FIB_EVENT_MAX_MACS_DEFAULT; + + if (mp->scan_delay) + fm->event_scan_delay = (f64) (mp->scan_delay) * 10e-3; + else + fm->event_scan_delay = L2FIB_EVENT_SCAN_DELAY_DEFAULT; + + /* change learn limit and flush all learned MACs */ + if (learn_limit && (learn_limit < L2LEARN_DEFAULT_LIMIT)) + lm->global_learn_limit = learn_limit; + else + lm->global_learn_limit = L2FIB_EVENT_LEARN_LIMIT_DEFAULT; + + l2fib_flush_all_mac (vlib_get_main ()); + } + else if (lm->client_pid != pid) + { + rv = VNET_API_ERROR_L2_MACS_EVENT_CLINET_PRESENT; + goto exit; + } + } + else if (lm->client_pid) + { + lm->client_pid = 0; + lm->client_index = 0; + lm->global_learn_limit = L2LEARN_DEFAULT_LIMIT; + } + +exit: + REPLY_MACRO (VL_API_WANT_L2_MACS_EVENTS_REPLY); +} + static void vl_api_l2fib_flush_int_t_handler (vl_api_l2fib_flush_int_t * mp) { @@ -293,8 +347,25 @@ vl_api_l2_flags_t_handler (vl_api_l2_flags_t * mp) VALIDATE_SW_IF_INDEX (mp); u32 sw_if_index = ntohl (mp->sw_if_index); - u32 flags = ntohl (mp->feature_bitmap) & L2INPUT_VALID_MASK; - rbm = l2input_intf_bitmap_enable (sw_if_index, flags, mp->is_set); + u32 flags = ntohl (mp->feature_bitmap); + u32 bitmap = 0; + + if (flags & L2_LEARN) + bitmap |= L2INPUT_FEAT_LEARN; + + if (flags & L2_FWD) + bitmap |= L2INPUT_FEAT_FWD; + + if (flags & L2_FLOOD) + bitmap |= L2INPUT_FEAT_FLOOD; + + if (flags & L2_UU_FLOOD) + bitmap |= L2INPUT_FEAT_UU_FLOOD; + + if (flags & L2_ARP_TERM) + bitmap |= L2INPUT_FEAT_ARP_TERM; + + rbm = l2input_intf_bitmap_enable (sw_if_index, bitmap, mp->is_set); BAD_SW_IF_INDEX_LABEL; @@ -455,13 +526,13 @@ vl_api_bridge_flags_t_handler (vl_api_bridge_flags_t * mp) goto out; } - bd_set_flags (vm, bd_index, flags, mp->is_set); + u32 bitmap = bd_set_flags (vm, bd_index, flags, mp->is_set); out: /* *INDENT-OFF* */ REPLY_MACRO2(VL_API_BRIDGE_FLAGS_REPLY, ({ - rmp->resulting_feature_bitmap = ntohl(flags); + rmp->resulting_feature_bitmap = ntohl(bitmap); })); /* *INDENT-ON* */ } diff --git a/src/vnet/l2/l2_bd.c b/src/vnet/l2/l2_bd.c index a87d02f2..6e0db058 100644 --- a/src/vnet/l2/l2_bd.c +++ b/src/vnet/l2/l2_bd.c @@ -263,7 +263,7 @@ bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable) bd_config->feature_bitmap &= ~feature_bitmap; } - return 0; + return bd_config->feature_bitmap; } /** @@ -328,12 +328,7 @@ bd_learn (vlib_main_t * vm, } /* set the bridge domain flag */ - if (bd_set_flags (vm, bd_index, L2_LEARN, enable)) - { - error = - clib_error_return (0, "bridge-domain id %d out of range", bd_index); - goto done; - } + bd_set_flags (vm, bd_index, L2_LEARN, enable); done: return error; @@ -397,12 +392,7 @@ bd_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) } /* set the bridge domain flag */ - if (bd_set_flags (vm, bd_index, L2_FWD, enable)) - { - error = - clib_error_return (0, "bridge-domain id %d out of range", bd_index); - goto done; - } + bd_set_flags (vm, bd_index, L2_FWD, enable); done: return error; @@ -468,12 +458,7 @@ bd_flood (vlib_main_t * vm, } /* set the bridge domain flag */ - if (bd_set_flags (vm, bd_index, L2_FLOOD, enable)) - { - error = - clib_error_return (0, "bridge-domain id %d out of range", bd_index); - goto done; - } + bd_set_flags (vm, bd_index, L2_FLOOD, enable); done: return error; @@ -538,12 +523,7 @@ bd_uu_flood (vlib_main_t * vm, } /* set the bridge domain flag */ - if (bd_set_flags (vm, bd_index, L2_UU_FLOOD, enable)) - { - error = - clib_error_return (0, "bridge-domain id %d out of range", bd_index); - goto done; - } + bd_set_flags (vm, bd_index, L2_UU_FLOOD, enable); done: return error; @@ -605,12 +585,7 @@ bd_arp_term (vlib_main_t * vm, enable = 0; /* set the bridge domain flag */ - if (bd_set_flags (vm, bd_index, L2_ARP_TERM, enable)) - { - error = - clib_error_return (0, "bridge-domain id %d out of range", bd_index); - goto done; - } + bd_set_flags (vm, bd_index, L2_ARP_TERM, enable); done: return error; diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c index 7e59b098..8aa0ac29 100644 --- a/src/vnet/l2/l2_fib.c +++ b/src/vnet/l2/l2_fib.c @@ -31,6 +31,17 @@ #include +#include +#include + +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + /** * @file * @brief Ethernet MAC Address FIB Table Management. @@ -117,6 +128,7 @@ show_l2fib (vlib_main_t * vm, int i, j, k; u8 verbose = 0; u8 raw = 0; + u8 learn = 0; u32 bd_id, bd_index = ~0; u8 now = (u8) (vlib_time_now (vm) / 60); u8 *s = 0; @@ -127,12 +139,18 @@ show_l2fib (vlib_main_t * vm, verbose = 1; else if (unformat (input, "bd_index %d", &bd_index)) verbose = 1; + else if (unformat (input, "learn")) + { + learn = 1; + verbose = 0; + } else if (unformat (input, "bd_id %d", &bd_id)) { uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p) { - verbose = 1; + if (learn == 0) + verbose = 1; bd_index = p[0]; } else @@ -155,7 +173,7 @@ show_l2fib (vlib_main_t * vm, if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL) continue; - if (verbose && first_entry) + if ((verbose || learn) && first_entry) { first_entry = 0; vlib_cli_output (vm, @@ -168,13 +186,19 @@ show_l2fib (vlib_main_t * vm, key.raw = v->kvp[k].key; result.raw = v->kvp[k].value; - if (verbose + if ((verbose || learn) & ((bd_index >> 31) || (bd_index == key.fields.bd_index))) { + if (learn && result.fields.age_not) + { + total_entries++; + continue; /* skip provisioned macs */ + } + bd_config = vec_elt_at_index (l2input_main.bd_configs, key.fields.bd_index); - if (bd_config->mac_age && !result.fields.static_mac) + if (bd_config->mac_age && !result.fields.age_not) { i16 delta = now - result.fields.timestamp; delta += delta < 0 ? 256 : 0; @@ -206,9 +230,19 @@ show_l2fib (vlib_main_t * vm, if (total_entries == 0) vlib_cli_output (vm, "no l2fib entries"); else - vlib_cli_output (vm, - "%lld l2fib entries with %d learned (or non-static) entries", - total_entries, l2learn_main.global_learn_count); + { + l2learn_main_t *lm = &l2learn_main; + vlib_cli_output (vm, "L2FIB total/learned entries: %d/%d " + "Last scan time: %.4esec Learn limit: %d ", + total_entries, lm->global_learn_count, + msm->age_scan_duration, lm->global_learn_limit); + if (lm->client_pid) + vlib_cli_output (vm, "L2MAC events client PID: %d " + "Last e-scan time: %.4esec Delay: %.2esec " + "Max macs in event: %d", + lm->client_pid, msm->evt_scan_duration, + msm->event_scan_delay, msm->max_macs_in_event); + } if (raw) vlib_cli_output (vm, "Raw Hash Table:\n%U\n", @@ -242,7 +276,7 @@ show_l2fib (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_l2fib_cli, static) = { .path = "show l2fib", - .short_help = "show l2fib [verbose | bd_id | bd_index | raw]", + .short_help = "show l2fib [verbose | learn | bd_id | bd_index | raw", .function = show_l2fib, }; /* *INDENT-ON* */ @@ -309,36 +343,39 @@ l2fib_cur_seq_num (u32 bd_index, u32 sw_if_index) */ void l2fib_add_entry (u64 mac, u32 bd_index, - u32 sw_if_index, u32 static_mac, u32 filter_mac, u32 bvi_mac) + u32 sw_if_index, u8 static_mac, u8 filter_mac, u8 bvi_mac) { l2fib_entry_key_t key; l2fib_entry_result_t result; __attribute__ ((unused)) u32 bucket_contents; - l2fib_main_t *mp = &l2fib_main; + l2fib_main_t *fm = &l2fib_main; + l2learn_main_t *lm = &l2learn_main; BVT (clib_bihash_kv) kv; /* set up key */ key.raw = l2fib_make_key ((u8 *) & mac, bd_index); + /* check if entry alread exist */ + if (BV (clib_bihash_search) (&fm->mac_table, &kv, &kv)) + { + /* decrement counter if overwriting a learned mac */ + result.raw = kv.value; + if ((result.fields.age_not == 0) && (lm->global_learn_count)) + lm->global_learn_count--; + } + /* set up result */ result.raw = 0; /* clear all fields */ result.fields.sw_if_index = sw_if_index; result.fields.static_mac = static_mac; result.fields.filter = filter_mac; result.fields.bvi = bvi_mac; - if (!static_mac) - result.fields.sn = l2fib_cur_seq_num (bd_index, sw_if_index); + result.fields.age_not = 1; /* no aging for provisioned entry */ kv.key = key.raw; kv.value = result.raw; - BV (clib_bihash_add_del) (&mp->mac_table, &kv, 1 /* is_add */ ); - - /* increment counter if dynamically learned mac */ - if (result.fields.static_mac == 0) - { - l2learn_main.global_learn_count++; - } + BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1 /* is_add */ ); } /** @@ -630,13 +667,8 @@ l2fib_del_entry_by_key (u64 raw_key) result.raw = kv.value; /* decrement counter if dynamically learned mac */ - if (result.fields.static_mac == 0) - { - if (l2learn_main.global_learn_count > 0) - { - l2learn_main.global_learn_count--; - } - } + if ((result.fields.age_not == 0) && (l2learn_main.global_learn_count)) + l2learn_main.global_learn_count--; /* Remove entry from hash table */ BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ ); @@ -910,111 +942,273 @@ BVT (clib_bihash) * get_mac_table (void) return &mp->mac_table; } +static_always_inline void * +allocate_mac_evt_buf (u32 client, u32 client_index) +{ + l2fib_main_t *fm = &l2fib_main; + vl_api_l2_macs_event_t *mp = vl_msg_api_alloc + (sizeof (*mp) + (fm->max_macs_in_event * sizeof (vl_api_mac_entry_t))); + mp->_vl_msg_id = htons (VL_API_L2_MACS_EVENT); + mp->pid = htonl (client); + mp->client_index = client_index; + return mp; +} + +static_always_inline f64 +l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) +{ + l2fib_main_t *fm = &l2fib_main; + l2learn_main_t *lm = &l2learn_main; + + BVT (clib_bihash) * h = &fm->mac_table; + int i, j, k; + f64 last_start = start_time; + f64 accum_t = 0; + f64 delta_t = 0; + u32 evt_idx = 0; + u32 learn_count = 0; + u32 client = lm->client_pid; + u32 cl_idx = lm->client_index; + vl_api_l2_macs_event_t *mp = 0; + unix_shared_memory_queue_t *q = 0; + + if (client) + { + mp = allocate_mac_evt_buf (client, cl_idx); + q = vl_api_client_index_to_input_queue (lm->client_index); + } + + for (i = 0; i < h->nbuckets; i++) + { + /* allow no more than 20us without a pause */ + delta_t = vlib_time_now (vm) - last_start; + if (delta_t > 20e-6) + { + vlib_process_suspend (vm, 100e-6); /* suspend for 100 us */ + last_start = vlib_time_now (vm); + accum_t += delta_t; + } + + if (i < (h->nbuckets - 3)) + { + BVT (clib_bihash_bucket) * b = &h->buckets[i + 3]; + CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD); + b = &h->buckets[i + 1]; + if (b->offset) + { + BVT (clib_bihash_value) * v = + BV (clib_bihash_get_value) (h, b->offset); + CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD); + } + } + + BVT (clib_bihash_bucket) * b = &h->buckets[i]; + if (b->offset == 0) + continue; + BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset); + for (j = 0; j < (1 << b->log2_pages); j++) + { + for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) + { + if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL) + continue; + + l2fib_entry_key_t key = {.raw = v->kvp[k].key }; + l2fib_entry_result_t result = {.raw = v->kvp[k].value }; + + if (result.fields.age_not == 0) + learn_count++; + + if (PREDICT_FALSE (evt_idx >= fm->max_macs_in_event)) + { + /* evet message full, sent it and start a new one */ + if (q && (q->cursize < q->maxsize)) + { + mp->n_macs = htonl (evt_idx); + vl_msg_api_send_shmem (q, (u8 *) & mp); + mp = allocate_mac_evt_buf (client, cl_idx); + } + else + { + clib_warning ("MAC event to pid %d queue stuffed!" + " %d MAC entries lost", client, evt_idx); + } + evt_idx = 0; + } + + if (client) + { + if (result.fields.lrn_evt) + { + /* copy mac entry to event msg */ + clib_memcpy (mp->mac[evt_idx].mac_addr, key.fields.mac, + 6); + mp->mac[evt_idx].is_del = 0; + mp->mac[evt_idx].sw_if_index = + htonl (result.fields.sw_if_index); + /* clear event bit and update mac entry */ + result.fields.lrn_evt = 0; + BVT (clib_bihash_kv) kv; + kv.key = key.raw; + kv.value = result.raw; + BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1); + evt_idx++; + continue; /* skip aging */ + } + } + + if (event_only || result.fields.age_not) + continue; /* skip aging - static_mac alsways age_not */ + + /* start aging processing */ + u32 bd_index = key.fields.bd_index; + u32 sw_if_index = result.fields.sw_if_index; + u16 sn = l2fib_cur_seq_num (bd_index, sw_if_index).as_u16; + if (result.fields.sn.as_u16 != sn) + goto age_out; /* stale mac */ + + l2_bridge_domain_t *bd_config = + vec_elt_at_index (l2input_main.bd_configs, bd_index); + + if (bd_config->mac_age == 0) + continue; /* skip aging */ + + i16 delta = (u8) (start_time / 60) - result.fields.timestamp; + delta += delta < 0 ? 256 : 0; + + if (delta < bd_config->mac_age) + continue; /* still valid */ + + age_out: + if (client) + { + /* copy mac entry to event msg */ + clib_memcpy (mp->mac[evt_idx].mac_addr, key.fields.mac, 6); + mp->mac[evt_idx].is_del = 1; + mp->mac[evt_idx].sw_if_index = + htonl (result.fields.sw_if_index); + evt_idx++; + } + /* delete mac entry */ + BVT (clib_bihash_kv) kv; + kv.key = key.raw; + BV (clib_bihash_add_del) (&fm->mac_table, &kv, 0); + learn_count--; + } + v++; + } + } + + /* keep learn count consistent */ + l2learn_main.global_learn_count = learn_count; + + if (mp) + { + /* send any outstanding mac event message else free message buffer */ + if (evt_idx) + { + if (q && (q->cursize < q->maxsize)) + { + mp->n_macs = htonl (evt_idx); + vl_msg_api_send_shmem (q, (u8 *) & mp); + } + else + { + clib_warning ("MAC event to pid %d queue stuffed!" + " %d MAC entries lost", client, evt_idx); + vl_msg_api_free (mp); + } + } + else + vl_msg_api_free (mp); + } + return delta_t + accum_t; +} + +/* Type of scan */ +#define SCAN_MAC_AGE 0 +#define SCAN_MAC_EVENT 1 + +/* Maximum f64 value */ +#define TIME_MAX (1.7976931348623157e+308) + static uword l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { uword event_type, *event_data = 0; - l2fib_main_t *msm = &l2fib_main; + l2fib_main_t *fm = &l2fib_main; + l2learn_main_t *lm = &l2learn_main; bool enabled = 0; - f64 start_time, last_run_duration = 0, t; + bool scan = SCAN_MAC_AGE; /* SCAN_FOR_AGE or SCAN_FOR_EVENT */ + f64 start_time, next_age_scan_time = TIME_MAX; while (1) { if (enabled) - vlib_process_wait_for_event_or_clock (vm, 60 - last_run_duration); + { + if (lm->client_pid) /* mac event client waiting */ + vlib_process_wait_for_event_or_clock (vm, fm->event_scan_delay); + else /* agin only */ + { + f64 t = next_age_scan_time - vlib_time_now (vm); + if (t < fm->event_scan_delay) + t = fm->event_scan_delay; + vlib_process_wait_for_event_or_clock (vm, t); + } + } else vlib_process_wait_for_event (vm); event_type = vlib_process_get_events (vm, &event_data); vec_reset_length (event_data); + start_time = vlib_time_now (vm); + switch (event_type) { - case ~0: + case ~0: /* timer expired */ + if ((lm->client_pid == 0) || (start_time >= next_age_scan_time)) + { + scan = SCAN_MAC_AGE; + if (enabled) + next_age_scan_time = start_time + L2FIB_AGE_SCAN_INTERVAL; + else + next_age_scan_time = TIME_MAX; + } + else + scan = SCAN_MAC_EVENT; break; + case L2_MAC_AGE_PROCESS_EVENT_START: + scan = SCAN_MAC_AGE; + next_age_scan_time = start_time + L2FIB_AGE_SCAN_INTERVAL; enabled = 1; break; + case L2_MAC_AGE_PROCESS_EVENT_STOP: enabled = 0; + next_age_scan_time = TIME_MAX; + l2fib_main.age_scan_duration = 0; + l2fib_main.evt_scan_duration = 0; continue; + case L2_MAC_AGE_PROCESS_EVENT_ONE_PASS: - enabled = 0; + scan = SCAN_MAC_AGE; + if (enabled) + next_age_scan_time = start_time + L2FIB_AGE_SCAN_INTERVAL; + else + next_age_scan_time = TIME_MAX; break; + default: ASSERT (0); } - last_run_duration = start_time = vlib_time_now (vm); - BVT (clib_bihash) * h = &msm->mac_table; - int i, j, k; - for (i = 0; i < h->nbuckets; i++) - { - /* Allow no more than 10us without a pause */ - t = vlib_time_now (vm); - if (t > start_time + 10e-6) - { - vlib_process_suspend (vm, 100e-6); /* suspend for 100 us */ - start_time = vlib_time_now (vm); - } - - if (i < (h->nbuckets - 3)) - { - BVT (clib_bihash_bucket) * b = &h->buckets[i + 3]; - CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD); - b = &h->buckets[i + 1]; - if (b->offset) - { - BVT (clib_bihash_value) * v = - BV (clib_bihash_get_value) (h, b->offset); - CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD); - } - } - - BVT (clib_bihash_bucket) * b = &h->buckets[i]; - if (b->offset == 0) - continue; - BVT (clib_bihash_value) * v = - BV (clib_bihash_get_value) (h, b->offset); - for (j = 0; j < (1 << b->log2_pages); j++) - { - for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) - { - if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL) - continue; - - l2fib_entry_key_t key = {.raw = v->kvp[k].key }; - l2fib_entry_result_t result = {.raw = v->kvp[k].value }; - - if (result.fields.static_mac) - continue; - - u32 bd_index = key.fields.bd_index; - u32 sw_if_index = result.fields.sw_if_index; - u16 sn = l2fib_cur_seq_num (bd_index, sw_if_index).as_u16; - if (result.fields.sn.as_u16 != sn) - { - l2fib_del_entry_by_key (key.raw); - continue; - } - l2_bridge_domain_t *bd_config = - vec_elt_at_index (l2input_main.bd_configs, bd_index); - - if (bd_config->mac_age == 0) - continue; - - i16 delta = - (u8) (start_time / 60) - result.fields.timestamp; - delta += delta < 0 ? 256 : 0; - - if (delta > bd_config->mac_age) - l2fib_del_entry_by_key (key.raw); - } - v++; - } - } - last_run_duration = vlib_time_now (vm) - last_run_duration; + if (scan == SCAN_MAC_EVENT) + l2fib_main.evt_scan_duration = l2fib_scan (vm, start_time, 1); + else + l2fib_main.age_scan_duration = l2fib_scan (vm, start_time, 0); } return 0; } diff --git a/src/vnet/l2/l2_fib.h b/src/vnet/l2/l2_fib.h index ee6f0dc5..49a8b5b6 100644 --- a/src/vnet/l2/l2_fib.h +++ b/src/vnet/l2/l2_fib.h @@ -27,6 +27,18 @@ #define L2FIB_NUM_BUCKETS (64 * 1024) #define L2FIB_MEMORY_SIZE (256<<20) +/* Ager scan interval is 1 minute for aging */ +#define L2FIB_AGE_SCAN_INTERVAL (60.0) + +/* MAC event scan delay is 100 msec unless specified by MAC event client */ +#define L2FIB_EVENT_SCAN_DELAY_DEFAULT (0.1) + +/* Max MACs in a event message is 100 unless specified by MAC event client */ +#define L2FIB_EVENT_MAX_MACS_DEFAULT (100) + +/* MAC event learn limit is 1000 unless specified by MAC event client */ +#define L2FIB_EVENT_LEARN_LIMIT_DEFAULT (1000) + typedef struct { @@ -36,6 +48,16 @@ typedef struct /* per swif vector of sequence number for interface based flush of MACs */ u8 *swif_seq_num; + /* last event or ager scan duration */ + f64 evt_scan_duration; + f64 age_scan_duration; + + /* delay between event scans, default to 100 msec */ + f64 event_scan_delay; + + /* max macs in evet message, default to 100 entries */ + u32 max_macs_in_event; + /* convenience variables */ vlib_main_t *vlib_main; vnet_main_t *vnet_main; @@ -89,12 +111,15 @@ typedef struct { struct { - u32 sw_if_index; /* output sw_if_index (L3 interface if bvi==1) */ + u32 sw_if_index; /* output sw_if_index (L3 intf if bvi==1) */ - u8 static_mac:1; /* static mac, no dataplane learning */ + u8 static_mac:1; /* static mac, no MAC move */ + u8 age_not:1; /* not subject to age */ u8 bvi:1; /* mac is for a bridged virtual interface */ u8 filter:1; /* drop packets to/from this mac */ - u8 unused1:5; + u8 lrn_evt:1; /* MAC learned to be sent in L2 MAC event */ + u8 unused:3; + u8 timestamp; /* timestamp for aging */ l2fib_seq_num_t sn; /* bd/int seq num */ } fields; @@ -348,11 +373,11 @@ void l2fib_clear_table (void); void l2fib_add_entry (u64 mac, u32 bd_index, - u32 sw_if_index, u32 static_mac, u32 drop_mac, u32 bvi_mac); + u32 sw_if_index, u8 static_mac, u8 drop_mac, u8 bvi_mac); static inline void -l2fib_add_fwd_entry (u64 mac, u32 bd_index, u32 sw_if_index, u32 static_mac, - u32 bvi_mac) +l2fib_add_fwd_entry (u64 mac, u32 bd_index, u32 sw_if_index, u8 static_mac, + u8 bvi_mac) { l2fib_add_entry (mac, bd_index, sw_if_index, static_mac, 0, bvi_mac); } diff --git a/src/vnet/l2/l2_fwd.c b/src/vnet/l2/l2_fwd.c index 8140728b..2bb7307c 100644 --- a/src/vnet/l2/l2_fwd.c +++ b/src/vnet/l2/l2_fwd.c @@ -141,8 +141,9 @@ l2fwd_process (vlib_main_t * vm, vnet_buffer (b0)->sw_if_index[VLIB_TX] = result0->fields.sw_if_index; *next0 = L2FWD_NEXT_L2_OUTPUT; int l2fib_seq_num_valid = 1; + /* check l2fib seq num for stale entries */ - if (!result0->fields.static_mac) + if (!result0->fields.age_not) { l2fib_seq_num_t in_sn = {.as_u16 = vnet_buffer (b0)->l2.l2fib_sn }; l2fib_seq_num_t expected_sn = { diff --git a/src/vnet/l2/l2_learn.c b/src/vnet/l2/l2_learn.c index 65406292..47c036b0 100644 --- a/src/vnet/l2/l2_learn.c +++ b/src/vnet/l2/l2_learn.c @@ -123,11 +123,9 @@ l2learn_process (vlib_node_runtime_t * node, /* Check mac table lookup result */ if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0)) { - /* - * The entry was in the table, and the sw_if_index matched, the normal case - */ + /* Entry in L2FIB with matching sw_if_index matched - normal fast path */ counter_base[L2LEARN_ERROR_HIT] += 1; - int update = !result0->fields.static_mac && + int update = !result0->fields.age_not && /* static_mac always age_not */ (result0->fields.timestamp != timestamp || result0->fields.sn.as_u16 != vnet_buffer (b0)->l2.l2fib_sn); @@ -136,10 +134,10 @@ l2learn_process (vlib_node_runtime_t * node, } else if (result0->raw == ~0) { - /* The entry was not in table, so add it */ + /* Entry not in L2FIB - add it */ counter_base[L2LEARN_ERROR_MISS] += 1; - if (msm->global_learn_count == msm->global_learn_limit) + if (msm->global_learn_count >= msm->global_learn_limit) { /* * Global limit reached. Do not learn the mac but forward the packet. @@ -149,15 +147,22 @@ l2learn_process (vlib_node_runtime_t * node, return; } + /* Do not learn if mac is 0 */ + l2fib_entry_key_t key = *key0; + key.fields.bd_index = 0; + if (key.raw == 0) + return; + /* It is ok to learn */ msm->global_learn_count++; result0->raw = 0; /* clear all fields */ result0->fields.sw_if_index = sw_if_index0; + result0->fields.lrn_evt = (msm->client_pid != 0); cached_key->raw = ~0; /* invalidate the cache */ } else { - /* The entry was in the table, but with the wrong sw_if_index mapping (mac move) */ + /* Entry in L2FIB with different sw_if_index - mac move or filter */ if (result0->fields.filter) { ASSERT (result0->fields.sw_if_index == ~0); @@ -167,8 +172,6 @@ l2learn_process (vlib_node_runtime_t * node, return; } - counter_base[L2LEARN_ERROR_MAC_MOVE] += 1; - if (result0->fields.static_mac) { /* @@ -185,6 +188,13 @@ l2learn_process (vlib_node_runtime_t * node, * TODO: check global/bridge domain/interface learn limits */ result0->fields.sw_if_index = sw_if_index0; + if (result0->fields.age_not) /* The mac was provisioned */ + { + msm->global_learn_count++; + result0->fields.age_not = 0; + } + result0->fields.lrn_evt = (msm->client_pid != 0); + counter_base[L2LEARN_ERROR_MAC_MOVE] += 1; } /* Update the entry */ @@ -479,7 +489,7 @@ VLIB_NODE_FUNCTION_MULTIARCH (l2learn_node, l2learn_node_fn) * Set the default number of dynamically learned macs to the number * of buckets. */ - mp->global_learn_limit = L2FIB_NUM_BUCKETS * 16; + mp->global_learn_limit = L2LEARN_DEFAULT_LIMIT; return 0; } diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h index 0d95de04..000ab59e 100644 --- a/src/vnet/l2/l2_learn.h +++ b/src/vnet/l2/l2_learn.h @@ -34,6 +34,10 @@ typedef struct /* maximum number of dynamically learned mac entries */ u32 global_learn_limit; + /* client waiting for L2 MAC events for learned and aged MACs */ + u32 client_pid; + u32 client_index; + /* Next nodes for each feature */ u32 feat_next_node_index[32]; @@ -42,6 +46,7 @@ typedef struct vnet_main_t *vnet_main; } l2learn_main_t; +#define L2LEARN_DEFAULT_LIMIT (L2FIB_NUM_BUCKETS * 16) l2learn_main_t l2learn_main; diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 520361f6..a57799cb 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -373,10 +373,19 @@ vl_api_l2_flags_t_print (vl_api_l2_flags_t * mp, void *handle) s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); -#define _(a,b) \ - if (flags & L2INPUT_FEAT_ ## a) s = format (s, #a " "); - foreach_l2input_feat; -#undef _ + if (flags & L2_LEARN) + s = format (s, "learn "); + if (flags & L2_FWD) + s = format (s, "forward "); + if (flags & L2_FLOOD) + s = format (s, "flood "); + if (flags & L2_UU_FLOOD) + s = format (s, "uu-flood "); + if (flags & L2_ARP_TERM) + s = format (s, "arp-term "); + + if (mp->is_set == 0) + s = format (s, "clear "); FINISH; } @@ -1783,6 +1792,21 @@ static void *vl_api_want_ip6_nd_events_t_print FINISH; } +static void *vl_api_want_l2_macs_events_t_print + (vl_api_want_l2_macs_events_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: want_l2_macs_events "); + s = format (s, "learn-limit %d ", ntohl (mp->learn_limit)); + s = format (s, "scan-delay %d ", (u32) mp->scan_delay); + s = format (s, "max-entries %d ", (u32) mp->max_macs_in_event * 10); + if (mp->enable_disable == 0) + s = format (s, "disable"); + + FINISH; +} + static void *vl_api_input_acl_set_interface_t_print (vl_api_input_acl_set_interface_t * mp, void *handle) { @@ -3066,6 +3090,7 @@ _(VXLAN_GPE_TUNNEL_DUMP, vxlan_gpe_tunnel_dump) \ _(INTERFACE_NAME_RENUMBER, interface_name_renumber) \ _(WANT_IP4_ARP_EVENTS, want_ip4_arp_events) \ _(WANT_IP6_ND_EVENTS, want_ip6_nd_events) \ +_(WANT_L2_MACS_EVENTS, want_l2_macs_events) \ _(INPUT_ACL_SET_INTERFACE, input_acl_set_interface) \ _(IP_ADDRESS_DUMP, ip_address_dump) \ _(IP_DUMP, ip_dump) \ -- cgit 1.2.3-korg From e531f4cb5766fbf27e7a8af8e19ccf667b53852b Mon Sep 17 00:00:00 2001 From: John Lo Date: Tue, 22 Aug 2017 09:16:50 -0400 Subject: Increase default MAC learn limit and check it in learn-update path 1. Increase default MAC learn limit from 1M to 8M entries. 2. Check MAC learn limit in MAC learning update path. 3. Allow disable of want_l2_macs_events to set MAC learn limit 4. Other minor cleanups Change-Id: I62438440937b5fa455e16f4a2e4d910277753395 Signed-off-by: John Lo --- src/vnet/l2/l2.api | 1 - src/vnet/l2/l2_api.c | 5 ++++- src/vnet/l2/l2_fib.c | 2 +- src/vnet/l2/l2_learn.c | 2 ++ src/vnet/l2/l2_learn.h | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/vnet/l2/l2_learn.h') diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index e508bfb5..9f97ebbe 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -134,7 +134,6 @@ autoreply define l2fib_add_del }; /** \brief Register to recive L2 MAC events for leanred and aged MAC - Will also change MAC learn limit to L2LEARN_INFORM_LIMIT @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param learn_limit - MAC learn limit, 0 => default to 1000 diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index c81cbad7..7e79d6fa 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -284,7 +284,10 @@ vl_api_want_l2_macs_events_t_handler (vl_api_want_l2_macs_events_t * mp) { lm->client_pid = 0; lm->client_index = 0; - lm->global_learn_limit = L2LEARN_DEFAULT_LIMIT; + if (learn_limit) + lm->global_learn_limit = learn_limit; + else + lm->global_learn_limit = L2LEARN_DEFAULT_LIMIT; } exit: diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c index 8aa0ac29..9f4c823f 100644 --- a/src/vnet/l2/l2_fib.c +++ b/src/vnet/l2/l2_fib.c @@ -1021,7 +1021,7 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) if (PREDICT_FALSE (evt_idx >= fm->max_macs_in_event)) { - /* evet message full, sent it and start a new one */ + /* event message full, send it and start a new one */ if (q && (q->cursize < q->maxsize)) { mp->n_macs = htonl (evt_idx); diff --git a/src/vnet/l2/l2_learn.c b/src/vnet/l2/l2_learn.c index 47c036b0..623c2de2 100644 --- a/src/vnet/l2/l2_learn.c +++ b/src/vnet/l2/l2_learn.c @@ -131,6 +131,8 @@ l2learn_process (vlib_node_runtime_t * node, if (PREDICT_TRUE (!update)) return; + else if (msm->global_learn_count > msm->global_learn_limit) + return; /* Above learn limit - do not update */ } else if (result0->raw == ~0) { diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h index 000ab59e..d6f41d40 100644 --- a/src/vnet/l2/l2_learn.h +++ b/src/vnet/l2/l2_learn.h @@ -46,7 +46,7 @@ typedef struct vnet_main_t *vnet_main; } l2learn_main_t; -#define L2LEARN_DEFAULT_LIMIT (L2FIB_NUM_BUCKETS * 16) +#define L2LEARN_DEFAULT_LIMIT (L2FIB_NUM_BUCKETS * 128) l2learn_main_t l2learn_main; -- cgit 1.2.3-korg From dbfa574a1d5ebb68a81fed20dcd4bb2f47c47066 Mon Sep 17 00:00:00 2001 From: John Lo Date: Sat, 2 Sep 2017 08:27:36 -0400 Subject: Improve L2FIB PDR/NDR performance (VPP-963) 1. Limit MAC entry update per l2-learn call to reduce update burst when wall clock advance to the the next minute so all MAC time stamps are behind current time needing update. 2. Optimize l2-learn node fast path code sequence. 3. Invalidate cache_key when update MAC entry. 4. Change L2 learn hit counter to L2 learn hit-update counter. 5. Increase L2FIB table memory size to 512MB to fit 4M entries 6. Set MAC learn limit at 4M entries Change-Id: I3075ee8fb59645a56850126bac2e3e6d341cef4d Signed-off-by: John Lo --- src/vnet/l2/l2_api.c | 2 +- src/vnet/l2/l2_fib.h | 2 +- src/vnet/l2/l2_learn.c | 42 +++++++++++++++++++++++++++--------------- src/vnet/l2/l2_learn.h | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) (limited to 'src/vnet/l2/l2_learn.h') diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index 7e79d6fa..989081fb 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -284,7 +284,7 @@ vl_api_want_l2_macs_events_t_handler (vl_api_want_l2_macs_events_t * mp) { lm->client_pid = 0; lm->client_index = 0; - if (learn_limit) + if (learn_limit && (learn_limit < L2LEARN_DEFAULT_LIMIT)) lm->global_learn_limit = learn_limit; else lm->global_learn_limit = L2LEARN_DEFAULT_LIMIT; diff --git a/src/vnet/l2/l2_fib.h b/src/vnet/l2/l2_fib.h index 49a8b5b6..7cc2dc5e 100644 --- a/src/vnet/l2/l2_fib.h +++ b/src/vnet/l2/l2_fib.h @@ -25,7 +25,7 @@ * The size of the hash table */ #define L2FIB_NUM_BUCKETS (64 * 1024) -#define L2FIB_MEMORY_SIZE (256<<20) +#define L2FIB_MEMORY_SIZE (512<<20) /* Ager scan interval is 1 minute for aging */ #define L2FIB_AGE_SCAN_INTERVAL (60.0) diff --git a/src/vnet/l2/l2_learn.c b/src/vnet/l2/l2_learn.c index 623c2de2..066bb54f 100644 --- a/src/vnet/l2/l2_learn.c +++ b/src/vnet/l2/l2_learn.c @@ -78,7 +78,7 @@ _(MISS, "L2 learn misses") \ _(MAC_MOVE, "L2 mac moves") \ _(MAC_MOVE_VIOLATE, "L2 mac move violations") \ _(LIMIT, "L2 not learned due to limit") \ -_(HIT, "L2 learn hits") \ +_(HIT_UPDATE, "L2 learn hit updates") \ _(FILTER_DROP, "L2 filter mac drops") typedef enum @@ -113,7 +113,7 @@ l2learn_process (vlib_node_runtime_t * node, u32 sw_if_index0, l2fib_entry_key_t * key0, l2fib_entry_key_t * cached_key, - u32 * bucket0, + u32 * count, l2fib_entry_result_t * result0, u32 * next0, u8 timestamp) { /* Set up the default next node (typically L2FWD) */ @@ -124,15 +124,24 @@ l2learn_process (vlib_node_runtime_t * node, if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0)) { /* Entry in L2FIB with matching sw_if_index matched - normal fast path */ - counter_base[L2LEARN_ERROR_HIT] += 1; - int update = !result0->fields.age_not && /* static_mac always age_not */ - (result0->fields.timestamp != timestamp || - result0->fields.sn.as_u16 != vnet_buffer (b0)->l2.l2fib_sn); + u32 dtime = timestamp - result0->fields.timestamp; + u32 dsn = result0->fields.sn.as_u16 - vnet_buffer (b0)->l2.l2fib_sn; + u32 check = dtime | dsn; + + if (PREDICT_TRUE (check == 0)) + return; /* MAC entry up to date */ + if (result0->fields.age_not) + return; /* Static MAC always age_not */ + if (msm->global_learn_count > msm->global_learn_limit) + return; /* Above learn limit - do not update */ - if (PREDICT_TRUE (!update)) + /* Limit updates per l2-learn node call to avoid prolonged update burst + * as dtime advance over 1 minute mark, unless more than 1 min behind */ + if ((*count > 2) && (dtime == 1)) return; - else if (msm->global_learn_count > msm->global_learn_limit) - return; /* Above learn limit - do not update */ + + counter_base[L2LEARN_ERROR_HIT_UPDATE] += 1; + *count += 1; } else if (result0->raw == ~0) { @@ -160,7 +169,6 @@ l2learn_process (vlib_node_runtime_t * node, result0->raw = 0; /* clear all fields */ result0->fields.sw_if_index = sw_if_index0; result0->fields.lrn_evt = (msm->client_pid != 0); - cached_key->raw = ~0; /* invalidate the cache */ } else { @@ -207,6 +215,9 @@ l2learn_process (vlib_node_runtime_t * node, kv.key = key0->raw; kv.value = result0->raw; BV (clib_bihash_add_del) (msm->mac_table, &kv, 1 /* is_add */ ); + + /* Invalidate the cache */ + cached_key->raw = ~0; } @@ -223,6 +234,7 @@ l2learn_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node, l2fib_entry_key_t cached_key; l2fib_entry_result_t cached_result; u8 timestamp = (u8) (vlib_time_now (vm) / 60); + u32 count = 0; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; /* number of packets to process */ @@ -358,19 +370,19 @@ l2learn_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node, l2learn_process (node, msm, &em->counters[node_counter_base_index], b0, sw_if_index0, &key0, &cached_key, - &bucket0, &result0, &next0, timestamp); + &count, &result0, &next0, timestamp); l2learn_process (node, msm, &em->counters[node_counter_base_index], b1, sw_if_index1, &key1, &cached_key, - &bucket1, &result1, &next1, timestamp); + &count, &result1, &next1, timestamp); l2learn_process (node, msm, &em->counters[node_counter_base_index], b2, sw_if_index2, &key2, &cached_key, - &bucket2, &result2, &next2, timestamp); + &count, &result2, &next2, timestamp); l2learn_process (node, msm, &em->counters[node_counter_base_index], b3, sw_if_index3, &key3, &cached_key, - &bucket3, &result3, &next3, timestamp); + &count, &result3, &next3, timestamp); /* verify speculative enqueues, maybe switch current next frame */ /* if next0==next1==next_index then nothing special needs to be done */ @@ -425,7 +437,7 @@ l2learn_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node, l2learn_process (node, msm, &em->counters[node_counter_base_index], b0, sw_if_index0, &key0, &cached_key, - &bucket0, &result0, &next0, timestamp); + &count, &result0, &next0, timestamp); /* verify speculative enqueue, maybe switch current next frame */ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h index d6f41d40..3aaf48e2 100644 --- a/src/vnet/l2/l2_learn.h +++ b/src/vnet/l2/l2_learn.h @@ -46,7 +46,7 @@ typedef struct vnet_main_t *vnet_main; } l2learn_main_t; -#define L2LEARN_DEFAULT_LIMIT (L2FIB_NUM_BUCKETS * 128) +#define L2LEARN_DEFAULT_LIMIT (L2FIB_NUM_BUCKETS * 64) l2learn_main_t l2learn_main; -- cgit 1.2.3-korg