From: Mark Stapp Date: Tue, 26 Jun 2018 18:28:58 +0000 (-0400) Subject: zebra: wip: early version of dplane result handler X-Git-Tag: frr-7.1-dev~233^2~20 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=e5ac2adf171d5557fdc7fe3effea6e219fd69630;p=matthieu%2Ffrr.git zebra: wip: early version of dplane result handler Early try at a result handler for async dplane route updates Signed-off-by: Mark Stapp --- diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 620dfef3b3..c99b5d784f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -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; }