zebra: Handle out of order kernel nexthop groups

Add a mechanism to requeue groups we receive from the
kernel if the IDs are in a weird order (Group ID is lower
than individual nexthop IDs for example).

Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
This commit is contained in:
Stephen Worley 2019-08-12 20:09:59 -04:00
parent 3e347f4181
commit 1b366e63be
2 changed files with 53 additions and 28 deletions

View file

@ -408,7 +408,7 @@ bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2)
return nhe1->id == nhe2->id; return nhe1->id == nhe2->id;
} }
static void zebra_nhg_process_grp(struct nexthop_group *nhg, static int zebra_nhg_process_grp(struct nexthop_group *nhg,
struct nhg_connected_tree_head *depends, struct nhg_connected_tree_head *depends,
struct nh_grp *grp, uint8_t count) struct nh_grp *grp, uint8_t count)
{ {
@ -428,7 +428,7 @@ static void zebra_nhg_process_grp(struct nexthop_group *nhg,
EC_ZEBRA_NHG_SYNC, EC_ZEBRA_NHG_SYNC,
"Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table", "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table",
grp[i].id); grp[i].id);
return; return -1;
} }
/* /*
@ -440,6 +440,8 @@ static void zebra_nhg_process_grp(struct nexthop_group *nhg,
copy_nexthops(&nhg->nexthop, depend->nhg->nexthop, NULL); copy_nexthops(&nhg->nexthop, depend->nhg->nexthop, NULL);
} }
return 0;
} }
static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends,
@ -566,12 +568,12 @@ static uint32_t nhg_ctx_get_id(const struct nhg_ctx *ctx)
return ctx->id; return ctx->id;
} }
static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_result status) static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_status status)
{ {
ctx->status = status; ctx->status = status;
} }
static enum nhg_ctx_result nhg_ctx_get_status(const struct nhg_ctx *ctx) static enum nhg_ctx_status nhg_ctx_get_status(const struct nhg_ctx *ctx)
{ {
return ctx->status; return ctx->status;
} }
@ -742,8 +744,13 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx)
if (nhg_ctx_get_count(ctx)) { if (nhg_ctx_get_count(ctx)) {
nhg = nexthop_group_new(); nhg = nexthop_group_new();
zebra_nhg_process_grp(nhg, &nhg_depends, nhg_ctx_get_grp(ctx), if (zebra_nhg_process_grp(nhg, &nhg_depends,
count); nhg_ctx_get_grp(ctx), count)) {
depends_decrement_free(&nhg_depends);
nexthop_group_free_delete(&nhg);
return ENOENT;
}
if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type, if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type,
afi)) afi))
depends_decrement_free(&nhg_depends); depends_decrement_free(&nhg_depends);
@ -839,6 +846,22 @@ done:
nhg_ctx_free(ctx); nhg_ctx_free(ctx);
} }
static int queue_add(struct nhg_ctx *ctx)
{
/* If its queued or already processed do nothing */
if (nhg_ctx_get_status(ctx) == NHG_CTX_QUEUED)
return 0;
if (rib_queue_nhg_add(ctx)) {
nhg_ctx_set_status(ctx, NHG_CTX_FAILURE);
return -1;
}
nhg_ctx_set_status(ctx, NHG_CTX_QUEUED);
return 0;
}
int nhg_ctx_process(struct nhg_ctx *ctx) int nhg_ctx_process(struct nhg_ctx *ctx)
{ {
int ret = 0; int ret = 0;
@ -846,6 +869,19 @@ int nhg_ctx_process(struct nhg_ctx *ctx)
switch (nhg_ctx_get_op(ctx)) { switch (nhg_ctx_get_op(ctx)) {
case NHG_CTX_OP_NEW: case NHG_CTX_OP_NEW:
ret = nhg_ctx_process_new(ctx); ret = nhg_ctx_process_new(ctx);
if (nhg_ctx_get_count(ctx) && ret == ENOENT
&& nhg_ctx_get_status(ctx) != NHG_CTX_REQUEUED) {
/* Depends probably came before group, re-queue.
*
* Only going to retry once, hence just using status
* flag rather than counter.
*/
nhg_ctx_set_status(ctx, NHG_CTX_NONE);
if (queue_add(ctx) == 0) {
nhg_ctx_set_status(ctx, NHG_CTX_REQUEUED);
return 0;
}
}
break; break;
case NHG_CTX_OP_DEL: case NHG_CTX_OP_DEL:
ret = nhg_ctx_process_del(ctx); ret = nhg_ctx_process_del(ctx);
@ -860,22 +896,6 @@ int nhg_ctx_process(struct nhg_ctx *ctx)
return ret; return ret;
} }
static int queue_add(struct nhg_ctx *ctx)
{
/* If its queued or already processed do nothing */
if (nhg_ctx_get_status(ctx))
return 0;
if (rib_queue_nhg_add(ctx)) {
nhg_ctx_set_status(ctx, NHG_CTX_FAILURE);
return -1;
}
nhg_ctx_set_status(ctx, NHG_CTX_QUEUED);
return 0;
}
/* Kernel-side, you either get a single new nexthop or a array of ID's */ /* Kernel-side, you either get a single new nexthop or a array of ID's */
int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp,
uint8_t count, vrf_id_t vrf_id, afi_t afi, int type, uint8_t count, vrf_id_t vrf_id, afi_t afi, int type,
@ -959,6 +979,8 @@ depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
struct nhg_hash_entry *depend = NULL; struct nhg_hash_entry *depend = NULL;
depend = depends_find(nh, afi); depend = depends_find(nh, afi);
if (depend)
depends_add(head, depend); depends_add(head, depend);
return depend; return depend;
@ -970,6 +992,8 @@ depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id)
struct nhg_hash_entry *depend = NULL; struct nhg_hash_entry *depend = NULL;
depend = zebra_nhg_lookup_id(id); depend = zebra_nhg_lookup_id(id);
if (depend)
depends_add(head, depend); depends_add(head, depend);
return depend; return depend;

View file

@ -125,9 +125,10 @@ enum nhg_ctx_op_e {
NHG_CTX_OP_DEL, NHG_CTX_OP_DEL,
}; };
enum nhg_ctx_result { enum nhg_ctx_status {
NHG_CTX_NONE = 0, NHG_CTX_NONE = 0,
NHG_CTX_QUEUED, NHG_CTX_QUEUED,
NHG_CTX_REQUEUED,
NHG_CTX_SUCCESS, NHG_CTX_SUCCESS,
NHG_CTX_FAILURE, NHG_CTX_FAILURE,
}; };
@ -159,7 +160,7 @@ struct nhg_ctx {
} u; } u;
enum nhg_ctx_op_e op; enum nhg_ctx_op_e op;
enum nhg_ctx_result status; enum nhg_ctx_status status;
}; };