zebra: wip: early version of dplane result handler

Early try at a result handler for async dplane route updates

Signed-off-by: Mark Stapp <mjs@voltanet.io>
This commit is contained in:
Mark Stapp 2018-06-26 14:28:58 -04:00
parent 7cdb1a8445
commit e5ac2adf17

View File

@ -1868,6 +1868,252 @@ static void rib_process(struct route_node *rn)
rib_gc_dest(rn);
}
/*
* Utility to match route with dplane context data
*/
static bool rib_route_match_ctx(const struct route_entry *re,
const dplane_ctx_h ctx, bool is_update)
{
bool result = false;
if (is_update) {
/*
* In 'update' case, we test info about the 'previous' or
* 'old' route
*/
if ((re->type == dplane_ctx_get_old_type(ctx)) &&
(re->instance == dplane_ctx_get_old_instance(ctx))) {
result = true;
/* TODO -- we're using this extra test, but it's not
* exactly clear why.
*/
if (re->type == ZEBRA_ROUTE_STATIC &&
(re->distance != dplane_ctx_get_old_distance(ctx) ||
re->tag != dplane_ctx_get_old_tag(ctx))) {
result = false;
}
}
} else {
/*
* Ordinary, single-route case using primary context info
*/
if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) &&
CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
/* Skip route that's been deleted */
goto done;
}
if ((re->type == dplane_ctx_get_type(ctx)) &&
(re->instance == dplane_ctx_get_instance(ctx))) {
result = true;
/* TODO -- we're using this extra test, but it's not
* exactly clear why.
*/
if (re->type == ZEBRA_ROUTE_STATIC &&
(re->distance != dplane_ctx_get_distance(ctx) ||
re->tag != dplane_ctx_get_tag(ctx))) {
result = false;
}
}
}
done:
return (result);
}
/*
* TODO - WIP
*/
static void rib_process_after(dplane_ctx_h ctx)
{
struct route_table *table = NULL;
struct route_node *rn = NULL;
struct route_entry *re = NULL, *old_re = NULL, *rib;
bool is_update = false;
struct nexthop *nexthop;
char dest_str[PREFIX_STRLEN];
dplane_op_e op;
enum zebra_dplane_result status;
const struct prefix *dest_pfx, *src_pfx;
/* Locate rn and re(s) from ctx */
table = zebra_vrf_table_with_table_id(dplane_ctx_get_afi(ctx),
dplane_ctx_get_safi(ctx),
dplane_ctx_get_vrf(ctx),
dplane_ctx_get_table(ctx));
if (table == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
zlog_debug("Failed to process dplane results: no table "
"for afi %d, safi %d, vrf %u",
dplane_ctx_get_afi(ctx),
dplane_ctx_get_safi(ctx),
dplane_ctx_get_vrf(ctx));
}
goto done;
}
dest_pfx = dplane_ctx_get_dest(ctx);
/* Note well: only capturing the prefix string if debug is enabled here;
* unconditional log messages will have to generate the string.
*/
if (IS_ZEBRA_DEBUG_DPLANE) {
prefix2str(dest_pfx, dest_str, sizeof(dest_str));
}
src_pfx = dplane_ctx_get_src(ctx);
rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx),
src_pfx ? (struct prefix_ipv6 * )src_pfx : NULL);
if (rn == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
zlog_debug("Failed to process dplane results: no "
"route for %u:%s",
dplane_ctx_get_vrf(ctx), dest_str);
}
goto done;
}
srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx);
op = dplane_ctx_get_op(ctx);
status = dplane_ctx_get_status(ctx);
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
zlog_debug("%u:%s Processing dplane ctx %p, op %s result %d",
dplane_ctx_get_vrf(ctx), dest_str, ctx,
dplane_op2str(op), status);
}
if (op == DPLANE_OP_ROUTE_DELETE) {
/*
* In the delete case, the zebra core datastructs were
* updated (or removed) at the time the delete was issued,
* so we're just notifying the route owner.
*/
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED);
} else {
zsend_route_notify_owner_ctx(ctx,
ZAPI_ROUTE_FAIL_INSTALL);
zlog_warn("%u:%s: Route Deletion failure",
dplane_ctx_get_vrf(ctx),
prefix2str(dest_pfx,
dest_str, sizeof(dest_str)));
}
/* Nothing more to do in delete case */
goto done;
}
/*
* Update is a bit of a special case, where we may have both old and new
* routes to post-process.
*/
is_update = dplane_ctx_is_update(ctx);
/*
* Take a pass through the routes, look for matches with the context
* info.
*/
RNODE_FOREACH_RE(rn, rib) {
if (re == NULL) {
if (rib_route_match_ctx(rib, ctx, false)) {
re = rib;
}
}
/* Check for old route match */
if (is_update && (old_re == NULL)) {
if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) {
old_re = rib;
}
}
/* Have we found the routes we need to work on? */
if (re && ((!is_update || old_re))) {
break;
}
}
/*
* Check sequence number(s) to detect stale results before continuing
*/
if (re && (re->dplane_sequence != dplane_ctx_get_seq(ctx))) {
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
zlog_debug("%u:%s Stale dplane result for re %p",
dplane_ctx_get_vrf(ctx), dest_str, re);
}
re = NULL;
}
if (old_re &&
(old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx))) {
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
zlog_debug("%u:%s Stale dplane result for old_re %p",
dplane_ctx_get_vrf(ctx), dest_str, old_re);
}
old_re = NULL;
}
/*
* Here's sort of a tough one: the route update result is stale.
* Is it better to use the context block info to generate
* redist and owner notification, or is it better to wait
* for the up-to-date result to arrive?
*/
if (re == NULL) {
/* TODO -- for now, only expose up-to-date results */
goto done;
}
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
/* Set nexthop FIB flags */
for (ALL_NEXTHOPS(re->ng, nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
/* Redistribute */
/* TODO -- still calling the redist api using the route_entries,
* and there's a corner-case here: if there's no client
* for the 'new' route, a redist deleting the 'old' route
* will be sent. But if the 'old' context info was stale,
* 'old_re' will be NULL here and that delete will not be sent.
*/
redistribute_update(dest_pfx, src_pfx, re, old_re);
/* Notify route owner */
zsend_route_notify_owner(re,
dest_pfx, ZAPI_ROUTE_INSTALLED);
} else {
zsend_route_notify_owner(re, dest_pfx,
ZAPI_ROUTE_FAIL_INSTALL);
zlog_warn("%u:%s: Route install failed",
dplane_ctx_get_vrf(ctx),
prefix2str(dest_pfx,
dest_str, sizeof(dest_str)));
}
done:
/* Return context to dataplane module */
dplane_ctx_fini(&ctx);
}
/* Take a list of route_node structs and return 1, if there was a record
* picked from it and processed by rib_process(). Don't process more,
* than one RN record; operate only in the specified sub-queue.
@ -3029,7 +3275,7 @@ static int rib_process_dplane_results(struct thread *thread)
pthread_mutex_unlock(&dplane_mutex);
if (ctx) {
dplane_ctx_fini(&ctx);
rib_process_after(ctx);
} else {
break;
}