Skip to content

Commit 3af381b

Browse files
committed
zebra: Modify dplane loop to allow backpressure to filter up
Currently when the dplane_thread_loop is run, it moves contexts from the dg_update_list and puts the contexts on the input queue of the first provider. This provider is given a chance to run and then the items on the output queue are pulled off and placed on the input queue of the next provider. Rinse/Repeat down through the entire list of providers. Now imagine that we have a list of multiple providers and the last provider is getting backed up. Contexts will end up sticking in the input Queue of the `slow` provider. This can grow without bounds. This is a real problem when you have a situation where an interface is flapping and an upper level protocol is sending a continous stream of route updates to reflect the change in ecmp. You can end up with a very very large backlog of contexts. This is bad because zebra can easily grow to a very very large memory size and on restricted systems you can run out of memory. Fortunately for us, the MetaQ already participates with this process by not doing more route processing until the dg_update_list goes below the working limit of dg_updates_per_cycle. Thus if FRR modifies the behavior of this loop to not move more contexts onto the input queue if either the input queue or output queue of the next provider has reached this limit. FRR will naturaly start auto handling backpressure for the dplane context system and memory will not go out of control. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
1 parent 34670c4 commit 3af381b

File tree

1 file changed

+94
-20
lines changed

1 file changed

+94
-20
lines changed

