Skip to content

Commit a8efa99

Browse files
zebra: backpressure - Zebra push back on Buffer/Stream creation
Currently, the way zebra works is it creates pthread per client (BGP is of interest in this case) and this thread loops itself in zserv_read() to check for any incoming data. If there is one, then it reads, validates and adds it in the ibuf_fifo signalling the main thread to process the message. The main thread when it gets a change, processes the message, and invokes the function pointer registered in the header command. (Ex: zserv_handlers). Finally, if all of this was successful, this task reschedules itself and loops in zserv_read() again However, if there are already items on ibuf FIFO, that means zebra is slow in processing. And with the current mechanism if Zebra main is busy, the ibuf FIFO keeps growing holding up the memory. Show memory zebra:(Example: 15k streams hoarding ~160 MB of data) --- qmem libfrr --- Stream : 44 variable 3432352 15042 161243800 Fix: Client IO Thread: (zserv_read) - Stop doing the read events when we know there are X number of items on the FIFO already.(X - zebra zapi-packets <1-10000> (Default-1000) - Determine the number of items on the zserv->ibuf_fifo. Subtract this from the work items and only pull the number of items off that would take us to X items on the ibuf_fifo again. - If the number of items in the ibuf_fifo has reached to the maximum * Either initially when zserv_read() is called (or) * when processing the remainders of the incoming buffer the client IO thread is woken by the the zebra main. Main thread: (zserv_process_message) If the client ibuf always schedules a wakeup to the client IO to read more items from the socked buffer. This way we ensure - Client IO thread always tries to read the socket buffer and add more items to the ibuf_fifo (until max limit) - hidden config change (zebra zapi-packets <>) is taken into account Ticket: #3390099 Signed-off-by: Donald Sharp <sharpd@nvidia.com> Signed-off-by: Rajasekar Raja <rajasekarr@nvidia.com>
1 parent ffab0d7 commit a8efa99

File tree

1 file changed

+43
-12
lines changed

1 file changed

+43
-12
lines changed

zebra/zserv.c

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -306,22 +306,40 @@ static void zserv_write(struct event *thread)
306306
* this task reschedules itself.
307307
*
308308
* Any failure in any of these actions is handled by terminating the client.
309+
*
310+
* The client's input buffer ibuf_fifo can have a maximum items as configured
311+
* in the packets_to_process. This way we are not filling up the FIFO more
312+
* than the maximum when the zebra main is busy. If the fifo has space, we
313+
* reschedule ourselves to read more.
314+
*
315+
* The main thread processes the items in ibuf_fifo and always signals the
316+
* client IO thread.
309317
*/
310318
static void zserv_read(struct event *thread)
311319
{
312320
struct zserv *client = EVENT_ARG(thread);
313321
int sock;
314322
size_t already;
315323
struct stream_fifo *cache;
316-
uint32_t p2p_orig;
317-
318-
uint32_t p2p;
324+
uint32_t p2p; /* Temp p2p used to process */
325+
uint32_t p2p_orig; /* Configured p2p (Default-1000) */
326+
int p2p_avail; /* How much space is available for p2p */
319327
struct zmsghdr hdr;
328+
size_t client_ibuf_fifo_cnt = stream_fifo_count_safe(client->ibuf_fifo);
320329

321330
p2p_orig = atomic_load_explicit(&zrouter.packets_to_process,
322331
memory_order_relaxed);
332+
p2p_avail = p2p_orig - client_ibuf_fifo_cnt;
333+
334+
/*
335+
* Do nothing if ibuf_fifo count has reached its max limit. Otherwise
336+
* proceed and reschedule ourselves if there is space in the ibuf_fifo.
337+
*/
338+
if (p2p_avail <= 0)
339+
return;
340+
341+
p2p = p2p_avail;
323342
cache = stream_fifo_new();
324-
p2p = p2p_orig;
325343
sock = EVENT_FD(thread);
326344

327345
while (p2p) {
@@ -421,7 +439,7 @@ static void zserv_read(struct event *thread)
421439
p2p--;
422440
}
423441

424-
if (p2p < p2p_orig) {
442+
if (p2p < (uint32_t)p2p_avail) {
425443
uint64_t time_now = monotime(NULL);
426444

427445
/* update session statistics */
@@ -435,19 +453,23 @@ static void zserv_read(struct event *thread)
435453
while (cache->head)
436454
stream_fifo_push(client->ibuf_fifo,
437455
stream_fifo_pop(cache));
456+
/* Need to update count as main thread could have processed few */
457+
client_ibuf_fifo_cnt =
458+
stream_fifo_count_safe(client->ibuf_fifo);
438459
}
439460

440461
/* Schedule job to process those packets */
441462
zserv_event(client, ZSERV_PROCESS_MESSAGES);
442-
443463
}
444464

445465
if (IS_ZEBRA_DEBUG_PACKET)
446-
zlog_debug("Read %d packets from client: %s", p2p_orig - p2p,
447-
zebra_route_string(client->proto));
466+
zlog_debug("Read %d packets from client: %s. Current ibuf fifo count: %zu. Conf P2p %d",
467+
p2p_avail - p2p, zebra_route_string(client->proto),
468+
client_ibuf_fifo_cnt, p2p_orig);
448469

449-
/* Reschedule ourselves */
450-
zserv_client_event(client, ZSERV_CLIENT_READ);
470+
/* Reschedule ourselves since we have space in ibuf_fifo */
471+
if (client_ibuf_fifo_cnt < p2p_orig)
472+
zserv_client_event(client, ZSERV_CLIENT_READ);
451473

452474
stream_fifo_free(cache);
453475

@@ -483,14 +505,20 @@ static void zserv_client_event(struct zserv *client,
483505
* as the task argument.
484506
*
485507
* Each message is popped off the client's input queue and the action associated
486-
* with the message is executed. This proceeds until there are no more messages,
487-
* an error occurs, or the processing limit is reached.
508+
* with the message is executed. This proceeds until an error occurs, or the
509+
* processing limit is reached.
488510
*
489511
* The client's I/O thread can push at most zrouter.packets_to_process messages
490512
* onto the input buffer before notifying us there are packets to read. As long
491513
* as we always process zrouter.packets_to_process messages here, then we can
492514
* rely on the read thread to handle queuing this task enough times to process
493515
* everything on the input queue.
516+
*
517+
* If the client ibuf always schedules a wakeup to the client IO to read more
518+
* items from the socked buffer. This way we ensure
519+
* - Client IO thread always tries to read the socket buffer and add more
520+
* items to the ibuf_fifo (until max limit)
521+
* - the hidden config change (zebra zapi-packets <>) is taken into account.
494522
*/
495523
static void zserv_process_messages(struct event *thread)
496524
{
@@ -524,6 +552,9 @@ static void zserv_process_messages(struct event *thread)
524552
/* Reschedule ourselves if necessary */
525553
if (need_resched)
526554
zserv_event(client, ZSERV_PROCESS_MESSAGES);
555+
556+
/* Ensure to include the read socket in the select/poll/etc.. */
557+
zserv_client_event(client, ZSERV_CLIENT_READ);
527558
}
528559

529560
int zserv_send_message(struct zserv *client, struct stream *msg)

0 commit comments

Comments
 (0)