2 * Zebra dataplane layer.
3 * Copyright (c) 2018 Volta Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "lib/libfrr.h"
21 #include "lib/debug.h"
22 #include "lib/frratomic.h"
23 #include "lib/frr_pthread.h"
24 #include "lib/memory.h"
25 #include "lib/queue.h"
26 #include "lib/zebra.h"
27 #include "zebra/zebra_memory.h"
28 #include "zebra/zserv.h"
29 #include "zebra/zebra_dplane.h"
31 #include "zebra/debug.h"
33 /* Memory type for context blocks */
34 DEFINE_MTYPE(ZEBRA
, DP_CTX
, "Zebra DPlane Ctx")
35 DEFINE_MTYPE(ZEBRA
, DP_PROV
, "Zebra DPlane Provider")
41 /* Enable test dataplane provider */
42 /*#define DPLANE_TEST_PROVIDER 1 */
44 /* Default value for max queued incoming updates */
45 const uint32_t DPLANE_DEFAULT_MAX_QUEUED
= 200;
47 /* Default value for new work per cycle */
48 const uint32_t DPLANE_DEFAULT_NEW_WORK
= 100;
50 /* Validation check macro for context blocks */
51 /* #define DPLANE_DEBUG 1 */
55 # define DPLANE_CTX_VALID(p) \
60 # define DPLANE_CTX_VALID(p)
62 #endif /* DPLANE_DEBUG */
65 * Route information captured for route updates.
67 struct dplane_route_info
{
69 /* Dest and (optional) source prefixes */
70 struct prefix zd_dest
;
80 route_tag_t zd_old_tag
;
82 uint32_t zd_old_metric
;
85 uint16_t zd_old_instance
;
88 uint8_t zd_old_distance
;
91 uint32_t zd_nexthop_mtu
;
94 struct nexthop_group zd_ng
;
96 /* "Previous" nexthops, used only in route updates without netlink */
97 struct nexthop_group zd_old_ng
;
99 /* TODO -- use fixed array of nexthops, to avoid mallocs? */
104 * The context block used to exchange info about route updates across
105 * the boundary between the zebra main context (and pthread) and the
106 * dataplane layer (and pthread).
108 struct zebra_dplane_ctx
{
111 enum dplane_op_e zd_op
;
113 /* Status on return */
114 enum zebra_dplane_result zd_status
;
116 /* Dplane provider id */
117 uint32_t zd_provider
;
119 /* Flags - used by providers, e.g. */
127 /* TODO -- internal/sub-operation status? */
128 enum zebra_dplane_result zd_remote_status
;
129 enum zebra_dplane_result zd_kernel_status
;
132 uint32_t zd_table_id
;
134 /* Support info for either route or LSP update */
136 struct dplane_route_info rinfo
;
140 /* Namespace info, used especially for netlink kernel communication */
141 struct zebra_dplane_info zd_ns_info
;
143 /* Embedded list linkage */
144 TAILQ_ENTRY(zebra_dplane_ctx
) zd_q_entries
;
147 /* Flag that can be set by a pre-kernel provider as a signal that an update
148 * should bypass the kernel.
150 #define DPLANE_CTX_FLAG_NO_KERNEL 0x01
154 * Registration block for one dataplane provider.
156 struct zebra_dplane_provider
{
158 char dp_name
[DPLANE_PROVIDER_NAMELEN
+ 1];
160 /* Priority, for ordering among providers */
167 pthread_mutex_t dp_mutex
;
169 /* Plugin-provided extra data */
175 int (*dp_fp
)(struct zebra_dplane_provider
*prov
);
177 int (*dp_fini
)(struct zebra_dplane_provider
*prov
, bool early_p
);
179 _Atomic
uint32_t dp_in_counter
;
180 _Atomic
uint32_t dp_in_queued
;
181 _Atomic
uint32_t dp_in_max
;
182 _Atomic
uint32_t dp_out_counter
;
183 _Atomic
uint32_t dp_out_queued
;
184 _Atomic
uint32_t dp_out_max
;
185 _Atomic
uint32_t dp_error_counter
;
187 /* Queue of contexts inbound to the provider */
188 struct dplane_ctx_q dp_ctx_in_q
;
190 /* Queue of completed contexts outbound from the provider back
191 * towards the dataplane module.
193 struct dplane_ctx_q dp_ctx_out_q
;
195 /* Embedded list linkage for provider objects */
196 TAILQ_ENTRY(zebra_dplane_provider
) dp_prov_link
;
202 static struct zebra_dplane_globals
{
203 /* Mutex to control access to dataplane components */
204 pthread_mutex_t dg_mutex
;
206 /* Results callback registered by zebra 'core' */
207 int (*dg_results_cb
)(struct dplane_ctx_q
*ctxlist
);
209 /* Sentinel for beginning of shutdown */
210 volatile bool dg_is_shutdown
;
212 /* Sentinel for end of shutdown */
213 volatile bool dg_run
;
215 /* Route-update context queue inbound to the dataplane */
216 TAILQ_HEAD(zdg_ctx_q
, zebra_dplane_ctx
) dg_route_ctx_q
;
218 /* Ordered list of providers */
219 TAILQ_HEAD(zdg_prov_q
, zebra_dplane_provider
) dg_providers_q
;
221 /* Counter used to assign internal ids to providers */
222 uint32_t dg_provider_id
;
224 /* Limit number of pending, unprocessed updates */
225 _Atomic
uint32_t dg_max_queued_updates
;
227 /* Limit number of new updates dequeued at once, to pace an
230 uint32_t dg_updates_per_cycle
;
232 _Atomic
uint32_t dg_routes_in
;
233 _Atomic
uint32_t dg_routes_queued
;
234 _Atomic
uint32_t dg_routes_queued_max
;
235 _Atomic
uint32_t dg_route_errors
;
236 _Atomic
uint32_t dg_other_errors
;
238 _Atomic
uint32_t dg_lsps_in
;
239 _Atomic
uint32_t dg_lsps_queued
;
240 _Atomic
uint32_t dg_lsps_queued_max
;
241 _Atomic
uint32_t dg_lsp_errors
;
243 _Atomic
uint32_t dg_update_yields
;
245 /* Dataplane pthread */
246 struct frr_pthread
*dg_pthread
;
248 /* Event-delivery context 'master' for the dplane */
249 struct thread_master
*dg_master
;
251 /* Event/'thread' pointer for queued updates */
252 struct thread
*dg_t_update
;
254 /* Event pointer for pending shutdown check loop */
255 struct thread
*dg_t_shutdown_check
;
260 * Lock and unlock for interactions with the zebra 'core' pthread
262 #define DPLANE_LOCK() pthread_mutex_lock(&zdplane_info.dg_mutex)
263 #define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_info.dg_mutex)
267 * Lock and unlock for individual providers
269 #define DPLANE_PROV_LOCK(p) pthread_mutex_lock(&((p)->dp_mutex))
270 #define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex))
273 static int dplane_thread_loop(struct thread
*event
);
274 static void dplane_info_from_zns(struct zebra_dplane_info
*ns_info
,
275 struct zebra_ns
*zns
);
276 static enum zebra_dplane_result
lsp_update_internal(zebra_lsp_t
*lsp
,
277 enum dplane_op_e op
);
283 /* Obtain thread_master for dataplane thread */
284 struct thread_master
*dplane_get_thread_master(void)
286 return zdplane_info
.dg_master
;
290 * Allocate a dataplane update context
292 static struct zebra_dplane_ctx
*dplane_ctx_alloc(void)
294 struct zebra_dplane_ctx
*p
;
296 /* TODO -- just alloc'ing memory, but would like to maintain
299 p
= XCALLOC(MTYPE_DP_CTX
, sizeof(struct zebra_dplane_ctx
));
305 * Free a dataplane results context.
307 static void dplane_ctx_free(struct zebra_dplane_ctx
**pctx
)
312 DPLANE_CTX_VALID(*pctx
);
314 /* TODO -- just freeing memory, but would like to maintain
318 /* Some internal allocations may need to be freed, depending on
319 * the type of info captured in the ctx.
321 switch ((*pctx
)->zd_op
) {
322 case DPLANE_OP_ROUTE_INSTALL
:
323 case DPLANE_OP_ROUTE_UPDATE
:
324 case DPLANE_OP_ROUTE_DELETE
:
326 /* Free allocated nexthops */
327 if ((*pctx
)->u
.rinfo
.zd_ng
.nexthop
) {
328 /* This deals with recursive nexthops too */
329 nexthops_free((*pctx
)->u
.rinfo
.zd_ng
.nexthop
);
331 (*pctx
)->u
.rinfo
.zd_ng
.nexthop
= NULL
;
334 if ((*pctx
)->u
.rinfo
.zd_old_ng
.nexthop
) {
335 /* This deals with recursive nexthops too */
336 nexthops_free((*pctx
)->u
.rinfo
.zd_old_ng
.nexthop
);
338 (*pctx
)->u
.rinfo
.zd_old_ng
.nexthop
= NULL
;
343 case DPLANE_OP_LSP_INSTALL
:
344 case DPLANE_OP_LSP_UPDATE
:
345 case DPLANE_OP_LSP_DELETE
:
347 zebra_nhlfe_t
*nhlfe
, *next
;
349 /* Free allocated NHLFEs */
350 for (nhlfe
= (*pctx
)->u
.lsp
.nhlfe_list
; nhlfe
; nhlfe
= next
) {
353 zebra_mpls_nhlfe_del(nhlfe
);
356 /* Clear pointers in lsp struct, in case we're cacheing
357 * free context structs.
359 (*pctx
)->u
.lsp
.nhlfe_list
= NULL
;
360 (*pctx
)->u
.lsp
.best_nhlfe
= NULL
;
369 XFREE(MTYPE_DP_CTX
, *pctx
);
374 * Return a context block to the dplane module after processing
376 void dplane_ctx_fini(struct zebra_dplane_ctx
**pctx
)
378 /* TODO -- maintain pool; for now, just free */
379 dplane_ctx_free(pctx
);
382 /* Enqueue a context block */
383 void dplane_ctx_enqueue_tail(struct dplane_ctx_q
*q
,
384 const struct zebra_dplane_ctx
*ctx
)
386 TAILQ_INSERT_TAIL(q
, (struct zebra_dplane_ctx
*)ctx
, zd_q_entries
);
389 /* Append a list of context blocks to another list */
390 void dplane_ctx_list_append(struct dplane_ctx_q
*to_list
,
391 struct dplane_ctx_q
*from_list
)
393 if (TAILQ_FIRST(from_list
)) {
394 TAILQ_CONCAT(to_list
, from_list
, zd_q_entries
);
396 /* And clear 'from' list */
397 TAILQ_INIT(from_list
);
401 /* Dequeue a context block from the head of a list */
402 struct zebra_dplane_ctx
*dplane_ctx_dequeue(struct dplane_ctx_q
*q
)
404 struct zebra_dplane_ctx
*ctx
= TAILQ_FIRST(q
);
407 TAILQ_REMOVE(q
, ctx
, zd_q_entries
);
413 * Accessors for information from the context object
415 enum zebra_dplane_result
dplane_ctx_get_status(
416 const struct zebra_dplane_ctx
*ctx
)
418 DPLANE_CTX_VALID(ctx
);
420 return ctx
->zd_status
;
423 void dplane_ctx_set_status(struct zebra_dplane_ctx
*ctx
,
424 enum zebra_dplane_result status
)
426 DPLANE_CTX_VALID(ctx
);
428 ctx
->zd_status
= status
;
431 /* Retrieve last/current provider id */
432 uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx
*ctx
)
434 DPLANE_CTX_VALID(ctx
);
435 return ctx
->zd_provider
;
438 /* Providers run before the kernel can control whether a kernel
439 * update should be done.
441 void dplane_ctx_set_skip_kernel(struct zebra_dplane_ctx
*ctx
)
443 DPLANE_CTX_VALID(ctx
);
445 SET_FLAG(ctx
->zd_flags
, DPLANE_CTX_FLAG_NO_KERNEL
);
448 bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx
*ctx
)
450 DPLANE_CTX_VALID(ctx
);
452 return CHECK_FLAG(ctx
->zd_flags
, DPLANE_CTX_FLAG_NO_KERNEL
);
455 enum dplane_op_e
dplane_ctx_get_op(const struct zebra_dplane_ctx
*ctx
)
457 DPLANE_CTX_VALID(ctx
);
462 const char *dplane_op2str(enum dplane_op_e op
)
464 const char *ret
= "UNKNOWN";
472 case DPLANE_OP_ROUTE_INSTALL
:
473 ret
= "ROUTE_INSTALL";
475 case DPLANE_OP_ROUTE_UPDATE
:
476 ret
= "ROUTE_UPDATE";
478 case DPLANE_OP_ROUTE_DELETE
:
479 ret
= "ROUTE_DELETE";
482 case DPLANE_OP_LSP_INSTALL
:
485 case DPLANE_OP_LSP_UPDATE
:
488 case DPLANE_OP_LSP_DELETE
:
497 const char *dplane_res2str(enum zebra_dplane_result res
)
499 const char *ret
= "<Unknown>";
502 case ZEBRA_DPLANE_REQUEST_FAILURE
:
505 case ZEBRA_DPLANE_REQUEST_QUEUED
:
508 case ZEBRA_DPLANE_REQUEST_SUCCESS
:
516 const struct prefix
*dplane_ctx_get_dest(const struct zebra_dplane_ctx
*ctx
)
518 DPLANE_CTX_VALID(ctx
);
520 return &(ctx
->u
.rinfo
.zd_dest
);
523 /* Source prefix is a little special - return NULL for "no src prefix" */
524 const struct prefix
*dplane_ctx_get_src(const struct zebra_dplane_ctx
*ctx
)
526 DPLANE_CTX_VALID(ctx
);
528 if (ctx
->u
.rinfo
.zd_src
.prefixlen
== 0 &&
529 IN6_IS_ADDR_UNSPECIFIED(&(ctx
->u
.rinfo
.zd_src
.u
.prefix6
))) {
532 return &(ctx
->u
.rinfo
.zd_src
);
536 bool dplane_ctx_is_update(const struct zebra_dplane_ctx
*ctx
)
538 DPLANE_CTX_VALID(ctx
);
540 return ctx
->zd_is_update
;
543 uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx
*ctx
)
545 DPLANE_CTX_VALID(ctx
);
550 uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx
*ctx
)
552 DPLANE_CTX_VALID(ctx
);
554 return ctx
->zd_old_seq
;
557 vrf_id_t
dplane_ctx_get_vrf(const struct zebra_dplane_ctx
*ctx
)
559 DPLANE_CTX_VALID(ctx
);
561 return ctx
->zd_vrf_id
;
564 int dplane_ctx_get_type(const struct zebra_dplane_ctx
*ctx
)
566 DPLANE_CTX_VALID(ctx
);
568 return ctx
->u
.rinfo
.zd_type
;
571 int dplane_ctx_get_old_type(const struct zebra_dplane_ctx
*ctx
)
573 DPLANE_CTX_VALID(ctx
);
575 return ctx
->u
.rinfo
.zd_old_type
;
578 afi_t
dplane_ctx_get_afi(const struct zebra_dplane_ctx
*ctx
)
580 DPLANE_CTX_VALID(ctx
);
582 return ctx
->u
.rinfo
.zd_afi
;
585 safi_t
dplane_ctx_get_safi(const struct zebra_dplane_ctx
*ctx
)
587 DPLANE_CTX_VALID(ctx
);
589 return ctx
->u
.rinfo
.zd_safi
;
592 uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx
*ctx
)
594 DPLANE_CTX_VALID(ctx
);
596 return ctx
->zd_table_id
;
599 route_tag_t
dplane_ctx_get_tag(const struct zebra_dplane_ctx
*ctx
)
601 DPLANE_CTX_VALID(ctx
);
603 return ctx
->u
.rinfo
.zd_tag
;
606 route_tag_t
dplane_ctx_get_old_tag(const struct zebra_dplane_ctx
*ctx
)
608 DPLANE_CTX_VALID(ctx
);
610 return ctx
->u
.rinfo
.zd_old_tag
;
613 uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx
*ctx
)
615 DPLANE_CTX_VALID(ctx
);
617 return ctx
->u
.rinfo
.zd_instance
;
620 uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx
*ctx
)
622 DPLANE_CTX_VALID(ctx
);
624 return ctx
->u
.rinfo
.zd_old_instance
;
627 uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx
*ctx
)
629 DPLANE_CTX_VALID(ctx
);
631 return ctx
->u
.rinfo
.zd_metric
;
634 uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx
*ctx
)
636 DPLANE_CTX_VALID(ctx
);
638 return ctx
->u
.rinfo
.zd_old_metric
;
641 uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx
*ctx
)
643 DPLANE_CTX_VALID(ctx
);
645 return ctx
->u
.rinfo
.zd_mtu
;
648 uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx
*ctx
)
650 DPLANE_CTX_VALID(ctx
);
652 return ctx
->u
.rinfo
.zd_nexthop_mtu
;
655 uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx
*ctx
)
657 DPLANE_CTX_VALID(ctx
);
659 return ctx
->u
.rinfo
.zd_distance
;
662 uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx
*ctx
)
664 DPLANE_CTX_VALID(ctx
);
666 return ctx
->u
.rinfo
.zd_old_distance
;
669 const struct nexthop_group
*dplane_ctx_get_ng(
670 const struct zebra_dplane_ctx
*ctx
)
672 DPLANE_CTX_VALID(ctx
);
674 return &(ctx
->u
.rinfo
.zd_ng
);
677 const struct nexthop_group
*dplane_ctx_get_old_ng(
678 const struct zebra_dplane_ctx
*ctx
)
680 DPLANE_CTX_VALID(ctx
);
682 return &(ctx
->u
.rinfo
.zd_old_ng
);
685 const struct zebra_dplane_info
*dplane_ctx_get_ns(
686 const struct zebra_dplane_ctx
*ctx
)
688 DPLANE_CTX_VALID(ctx
);
690 return &(ctx
->zd_ns_info
);
693 /* Accessors for LSP information */
695 mpls_label_t
dplane_ctx_get_in_label(const struct zebra_dplane_ctx
*ctx
)
697 DPLANE_CTX_VALID(ctx
);
699 return ctx
->u
.lsp
.ile
.in_label
;
702 uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx
*ctx
)
704 DPLANE_CTX_VALID(ctx
);
706 return ctx
->u
.lsp
.addr_family
;
709 uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx
*ctx
)
711 DPLANE_CTX_VALID(ctx
);
713 return ctx
->u
.lsp
.flags
;
716 zebra_nhlfe_t
*dplane_ctx_get_nhlfe(struct zebra_dplane_ctx
*ctx
)
718 DPLANE_CTX_VALID(ctx
);
720 return ctx
->u
.lsp
.nhlfe_list
;
723 zebra_nhlfe_t
*dplane_ctx_get_best_nhlfe(struct zebra_dplane_ctx
*ctx
)
725 DPLANE_CTX_VALID(ctx
);
727 return ctx
->u
.lsp
.best_nhlfe
;
730 uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx
*ctx
)
732 DPLANE_CTX_VALID(ctx
);
734 return ctx
->u
.lsp
.num_ecmp
;
738 * End of dplane context accessors
743 * Retrieve the limit on the number of pending, unprocessed updates.
745 uint32_t dplane_get_in_queue_limit(void)
747 return atomic_load_explicit(&zdplane_info
.dg_max_queued_updates
,
748 memory_order_relaxed
);
752 * Configure limit on the number of pending, queued updates.
754 void dplane_set_in_queue_limit(uint32_t limit
, bool set
)
756 /* Reset to default on 'unset' */
758 limit
= DPLANE_DEFAULT_MAX_QUEUED
;
760 atomic_store_explicit(&zdplane_info
.dg_max_queued_updates
, limit
,
761 memory_order_relaxed
);
765 * Retrieve the current queue depth of incoming, unprocessed updates
767 uint32_t dplane_get_in_queue_len(void)
769 return atomic_load_explicit(&zdplane_info
.dg_routes_queued
,
770 memory_order_seq_cst
);
774 * Common dataplane context init with zebra namespace info.
776 static int dplane_ctx_ns_init(struct zebra_dplane_ctx
*ctx
,
777 struct zebra_ns
*zns
,
780 dplane_info_from_zns(&(ctx
->zd_ns_info
), zns
);
782 #if defined(HAVE_NETLINK)
783 /* Increment message counter after copying to context struct - may need
784 * two messages in some 'update' cases.
787 zns
->netlink_dplane
.seq
+= 2;
789 zns
->netlink_dplane
.seq
++;
790 #endif /* HAVE_NETLINK */
796 * Initialize a context block for a route update from zebra data structs.
798 static int dplane_ctx_route_init(struct zebra_dplane_ctx
*ctx
,
800 struct route_node
*rn
,
801 struct route_entry
*re
)
804 const struct route_table
*table
= NULL
;
805 const rib_table_info_t
*info
;
806 const struct prefix
*p
, *src_p
;
807 struct zebra_ns
*zns
;
808 struct zebra_vrf
*zvrf
;
809 struct nexthop
*nexthop
;
811 if (!ctx
|| !rn
|| !re
)
815 ctx
->zd_status
= ZEBRA_DPLANE_REQUEST_SUCCESS
;
817 ctx
->u
.rinfo
.zd_type
= re
->type
;
818 ctx
->u
.rinfo
.zd_old_type
= re
->type
;
820 /* Prefixes: dest, and optional source */
821 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
823 prefix_copy(&(ctx
->u
.rinfo
.zd_dest
), p
);
826 prefix_copy(&(ctx
->u
.rinfo
.zd_src
), src_p
);
828 memset(&(ctx
->u
.rinfo
.zd_src
), 0, sizeof(ctx
->u
.rinfo
.zd_src
));
830 ctx
->zd_table_id
= re
->table
;
832 ctx
->u
.rinfo
.zd_metric
= re
->metric
;
833 ctx
->u
.rinfo
.zd_old_metric
= re
->metric
;
834 ctx
->zd_vrf_id
= re
->vrf_id
;
835 ctx
->u
.rinfo
.zd_mtu
= re
->mtu
;
836 ctx
->u
.rinfo
.zd_nexthop_mtu
= re
->nexthop_mtu
;
837 ctx
->u
.rinfo
.zd_instance
= re
->instance
;
838 ctx
->u
.rinfo
.zd_tag
= re
->tag
;
839 ctx
->u
.rinfo
.zd_old_tag
= re
->tag
;
840 ctx
->u
.rinfo
.zd_distance
= re
->distance
;
842 table
= srcdest_rnode_table(rn
);
845 ctx
->u
.rinfo
.zd_afi
= info
->afi
;
846 ctx
->u
.rinfo
.zd_safi
= info
->safi
;
848 /* Extract ns info - can't use pointers to 'core' structs */
849 zvrf
= vrf_info_lookup(re
->vrf_id
);
852 dplane_ctx_ns_init(ctx
, zns
, (op
== DPLANE_OP_ROUTE_UPDATE
));
854 /* Copy nexthops; recursive info is included too */
855 copy_nexthops(&(ctx
->u
.rinfo
.zd_ng
.nexthop
), re
->ng
.nexthop
, NULL
);
857 /* TODO -- maybe use array of nexthops to avoid allocs? */
859 /* Ensure that the dplane's nexthops flags are clear. */
860 for (ALL_NEXTHOPS(ctx
->u
.rinfo
.zd_ng
, nexthop
))
861 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_FIB
);
863 /* Trying out the sequence number idea, so we can try to detect
864 * when a result is stale.
866 re
->dplane_sequence
++;
867 ctx
->zd_seq
= re
->dplane_sequence
;
876 * Capture information for an LSP update in a dplane context.
878 static int dplane_ctx_lsp_init(struct zebra_dplane_ctx
*ctx
,
883 zebra_nhlfe_t
*nhlfe
, *new_nhlfe
;
885 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
886 zlog_debug("init dplane ctx %s: in-label %u ecmp# %d",
887 dplane_op2str(op
), lsp
->ile
.in_label
,
891 ctx
->zd_status
= ZEBRA_DPLANE_REQUEST_SUCCESS
;
893 /* Capture namespace info */
894 dplane_ctx_ns_init(ctx
, zebra_ns_lookup(NS_DEFAULT
),
895 (op
== DPLANE_OP_LSP_UPDATE
));
897 memset(&ctx
->u
.lsp
, 0, sizeof(ctx
->u
.lsp
));
899 ctx
->u
.lsp
.ile
= lsp
->ile
;
900 ctx
->u
.lsp
.addr_family
= lsp
->addr_family
;
901 ctx
->u
.lsp
.num_ecmp
= lsp
->num_ecmp
;
902 ctx
->u
.lsp
.flags
= lsp
->flags
;
904 /* Copy source LSP's nhlfes, and capture 'best' nhlfe */
905 for (nhlfe
= lsp
->nhlfe_list
; nhlfe
; nhlfe
= nhlfe
->next
) {
906 /* Not sure if this is meaningful... */
907 if (nhlfe
->nexthop
== NULL
)
911 zebra_mpls_lsp_add_nhlfe(
914 nhlfe
->nexthop
->type
,
915 &(nhlfe
->nexthop
->gate
),
916 nhlfe
->nexthop
->ifindex
,
917 nhlfe
->nexthop
->nh_label
->label
[0]);
919 if (new_nhlfe
== NULL
|| new_nhlfe
->nexthop
== NULL
) {
924 /* Need to copy flags too */
925 new_nhlfe
->flags
= nhlfe
->flags
;
926 new_nhlfe
->nexthop
->flags
= nhlfe
->nexthop
->flags
;
928 if (nhlfe
== lsp
->best_nhlfe
)
929 ctx
->u
.lsp
.best_nhlfe
= new_nhlfe
;
932 /* On error the ctx will be cleaned-up, so we don't need to
933 * deal with any allocated nhlfe or nexthop structs here.
940 * Enqueue a new route update,
941 * and ensure an event is active for the dataplane pthread.
943 static int dplane_route_enqueue(struct zebra_dplane_ctx
*ctx
)
948 /* Enqueue for processing by the dataplane pthread */
951 TAILQ_INSERT_TAIL(&zdplane_info
.dg_route_ctx_q
, ctx
,
956 curr
= atomic_add_fetch_explicit(
958 /* TODO -- issue with the clang atomic/intrinsics currently;
959 * casting away the 'Atomic'-ness of the variable works.
961 (uint32_t *)&(zdplane_info
.dg_routes_queued
),
963 &(zdplane_info
.dg_routes_queued
),
965 1, memory_order_seq_cst
);
967 /* Maybe update high-water counter also */
968 high
= atomic_load_explicit(&zdplane_info
.dg_routes_queued_max
,
969 memory_order_seq_cst
);
970 while (high
< curr
) {
971 if (atomic_compare_exchange_weak_explicit(
972 &zdplane_info
.dg_routes_queued_max
,
974 memory_order_seq_cst
,
975 memory_order_seq_cst
))
979 /* Ensure that an event for the dataplane thread is active */
980 ret
= dplane_provider_work_ready();
986 * Utility that prepares a route update and enqueues it for processing
988 static enum zebra_dplane_result
989 dplane_route_update_internal(struct route_node
*rn
,
990 struct route_entry
*re
,
991 struct route_entry
*old_re
,
994 enum zebra_dplane_result result
= ZEBRA_DPLANE_REQUEST_FAILURE
;
996 struct zebra_dplane_ctx
*ctx
= NULL
;
998 /* Obtain context block */
999 ctx
= dplane_ctx_alloc();
1005 /* Init context with info from zebra data structs */
1006 ret
= dplane_ctx_route_init(ctx
, op
, rn
, re
);
1008 /* Capture some extra info for update case
1009 * where there's a different 'old' route.
1011 if ((op
== DPLANE_OP_ROUTE_UPDATE
) &&
1012 old_re
&& (old_re
!= re
)) {
1013 ctx
->zd_is_update
= true;
1015 old_re
->dplane_sequence
++;
1016 ctx
->zd_old_seq
= old_re
->dplane_sequence
;
1018 ctx
->u
.rinfo
.zd_old_tag
= old_re
->tag
;
1019 ctx
->u
.rinfo
.zd_old_type
= old_re
->type
;
1020 ctx
->u
.rinfo
.zd_old_instance
= old_re
->instance
;
1021 ctx
->u
.rinfo
.zd_old_distance
= old_re
->distance
;
1022 ctx
->u
.rinfo
.zd_old_metric
= old_re
->metric
;
1024 #ifndef HAVE_NETLINK
1025 /* For bsd, capture previous re's nexthops too, sigh.
1026 * We'll need these to do per-nexthop deletes.
1028 copy_nexthops(&(ctx
->u
.rinfo
.zd_old_ng
.nexthop
),
1029 old_re
->ng
.nexthop
, NULL
);
1030 #endif /* !HAVE_NETLINK */
1033 /* Enqueue context for processing */
1034 ret
= dplane_route_enqueue(ctx
);
1038 /* Update counter */
1039 atomic_fetch_add_explicit(&zdplane_info
.dg_routes_in
, 1,
1040 memory_order_relaxed
);
1043 result
= ZEBRA_DPLANE_REQUEST_QUEUED
;
1045 atomic_fetch_add_explicit(&zdplane_info
.dg_route_errors
, 1,
1046 memory_order_relaxed
);
1048 dplane_ctx_free(&ctx
);
1055 * Enqueue a route 'add' for the dataplane.
1057 enum zebra_dplane_result
dplane_route_add(struct route_node
*rn
,
1058 struct route_entry
*re
)
1060 enum zebra_dplane_result ret
= ZEBRA_DPLANE_REQUEST_FAILURE
;
1062 if (rn
== NULL
|| re
== NULL
)
1065 ret
= dplane_route_update_internal(rn
, re
, NULL
,
1066 DPLANE_OP_ROUTE_INSTALL
);
1073 * Enqueue a route update for the dataplane.
1075 enum zebra_dplane_result
dplane_route_update(struct route_node
*rn
,
1076 struct route_entry
*re
,
1077 struct route_entry
*old_re
)
1079 enum zebra_dplane_result ret
= ZEBRA_DPLANE_REQUEST_FAILURE
;
1081 if (rn
== NULL
|| re
== NULL
)
1084 ret
= dplane_route_update_internal(rn
, re
, old_re
,
1085 DPLANE_OP_ROUTE_UPDATE
);
1091 * Enqueue a route removal for the dataplane.
1093 enum zebra_dplane_result
dplane_route_delete(struct route_node
*rn
,
1094 struct route_entry
*re
)
1096 enum zebra_dplane_result ret
= ZEBRA_DPLANE_REQUEST_FAILURE
;
1098 if (rn
== NULL
|| re
== NULL
)
1101 ret
= dplane_route_update_internal(rn
, re
, NULL
,
1102 DPLANE_OP_ROUTE_DELETE
);
1109 * Enqueue LSP add for the dataplane.
1111 enum zebra_dplane_result
dplane_lsp_add(zebra_lsp_t
*lsp
)
1113 enum zebra_dplane_result ret
=
1114 lsp_update_internal(lsp
, DPLANE_OP_LSP_INSTALL
);
1120 * Enqueue LSP update for the dataplane.
1122 enum zebra_dplane_result
dplane_lsp_update(zebra_lsp_t
*lsp
)
1124 enum zebra_dplane_result ret
=
1125 lsp_update_internal(lsp
, DPLANE_OP_LSP_UPDATE
);
1131 * Enqueue LSP delete for the dataplane.
1133 enum zebra_dplane_result
dplane_lsp_delete(zebra_lsp_t
*lsp
)
1135 enum zebra_dplane_result ret
=
1136 lsp_update_internal(lsp
, DPLANE_OP_LSP_DELETE
);
1142 * Common internal LSP update utility
1144 static enum zebra_dplane_result
lsp_update_internal(zebra_lsp_t
*lsp
,
1145 enum dplane_op_e op
)
1147 enum zebra_dplane_result result
= ZEBRA_DPLANE_REQUEST_FAILURE
;
1149 struct zebra_dplane_ctx
*ctx
= NULL
;
1151 /* Obtain context block */
1152 ctx
= dplane_ctx_alloc();
1158 ret
= dplane_ctx_lsp_init(ctx
, op
, lsp
);
1162 ret
= dplane_route_enqueue(ctx
);
1165 /* Update counter */
1166 atomic_fetch_add_explicit(&zdplane_info
.dg_lsps_in
, 1,
1167 memory_order_relaxed
);
1170 result
= ZEBRA_DPLANE_REQUEST_QUEUED
;
1172 atomic_fetch_add_explicit(&zdplane_info
.dg_lsp_errors
, 1,
1173 memory_order_relaxed
);
1175 dplane_ctx_free(&ctx
);
1182 * Handler for 'show dplane'
1184 int dplane_show_helper(struct vty
*vty
, bool detailed
)
1186 uint64_t queued
, queue_max
, limit
, errs
, incoming
, yields
,
1189 /* Using atomics because counters are being changed in different
1192 incoming
= atomic_load_explicit(&zdplane_info
.dg_routes_in
,
1193 memory_order_relaxed
);
1194 limit
= atomic_load_explicit(&zdplane_info
.dg_max_queued_updates
,
1195 memory_order_relaxed
);
1196 queued
= atomic_load_explicit(&zdplane_info
.dg_routes_queued
,
1197 memory_order_relaxed
);
1198 queue_max
= atomic_load_explicit(&zdplane_info
.dg_routes_queued_max
,
1199 memory_order_relaxed
);
1200 errs
= atomic_load_explicit(&zdplane_info
.dg_route_errors
,
1201 memory_order_relaxed
);
1202 yields
= atomic_load_explicit(&zdplane_info
.dg_update_yields
,
1203 memory_order_relaxed
);
1204 other_errs
= atomic_load_explicit(&zdplane_info
.dg_other_errors
,
1205 memory_order_relaxed
);
1207 vty_out(vty
, "Zebra dataplane:\nRoute updates: %"PRIu64
"\n",
1209 vty_out(vty
, "Route update errors: %"PRIu64
"\n", errs
);
1210 vty_out(vty
, "Other errors : %"PRIu64
"\n", other_errs
);
1211 vty_out(vty
, "Route update queue limit: %"PRIu64
"\n", limit
);
1212 vty_out(vty
, "Route update queue depth: %"PRIu64
"\n", queued
);
1213 vty_out(vty
, "Route update queue max: %"PRIu64
"\n", queue_max
);
1214 vty_out(vty
, "Dplane update yields: %"PRIu64
"\n", yields
);
1220 * Handler for 'show dplane providers'
1222 int dplane_show_provs_helper(struct vty
*vty
, bool detailed
)
1224 struct zebra_dplane_provider
*prov
;
1225 uint64_t in
, in_max
, out
, out_max
;
1227 vty_out(vty
, "Zebra dataplane providers:\n");
1230 prov
= TAILQ_FIRST(&zdplane_info
.dg_providers_q
);
1233 /* Show counters, useful info from each registered provider */
1236 in
= atomic_load_explicit(&prov
->dp_in_counter
,
1237 memory_order_relaxed
);
1238 in_max
= atomic_load_explicit(&prov
->dp_in_max
,
1239 memory_order_relaxed
);
1240 out
= atomic_load_explicit(&prov
->dp_out_counter
,
1241 memory_order_relaxed
);
1242 out_max
= atomic_load_explicit(&prov
->dp_out_max
,
1243 memory_order_relaxed
);
1245 vty_out(vty
, "%s (%u): in: %"PRIu64
", q_max: %"PRIu64
", "
1246 "out: %"PRIu64
", q_max: %"PRIu64
"\n",
1247 prov
->dp_name
, prov
->dp_id
, in
, in_max
, out
, out_max
);
1250 prov
= TAILQ_NEXT(prov
, dp_prov_link
);
1258 * Provider registration
1260 int dplane_provider_register(const char *name
,
1261 enum dplane_provider_prio prio
,
1263 int (*fp
)(struct zebra_dplane_provider
*),
1264 int (*fini_fp
)(struct zebra_dplane_provider
*,
1267 struct zebra_dplane_provider
**prov_p
)
1270 struct zebra_dplane_provider
*p
= NULL
, *last
;
1278 if (prio
<= DPLANE_PRIO_NONE
||
1279 prio
> DPLANE_PRIO_LAST
) {
1284 /* Allocate and init new provider struct */
1285 p
= XCALLOC(MTYPE_DP_PROV
, sizeof(struct zebra_dplane_provider
));
1291 pthread_mutex_init(&(p
->dp_mutex
), NULL
);
1292 TAILQ_INIT(&(p
->dp_ctx_in_q
));
1293 TAILQ_INIT(&(p
->dp_ctx_out_q
));
1295 p
->dp_priority
= prio
;
1297 p
->dp_fini
= fini_fp
;
1300 /* Lock - the dplane pthread may be running */
1303 p
->dp_id
= ++zdplane_info
.dg_provider_id
;
1306 strlcpy(p
->dp_name
, name
, DPLANE_PROVIDER_NAMELEN
);
1308 snprintf(p
->dp_name
, DPLANE_PROVIDER_NAMELEN
,
1309 "provider-%u", p
->dp_id
);
1311 /* Insert into list ordered by priority */
1312 TAILQ_FOREACH(last
, &zdplane_info
.dg_providers_q
, dp_prov_link
) {
1313 if (last
->dp_priority
> p
->dp_priority
)
1318 TAILQ_INSERT_BEFORE(last
, p
, dp_prov_link
);
1320 TAILQ_INSERT_TAIL(&zdplane_info
.dg_providers_q
, p
,
1326 if (IS_ZEBRA_DEBUG_DPLANE
)
1327 zlog_debug("dplane: registered new provider '%s' (%u), prio %d",
1328 p
->dp_name
, p
->dp_id
, p
->dp_priority
);
1337 /* Accessors for provider attributes */
1338 const char *dplane_provider_get_name(const struct zebra_dplane_provider
*prov
)
1340 return prov
->dp_name
;
1343 uint32_t dplane_provider_get_id(const struct zebra_dplane_provider
*prov
)
1348 void *dplane_provider_get_data(const struct zebra_dplane_provider
*prov
)
1350 return prov
->dp_data
;
1353 int dplane_provider_get_work_limit(const struct zebra_dplane_provider
*prov
)
1355 return zdplane_info
.dg_updates_per_cycle
;
1358 /* Lock/unlock a provider's mutex - iff the provider was registered with
1359 * the THREADED flag.
1361 void dplane_provider_lock(struct zebra_dplane_provider
*prov
)
1363 if (dplane_provider_is_threaded(prov
))
1364 DPLANE_PROV_LOCK(prov
);
1367 void dplane_provider_unlock(struct zebra_dplane_provider
*prov
)
1369 if (dplane_provider_is_threaded(prov
))
1370 DPLANE_PROV_UNLOCK(prov
);
1374 * Dequeue and maintain associated counter
1376 struct zebra_dplane_ctx
*dplane_provider_dequeue_in_ctx(
1377 struct zebra_dplane_provider
*prov
)
1379 struct zebra_dplane_ctx
*ctx
= NULL
;
1381 dplane_provider_lock(prov
);
1383 ctx
= TAILQ_FIRST(&(prov
->dp_ctx_in_q
));
1385 TAILQ_REMOVE(&(prov
->dp_ctx_in_q
), ctx
, zd_q_entries
);
1387 atomic_fetch_sub_explicit(&prov
->dp_in_queued
, 1,
1388 memory_order_relaxed
);
1391 dplane_provider_unlock(prov
);
1397 * Dequeue work to a list, return count
1399 int dplane_provider_dequeue_in_list(struct zebra_dplane_provider
*prov
,
1400 struct dplane_ctx_q
*listp
)
1403 struct zebra_dplane_ctx
*ctx
;
1405 limit
= zdplane_info
.dg_updates_per_cycle
;
1407 dplane_provider_lock(prov
);
1409 for (ret
= 0; ret
< limit
; ret
++) {
1410 ctx
= TAILQ_FIRST(&(prov
->dp_ctx_in_q
));
1412 TAILQ_REMOVE(&(prov
->dp_ctx_in_q
), ctx
, zd_q_entries
);
1414 TAILQ_INSERT_TAIL(listp
, ctx
, zd_q_entries
);
1421 atomic_fetch_sub_explicit(&prov
->dp_in_queued
, ret
,
1422 memory_order_relaxed
);
1424 dplane_provider_unlock(prov
);
1430 * Enqueue and maintain associated counter
1432 void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider
*prov
,
1433 struct zebra_dplane_ctx
*ctx
)
1435 dplane_provider_lock(prov
);
1437 TAILQ_INSERT_TAIL(&(prov
->dp_ctx_out_q
), ctx
,
1440 dplane_provider_unlock(prov
);
1442 atomic_fetch_add_explicit(&(prov
->dp_out_counter
), 1,
1443 memory_order_relaxed
);
1447 * Accessor for provider object
1449 bool dplane_provider_is_threaded(const struct zebra_dplane_provider
*prov
)
1451 return (prov
->dp_flags
& DPLANE_PROV_FLAG_THREADED
);
1455 * Internal helper that copies information from a zebra ns object; this is
1456 * called in the zebra main pthread context as part of dplane ctx init.
1458 static void dplane_info_from_zns(struct zebra_dplane_info
*ns_info
,
1459 struct zebra_ns
*zns
)
1461 ns_info
->ns_id
= zns
->ns_id
;
1463 #if defined(HAVE_NETLINK)
1464 ns_info
->is_cmd
= true;
1465 ns_info
->nls
= zns
->netlink_dplane
;
1466 #endif /* NETLINK */
1470 * Provider api to signal that work/events are available
1471 * for the dataplane pthread.
1473 int dplane_provider_work_ready(void)
1475 /* Note that during zebra startup, we may be offered work before
1476 * the dataplane pthread (and thread-master) are ready. We want to
1477 * enqueue the work, but the event-scheduling machinery may not be
1480 if (zdplane_info
.dg_run
) {
1481 thread_add_event(zdplane_info
.dg_master
,
1482 dplane_thread_loop
, NULL
, 0,
1483 &zdplane_info
.dg_t_update
);
1490 * Kernel dataplane provider
1494 * Handler for kernel LSP updates
1496 static enum zebra_dplane_result
1497 kernel_dplane_lsp_update(struct zebra_dplane_ctx
*ctx
)
1499 enum zebra_dplane_result res
;
1501 /* Call into the synchronous kernel-facing code here */
1502 res
= kernel_lsp_update(ctx
);
1504 if (res
!= ZEBRA_DPLANE_REQUEST_SUCCESS
)
1505 atomic_fetch_add_explicit(
1506 &zdplane_info
.dg_lsp_errors
, 1,
1507 memory_order_relaxed
);
1513 * Handler for kernel route updates
1515 static enum zebra_dplane_result
1516 kernel_dplane_route_update(struct zebra_dplane_ctx
*ctx
)
1518 enum zebra_dplane_result res
;
1520 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
) {
1521 char dest_str
[PREFIX_STRLEN
];
1523 prefix2str(dplane_ctx_get_dest(ctx
),
1524 dest_str
, sizeof(dest_str
));
1526 zlog_debug("%u:%s Dplane route update ctx %p op %s",
1527 dplane_ctx_get_vrf(ctx
), dest_str
,
1528 ctx
, dplane_op2str(dplane_ctx_get_op(ctx
)));
1531 /* Call into the synchronous kernel-facing code here */
1532 res
= kernel_route_update(ctx
);
1534 if (res
!= ZEBRA_DPLANE_REQUEST_SUCCESS
)
1535 atomic_fetch_add_explicit(
1536 &zdplane_info
.dg_route_errors
, 1,
1537 memory_order_relaxed
);
1543 * Kernel provider callback
1545 static int kernel_dplane_process_func(struct zebra_dplane_provider
*prov
)
1547 enum zebra_dplane_result res
;
1548 struct zebra_dplane_ctx
*ctx
;
1551 limit
= dplane_provider_get_work_limit(prov
);
1553 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1554 zlog_debug("dplane provider '%s': processing",
1555 dplane_provider_get_name(prov
));
1557 for (counter
= 0; counter
< limit
; counter
++) {
1559 ctx
= dplane_provider_dequeue_in_ctx(prov
);
1563 /* Dispatch to appropriate kernel-facing apis */
1564 switch (dplane_ctx_get_op(ctx
)) {
1566 case DPLANE_OP_ROUTE_INSTALL
:
1567 case DPLANE_OP_ROUTE_UPDATE
:
1568 case DPLANE_OP_ROUTE_DELETE
:
1569 res
= kernel_dplane_route_update(ctx
);
1572 case DPLANE_OP_LSP_INSTALL
:
1573 case DPLANE_OP_LSP_UPDATE
:
1574 case DPLANE_OP_LSP_DELETE
:
1575 res
= kernel_dplane_lsp_update(ctx
);
1579 atomic_fetch_add_explicit(
1580 &zdplane_info
.dg_other_errors
, 1,
1581 memory_order_relaxed
);
1583 res
= ZEBRA_DPLANE_REQUEST_FAILURE
;
1587 dplane_ctx_set_status(ctx
, res
);
1589 dplane_provider_enqueue_out_ctx(prov
, ctx
);
1592 /* Ensure that we'll run the work loop again if there's still
1595 if (counter
>= limit
) {
1596 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1597 zlog_debug("dplane provider '%s' reached max updates %d",
1598 dplane_provider_get_name(prov
), counter
);
1600 atomic_fetch_add_explicit(&zdplane_info
.dg_update_yields
,
1601 1, memory_order_relaxed
);
1603 dplane_provider_work_ready();
1609 #if DPLANE_TEST_PROVIDER
1612 * Test dataplane provider plugin
1616 * Test provider process callback
1618 static int test_dplane_process_func(struct zebra_dplane_provider
*prov
)
1620 struct zebra_dplane_ctx
*ctx
;
1623 /* Just moving from 'in' queue to 'out' queue */
1625 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1626 zlog_debug("dplane provider '%s': processing",
1627 dplane_provider_get_name(prov
));
1629 limit
= dplane_provider_get_work_limit(prov
);
1631 for (counter
= 0; counter
< limit
; counter
++) {
1633 ctx
= dplane_provider_dequeue_in_ctx(prov
);
1637 dplane_ctx_set_status(ctx
, ZEBRA_DPLANE_REQUEST_SUCCESS
);
1639 dplane_provider_enqueue_out_ctx(prov
, ctx
);
1642 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1643 zlog_debug("dplane provider '%s': processed %d",
1644 dplane_provider_get_name(prov
), counter
);
1646 /* Ensure that we'll run the work loop again if there's still
1649 if (counter
>= limit
)
1650 dplane_provider_work_ready();
1656 * Test provider shutdown/fini callback
1658 static int test_dplane_shutdown_func(struct zebra_dplane_provider
*prov
,
1661 if (IS_ZEBRA_DEBUG_DPLANE
)
1662 zlog_debug("dplane provider '%s': %sshutdown",
1663 dplane_provider_get_name(prov
),
1664 early
? "early " : "");
1668 #endif /* DPLANE_TEST_PROVIDER */
1671 * Register default kernel provider
1673 static void dplane_provider_init(void)
1677 ret
= dplane_provider_register("Kernel",
1679 DPLANE_PROV_FLAGS_DEFAULT
,
1680 kernel_dplane_process_func
,
1685 zlog_err("Unable to register kernel dplane provider: %d",
1688 #if DPLANE_TEST_PROVIDER
1689 /* Optional test provider ... */
1690 ret
= dplane_provider_register("Test",
1691 DPLANE_PRIO_PRE_KERNEL
,
1692 DPLANE_PROV_FLAGS_DEFAULT
,
1693 test_dplane_process_func
,
1694 test_dplane_shutdown_func
,
1695 NULL
/* data */, NULL
);
1698 zlog_err("Unable to register test dplane provider: %d",
1700 #endif /* DPLANE_TEST_PROVIDER */
1703 /* Indicates zebra shutdown/exit is in progress. Some operations may be
1704 * simplified or skipped during shutdown processing.
1706 bool dplane_is_in_shutdown(void)
1708 return zdplane_info
.dg_is_shutdown
;
1712 * Early or pre-shutdown, de-init notification api. This runs pretty
1713 * early during zebra shutdown, as a signal to stop new work and prepare
1714 * for updates generated by shutdown/cleanup activity, as zebra tries to
1715 * remove everything it's responsible for.
1716 * NB: This runs in the main zebra pthread context.
1718 void zebra_dplane_pre_finish(void)
1720 if (IS_ZEBRA_DEBUG_DPLANE
)
1721 zlog_debug("Zebra dataplane pre-fini called");
1723 zdplane_info
.dg_is_shutdown
= true;
1725 /* TODO -- Notify provider(s) of pending shutdown */
1729 * Utility to determine whether work remains enqueued within the dplane;
1730 * used during system shutdown processing.
1732 static bool dplane_work_pending(void)
1735 struct zebra_dplane_ctx
*ctx
;
1736 struct zebra_dplane_provider
*prov
;
1738 /* TODO -- just checking incoming/pending work for now, must check
1743 ctx
= TAILQ_FIRST(&zdplane_info
.dg_route_ctx_q
);
1744 prov
= TAILQ_FIRST(&zdplane_info
.dg_providers_q
);
1755 dplane_provider_lock(prov
);
1757 ctx
= TAILQ_FIRST(&(prov
->dp_ctx_in_q
));
1759 ctx
= TAILQ_FIRST(&(prov
->dp_ctx_out_q
));
1761 dplane_provider_unlock(prov
);
1767 prov
= TAILQ_NEXT(prov
, dp_prov_link
);
1779 * Shutdown-time intermediate callback, used to determine when all pending
1780 * in-flight updates are done. If there's still work to do, reschedules itself.
1781 * If all work is done, schedules an event to the main zebra thread for
1782 * final zebra shutdown.
1783 * This runs in the dplane pthread context.
1785 static int dplane_check_shutdown_status(struct thread
*event
)
1787 if (IS_ZEBRA_DEBUG_DPLANE
)
1788 zlog_debug("Zebra dataplane shutdown status check called");
1790 if (dplane_work_pending()) {
1791 /* Reschedule dplane check on a short timer */
1792 thread_add_timer_msec(zdplane_info
.dg_master
,
1793 dplane_check_shutdown_status
,
1795 &zdplane_info
.dg_t_shutdown_check
);
1797 /* TODO - give up and stop waiting after a short time? */
1800 /* We appear to be done - schedule a final callback event
1801 * for the zebra main pthread.
1803 thread_add_event(zebrad
.master
, zebra_finalize
, NULL
, 0, NULL
);
1810 * Shutdown, de-init api. This runs pretty late during shutdown,
1811 * after zebra has tried to free/remove/uninstall all routes during shutdown.
1812 * At this point, dplane work may still remain to be done, so we can't just
1813 * blindly terminate. If there's still work to do, we'll periodically check
1814 * and when done, we'll enqueue a task to the zebra main thread for final
1815 * termination processing.
1817 * NB: This runs in the main zebra thread context.
1819 void zebra_dplane_finish(void)
1821 if (IS_ZEBRA_DEBUG_DPLANE
)
1822 zlog_debug("Zebra dataplane fini called");
1824 thread_add_event(zdplane_info
.dg_master
,
1825 dplane_check_shutdown_status
, NULL
, 0,
1826 &zdplane_info
.dg_t_shutdown_check
);
1830 * Main dataplane pthread event loop. The thread takes new incoming work
1831 * and offers it to the first provider. It then iterates through the
1832 * providers, taking complete work from each one and offering it
1833 * to the next in order. At each step, a limited number of updates are
1834 * processed during a cycle in order to provide some fairness.
1836 * This loop through the providers is only run once, so that the dataplane
1837 * pthread can look for other pending work - such as i/o work on behalf of
1840 static int dplane_thread_loop(struct thread
*event
)
1842 struct dplane_ctx_q work_list
;
1843 struct dplane_ctx_q error_list
;
1844 struct zebra_dplane_provider
*prov
;
1845 struct zebra_dplane_ctx
*ctx
, *tctx
;
1846 int limit
, counter
, error_counter
;
1847 uint64_t curr
, high
;
1849 /* Capture work limit per cycle */
1850 limit
= zdplane_info
.dg_updates_per_cycle
;
1852 /* Init temporary lists used to move contexts among providers */
1853 TAILQ_INIT(&work_list
);
1854 TAILQ_INIT(&error_list
);
1857 /* Check for zebra shutdown */
1858 if (!zdplane_info
.dg_run
)
1861 /* Dequeue some incoming work from zebra (if any) onto the temporary
1866 /* Locate initial registered provider */
1867 prov
= TAILQ_FIRST(&zdplane_info
.dg_providers_q
);
1869 /* Move new work from incoming list to temp list */
1870 for (counter
= 0; counter
< limit
; counter
++) {
1871 ctx
= TAILQ_FIRST(&zdplane_info
.dg_route_ctx_q
);
1873 TAILQ_REMOVE(&zdplane_info
.dg_route_ctx_q
, ctx
,
1876 ctx
->zd_provider
= prov
->dp_id
;
1878 TAILQ_INSERT_TAIL(&work_list
, ctx
, zd_q_entries
);
1886 atomic_fetch_sub_explicit(&zdplane_info
.dg_routes_queued
, counter
,
1887 memory_order_relaxed
);
1889 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1890 zlog_debug("dplane: incoming new work counter: %d", counter
);
1892 /* Iterate through the registered providers, offering new incoming
1893 * work. If the provider has outgoing work in its queue, take that
1894 * work for the next provider
1898 /* At each iteration, the temporary work list has 'counter'
1901 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1902 zlog_debug("dplane enqueues %d new work to provider '%s'",
1903 counter
, dplane_provider_get_name(prov
));
1905 /* Capture current provider id in each context; check for
1908 TAILQ_FOREACH_SAFE(ctx
, &work_list
, zd_q_entries
, tctx
) {
1909 if (dplane_ctx_get_status(ctx
) ==
1910 ZEBRA_DPLANE_REQUEST_SUCCESS
) {
1911 ctx
->zd_provider
= prov
->dp_id
;
1914 * TODO -- improve error-handling: recirc
1915 * errors backwards so that providers can
1916 * 'undo' their work (if they want to)
1919 /* Move to error list; will be returned
1922 TAILQ_REMOVE(&work_list
, ctx
, zd_q_entries
);
1923 TAILQ_INSERT_TAIL(&error_list
,
1929 /* Enqueue new work to the provider */
1930 dplane_provider_lock(prov
);
1932 if (TAILQ_FIRST(&work_list
))
1933 TAILQ_CONCAT(&(prov
->dp_ctx_in_q
), &work_list
,
1936 atomic_fetch_add_explicit(&prov
->dp_in_counter
, counter
,
1937 memory_order_relaxed
);
1938 atomic_fetch_add_explicit(&prov
->dp_in_queued
, counter
,
1939 memory_order_relaxed
);
1940 curr
= atomic_load_explicit(&prov
->dp_in_queued
,
1941 memory_order_relaxed
);
1942 high
= atomic_load_explicit(&prov
->dp_in_max
,
1943 memory_order_relaxed
);
1945 atomic_store_explicit(&prov
->dp_in_max
, curr
,
1946 memory_order_relaxed
);
1948 dplane_provider_unlock(prov
);
1950 /* Reset the temp list (though the 'concat' may have done this
1951 * already), and the counter
1953 TAILQ_INIT(&work_list
);
1956 /* Call into the provider code. Note that this is
1957 * unconditional: we offer to do work even if we don't enqueue
1960 (*prov
->dp_fp
)(prov
);
1962 /* Check for zebra shutdown */
1963 if (!zdplane_info
.dg_run
)
1966 /* Dequeue completed work from the provider */
1967 dplane_provider_lock(prov
);
1969 while (counter
< limit
) {
1970 ctx
= TAILQ_FIRST(&(prov
->dp_ctx_out_q
));
1972 TAILQ_REMOVE(&(prov
->dp_ctx_out_q
), ctx
,
1975 TAILQ_INSERT_TAIL(&work_list
,
1982 dplane_provider_unlock(prov
);
1984 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1985 zlog_debug("dplane dequeues %d completed work from provider %s",
1986 counter
, dplane_provider_get_name(prov
));
1988 /* Locate next provider */
1990 prov
= TAILQ_NEXT(prov
, dp_prov_link
);
1994 /* After all providers have been serviced, enqueue any completed
1995 * work and any errors back to zebra so it can process the results.
1997 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
)
1998 zlog_debug("dplane has %d completed, %d errors, for zebra main",
1999 counter
, error_counter
);
2002 * Hand lists through the api to zebra main,
2003 * to reduce the number of lock/unlock cycles
2006 /* Call through to zebra main */
2007 (zdplane_info
.dg_results_cb
)(&error_list
);
2009 TAILQ_INIT(&error_list
);
2012 /* Call through to zebra main */
2013 (zdplane_info
.dg_results_cb
)(&work_list
);
2015 TAILQ_INIT(&work_list
);
2022 * Final phase of shutdown, after all work enqueued to dplane has been
2023 * processed. This is called from the zebra main pthread context.
2025 void zebra_dplane_shutdown(void)
2027 if (IS_ZEBRA_DEBUG_DPLANE
)
2028 zlog_debug("Zebra dataplane shutdown called");
2030 /* Stop dplane thread, if it's running */
2032 zdplane_info
.dg_run
= false;
2034 THREAD_OFF(zdplane_info
.dg_t_update
);
2036 frr_pthread_stop(zdplane_info
.dg_pthread
, NULL
);
2038 /* Destroy pthread */
2039 frr_pthread_destroy(zdplane_info
.dg_pthread
);
2040 zdplane_info
.dg_pthread
= NULL
;
2041 zdplane_info
.dg_master
= NULL
;
2043 /* TODO -- Notify provider(s) of final shutdown */
2045 /* TODO -- Clean-up provider objects */
2047 /* TODO -- Clean queue(s), free memory */
2051 * Initialize the dataplane module during startup, internal/private version
2053 static void zebra_dplane_init_internal(struct zebra_t
*zebra
)
2055 memset(&zdplane_info
, 0, sizeof(zdplane_info
));
2057 pthread_mutex_init(&zdplane_info
.dg_mutex
, NULL
);
2059 TAILQ_INIT(&zdplane_info
.dg_route_ctx_q
);
2060 TAILQ_INIT(&zdplane_info
.dg_providers_q
);
2062 zdplane_info
.dg_updates_per_cycle
= DPLANE_DEFAULT_NEW_WORK
;
2064 zdplane_info
.dg_max_queued_updates
= DPLANE_DEFAULT_MAX_QUEUED
;
2066 /* Register default kernel 'provider' during init */
2067 dplane_provider_init();
2071 * Start the dataplane pthread. This step needs to be run later than the
2072 * 'init' step, in case zebra has fork-ed.
2074 void zebra_dplane_start(void)
2076 /* Start dataplane pthread */
2078 struct frr_pthread_attr pattr
= {
2079 .start
= frr_pthread_attr_default
.start
,
2080 .stop
= frr_pthread_attr_default
.stop
2083 zdplane_info
.dg_pthread
= frr_pthread_new(&pattr
, "Zebra dplane thread",
2086 zdplane_info
.dg_master
= zdplane_info
.dg_pthread
->master
;
2088 zdplane_info
.dg_run
= true;
2090 /* Enqueue an initial event for the dataplane pthread */
2091 thread_add_event(zdplane_info
.dg_master
, dplane_thread_loop
, NULL
, 0,
2092 &zdplane_info
.dg_t_update
);
2094 frr_pthread_run(zdplane_info
.dg_pthread
, NULL
);
2098 * Initialize the dataplane module at startup; called by zebra rib_init()
2100 void zebra_dplane_init(int (*results_fp
)(struct dplane_ctx_q
*))
2102 zebra_dplane_init_internal(&zebrad
);
2103 zdplane_info
.dg_results_cb
= results_fp
;