zebra/zebra_dplane.c

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7317,10 +7317,10 @@ static void dplane_thread_loop(struct event *event)
73177317
{
73187318
struct dplane_ctx_list_head work_list;
73197319
struct dplane_ctx_list_head error_list;
7320-
struct zebra_dplane_provider *prov;
7320+
struct zebra_dplane_provider *prov, *next_prov;
73217321
struct zebra_dplane_ctx *ctx;
73227322
int limit, counter, error_counter;
7323-
uint64_t curr, high;
7323+
uint64_t curr, out_curr, high;
73247324
bool reschedule = false;
73257325

73267326
/* Capture work limit per cycle */
@@ -7344,18 +7344,48 @@ static void dplane_thread_loop(struct event *event)
73447344
/* Locate initial registered provider */
73457345
prov = dplane_prov_list_first(&zdplane_info.dg_providers);
73467346

7347-
/* Move new work from incoming list to temp list */
7348-
for (counter = 0; counter < limit; counter++) {
7349-
ctx = dplane_ctx_list_pop(&zdplane_info.dg_update_list);
7350-
if (ctx) {
7351-
ctx->zd_provider = prov->dp_id;
7347+
curr = dplane_ctx_queue_count(&prov->dp_ctx_in_list);
7348+
out_curr = dplane_ctx_queue_count(&prov->dp_ctx_out_list);
73527349

7353-
dplane_ctx_list_add_tail(&work_list, ctx);
7354-
} else {
7355-
break;
7350+
if (curr >= (uint64_t)limit) {
7351+
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
7352+
zlog_debug("%s: Current first provider(%s) Input queue is %" PRIu64
7353+
", holding off work",
7354+
__func__, prov->dp_name, curr);
7355+
counter = 0;
7356+
} else if (out_curr >= (uint64_t)limit) {
7357+
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
7358+
zlog_debug("%s: Current first provider(%s) Output queue is %" PRIu64
7359+
", holding off work",
7360+
__func__, prov->dp_name, out_curr);
7361+
counter = 0;
7362+
} else {
7363+
int tlimit;
7364+
/*
7365+
* Let's limit the work to how what can be put on the
7366+
* in or out queue without going over
7367+
*/
7368+
tlimit = limit - MAX(curr, out_curr);
7369+
/* Move new work from incoming list to temp list */
7370+
for (counter = 0; counter < tlimit; counter++) {
7371+
ctx = dplane_ctx_list_pop(&zdplane_info.dg_update_list);
7372+
if (ctx) {
7373+
ctx->zd_provider = prov->dp_id;
7374+
7375+
dplane_ctx_list_add_tail(&work_list, ctx);
7376+
} else {
7377+
break;
7378+
}
73567379
}
73577380
}
73587381

7382+
/*
7383+
* If there is anything still on the two input queues reschedule
7384+
*/
7385+
if (dplane_ctx_queue_count(&prov->dp_ctx_in_list) > 0 ||
7386+
dplane_ctx_queue_count(&zdplane_info.dg_update_list) > 0)
7387+
reschedule = true;
7388+
73597389
DPLANE_UNLOCK();
73607390

73617391
atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, counter,
@@ -7374,8 +7404,9 @@ static void dplane_thread_loop(struct event *event)
73747404
* items.
73757405
*/
73767406
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
7377-
zlog_debug("dplane enqueues %d new work to provider '%s'",
7378-
counter, dplane_provider_get_name(prov));
7407+
zlog_debug("dplane enqueues %d new work to provider '%s' curr is %" PRIu64,
7408+
counter, dplane_provider_get_name(prov),
7409+
curr);
73797410

73807411
/* Capture current provider id in each context; check for
73817412
* error status.
@@ -7433,18 +7464,61 @@ static void dplane_thread_loop(struct event *event)
74337464
if (!zdplane_info.dg_run)
74347465
break;
74357466

7467+
/* Locate next provider */
7468+
next_prov = dplane_prov_list_next(&zdplane_info.dg_providers,
7469+
prov);
7470+
if (next_prov) {
7471+
curr = dplane_ctx_queue_count(
7472+
&next_prov->dp_ctx_in_list);
7473+
out_curr = dplane_ctx_queue_count(
7474+
&next_prov->dp_ctx_out_list);
7475+
} else
7476+
out_curr = curr = 0;
7477+
74367478
/* Dequeue completed work from the provider */
74377479
dplane_provider_lock(prov);
74387480

7439-
while (counter < limit) {
7440-
ctx = dplane_provider_dequeue_out_ctx(prov);
7441-
if (ctx) {
7442-
dplane_ctx_list_add_tail(&work_list, ctx);
7443-
counter++;
7444-
} else
7445-
break;
7481+
if (curr >= (uint64_t)limit) {
7482+
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
7483+
zlog_debug("%s: Next Provider(%s) Input queue is %" PRIu64
7484+
", holding off work",
7485+
__func__, next_prov->dp_name, curr);
7486+
counter = 0;
7487+
} else if (out_curr >= (uint64_t)limit) {
7488+
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
7489+
zlog_debug("%s: Next Provider(%s) Output queue is %" PRIu64
7490+
", holding off work",
7491+
__func__, next_prov->dp_name,
7492+
out_curr);
7493+
counter = 0;
7494+
} else {
7495+
int tlimit;
7496+
7497+
/*
7498+
* Let's limit the work to how what can be put on the
7499+
* in or out queue without going over
7500+
*/
7501+
tlimit = limit - MAX(curr, out_curr);
7502+
while (counter < tlimit) {
7503+
ctx = dplane_provider_dequeue_out_ctx(prov);
7504+
if (ctx) {
7505+
dplane_ctx_list_add_tail(&work_list,
7506+
ctx);
7507+
counter++;
7508+
} else
7509+
break;
7510+
}
74467511
}
74477512

7513+
/*
7514+
* Let's check if there are still any items on the
7515+
* input or output queus of the current provider
7516+
* if so then we know we need to reschedule.
7517+
*/
7518+
if (dplane_ctx_queue_count(&prov->dp_ctx_in_list) > 0 ||
7519+
dplane_ctx_queue_count(&prov->dp_ctx_out_list) > 0)
7520+
reschedule = true;
7521+
74487522
dplane_provider_unlock(prov);
74497523

74507524
if (counter >= limit)
@@ -7460,7 +7534,7 @@ static void dplane_thread_loop(struct event *event)
74607534
}
74617535

74627536
/* Locate next provider */
7463-
prov = dplane_prov_list_next(&zdplane_info.dg_providers, prov);
7537+
prov = next_prov;
74647538
}
74657539

74667540
/*

0 commit comments

Comments
 (0)