u32 next_node,
u8 *rewrite)
{
+ ip_adjacency_t *walk_adj;
adj_index_t walk_ai;
vlib_main_t * vm;
u32 old_next;
+ int do_walk;
vm = vlib_get_main();
old_next = adj->lookup_next_index;
adj->rewrite_header.sw_if_index);
}
+ /*
+ * Don't call the walk re-entrantly
+ */
+ if (ADJ_INDEX_INVALID != walk_ai)
+ {
+ walk_adj = adj_get(walk_ai);
+ if (IP_ADJ_SYNC_WALK_ACTIVE & walk_adj->ia_flags)
+ {
+ do_walk = 0;
+ }
+ else
+ {
+ /*
+ * Prevent re-entrant walk of the same adj
+ */
+ walk_adj->ia_flags |= IP_ADJ_SYNC_WALK_ACTIVE;
+ do_walk = 1;
+ }
+ }
+ else
+ {
+ do_walk = 0;
+ }
+
+ /*
+ * lock the adjacencies that are affected by updates this walk will provoke.
+ * Since the aim of the walk is to update children to link to a different
+ * DPO, this adj will no longer be in use and its lock count will drop to 0.
+ * We don't want it to be deleted as part of this endevour.
+ */
+ adj_lock(adj_get_index(adj));
+ adj_lock(walk_ai);
+
/*
* Updating a rewrite string is not atomic;
* - the rewrite string is too long to write in one instruction
* 1 - mac change
* 2 - adj type change
*/
- if (old_next != adj_next_index &&
+ if (do_walk &&
+ old_next != adj_next_index &&
ADJ_INDEX_INVALID != walk_ai)
{
/*
*/
vlib_worker_thread_barrier_release(vm);
- if (old_next != adj->lookup_next_index &&
- ADJ_INDEX_INVALID != walk_ai)
+ if (do_walk &&
+ (old_next != adj->lookup_next_index) &&
+ (ADJ_INDEX_INVALID != walk_ai))
{
/*
* backwalk to the children so they can stack on the now updated
fib_walk_sync(FIB_NODE_TYPE_ADJ, walk_ai, &bw_ctx);
}
+ /*
+ * Prevent re-entrant walk of the same adj
+ */
+ if (do_walk)
+ {
+ walk_adj->ia_flags &= ~IP_ADJ_SYNC_WALK_ACTIVE;
+ }
+
+ adj_unlock(adj_get_index(adj));
+ adj_unlock(walk_ai);
}
typedef struct adj_db_count_ctx_t_ {
* @brief History of state for the last 128 walks
*/
#define HISTORY_N_WALKS 128
+#define MAX_HISTORY_REASONS 16
static u32 history_last_walk_pos;
typedef struct fib_walk_history_t_ {
u32 fwh_n_visits;
f64 fwh_completed;
fib_node_ptr_t fwh_parent;
fib_walk_flags_t fwh_flags;
- fib_node_bw_reason_flag_t fwh_reason;
+ fib_node_bw_reason_flag_t fwh_reason[MAX_HISTORY_REASONS];
} fib_walk_history_t;
static fib_walk_history_t fib_walk_history[HISTORY_N_WALKS];
static void
fib_walk_destroy (fib_walk_t *fwalk)
{
- fib_node_back_walk_ctx_t *ctx;
- u32 bucket;
+ u32 bucket, ii;
if (FIB_NODE_INDEX_INVALID != fwalk->fw_prio_sibling)
{
fib_walk_history[history_last_walk_pos].fwh_flags =
fwalk->fw_flags;
- fib_walk_history[history_last_walk_pos].fwh_reason = 0;
- vec_foreach(ctx, fwalk->fw_ctx)
+ vec_foreach_index(ii, fwalk->fw_ctx)
{
- fib_walk_history[history_last_walk_pos].fwh_reason |=
- ctx->fnbw_reason;
+ if (ii < MAX_HISTORY_REASONS)
+ {
+ fib_walk_history[history_last_walk_pos].fwh_reason[ii] =
+ fwalk->fw_ctx[ii].fnbw_reason;
+ }
}
history_last_walk_pos = (history_last_walk_pos + 1) % HISTORY_N_WALKS;
fib_node_deinit(&fwalk->fw_node);
+ vec_free(fwalk->fw_ctx);
pool_put(fib_walk_pool, fwalk);
}
static fib_walk_advance_rc_t
fib_walk_advance (fib_node_index_t fwi)
{
- fib_node_back_walk_ctx_t *ctx;
+ fib_node_back_walk_ctx_t *ctx, *old;
fib_node_back_walk_rc_t wrc;
fib_node_ptr_t sibling;
fib_walk_t *fwalk;
if (more_elts)
{
+ old = fwalk->fw_ctx;
+
vec_foreach(ctx, fwalk->fw_ctx)
{
wrc = fib_node_back_walk_one(&sibling, ctx);
*/
return (FIB_WALK_ADVANCE_MERGE);
}
+ if (old != fwalk->fw_ctx)
+ {
+ /*
+ * nasty re-entrant addition of a walk has realloc'd the vector
+ * break out
+ */
+ return (FIB_WALK_ADVANCE_MERGE);
+ }
}
/*
* move foward to the next node to visit
/*
* make a copy of the backwalk context so the depth count remains
* the same for each sibling visitsed. This is important in the case
- * where a parents has a loop via one child, but all the others are not.
+ * where a parent has a loop via one child, but all the others are not.
* if the looped child were visited first, the depth count would exceed, the
* max and the walk would terminate before it reached the other siblings.
*/
fib_walk_back_walk_notify (fib_node_t *node,
fib_node_back_walk_ctx_t *ctx)
{
- fib_node_back_walk_ctx_t *old;
+ fib_node_back_walk_ctx_t *last;
fib_walk_t *fwalk;
fwalk = fib_walk_get_from_node(node);
/*
- * check whether the walk context can be merge with another,
- * or whether it needs to be appended.
+ * check whether the walk context can be merged with the most recent.
+ * the most recent was the one last added and is thus at the back of the vector.
+ * we can merge walks if the reason for the walk is the same.
*/
- vec_foreach(old, fwalk->fw_ctx)
+ last = vec_end(fwalk->fw_ctx) - 1;
+
+ if (last->fnbw_reason == ctx->fnbw_reason)
{
- /*
- * we can merge walks if the reason for the walk is the same.
- */
- if (old->fnbw_reason == ctx->fnbw_reason)
- {
- /*
- * copy the largest of the depth values. in the presence of a loop,
- * the same walk will merge with itself. if we take the smaller depth
- * then it will never end.
- */
- old->fnbw_depth = ((old->fnbw_depth >= ctx->fnbw_depth) ?
- old->fnbw_depth :
- ctx->fnbw_depth);
- goto out;
- }
+ /*
+ * copy the largest of the depth values. in the presence of a loop,
+ * the same walk will merge with itself. if we take the smaller depth
+ * then it will never end.
+ */
+ last->fnbw_depth = ((last->fnbw_depth >= ctx->fnbw_depth) ?
+ last->fnbw_depth :
+ ctx->fnbw_depth);
+ }
+ else
+ {
+ /*
+ * walks could not be merged, this means that the walk infront needs to
+ * perform different action to this one that has caught up. the one in
+ * front was scheduled first so append the new walk context to the back
+ * of the list.
+ */
+ vec_add1(fwalk->fw_ctx, *ctx);
}
- /*
- * walks could not be merged, this means that the walk infront needs to
- * perform different action to this one that has caught up. the one in front
- * was scheduled first so append the new walk context to the back of the list.
- */
- vec_add1(fwalk->fw_ctx, *ctx);
-
-out:
return (FIB_NODE_BACK_WALK_MERGE);
}
while (ii != history_last_walk_pos)
{
- if (0 != fib_walk_history[ii].fwh_reason)
+ if (0 != fib_walk_history[ii].fwh_reason[0])
{
fib_node_back_walk_reason_t reason;
u8 *s = NULL;
+ u32 jj;
s = format(s, "[@%d]: %s:%d visits:%d duration:%.2f completed:%.2f ",
ii, fib_node_type_get_name(fib_walk_history[ii].fwh_parent.fnp_type),
s = format(s, "async, ");
s = format(s, "reason:");
- FOR_EACH_FIB_NODE_BW_REASON(reason) {
- if ((1<<reason) & fib_walk_history[ii].fwh_reason) {
- s = format (s, "%s,", fib_node_bw_reason_names[reason]);
+ jj = 0;
+ while (0 != fib_walk_history[ii].fwh_reason[jj])
+ {
+ FOR_EACH_FIB_NODE_BW_REASON(reason) {
+ if ((1<<reason) & fib_walk_history[ii].fwh_reason[jj]) {
+ s = format (s, "%s,", fib_node_bw_reason_names[reason]);
+ }
}
+ jj++;
}
vlib_cli_output(vm, "%v", s);
}
}
static void
-ip6_nd_mk_incomplete (adj_index_t ai, ip6_neighbor_t * nbr)
+ip6_nd_mk_incomplete (adj_index_t ai)
{
+ ip_adjacency_t *adj = adj_get(ai);
+
adj_nbr_update_rewrite (
ai,
ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
ethernet_build_rewrite (vnet_get_main (),
- nbr->key.sw_if_index,
+ adj->rewrite_header.sw_if_index,
adj_get_link_type(ai),
VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
}
static adj_walk_rc_t
ip6_nd_mk_incomplete_walk (adj_index_t ai, void *ctx)
{
- ip6_neighbor_t *nbr = ctx;
-
- ip6_nd_mk_incomplete (ai, nbr);
+ ip6_nd_mk_incomplete (ai);
return (ADJ_WALK_RC_CONTINUE);
}
}
n = pool_elt_at_index (nm->neighbor_pool, p[0]);
+ mhash_unset (&nm->neighbor_index_by_key, &n->key, 0);
adj_nbr_walk_nh6 (sw_if_index,
&n->key.ip6_address,
ip6_nd_mk_incomplete_walk,
- n);
+ NULL);
- mhash_unset (&nm->neighbor_index_by_key, &n->key, 0);
fib_table_entry_delete_index (n->fib_entry_index, FIB_SOURCE_ADJ);
pool_put (nm->neighbor_pool, n);