return (res);
}
+static int
+fib_test_multipath_v4 (const test_main_t *tm, const u32 fib_index,
+ const fib_prefix_t *pfx, const int n_paths,
+ const int expected_n_buckets)
+{
+ const int path_list_pool_size = fib_path_list_pool_size();
+ const int path_list_db_size = fib_path_list_db_size();
+ const int entry_pool_size = fib_entry_pool_size();
+ fib_route_path_t *r_paths = NULL;
+ const load_balance_t *lb;
+ const dpo_id_t *dpo;
+ u32 fei;
+ int res = 0;
+ int i;
+
+ for (i = 0; i < n_paths; i++)
+ {
+ fib_route_path_t r_path = {
+ .frp_proto = DPO_PROTO_IP4,
+ .frp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02 + i),
+ },
+ .frp_sw_if_index = tm->hw[0]->sw_if_index,
+ .frp_weight = 1,
+ .frp_fib_index = ~0,
+ .frp_flags = FIB_ROUTE_PATH_ATTACHED,
+ };
+ vec_add1(r_paths, r_path);
+ }
+
+ fib_table_entry_update(fib_index,
+ pfx,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ r_paths);
+
+ fei = fib_table_lookup_exact_match(fib_index, pfx);
+ FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "prefix present");
+ dpo = fib_entry_contribute_ip_forwarding(fei);
+
+ lb = load_balance_get(dpo->dpoi_index);
+ FIB_TEST((lb->lb_n_buckets == expected_n_buckets),
+ "prefix lb over %d paths", lb->lb_n_buckets);
+
+ fib_table_entry_delete(fib_index,
+ pfx,
+ FIB_SOURCE_API);
+ FIB_TEST(FIB_NODE_INDEX_INVALID ==
+ fib_table_lookup_exact_match(fib_index, pfx), "prefix removed");
+ vec_free(r_paths);
+
+ /*
+ * add-remove test. no change.
+ */
+ FIB_TEST((path_list_db_size == fib_path_list_db_size()),
+ "path list DB population:%d", fib_path_list_db_size());
+ FIB_TEST((path_list_pool_size == fib_path_list_pool_size()),
+ "path list pool size is %d", fib_path_list_pool_size());
+ FIB_TEST((entry_pool_size == fib_entry_pool_size()),
+ "entry pool size is %d", fib_entry_pool_size());
+ return res;
+}
+
static int
fib_test_v4 (void)
{
/*
* A route with multiple paths at once
*/
- fib_route_path_t *r_paths = NULL;
+ FIB_TEST(0 ==
+ fib_test_multipath_v4(tm, fib_index, &pfx_4_4_4_4_s_32, 4, 4),
+ "multipath with 4 nexthops");
- for (ii = 0; ii < 4; ii++)
- {
- fib_route_path_t r_path = {
- .frp_proto = DPO_PROTO_IP4,
- .frp_addr = {
- .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02 + ii),
- },
- .frp_sw_if_index = tm->hw[0]->sw_if_index,
- .frp_weight = 1,
- .frp_fib_index = ~0,
- };
- vec_add1(r_paths, r_path);
- }
-
- fib_table_entry_update(fib_index,
- &pfx_4_4_4_4_s_32,
- FIB_SOURCE_API,
- FIB_ENTRY_FLAG_NONE,
- r_paths);
-
- fei = fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32);
- FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "4.4.4.4/32 present");
- dpo = fib_entry_contribute_ip_forwarding(fei);
-
- lb = load_balance_get(dpo->dpoi_index);
- FIB_TEST((lb->lb_n_buckets == 4), "4.4.4.4/32 lb over %d paths", lb->lb_n_buckets);
-
- fib_table_entry_delete(fib_index,
- &pfx_4_4_4_4_s_32,
- FIB_SOURCE_API);
- FIB_TEST(FIB_NODE_INDEX_INVALID ==
- fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
- "4.4.4.4/32 removed");
- vec_free(r_paths);
+ /*
+ * A route with lots of multiple paths that will overflow max supported
+ * lb buckets because of normalization
+ */
+ FIB_TEST(0 ==
+ fib_test_multipath_v4(tm, fib_index, &pfx_4_4_4_4_s_32,
+ LB_MAX_BUCKETS / 2 + 23, LB_MAX_BUCKETS),
+ "multipath with too many nexthops");
/*
- * add-remove test. no change.
+ * A route with more paths than max supported lb buckets
*/
- FIB_TEST((1 == fib_path_list_db_size()), "path list DB population:%d",
- fib_path_list_db_size());
- FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
- fib_path_list_pool_size());
- FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
- fib_entry_pool_size());
+ FIB_TEST(0 ==
+ fib_test_multipath_v4 (tm, fib_index, &pfx_4_4_4_4_s_32,
+ LB_MAX_BUCKETS + 13, LB_MAX_BUCKETS),
+ "multipath with too many nexthops");
/*
* A route deag route
FIB_TEST(FIB_NODE_INDEX_INVALID ==
fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
"4.4.4.4/32 removed");
- vec_free(r_paths);
/*
* A route deag route in a source lookup table
FIB_TEST(FIB_NODE_INDEX_INVALID ==
fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
"4.4.4.4/32 removed");
- vec_free(r_paths);
/*
* add-remove test. no change.
{
load_balance_t *lb;
+ ASSERT (num_buckets <= LB_MAX_BUCKETS);
+
lb = load_balance_alloc_i();
lb->lb_hash_config = fhc;
lb->lb_n_buckets = num_buckets;
/* Try larger and larger power of 2 sized adjacency blocks until we
find one where traffic flows to within 1% of specified weights. */
- for (n_adj = max_pow2 (n_nhs); ; n_adj *= 2)
+ for (n_adj = clib_min(max_pow2 (n_nhs), LB_MAX_BUCKETS); ; n_adj *= 2)
{
+ ASSERT (n_adj <= LB_MAX_BUCKETS);
error = 0;
norm = n_adj / ((f64) sum_weight);
nhs[0].path_weight += n_adj_left;
- /* Less than 5% average error per adjacency with this size adjacency block? */
- if (error <= multipath_next_hop_error_tolerance*n_adj)
+ /* Less than 1% average error per adjacency with this size adjacency block,
+ * or did we reached the maximum number of buckets we support? */
+ if (error <= multipath_next_hop_error_tolerance*n_adj ||
+ n_adj >= LB_MAX_BUCKETS)
{
- /* Truncate any next hops with zero weight. */
- vec_set_len (nhs, i);
- break;
+ if (i < n_nhs)
+ {
+ /* Truncate any next hops in excess */
+ vlib_log_err(load_balance_logger,
+ "Too many paths for load-balance, truncating %d -> %d",
+ n_nhs, i);
+ for (int j = i; j < n_nhs; j++)
+ dpo_reset (&vec_elt(nhs, j).path_dpo);
+ }
+ vec_set_len (nhs, i);
+ break;
}
}
load_balance_set_n_buckets (load_balance_t *lb,
u32 n_buckets)
{
+ ASSERT (n_buckets <= LB_MAX_BUCKETS);
lb->lb_n_buckets = n_buckets;
lb->lb_n_buckets_minus_1 = n_buckets-1;
}
&sum_of_weights,
multipath_next_hop_error_tolerance);
- ASSERT (n_buckets >= vec_len (raw_nhs));
-
/*
* Save the old load-balance map used, and get a new one if required.
*/
{
replicate_t *rep;
+ ASSERT (num_buckets <= REP_MAX_BUCKETS);
+
rep = replicate_alloc_i();
rep->rep_n_buckets = num_buckets;
rep->rep_proto = rep_proto;
replicate_set_n_buckets (replicate_t *rep,
u32 n_buckets)
{
- rep->rep_n_buckets = n_buckets;
+ ASSERT (n_buckets <= REP_MAX_BUCKETS);
+ rep->rep_n_buckets = n_buckets;
}
void
rep->rep_proto);
n_buckets = vec_len(nhs);
+ if (n_buckets > REP_MAX_BUCKETS)
+ {
+ vlib_log_err (replicate_logger,
+ "Too many paths for replicate, truncating %d -> %d",
+ n_buckets, REP_MAX_BUCKETS);
+ for (int i = REP_MAX_BUCKETS; i < n_buckets; i++)
+ dpo_reset (&vec_elt (nhs, i).path_dpo);
+ vec_set_len (nhs, REP_MAX_BUCKETS);
+ n_buckets = REP_MAX_BUCKETS;
+ }
+
if (0 == rep->rep_n_buckets)
{
/*