]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_dplane.c
df6188661b3098f56483b86e5bacbf29bd28700b
[mirror_frr.git] / zebra / zebra_dplane.c
1 /*
2 * Zebra dataplane layer.
3 * Copyright (c) 2018 Volta Networks, Inc.
4 *
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.
9 *
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.
14 *
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
18 */
19
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_router.h"
28 #include "zebra/zebra_memory.h"
29 #include "zebra/zebra_router.h"
30 #include "zebra/zebra_dplane.h"
31 #include "zebra/rt.h"
32 #include "zebra/debug.h"
33
34 /* Memory type for context blocks */
35 DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
36 DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider")
37
38 #ifndef AOK
39 # define AOK 0
40 #endif
41
42 /* Enable test dataplane provider */
43 /*#define DPLANE_TEST_PROVIDER 1 */
44
45 /* Default value for max queued incoming updates */
46 const uint32_t DPLANE_DEFAULT_MAX_QUEUED = 200;
47
48 /* Default value for new work per cycle */
49 const uint32_t DPLANE_DEFAULT_NEW_WORK = 100;
50
51 /* Validation check macro for context blocks */
52 /* #define DPLANE_DEBUG 1 */
53
54 #ifdef DPLANE_DEBUG
55
56 # define DPLANE_CTX_VALID(p) \
57 assert((p) != NULL)
58
59 #else
60
61 # define DPLANE_CTX_VALID(p)
62
63 #endif /* DPLANE_DEBUG */
64
65 /*
66 * Route information captured for route updates.
67 */
68 struct dplane_route_info {
69
70 /* Dest and (optional) source prefixes */
71 struct prefix zd_dest;
72 struct prefix zd_src;
73
74 afi_t zd_afi;
75 safi_t zd_safi;
76
77 int zd_type;
78 int zd_old_type;
79
80 route_tag_t zd_tag;
81 route_tag_t zd_old_tag;
82 uint32_t zd_metric;
83 uint32_t zd_old_metric;
84
85 uint16_t zd_instance;
86 uint16_t zd_old_instance;
87
88 uint8_t zd_distance;
89 uint8_t zd_old_distance;
90
91 uint32_t zd_mtu;
92 uint32_t zd_nexthop_mtu;
93
94 /* Nexthops */
95 struct nexthop_group zd_ng;
96
97 /* "Previous" nexthops, used only in route updates without netlink */
98 struct nexthop_group zd_old_ng;
99
100 /* TODO -- use fixed array of nexthops, to avoid mallocs? */
101
102 };
103
104 /*
105 * Pseudowire info for the dataplane
106 */
107 struct dplane_pw_info {
108 char ifname[IF_NAMESIZE];
109 ifindex_t ifindex;
110 int type;
111 int af;
112 int status;
113 uint32_t flags;
114 union g_addr dest;
115 mpls_label_t local_label;
116 mpls_label_t remote_label;
117
118 /* Nexthops */
119 struct nexthop_group nhg;
120
121 union pw_protocol_fields fields;
122 };
123
124 /*
125 * Interface/prefix info for the dataplane
126 */
127 struct dplane_intf_info {
128
129 char ifname[INTERFACE_NAMSIZ];
130 ifindex_t ifindex;
131
132 uint32_t metric;
133 uint32_t flags;
134
135 #define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
136 #define DPLANE_INTF_SECONDARY (1 << 1)
137 #define DPLANE_INTF_BROADCAST (1 << 2)
138 #define DPLANE_INTF_HAS_DEST (1 << 3)
139 #define DPLANE_INTF_HAS_LABEL (1 << 4)
140
141 /* Interface address/prefix */
142 struct prefix prefix;
143
144 /* Dest address, for p2p, or broadcast prefix */
145 struct prefix dest_prefix;
146
147 char *label;
148 char label_buf[32];
149 };
150
151 /*
152 * The context block used to exchange info about route updates across
153 * the boundary between the zebra main context (and pthread) and the
154 * dataplane layer (and pthread).
155 */
156 struct zebra_dplane_ctx {
157
158 /* Operation code */
159 enum dplane_op_e zd_op;
160
161 /* Status on return */
162 enum zebra_dplane_result zd_status;
163
164 /* Dplane provider id */
165 uint32_t zd_provider;
166
167 /* Flags - used by providers, e.g. */
168 int zd_flags;
169
170 bool zd_is_update;
171
172 uint32_t zd_seq;
173 uint32_t zd_old_seq;
174
175 /* Some updates may be generated by notifications: allow the
176 * plugin to notice and ignore results from its own notifications.
177 */
178 uint32_t zd_notif_provider;
179
180 /* TODO -- internal/sub-operation status? */
181 enum zebra_dplane_result zd_remote_status;
182 enum zebra_dplane_result zd_kernel_status;
183
184 vrf_id_t zd_vrf_id;
185 uint32_t zd_table_id;
186
187 /* Support info for different kinds of updates */
188 union {
189 struct dplane_route_info rinfo;
190 zebra_lsp_t lsp;
191 struct dplane_pw_info pw;
192 struct dplane_intf_info intf;
193 } u;
194
195 /* Namespace info, used especially for netlink kernel communication */
196 struct zebra_dplane_info zd_ns_info;
197
198 /* Embedded list linkage */
199 TAILQ_ENTRY(zebra_dplane_ctx) zd_q_entries;
200 };
201
202 /* Flag that can be set by a pre-kernel provider as a signal that an update
203 * should bypass the kernel.
204 */
205 #define DPLANE_CTX_FLAG_NO_KERNEL 0x01
206
207
208 /*
209 * Registration block for one dataplane provider.
210 */
211 struct zebra_dplane_provider {
212 /* Name */
213 char dp_name[DPLANE_PROVIDER_NAMELEN + 1];
214
215 /* Priority, for ordering among providers */
216 uint8_t dp_priority;
217
218 /* Id value */
219 uint32_t dp_id;
220
221 /* Mutex */
222 pthread_mutex_t dp_mutex;
223
224 /* Plugin-provided extra data */
225 void *dp_data;
226
227 /* Flags */
228 int dp_flags;
229
230 int (*dp_start)(struct zebra_dplane_provider *prov);
231
232 int (*dp_fp)(struct zebra_dplane_provider *prov);
233
234 int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
235
236 _Atomic uint32_t dp_in_counter;
237 _Atomic uint32_t dp_in_queued;
238 _Atomic uint32_t dp_in_max;
239 _Atomic uint32_t dp_out_counter;
240 _Atomic uint32_t dp_out_queued;
241 _Atomic uint32_t dp_out_max;
242 _Atomic uint32_t dp_error_counter;
243
244 /* Queue of contexts inbound to the provider */
245 struct dplane_ctx_q dp_ctx_in_q;
246
247 /* Queue of completed contexts outbound from the provider back
248 * towards the dataplane module.
249 */
250 struct dplane_ctx_q dp_ctx_out_q;
251
252 /* Embedded list linkage for provider objects */
253 TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link;
254 };
255
256 /*
257 * Globals
258 */
259 static struct zebra_dplane_globals {
260 /* Mutex to control access to dataplane components */
261 pthread_mutex_t dg_mutex;
262
263 /* Results callback registered by zebra 'core' */
264 int (*dg_results_cb)(struct dplane_ctx_q *ctxlist);
265
266 /* Sentinel for beginning of shutdown */
267 volatile bool dg_is_shutdown;
268
269 /* Sentinel for end of shutdown */
270 volatile bool dg_run;
271
272 /* Route-update context queue inbound to the dataplane */
273 TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q;
274
275 /* Ordered list of providers */
276 TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
277
278 /* Counter used to assign internal ids to providers */
279 uint32_t dg_provider_id;
280
281 /* Limit number of pending, unprocessed updates */
282 _Atomic uint32_t dg_max_queued_updates;
283
284 /* Control whether system route notifications should be produced. */
285 bool dg_sys_route_notifs;
286
287 /* Limit number of new updates dequeued at once, to pace an
288 * incoming burst.
289 */
290 uint32_t dg_updates_per_cycle;
291
292 _Atomic uint32_t dg_routes_in;
293 _Atomic uint32_t dg_routes_queued;
294 _Atomic uint32_t dg_routes_queued_max;
295 _Atomic uint32_t dg_route_errors;
296 _Atomic uint32_t dg_other_errors;
297
298 _Atomic uint32_t dg_lsps_in;
299 _Atomic uint32_t dg_lsp_errors;
300
301 _Atomic uint32_t dg_pws_in;
302 _Atomic uint32_t dg_pw_errors;
303
304 _Atomic uint32_t dg_intf_addrs_in;
305 _Atomic uint32_t dg_intf_addr_errors;
306
307 _Atomic uint32_t dg_update_yields;
308
309 /* Dataplane pthread */
310 struct frr_pthread *dg_pthread;
311
312 /* Event-delivery context 'master' for the dplane */
313 struct thread_master *dg_master;
314
315 /* Event/'thread' pointer for queued updates */
316 struct thread *dg_t_update;
317
318 /* Event pointer for pending shutdown check loop */
319 struct thread *dg_t_shutdown_check;
320
321 } zdplane_info;
322
323 /*
324 * Lock and unlock for interactions with the zebra 'core' pthread
325 */
326 #define DPLANE_LOCK() pthread_mutex_lock(&zdplane_info.dg_mutex)
327 #define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_info.dg_mutex)
328
329
330 /*
331 * Lock and unlock for individual providers
332 */
333 #define DPLANE_PROV_LOCK(p) pthread_mutex_lock(&((p)->dp_mutex))
334 #define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex))
335
336 /* Prototypes */
337 static int dplane_thread_loop(struct thread *event);
338 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
339 struct zebra_ns *zns);
340 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
341 enum dplane_op_e op);
342 static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
343 enum dplane_op_e op);
344 static enum zebra_dplane_result intf_addr_update_internal(
345 const struct interface *ifp, const struct connected *ifc,
346 enum dplane_op_e op);
347
348 /*
349 * Public APIs
350 */
351
352 /* Obtain thread_master for dataplane thread */
353 struct thread_master *dplane_get_thread_master(void)
354 {
355 return zdplane_info.dg_master;
356 }
357
358 /*
359 * Allocate a dataplane update context
360 */
361 struct zebra_dplane_ctx *dplane_ctx_alloc(void)
362 {
363 struct zebra_dplane_ctx *p;
364
365 /* TODO -- just alloc'ing memory, but would like to maintain
366 * a pool
367 */
368 p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx));
369
370 return p;
371 }
372
373 /* Enable system route notifications */
374 void dplane_enable_sys_route_notifs(void)
375 {
376 zdplane_info.dg_sys_route_notifs = true;
377 }
378
379 /*
380 * Free a dataplane results context.
381 */
382 static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
383 {
384 if (pctx == NULL)
385 return;
386
387 DPLANE_CTX_VALID(*pctx);
388
389 /* TODO -- just freeing memory, but would like to maintain
390 * a pool
391 */
392
393 /* Some internal allocations may need to be freed, depending on
394 * the type of info captured in the ctx.
395 */
396 switch ((*pctx)->zd_op) {
397 case DPLANE_OP_ROUTE_INSTALL:
398 case DPLANE_OP_ROUTE_UPDATE:
399 case DPLANE_OP_ROUTE_DELETE:
400 case DPLANE_OP_SYS_ROUTE_ADD:
401 case DPLANE_OP_SYS_ROUTE_DELETE:
402 case DPLANE_OP_ROUTE_NOTIFY:
403
404 /* Free allocated nexthops */
405 if ((*pctx)->u.rinfo.zd_ng.nexthop) {
406 /* This deals with recursive nexthops too */
407 nexthops_free((*pctx)->u.rinfo.zd_ng.nexthop);
408
409 (*pctx)->u.rinfo.zd_ng.nexthop = NULL;
410 }
411
412 if ((*pctx)->u.rinfo.zd_old_ng.nexthop) {
413 /* This deals with recursive nexthops too */
414 nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop);
415
416 (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL;
417 }
418
419 break;
420
421 case DPLANE_OP_LSP_INSTALL:
422 case DPLANE_OP_LSP_UPDATE:
423 case DPLANE_OP_LSP_DELETE:
424 {
425 zebra_nhlfe_t *nhlfe, *next;
426
427 /* Free allocated NHLFEs */
428 for (nhlfe = (*pctx)->u.lsp.nhlfe_list; nhlfe; nhlfe = next) {
429 next = nhlfe->next;
430
431 zebra_mpls_nhlfe_del(nhlfe);
432 }
433
434 /* Clear pointers in lsp struct, in case we're cacheing
435 * free context structs.
436 */
437 (*pctx)->u.lsp.nhlfe_list = NULL;
438 (*pctx)->u.lsp.best_nhlfe = NULL;
439
440 break;
441 }
442
443 case DPLANE_OP_PW_INSTALL:
444 case DPLANE_OP_PW_UNINSTALL:
445 /* Free allocated nexthops */
446 if ((*pctx)->u.pw.nhg.nexthop) {
447 /* This deals with recursive nexthops too */
448 nexthops_free((*pctx)->u.pw.nhg.nexthop);
449
450 (*pctx)->u.pw.nhg.nexthop = NULL;
451 }
452 break;
453
454 case DPLANE_OP_ADDR_INSTALL:
455 case DPLANE_OP_ADDR_UNINSTALL:
456 /* Maybe free label string, if allocated */
457 if ((*pctx)->u.intf.label != NULL &&
458 (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) {
459 free((*pctx)->u.intf.label);
460 (*pctx)->u.intf.label = NULL;
461 }
462 break;
463
464 case DPLANE_OP_NONE:
465 break;
466 }
467
468 XFREE(MTYPE_DP_CTX, *pctx);
469 *pctx = NULL;
470 }
471
472 /*
473 * Return a context block to the dplane module after processing
474 */
475 void dplane_ctx_fini(struct zebra_dplane_ctx **pctx)
476 {
477 /* TODO -- maintain pool; for now, just free */
478 dplane_ctx_free(pctx);
479 }
480
481 /* Enqueue a context block */
482 void dplane_ctx_enqueue_tail(struct dplane_ctx_q *q,
483 const struct zebra_dplane_ctx *ctx)
484 {
485 TAILQ_INSERT_TAIL(q, (struct zebra_dplane_ctx *)ctx, zd_q_entries);
486 }
487
488 /* Append a list of context blocks to another list */
489 void dplane_ctx_list_append(struct dplane_ctx_q *to_list,
490 struct dplane_ctx_q *from_list)
491 {
492 if (TAILQ_FIRST(from_list)) {
493 TAILQ_CONCAT(to_list, from_list, zd_q_entries);
494
495 /* And clear 'from' list */
496 TAILQ_INIT(from_list);
497 }
498 }
499
500 /* Dequeue a context block from the head of a list */
501 struct zebra_dplane_ctx *dplane_ctx_dequeue(struct dplane_ctx_q *q)
502 {
503 struct zebra_dplane_ctx *ctx = TAILQ_FIRST(q);
504
505 if (ctx)
506 TAILQ_REMOVE(q, ctx, zd_q_entries);
507
508 return ctx;
509 }
510
511 /*
512 * Accessors for information from the context object
513 */
514 enum zebra_dplane_result dplane_ctx_get_status(
515 const struct zebra_dplane_ctx *ctx)
516 {
517 DPLANE_CTX_VALID(ctx);
518
519 return ctx->zd_status;
520 }
521
522 void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx,
523 enum zebra_dplane_result status)
524 {
525 DPLANE_CTX_VALID(ctx);
526
527 ctx->zd_status = status;
528 }
529
530 /* Retrieve last/current provider id */
531 uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx)
532 {
533 DPLANE_CTX_VALID(ctx);
534 return ctx->zd_provider;
535 }
536
537 /* Providers run before the kernel can control whether a kernel
538 * update should be done.
539 */
540 void dplane_ctx_set_skip_kernel(struct zebra_dplane_ctx *ctx)
541 {
542 DPLANE_CTX_VALID(ctx);
543
544 SET_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
545 }
546
547 bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx)
548 {
549 DPLANE_CTX_VALID(ctx);
550
551 return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
552 }
553
554 void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
555 {
556 DPLANE_CTX_VALID(ctx);
557 ctx->zd_op = op;
558 }
559
560 enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx)
561 {
562 DPLANE_CTX_VALID(ctx);
563
564 return ctx->zd_op;
565 }
566
567 const char *dplane_op2str(enum dplane_op_e op)
568 {
569 const char *ret = "UNKNOWN";
570
571 switch (op) {
572 case DPLANE_OP_NONE:
573 ret = "NONE";
574 break;
575
576 /* Route update */
577 case DPLANE_OP_ROUTE_INSTALL:
578 ret = "ROUTE_INSTALL";
579 break;
580 case DPLANE_OP_ROUTE_UPDATE:
581 ret = "ROUTE_UPDATE";
582 break;
583 case DPLANE_OP_ROUTE_DELETE:
584 ret = "ROUTE_DELETE";
585 break;
586 case DPLANE_OP_ROUTE_NOTIFY:
587 ret = "ROUTE_NOTIFY";
588 break;
589
590 case DPLANE_OP_LSP_INSTALL:
591 ret = "LSP_INSTALL";
592 break;
593 case DPLANE_OP_LSP_UPDATE:
594 ret = "LSP_UPDATE";
595 break;
596 case DPLANE_OP_LSP_DELETE:
597 ret = "LSP_DELETE";
598 break;
599
600 case DPLANE_OP_PW_INSTALL:
601 ret = "PW_INSTALL";
602 break;
603 case DPLANE_OP_PW_UNINSTALL:
604 ret = "PW_UNINSTALL";
605 break;
606
607 case DPLANE_OP_SYS_ROUTE_ADD:
608 ret = "SYS_ROUTE_ADD";
609 break;
610 case DPLANE_OP_SYS_ROUTE_DELETE:
611 ret = "SYS_ROUTE_DEL";
612 break;
613
614 case DPLANE_OP_ADDR_INSTALL:
615 ret = "ADDR_INSTALL";
616 break;
617 case DPLANE_OP_ADDR_UNINSTALL:
618 ret = "ADDR_UNINSTALL";
619 break;
620
621 }
622
623 return ret;
624 }
625
626 const char *dplane_res2str(enum zebra_dplane_result res)
627 {
628 const char *ret = "<Unknown>";
629
630 switch (res) {
631 case ZEBRA_DPLANE_REQUEST_FAILURE:
632 ret = "FAILURE";
633 break;
634 case ZEBRA_DPLANE_REQUEST_QUEUED:
635 ret = "QUEUED";
636 break;
637 case ZEBRA_DPLANE_REQUEST_SUCCESS:
638 ret = "SUCCESS";
639 break;
640 }
641
642 return ret;
643 }
644
645 void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
646 const struct prefix *dest)
647 {
648 DPLANE_CTX_VALID(ctx);
649
650 prefix_copy(&(ctx->u.rinfo.zd_dest), dest);
651 }
652
653 const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
654 {
655 DPLANE_CTX_VALID(ctx);
656
657 return &(ctx->u.rinfo.zd_dest);
658 }
659
660 void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src)
661 {
662 DPLANE_CTX_VALID(ctx);
663
664 if (src)
665 prefix_copy(&(ctx->u.rinfo.zd_src), src);
666 else
667 memset(&(ctx->u.rinfo.zd_src), 0, sizeof(struct prefix));
668 }
669
670 /* Source prefix is a little special - return NULL for "no src prefix" */
671 const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx)
672 {
673 DPLANE_CTX_VALID(ctx);
674
675 if (ctx->u.rinfo.zd_src.prefixlen == 0 &&
676 IN6_IS_ADDR_UNSPECIFIED(&(ctx->u.rinfo.zd_src.u.prefix6))) {
677 return NULL;
678 } else {
679 return &(ctx->u.rinfo.zd_src);
680 }
681 }
682
683 bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx)
684 {
685 DPLANE_CTX_VALID(ctx);
686
687 return ctx->zd_is_update;
688 }
689
690 uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx)
691 {
692 DPLANE_CTX_VALID(ctx);
693
694 return ctx->zd_seq;
695 }
696
697 uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx)
698 {
699 DPLANE_CTX_VALID(ctx);
700
701 return ctx->zd_old_seq;
702 }
703
704 void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf)
705 {
706 DPLANE_CTX_VALID(ctx);
707
708 ctx->zd_vrf_id = vrf;
709 }
710
711 vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
712 {
713 DPLANE_CTX_VALID(ctx);
714
715 return ctx->zd_vrf_id;
716 }
717
718 bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx)
719 {
720 DPLANE_CTX_VALID(ctx);
721
722 return (ctx->zd_notif_provider != 0);
723 }
724
725 uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx)
726 {
727 DPLANE_CTX_VALID(ctx);
728
729 return ctx->zd_notif_provider;
730 }
731
732 void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
733 uint32_t id)
734 {
735 DPLANE_CTX_VALID(ctx);
736
737 ctx->zd_notif_provider = id;
738 }
739
740 void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
741 {
742 DPLANE_CTX_VALID(ctx);
743
744 ctx->u.rinfo.zd_type = type;
745 }
746
747 int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx)
748 {
749 DPLANE_CTX_VALID(ctx);
750
751 return ctx->u.rinfo.zd_type;
752 }
753
754 int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx)
755 {
756 DPLANE_CTX_VALID(ctx);
757
758 return ctx->u.rinfo.zd_old_type;
759 }
760
761 void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi)
762 {
763 DPLANE_CTX_VALID(ctx);
764
765 ctx->u.rinfo.zd_afi = afi;
766 }
767
768 afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
769 {
770 DPLANE_CTX_VALID(ctx);
771
772 return ctx->u.rinfo.zd_afi;
773 }
774
775 void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi)
776 {
777 DPLANE_CTX_VALID(ctx);
778
779 ctx->u.rinfo.zd_safi = safi;
780 }
781
782 safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
783 {
784 DPLANE_CTX_VALID(ctx);
785
786 return ctx->u.rinfo.zd_safi;
787 }
788
789 void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table)
790 {
791 DPLANE_CTX_VALID(ctx);
792
793 ctx->zd_table_id = table;
794 }
795
796 uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx)
797 {
798 DPLANE_CTX_VALID(ctx);
799
800 return ctx->zd_table_id;
801 }
802
803 route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx)
804 {
805 DPLANE_CTX_VALID(ctx);
806
807 return ctx->u.rinfo.zd_tag;
808 }
809
810 route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx)
811 {
812 DPLANE_CTX_VALID(ctx);
813
814 return ctx->u.rinfo.zd_old_tag;
815 }
816
817 uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx)
818 {
819 DPLANE_CTX_VALID(ctx);
820
821 return ctx->u.rinfo.zd_instance;
822 }
823
824 uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
825 {
826 DPLANE_CTX_VALID(ctx);
827
828 return ctx->u.rinfo.zd_old_instance;
829 }
830
831 uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx)
832 {
833 DPLANE_CTX_VALID(ctx);
834
835 return ctx->u.rinfo.zd_metric;
836 }
837
838 uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx)
839 {
840 DPLANE_CTX_VALID(ctx);
841
842 return ctx->u.rinfo.zd_old_metric;
843 }
844
845 uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx)
846 {
847 DPLANE_CTX_VALID(ctx);
848
849 return ctx->u.rinfo.zd_mtu;
850 }
851
852 uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx)
853 {
854 DPLANE_CTX_VALID(ctx);
855
856 return ctx->u.rinfo.zd_nexthop_mtu;
857 }
858
859 uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx)
860 {
861 DPLANE_CTX_VALID(ctx);
862
863 return ctx->u.rinfo.zd_distance;
864 }
865
866 uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
867 {
868 DPLANE_CTX_VALID(ctx);
869
870 return ctx->u.rinfo.zd_old_distance;
871 }
872
873 void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
874 {
875 DPLANE_CTX_VALID(ctx);
876
877 if (ctx->u.rinfo.zd_ng.nexthop) {
878 nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
879 ctx->u.rinfo.zd_ng.nexthop = NULL;
880 }
881 copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL);
882 }
883
884 const struct nexthop_group *dplane_ctx_get_ng(
885 const struct zebra_dplane_ctx *ctx)
886 {
887 DPLANE_CTX_VALID(ctx);
888
889 return &(ctx->u.rinfo.zd_ng);
890 }
891
892 const struct nexthop_group *dplane_ctx_get_old_ng(
893 const struct zebra_dplane_ctx *ctx)
894 {
895 DPLANE_CTX_VALID(ctx);
896
897 return &(ctx->u.rinfo.zd_old_ng);
898 }
899
900 const struct zebra_dplane_info *dplane_ctx_get_ns(
901 const struct zebra_dplane_ctx *ctx)
902 {
903 DPLANE_CTX_VALID(ctx);
904
905 return &(ctx->zd_ns_info);
906 }
907
908 /* Accessors for LSP information */
909
910 mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
911 {
912 DPLANE_CTX_VALID(ctx);
913
914 return ctx->u.lsp.ile.in_label;
915 }
916
917 void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label)
918 {
919 DPLANE_CTX_VALID(ctx);
920
921 ctx->u.lsp.ile.in_label = label;
922 }
923
924 uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
925 {
926 DPLANE_CTX_VALID(ctx);
927
928 return ctx->u.lsp.addr_family;
929 }
930
931 void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx,
932 uint8_t family)
933 {
934 DPLANE_CTX_VALID(ctx);
935
936 ctx->u.lsp.addr_family = family;
937 }
938
939 uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
940 {
941 DPLANE_CTX_VALID(ctx);
942
943 return ctx->u.lsp.flags;
944 }
945
946 void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx,
947 uint32_t flags)
948 {
949 DPLANE_CTX_VALID(ctx);
950
951 ctx->u.lsp.flags = flags;
952 }
953
954 const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx)
955 {
956 DPLANE_CTX_VALID(ctx);
957
958 return ctx->u.lsp.nhlfe_list;
959 }
960
961 zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,
962 enum lsp_types_t lsp_type,
963 enum nexthop_types_t nh_type,
964 union g_addr *gate,
965 ifindex_t ifindex,
966 mpls_label_t out_label)
967 {
968 zebra_nhlfe_t *nhlfe;
969
970 DPLANE_CTX_VALID(ctx);
971
972 nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp),
973 lsp_type, nh_type, gate,
974 ifindex, out_label);
975
976 return nhlfe;
977 }
978
979 const zebra_nhlfe_t *
980 dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx)
981 {
982 DPLANE_CTX_VALID(ctx);
983
984 return ctx->u.lsp.best_nhlfe;
985 }
986
987 const zebra_nhlfe_t *
988 dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
989 zebra_nhlfe_t *nhlfe)
990 {
991 DPLANE_CTX_VALID(ctx);
992
993 ctx->u.lsp.best_nhlfe = nhlfe;
994 return ctx->u.lsp.best_nhlfe;
995 }
996
997 uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
998 {
999 DPLANE_CTX_VALID(ctx);
1000
1001 return ctx->u.lsp.num_ecmp;
1002 }
1003
1004 const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx)
1005 {
1006 DPLANE_CTX_VALID(ctx);
1007
1008 return ctx->u.pw.ifname;
1009 }
1010
1011 mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx)
1012 {
1013 DPLANE_CTX_VALID(ctx);
1014
1015 return ctx->u.pw.local_label;
1016 }
1017
1018 mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx)
1019 {
1020 DPLANE_CTX_VALID(ctx);
1021
1022 return ctx->u.pw.remote_label;
1023 }
1024
1025 int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx)
1026 {
1027 DPLANE_CTX_VALID(ctx);
1028
1029 return ctx->u.pw.type;
1030 }
1031
1032 int dplane_ctx_get_pw_af(const struct zebra_dplane_ctx *ctx)
1033 {
1034 DPLANE_CTX_VALID(ctx);
1035
1036 return ctx->u.pw.af;
1037 }
1038
1039 uint32_t dplane_ctx_get_pw_flags(const struct zebra_dplane_ctx *ctx)
1040 {
1041 DPLANE_CTX_VALID(ctx);
1042
1043 return ctx->u.pw.flags;
1044 }
1045
1046 int dplane_ctx_get_pw_status(const struct zebra_dplane_ctx *ctx)
1047 {
1048 DPLANE_CTX_VALID(ctx);
1049
1050 return ctx->u.pw.status;
1051 }
1052
1053 const union g_addr *dplane_ctx_get_pw_dest(
1054 const struct zebra_dplane_ctx *ctx)
1055 {
1056 DPLANE_CTX_VALID(ctx);
1057
1058 return &(ctx->u.pw.dest);
1059 }
1060
1061 const union pw_protocol_fields *dplane_ctx_get_pw_proto(
1062 const struct zebra_dplane_ctx *ctx)
1063 {
1064 DPLANE_CTX_VALID(ctx);
1065
1066 return &(ctx->u.pw.fields);
1067 }
1068
1069 const struct nexthop_group *
1070 dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx)
1071 {
1072 DPLANE_CTX_VALID(ctx);
1073
1074 return &(ctx->u.pw.nhg);
1075 }
1076
1077 /* Accessors for interface information */
1078 const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx)
1079 {
1080 DPLANE_CTX_VALID(ctx);
1081
1082 return ctx->u.intf.ifname;
1083 }
1084
1085 ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx)
1086 {
1087 DPLANE_CTX_VALID(ctx);
1088
1089 return ctx->u.intf.ifindex;
1090 }
1091
1092 uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx)
1093 {
1094 DPLANE_CTX_VALID(ctx);
1095
1096 return ctx->u.intf.metric;
1097 }
1098
1099 /* Is interface addr p2p? */
1100 bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
1101 {
1102 DPLANE_CTX_VALID(ctx);
1103
1104 return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED);
1105 }
1106
1107 bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx)
1108 {
1109 DPLANE_CTX_VALID(ctx);
1110
1111 return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY);
1112 }
1113
1114 bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx)
1115 {
1116 DPLANE_CTX_VALID(ctx);
1117
1118 return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST);
1119 }
1120
1121 const struct prefix *dplane_ctx_get_intf_addr(
1122 const struct zebra_dplane_ctx *ctx)
1123 {
1124 DPLANE_CTX_VALID(ctx);
1125
1126 return &(ctx->u.intf.prefix);
1127 }
1128
1129 bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx)
1130 {
1131 DPLANE_CTX_VALID(ctx);
1132
1133 return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST);
1134 }
1135
1136 const struct prefix *dplane_ctx_get_intf_dest(
1137 const struct zebra_dplane_ctx *ctx)
1138 {
1139 DPLANE_CTX_VALID(ctx);
1140
1141 if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST)
1142 return &(ctx->u.intf.dest_prefix);
1143 else
1144 return NULL;
1145 }
1146
1147 bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx)
1148 {
1149 DPLANE_CTX_VALID(ctx);
1150
1151 return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL);
1152 }
1153
1154 const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx)
1155 {
1156 DPLANE_CTX_VALID(ctx);
1157
1158 return ctx->u.intf.label;
1159 }
1160
1161 /*
1162 * End of dplane context accessors
1163 */
1164
1165
1166 /*
1167 * Retrieve the limit on the number of pending, unprocessed updates.
1168 */
1169 uint32_t dplane_get_in_queue_limit(void)
1170 {
1171 return atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
1172 memory_order_relaxed);
1173 }
1174
1175 /*
1176 * Configure limit on the number of pending, queued updates.
1177 */
1178 void dplane_set_in_queue_limit(uint32_t limit, bool set)
1179 {
1180 /* Reset to default on 'unset' */
1181 if (!set)
1182 limit = DPLANE_DEFAULT_MAX_QUEUED;
1183
1184 atomic_store_explicit(&zdplane_info.dg_max_queued_updates, limit,
1185 memory_order_relaxed);
1186 }
1187
1188 /*
1189 * Retrieve the current queue depth of incoming, unprocessed updates
1190 */
1191 uint32_t dplane_get_in_queue_len(void)
1192 {
1193 return atomic_load_explicit(&zdplane_info.dg_routes_queued,
1194 memory_order_seq_cst);
1195 }
1196
1197 /*
1198 * Common dataplane context init with zebra namespace info.
1199 */
1200 static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
1201 struct zebra_ns *zns,
1202 bool is_update)
1203 {
1204 dplane_info_from_zns(&(ctx->zd_ns_info), zns);
1205
1206 #if defined(HAVE_NETLINK)
1207 /* Increment message counter after copying to context struct - may need
1208 * two messages in some 'update' cases.
1209 */
1210 if (is_update)
1211 zns->netlink_dplane.seq += 2;
1212 else
1213 zns->netlink_dplane.seq++;
1214 #endif /* HAVE_NETLINK */
1215
1216 return AOK;
1217 }
1218
1219 /*
1220 * Initialize a context block for a route update from zebra data structs.
1221 */
1222 static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
1223 enum dplane_op_e op,
1224 struct route_node *rn,
1225 struct route_entry *re)
1226 {
1227 int ret = EINVAL;
1228 const struct route_table *table = NULL;
1229 const rib_table_info_t *info;
1230 const struct prefix *p, *src_p;
1231 struct zebra_ns *zns;
1232 struct zebra_vrf *zvrf;
1233 struct nexthop *nexthop;
1234
1235 if (!ctx || !rn || !re)
1236 goto done;
1237
1238 ctx->zd_op = op;
1239 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1240
1241 ctx->u.rinfo.zd_type = re->type;
1242 ctx->u.rinfo.zd_old_type = re->type;
1243
1244 /* Prefixes: dest, and optional source */
1245 srcdest_rnode_prefixes(rn, &p, &src_p);
1246
1247 prefix_copy(&(ctx->u.rinfo.zd_dest), p);
1248
1249 if (src_p)
1250 prefix_copy(&(ctx->u.rinfo.zd_src), src_p);
1251 else
1252 memset(&(ctx->u.rinfo.zd_src), 0, sizeof(ctx->u.rinfo.zd_src));
1253
1254 ctx->zd_table_id = re->table;
1255
1256 ctx->u.rinfo.zd_metric = re->metric;
1257 ctx->u.rinfo.zd_old_metric = re->metric;
1258 ctx->zd_vrf_id = re->vrf_id;
1259 ctx->u.rinfo.zd_mtu = re->mtu;
1260 ctx->u.rinfo.zd_nexthop_mtu = re->nexthop_mtu;
1261 ctx->u.rinfo.zd_instance = re->instance;
1262 ctx->u.rinfo.zd_tag = re->tag;
1263 ctx->u.rinfo.zd_old_tag = re->tag;
1264 ctx->u.rinfo.zd_distance = re->distance;
1265
1266 table = srcdest_rnode_table(rn);
1267 info = table->info;
1268
1269 ctx->u.rinfo.zd_afi = info->afi;
1270 ctx->u.rinfo.zd_safi = info->safi;
1271
1272 /* Copy nexthops; recursive info is included too */
1273 copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL);
1274
1275 /* Ensure that the dplane's nexthops flags are clear. */
1276 for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
1277 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
1278
1279 /* Don't need some info when capturing a system notification */
1280 if (op == DPLANE_OP_SYS_ROUTE_ADD ||
1281 op == DPLANE_OP_SYS_ROUTE_DELETE) {
1282 ret = AOK;
1283 goto done;
1284 }
1285
1286 /* Extract ns info - can't use pointers to 'core' structs */
1287 zvrf = vrf_info_lookup(re->vrf_id);
1288 zns = zvrf->zns;
1289 dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
1290
1291 /* Trying out the sequence number idea, so we can try to detect
1292 * when a result is stale.
1293 */
1294 re->dplane_sequence = zebra_router_get_next_sequence();
1295 ctx->zd_seq = re->dplane_sequence;
1296
1297 ret = AOK;
1298
1299 done:
1300 return ret;
1301 }
1302
1303 /*
1304 * Capture information for an LSP update in a dplane context.
1305 */
1306 static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx,
1307 enum dplane_op_e op,
1308 zebra_lsp_t *lsp)
1309 {
1310 int ret = AOK;
1311 zebra_nhlfe_t *nhlfe, *new_nhlfe;
1312
1313 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1314 zlog_debug("init dplane ctx %s: in-label %u ecmp# %d",
1315 dplane_op2str(op), lsp->ile.in_label,
1316 lsp->num_ecmp);
1317
1318 ctx->zd_op = op;
1319 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1320
1321 /* Capture namespace info */
1322 dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
1323 (op == DPLANE_OP_LSP_UPDATE));
1324
1325 memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
1326
1327 ctx->u.lsp.ile = lsp->ile;
1328 ctx->u.lsp.addr_family = lsp->addr_family;
1329 ctx->u.lsp.num_ecmp = lsp->num_ecmp;
1330 ctx->u.lsp.flags = lsp->flags;
1331
1332 /* Copy source LSP's nhlfes, and capture 'best' nhlfe */
1333 for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
1334 /* Not sure if this is meaningful... */
1335 if (nhlfe->nexthop == NULL)
1336 continue;
1337
1338 new_nhlfe =
1339 zebra_mpls_lsp_add_nhlfe(
1340 &(ctx->u.lsp),
1341 nhlfe->type,
1342 nhlfe->nexthop->type,
1343 &(nhlfe->nexthop->gate),
1344 nhlfe->nexthop->ifindex,
1345 nhlfe->nexthop->nh_label->label[0]);
1346
1347 if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) {
1348 ret = ENOMEM;
1349 break;
1350 }
1351
1352 /* Need to copy flags too */
1353 new_nhlfe->flags = nhlfe->flags;
1354 new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
1355
1356 if (nhlfe == lsp->best_nhlfe)
1357 ctx->u.lsp.best_nhlfe = new_nhlfe;
1358 }
1359
1360 /* On error the ctx will be cleaned-up, so we don't need to
1361 * deal with any allocated nhlfe or nexthop structs here.
1362 */
1363
1364 return ret;
1365 }
1366
1367 /*
1368 * Capture information for an LSP update in a dplane context.
1369 */
1370 static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
1371 enum dplane_op_e op,
1372 struct zebra_pw *pw)
1373 {
1374 struct prefix p;
1375 afi_t afi;
1376 struct route_table *table;
1377 struct route_node *rn;
1378 struct route_entry *re;
1379
1380 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1381 zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u",
1382 dplane_op2str(op), pw->ifname, pw->local_label,
1383 pw->remote_label);
1384
1385 ctx->zd_op = op;
1386 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1387
1388 /* Capture namespace info: no netlink support as of 12/18,
1389 * but just in case...
1390 */
1391 dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), false);
1392
1393 memset(&ctx->u.pw, 0, sizeof(ctx->u.pw));
1394
1395 /* This name appears to be c-string, so we use string copy. */
1396 strlcpy(ctx->u.pw.ifname, pw->ifname, sizeof(ctx->u.pw.ifname));
1397
1398 ctx->zd_vrf_id = pw->vrf_id;
1399 ctx->u.pw.ifindex = pw->ifindex;
1400 ctx->u.pw.type = pw->type;
1401 ctx->u.pw.af = pw->af;
1402 ctx->u.pw.local_label = pw->local_label;
1403 ctx->u.pw.remote_label = pw->remote_label;
1404 ctx->u.pw.flags = pw->flags;
1405
1406 ctx->u.pw.dest = pw->nexthop;
1407
1408 ctx->u.pw.fields = pw->data;
1409
1410 /* Capture nexthop info for the pw destination. We need to look
1411 * up and use zebra datastructs, but we're running in the zebra
1412 * pthread here so that should be ok.
1413 */
1414 memcpy(&p.u, &pw->nexthop, sizeof(pw->nexthop));
1415 p.family = pw->af;
1416 p.prefixlen = ((pw->af == AF_INET) ?
1417 IPV4_MAX_PREFIXLEN : IPV6_MAX_PREFIXLEN);
1418
1419 afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
1420 table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
1421 if (table) {
1422 rn = route_node_match(table, &p);
1423 if (rn) {
1424 RNODE_FOREACH_RE(rn, re) {
1425 if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
1426 break;
1427 }
1428
1429 if (re)
1430 copy_nexthops(&(ctx->u.pw.nhg.nexthop),
1431 re->ng.nexthop, NULL);
1432
1433 route_unlock_node(rn);
1434 }
1435 }
1436
1437 return AOK;
1438 }
1439
1440 /*
1441 * Enqueue a new route update,
1442 * and ensure an event is active for the dataplane pthread.
1443 */
1444 static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
1445 {
1446 int ret = EINVAL;
1447 uint32_t high, curr;
1448
1449 /* Enqueue for processing by the dataplane pthread */
1450 DPLANE_LOCK();
1451 {
1452 TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx,
1453 zd_q_entries);
1454 }
1455 DPLANE_UNLOCK();
1456
1457 curr = atomic_add_fetch_explicit(
1458 #ifdef __clang__
1459 /* TODO -- issue with the clang atomic/intrinsics currently;
1460 * casting away the 'Atomic'-ness of the variable works.
1461 */
1462 (uint32_t *)&(zdplane_info.dg_routes_queued),
1463 #else
1464 &(zdplane_info.dg_routes_queued),
1465 #endif
1466 1, memory_order_seq_cst);
1467
1468 /* Maybe update high-water counter also */
1469 high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
1470 memory_order_seq_cst);
1471 while (high < curr) {
1472 if (atomic_compare_exchange_weak_explicit(
1473 &zdplane_info.dg_routes_queued_max,
1474 &high, curr,
1475 memory_order_seq_cst,
1476 memory_order_seq_cst))
1477 break;
1478 }
1479
1480 /* Ensure that an event for the dataplane thread is active */
1481 ret = dplane_provider_work_ready();
1482
1483 return ret;
1484 }
1485
1486 /*
1487 * Utility that prepares a route update and enqueues it for processing
1488 */
1489 static enum zebra_dplane_result
1490 dplane_route_update_internal(struct route_node *rn,
1491 struct route_entry *re,
1492 struct route_entry *old_re,
1493 enum dplane_op_e op)
1494 {
1495 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1496 int ret = EINVAL;
1497 struct zebra_dplane_ctx *ctx = NULL;
1498
1499 /* Obtain context block */
1500 ctx = dplane_ctx_alloc();
1501
1502 /* Init context with info from zebra data structs */
1503 ret = dplane_ctx_route_init(ctx, op, rn, re);
1504 if (ret == AOK) {
1505 /* Capture some extra info for update case
1506 * where there's a different 'old' route.
1507 */
1508 if ((op == DPLANE_OP_ROUTE_UPDATE) &&
1509 old_re && (old_re != re)) {
1510 ctx->zd_is_update = true;
1511
1512 old_re->dplane_sequence =
1513 zebra_router_get_next_sequence();
1514 ctx->zd_old_seq = old_re->dplane_sequence;
1515
1516 ctx->u.rinfo.zd_old_tag = old_re->tag;
1517 ctx->u.rinfo.zd_old_type = old_re->type;
1518 ctx->u.rinfo.zd_old_instance = old_re->instance;
1519 ctx->u.rinfo.zd_old_distance = old_re->distance;
1520 ctx->u.rinfo.zd_old_metric = old_re->metric;
1521
1522 #ifndef HAVE_NETLINK
1523 /* For bsd, capture previous re's nexthops too, sigh.
1524 * We'll need these to do per-nexthop deletes.
1525 */
1526 copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
1527 old_re->ng.nexthop, NULL);
1528 #endif /* !HAVE_NETLINK */
1529 }
1530
1531 /* Enqueue context for processing */
1532 ret = dplane_route_enqueue(ctx);
1533 }
1534
1535 /* Update counter */
1536 atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1,
1537 memory_order_relaxed);
1538
1539 if (ret == AOK)
1540 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1541 else {
1542 atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
1543 memory_order_relaxed);
1544 if (ctx)
1545 dplane_ctx_free(&ctx);
1546 }
1547
1548 return result;
1549 }
1550
1551 /*
1552 * Enqueue a route 'add' for the dataplane.
1553 */
1554 enum zebra_dplane_result dplane_route_add(struct route_node *rn,
1555 struct route_entry *re)
1556 {
1557 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1558
1559 if (rn == NULL || re == NULL)
1560 goto done;
1561
1562 ret = dplane_route_update_internal(rn, re, NULL,
1563 DPLANE_OP_ROUTE_INSTALL);
1564
1565 done:
1566 return ret;
1567 }
1568
1569 /*
1570 * Enqueue a route update for the dataplane.
1571 */
1572 enum zebra_dplane_result dplane_route_update(struct route_node *rn,
1573 struct route_entry *re,
1574 struct route_entry *old_re)
1575 {
1576 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1577
1578 if (rn == NULL || re == NULL)
1579 goto done;
1580
1581 ret = dplane_route_update_internal(rn, re, old_re,
1582 DPLANE_OP_ROUTE_UPDATE);
1583 done:
1584 return ret;
1585 }
1586
1587 /*
1588 * Enqueue a route removal for the dataplane.
1589 */
1590 enum zebra_dplane_result dplane_route_delete(struct route_node *rn,
1591 struct route_entry *re)
1592 {
1593 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1594
1595 if (rn == NULL || re == NULL)
1596 goto done;
1597
1598 ret = dplane_route_update_internal(rn, re, NULL,
1599 DPLANE_OP_ROUTE_DELETE);
1600
1601 done:
1602 return ret;
1603 }
1604
1605 /*
1606 * Notify the dplane when system/connected routes change.
1607 */
1608 enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn,
1609 struct route_entry *re)
1610 {
1611 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1612
1613 /* Ignore this event unless a provider plugin has requested it. */
1614 if (!zdplane_info.dg_sys_route_notifs) {
1615 ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
1616 goto done;
1617 }
1618
1619 if (rn == NULL || re == NULL)
1620 goto done;
1621
1622 ret = dplane_route_update_internal(rn, re, NULL,
1623 DPLANE_OP_SYS_ROUTE_ADD);
1624
1625 done:
1626 return ret;
1627 }
1628
1629 /*
1630 * Notify the dplane when system/connected routes are deleted.
1631 */
1632 enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn,
1633 struct route_entry *re)
1634 {
1635 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1636
1637 /* Ignore this event unless a provider plugin has requested it. */
1638 if (!zdplane_info.dg_sys_route_notifs) {
1639 ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
1640 goto done;
1641 }
1642
1643 if (rn == NULL || re == NULL)
1644 goto done;
1645
1646 ret = dplane_route_update_internal(rn, re, NULL,
1647 DPLANE_OP_SYS_ROUTE_DELETE);
1648
1649 done:
1650 return ret;
1651 }
1652
1653 /*
1654 * Enqueue LSP add for the dataplane.
1655 */
1656 enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp)
1657 {
1658 enum zebra_dplane_result ret =
1659 lsp_update_internal(lsp, DPLANE_OP_LSP_INSTALL);
1660
1661 return ret;
1662 }
1663
1664 /*
1665 * Enqueue LSP update for the dataplane.
1666 */
1667 enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp)
1668 {
1669 enum zebra_dplane_result ret =
1670 lsp_update_internal(lsp, DPLANE_OP_LSP_UPDATE);
1671
1672 return ret;
1673 }
1674
1675 /*
1676 * Enqueue LSP delete for the dataplane.
1677 */
1678 enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp)
1679 {
1680 enum zebra_dplane_result ret =
1681 lsp_update_internal(lsp, DPLANE_OP_LSP_DELETE);
1682
1683 return ret;
1684 }
1685
1686 /*
1687 * Enqueue pseudowire install for the dataplane.
1688 */
1689 enum zebra_dplane_result dplane_pw_install(struct zebra_pw *pw)
1690 {
1691 return pw_update_internal(pw, DPLANE_OP_PW_INSTALL);
1692 }
1693
1694 /*
1695 * Enqueue pseudowire un-install for the dataplane.
1696 */
1697 enum zebra_dplane_result dplane_pw_uninstall(struct zebra_pw *pw)
1698 {
1699 return pw_update_internal(pw, DPLANE_OP_PW_UNINSTALL);
1700 }
1701
1702 /*
1703 * Common internal LSP update utility
1704 */
1705 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
1706 enum dplane_op_e op)
1707 {
1708 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1709 int ret = EINVAL;
1710 struct zebra_dplane_ctx *ctx = NULL;
1711
1712 /* Obtain context block */
1713 ctx = dplane_ctx_alloc();
1714
1715 ret = dplane_ctx_lsp_init(ctx, op, lsp);
1716 if (ret != AOK)
1717 goto done;
1718
1719 ret = dplane_route_enqueue(ctx);
1720
1721 done:
1722 /* Update counter */
1723 atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1,
1724 memory_order_relaxed);
1725
1726 if (ret == AOK)
1727 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1728 else {
1729 atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
1730 memory_order_relaxed);
1731 dplane_ctx_free(&ctx);
1732 }
1733
1734 return result;
1735 }
1736
1737 /*
1738 * Internal, common handler for pseudowire updates.
1739 */
1740 static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
1741 enum dplane_op_e op)
1742 {
1743 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1744 int ret;
1745 struct zebra_dplane_ctx *ctx = NULL;
1746
1747 ctx = dplane_ctx_alloc();
1748
1749 ret = dplane_ctx_pw_init(ctx, op, pw);
1750 if (ret != AOK)
1751 goto done;
1752
1753 ret = dplane_route_enqueue(ctx);
1754
1755 done:
1756 /* Update counter */
1757 atomic_fetch_add_explicit(&zdplane_info.dg_pws_in, 1,
1758 memory_order_relaxed);
1759
1760 if (ret == AOK)
1761 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1762 else {
1763 atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1,
1764 memory_order_relaxed);
1765 dplane_ctx_free(&ctx);
1766 }
1767
1768 return result;
1769 }
1770
1771 /*
1772 * Enqueue interface address add for the dataplane.
1773 */
1774 enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp,
1775 const struct connected *ifc)
1776 {
1777 #if !defined(HAVE_NETLINK) && defined(HAVE_STRUCT_IFALIASREQ)
1778 /* Extra checks for this OS path. */
1779
1780 /* Don't configure PtP addresses on broadcast ifs or reverse */
1781 if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) {
1782 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_DPLANE)
1783 zlog_debug("Failed to set intf addr: mismatch p2p and connected");
1784
1785 return ZEBRA_DPLANE_REQUEST_FAILURE;
1786 }
1787
1788 /* Ensure that no existing installed v4 route conflicts with
1789 * the new interface prefix. This check must be done in the
1790 * zebra pthread context, and any route delete (if needed)
1791 * is enqueued before the interface address programming attempt.
1792 */
1793 if (ifc->address->family == AF_INET) {
1794 struct prefix_ipv4 *p;
1795
1796 p = (struct prefix_ipv4 *)ifc->address;
1797 rib_lookup_and_pushup(p, ifp->vrf_id);
1798 }
1799 #endif
1800
1801 return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_INSTALL);
1802 }
1803
1804 /*
1805 * Enqueue interface address remove/uninstall for the dataplane.
1806 */
1807 enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
1808 const struct connected *ifc)
1809 {
1810 return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_UNINSTALL);
1811 }
1812
1813 static enum zebra_dplane_result intf_addr_update_internal(
1814 const struct interface *ifp, const struct connected *ifc,
1815 enum dplane_op_e op)
1816 {
1817 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1818 int ret = EINVAL;
1819 struct zebra_dplane_ctx *ctx = NULL;
1820 struct zebra_ns *zns;
1821
1822 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
1823 char addr_str[PREFIX_STRLEN];
1824
1825 prefix2str(ifc->address, addr_str, sizeof(addr_str));
1826
1827 zlog_debug("init intf ctx %s: idx %d, addr %u:%s",
1828 dplane_op2str(op), ifp->ifindex, ifp->vrf_id,
1829 addr_str);
1830 }
1831
1832 ctx = dplane_ctx_alloc();
1833
1834 ctx->zd_op = op;
1835 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1836 ctx->zd_vrf_id = ifp->vrf_id;
1837
1838 zns = zebra_ns_lookup(ifp->vrf_id);
1839 dplane_ctx_ns_init(ctx, zns, false);
1840
1841 /* Init the interface-addr-specific area */
1842 memset(&ctx->u.intf, 0, sizeof(ctx->u.intf));
1843
1844 strlcpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
1845 ctx->u.intf.ifindex = ifp->ifindex;
1846 ctx->u.intf.prefix = *(ifc->address);
1847
1848 if (if_is_broadcast(ifp))
1849 ctx->u.intf.flags |= DPLANE_INTF_BROADCAST;
1850
1851 if (CONNECTED_PEER(ifc)) {
1852 ctx->u.intf.dest_prefix = *(ifc->destination);
1853 ctx->u.intf.flags |=
1854 (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST);
1855 } else if (ifc->destination) {
1856 ctx->u.intf.dest_prefix = *(ifc->destination);
1857 ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST;
1858 }
1859
1860 if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
1861 ctx->u.intf.flags |= DPLANE_INTF_SECONDARY;
1862
1863 if (ifc->label) {
1864 size_t len;
1865
1866 ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL;
1867
1868 /* Use embedded buffer if it's adequate; else allocate. */
1869 len = strlen(ifc->label);
1870
1871 if (len < sizeof(ctx->u.intf.label_buf)) {
1872 strlcpy(ctx->u.intf.label_buf, ifc->label,
1873 sizeof(ctx->u.intf.label_buf));
1874 ctx->u.intf.label = ctx->u.intf.label_buf;
1875 } else {
1876 ctx->u.intf.label = strdup(ifc->label);
1877 }
1878 }
1879
1880 ret = dplane_route_enqueue(ctx);
1881
1882 /* Increment counter */
1883 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1,
1884 memory_order_relaxed);
1885
1886 if (ret == AOK)
1887 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1888 else {
1889 /* Error counter */
1890 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
1891 1, memory_order_relaxed);
1892 dplane_ctx_free(&ctx);
1893 }
1894
1895 return result;
1896 }
1897
1898 /*
1899 * Handler for 'show dplane'
1900 */
1901 int dplane_show_helper(struct vty *vty, bool detailed)
1902 {
1903 uint64_t queued, queue_max, limit, errs, incoming, yields,
1904 other_errs;
1905
1906 /* Using atomics because counters are being changed in different
1907 * pthread contexts.
1908 */
1909 incoming = atomic_load_explicit(&zdplane_info.dg_routes_in,
1910 memory_order_relaxed);
1911 limit = atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
1912 memory_order_relaxed);
1913 queued = atomic_load_explicit(&zdplane_info.dg_routes_queued,
1914 memory_order_relaxed);
1915 queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
1916 memory_order_relaxed);
1917 errs = atomic_load_explicit(&zdplane_info.dg_route_errors,
1918 memory_order_relaxed);
1919 yields = atomic_load_explicit(&zdplane_info.dg_update_yields,
1920 memory_order_relaxed);
1921 other_errs = atomic_load_explicit(&zdplane_info.dg_other_errors,
1922 memory_order_relaxed);
1923
1924 vty_out(vty, "Zebra dataplane:\nRoute updates: %"PRIu64"\n",
1925 incoming);
1926 vty_out(vty, "Route update errors: %"PRIu64"\n", errs);
1927 vty_out(vty, "Other errors : %"PRIu64"\n", other_errs);
1928 vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit);
1929 vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued);
1930 vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max);
1931 vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields);
1932
1933 return CMD_SUCCESS;
1934 }
1935
1936 /*
1937 * Handler for 'show dplane providers'
1938 */
1939 int dplane_show_provs_helper(struct vty *vty, bool detailed)
1940 {
1941 struct zebra_dplane_provider *prov;
1942 uint64_t in, in_max, out, out_max;
1943
1944 vty_out(vty, "Zebra dataplane providers:\n");
1945
1946 DPLANE_LOCK();
1947 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1948 DPLANE_UNLOCK();
1949
1950 /* Show counters, useful info from each registered provider */
1951 while (prov) {
1952
1953 in = atomic_load_explicit(&prov->dp_in_counter,
1954 memory_order_relaxed);
1955 in_max = atomic_load_explicit(&prov->dp_in_max,
1956 memory_order_relaxed);
1957 out = atomic_load_explicit(&prov->dp_out_counter,
1958 memory_order_relaxed);
1959 out_max = atomic_load_explicit(&prov->dp_out_max,
1960 memory_order_relaxed);
1961
1962 vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", "
1963 "out: %"PRIu64", q_max: %"PRIu64"\n",
1964 prov->dp_name, prov->dp_id, in, in_max, out, out_max);
1965
1966 DPLANE_LOCK();
1967 prov = TAILQ_NEXT(prov, dp_prov_link);
1968 DPLANE_UNLOCK();
1969 }
1970
1971 return CMD_SUCCESS;
1972 }
1973
1974 /*
1975 * Provider registration
1976 */
1977 int dplane_provider_register(const char *name,
1978 enum dplane_provider_prio prio,
1979 int flags,
1980 int (*start_fp)(struct zebra_dplane_provider *),
1981 int (*fp)(struct zebra_dplane_provider *),
1982 int (*fini_fp)(struct zebra_dplane_provider *,
1983 bool early),
1984 void *data,
1985 struct zebra_dplane_provider **prov_p)
1986 {
1987 int ret = 0;
1988 struct zebra_dplane_provider *p = NULL, *last;
1989
1990 /* Validate */
1991 if (fp == NULL) {
1992 ret = EINVAL;
1993 goto done;
1994 }
1995
1996 if (prio <= DPLANE_PRIO_NONE ||
1997 prio > DPLANE_PRIO_LAST) {
1998 ret = EINVAL;
1999 goto done;
2000 }
2001
2002 /* Allocate and init new provider struct */
2003 p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider));
2004
2005 pthread_mutex_init(&(p->dp_mutex), NULL);
2006 TAILQ_INIT(&(p->dp_ctx_in_q));
2007 TAILQ_INIT(&(p->dp_ctx_out_q));
2008
2009 p->dp_priority = prio;
2010 p->dp_fp = fp;
2011 p->dp_start = start_fp;
2012 p->dp_fini = fini_fp;
2013 p->dp_data = data;
2014
2015 /* Lock - the dplane pthread may be running */
2016 DPLANE_LOCK();
2017
2018 p->dp_id = ++zdplane_info.dg_provider_id;
2019
2020 if (name)
2021 strlcpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN);
2022 else
2023 snprintf(p->dp_name, DPLANE_PROVIDER_NAMELEN,
2024 "provider-%u", p->dp_id);
2025
2026 /* Insert into list ordered by priority */
2027 TAILQ_FOREACH(last, &zdplane_info.dg_providers_q, dp_prov_link) {
2028 if (last->dp_priority > p->dp_priority)
2029 break;
2030 }
2031
2032 if (last)
2033 TAILQ_INSERT_BEFORE(last, p, dp_prov_link);
2034 else
2035 TAILQ_INSERT_TAIL(&zdplane_info.dg_providers_q, p,
2036 dp_prov_link);
2037
2038 /* And unlock */
2039 DPLANE_UNLOCK();
2040
2041 if (IS_ZEBRA_DEBUG_DPLANE)
2042 zlog_debug("dplane: registered new provider '%s' (%u), prio %d",
2043 p->dp_name, p->dp_id, p->dp_priority);
2044
2045 done:
2046 if (prov_p)
2047 *prov_p = p;
2048
2049 return ret;
2050 }
2051
2052 /* Accessors for provider attributes */
2053 const char *dplane_provider_get_name(const struct zebra_dplane_provider *prov)
2054 {
2055 return prov->dp_name;
2056 }
2057
2058 uint32_t dplane_provider_get_id(const struct zebra_dplane_provider *prov)
2059 {
2060 return prov->dp_id;
2061 }
2062
2063 void *dplane_provider_get_data(const struct zebra_dplane_provider *prov)
2064 {
2065 return prov->dp_data;
2066 }
2067
2068 int dplane_provider_get_work_limit(const struct zebra_dplane_provider *prov)
2069 {
2070 return zdplane_info.dg_updates_per_cycle;
2071 }
2072
2073 /* Lock/unlock a provider's mutex - iff the provider was registered with
2074 * the THREADED flag.
2075 */
2076 void dplane_provider_lock(struct zebra_dplane_provider *prov)
2077 {
2078 if (dplane_provider_is_threaded(prov))
2079 DPLANE_PROV_LOCK(prov);
2080 }
2081
2082 void dplane_provider_unlock(struct zebra_dplane_provider *prov)
2083 {
2084 if (dplane_provider_is_threaded(prov))
2085 DPLANE_PROV_UNLOCK(prov);
2086 }
2087
2088 /*
2089 * Dequeue and maintain associated counter
2090 */
2091 struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx(
2092 struct zebra_dplane_provider *prov)
2093 {
2094 struct zebra_dplane_ctx *ctx = NULL;
2095
2096 dplane_provider_lock(prov);
2097
2098 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
2099 if (ctx) {
2100 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
2101
2102 atomic_fetch_sub_explicit(&prov->dp_in_queued, 1,
2103 memory_order_relaxed);
2104 }
2105
2106 dplane_provider_unlock(prov);
2107
2108 return ctx;
2109 }
2110
2111 /*
2112 * Dequeue work to a list, return count
2113 */
2114 int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
2115 struct dplane_ctx_q *listp)
2116 {
2117 int limit, ret;
2118 struct zebra_dplane_ctx *ctx;
2119
2120 limit = zdplane_info.dg_updates_per_cycle;
2121
2122 dplane_provider_lock(prov);
2123
2124 for (ret = 0; ret < limit; ret++) {
2125 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
2126 if (ctx) {
2127 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
2128
2129 TAILQ_INSERT_TAIL(listp, ctx, zd_q_entries);
2130 } else {
2131 break;
2132 }
2133 }
2134
2135 if (ret > 0)
2136 atomic_fetch_sub_explicit(&prov->dp_in_queued, ret,
2137 memory_order_relaxed);
2138
2139 dplane_provider_unlock(prov);
2140
2141 return ret;
2142 }
2143
2144 /*
2145 * Enqueue and maintain associated counter
2146 */
2147 void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
2148 struct zebra_dplane_ctx *ctx)
2149 {
2150 dplane_provider_lock(prov);
2151
2152 TAILQ_INSERT_TAIL(&(prov->dp_ctx_out_q), ctx,
2153 zd_q_entries);
2154
2155 dplane_provider_unlock(prov);
2156
2157 atomic_fetch_add_explicit(&(prov->dp_out_counter), 1,
2158 memory_order_relaxed);
2159 }
2160
2161 /*
2162 * Accessor for provider object
2163 */
2164 bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
2165 {
2166 return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
2167 }
2168
2169 /*
2170 * Internal helper that copies information from a zebra ns object; this is
2171 * called in the zebra main pthread context as part of dplane ctx init.
2172 */
2173 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
2174 struct zebra_ns *zns)
2175 {
2176 ns_info->ns_id = zns->ns_id;
2177
2178 #if defined(HAVE_NETLINK)
2179 ns_info->is_cmd = true;
2180 ns_info->nls = zns->netlink_dplane;
2181 #endif /* NETLINK */
2182 }
2183
2184 /*
2185 * Provider api to signal that work/events are available
2186 * for the dataplane pthread.
2187 */
2188 int dplane_provider_work_ready(void)
2189 {
2190 /* Note that during zebra startup, we may be offered work before
2191 * the dataplane pthread (and thread-master) are ready. We want to
2192 * enqueue the work, but the event-scheduling machinery may not be
2193 * available.
2194 */
2195 if (zdplane_info.dg_run) {
2196 thread_add_event(zdplane_info.dg_master,
2197 dplane_thread_loop, NULL, 0,
2198 &zdplane_info.dg_t_update);
2199 }
2200
2201 return AOK;
2202 }
2203
2204 /*
2205 * Enqueue a context directly to zebra main.
2206 */
2207 void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
2208 {
2209 struct dplane_ctx_q temp_list;
2210
2211 /* Zebra's api takes a list, so we need to use a temporary list */
2212 TAILQ_INIT(&temp_list);
2213
2214 TAILQ_INSERT_TAIL(&temp_list, ctx, zd_q_entries);
2215 (zdplane_info.dg_results_cb)(&temp_list);
2216 }
2217
2218 /*
2219 * Kernel dataplane provider
2220 */
2221
2222 /*
2223 * Handler for kernel LSP updates
2224 */
2225 static enum zebra_dplane_result
2226 kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
2227 {
2228 enum zebra_dplane_result res;
2229
2230 /* Call into the synchronous kernel-facing code here */
2231 res = kernel_lsp_update(ctx);
2232
2233 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2234 atomic_fetch_add_explicit(
2235 &zdplane_info.dg_lsp_errors, 1,
2236 memory_order_relaxed);
2237
2238 return res;
2239 }
2240
2241 /*
2242 * Handler for kernel pseudowire updates
2243 */
2244 static enum zebra_dplane_result
2245 kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
2246 {
2247 enum zebra_dplane_result res;
2248
2249 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2250 zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
2251 dplane_ctx_get_pw_ifname(ctx),
2252 dplane_op2str(ctx->zd_op),
2253 dplane_ctx_get_pw_af(ctx),
2254 dplane_ctx_get_pw_local_label(ctx),
2255 dplane_ctx_get_pw_remote_label(ctx));
2256
2257 res = kernel_pw_update(ctx);
2258
2259 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2260 atomic_fetch_add_explicit(
2261 &zdplane_info.dg_pw_errors, 1,
2262 memory_order_relaxed);
2263
2264 return res;
2265 }
2266
2267 /*
2268 * Handler for kernel route updates
2269 */
2270 static enum zebra_dplane_result
2271 kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
2272 {
2273 enum zebra_dplane_result res;
2274
2275 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
2276 char dest_str[PREFIX_STRLEN];
2277
2278 prefix2str(dplane_ctx_get_dest(ctx),
2279 dest_str, sizeof(dest_str));
2280
2281 zlog_debug("%u:%s Dplane route update ctx %p op %s",
2282 dplane_ctx_get_vrf(ctx), dest_str,
2283 ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
2284 }
2285
2286 /* Call into the synchronous kernel-facing code here */
2287 res = kernel_route_update(ctx);
2288
2289 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2290 atomic_fetch_add_explicit(
2291 &zdplane_info.dg_route_errors, 1,
2292 memory_order_relaxed);
2293
2294 return res;
2295 }
2296
2297 /*
2298 * Handler for kernel-facing interface address updates
2299 */
2300 static enum zebra_dplane_result
2301 kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
2302 {
2303 enum zebra_dplane_result res;
2304
2305
2306 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
2307 char dest_str[PREFIX_STRLEN];
2308
2309 prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
2310 sizeof(dest_str));
2311
2312 zlog_debug("Dplane intf %s, idx %u, addr %s",
2313 dplane_op2str(dplane_ctx_get_op(ctx)),
2314 dplane_ctx_get_ifindex(ctx), dest_str);
2315 }
2316
2317 res = kernel_address_update_ctx(ctx);
2318
2319 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2320 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
2321 1, memory_order_relaxed);
2322
2323 return res;
2324 }
2325
2326 /*
2327 * Kernel provider callback
2328 */
2329 static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
2330 {
2331 enum zebra_dplane_result res;
2332 struct zebra_dplane_ctx *ctx;
2333 int counter, limit;
2334
2335 limit = dplane_provider_get_work_limit(prov);
2336
2337 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2338 zlog_debug("dplane provider '%s': processing",
2339 dplane_provider_get_name(prov));
2340
2341 for (counter = 0; counter < limit; counter++) {
2342
2343 ctx = dplane_provider_dequeue_in_ctx(prov);
2344 if (ctx == NULL)
2345 break;
2346
2347 /* A previous provider plugin may have asked to skip the
2348 * kernel update.
2349 */
2350 if (dplane_ctx_is_skip_kernel(ctx)) {
2351 res = ZEBRA_DPLANE_REQUEST_SUCCESS;
2352 goto skip_one;
2353 }
2354
2355 /* Dispatch to appropriate kernel-facing apis */
2356 switch (dplane_ctx_get_op(ctx)) {
2357
2358 case DPLANE_OP_ROUTE_INSTALL:
2359 case DPLANE_OP_ROUTE_UPDATE:
2360 case DPLANE_OP_ROUTE_DELETE:
2361 res = kernel_dplane_route_update(ctx);
2362 break;
2363
2364 case DPLANE_OP_LSP_INSTALL:
2365 case DPLANE_OP_LSP_UPDATE:
2366 case DPLANE_OP_LSP_DELETE:
2367 res = kernel_dplane_lsp_update(ctx);
2368 break;
2369
2370 case DPLANE_OP_PW_INSTALL:
2371 case DPLANE_OP_PW_UNINSTALL:
2372 res = kernel_dplane_pw_update(ctx);
2373 break;
2374
2375 case DPLANE_OP_ADDR_INSTALL:
2376 case DPLANE_OP_ADDR_UNINSTALL:
2377 res = kernel_dplane_address_update(ctx);
2378 break;
2379
2380 /* Ignore 'notifications' */
2381 case DPLANE_OP_SYS_ROUTE_ADD:
2382 case DPLANE_OP_SYS_ROUTE_DELETE:
2383 case DPLANE_OP_ROUTE_NOTIFY:
2384 res = ZEBRA_DPLANE_REQUEST_SUCCESS;
2385 break;
2386
2387 default:
2388 atomic_fetch_add_explicit(
2389 &zdplane_info.dg_other_errors, 1,
2390 memory_order_relaxed);
2391
2392 res = ZEBRA_DPLANE_REQUEST_FAILURE;
2393 break;
2394 }
2395
2396 skip_one:
2397 dplane_ctx_set_status(ctx, res);
2398
2399 dplane_provider_enqueue_out_ctx(prov, ctx);
2400 }
2401
2402 /* Ensure that we'll run the work loop again if there's still
2403 * more work to do.
2404 */
2405 if (counter >= limit) {
2406 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2407 zlog_debug("dplane provider '%s' reached max updates %d",
2408 dplane_provider_get_name(prov), counter);
2409
2410 atomic_fetch_add_explicit(&zdplane_info.dg_update_yields,
2411 1, memory_order_relaxed);
2412
2413 dplane_provider_work_ready();
2414 }
2415
2416 return 0;
2417 }
2418
2419 #if DPLANE_TEST_PROVIDER
2420
2421 /*
2422 * Test dataplane provider plugin
2423 */
2424
2425 /*
2426 * Test provider process callback
2427 */
2428 static int test_dplane_process_func(struct zebra_dplane_provider *prov)
2429 {
2430 struct zebra_dplane_ctx *ctx;
2431 int counter, limit;
2432
2433 /* Just moving from 'in' queue to 'out' queue */
2434
2435 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2436 zlog_debug("dplane provider '%s': processing",
2437 dplane_provider_get_name(prov));
2438
2439 limit = dplane_provider_get_work_limit(prov);
2440
2441 for (counter = 0; counter < limit; counter++) {
2442
2443 ctx = dplane_provider_dequeue_in_ctx(prov);
2444 if (ctx == NULL)
2445 break;
2446
2447 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2448 zlog_debug("dplane provider '%s': op %s",
2449 dplane_provider_get_name(prov),
2450 dplane_op2str(dplane_ctx_get_op(ctx)));
2451
2452 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
2453
2454 dplane_provider_enqueue_out_ctx(prov, ctx);
2455 }
2456
2457 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2458 zlog_debug("dplane provider '%s': processed %d",
2459 dplane_provider_get_name(prov), counter);
2460
2461 /* Ensure that we'll run the work loop again if there's still
2462 * more work to do.
2463 */
2464 if (counter >= limit)
2465 dplane_provider_work_ready();
2466
2467 return 0;
2468 }
2469
2470 /*
2471 * Test provider shutdown/fini callback
2472 */
2473 static int test_dplane_shutdown_func(struct zebra_dplane_provider *prov,
2474 bool early)
2475 {
2476 if (IS_ZEBRA_DEBUG_DPLANE)
2477 zlog_debug("dplane provider '%s': %sshutdown",
2478 dplane_provider_get_name(prov),
2479 early ? "early " : "");
2480
2481 return 0;
2482 }
2483 #endif /* DPLANE_TEST_PROVIDER */
2484
2485 /*
2486 * Register default kernel provider
2487 */
2488 static void dplane_provider_init(void)
2489 {
2490 int ret;
2491
2492 ret = dplane_provider_register("Kernel",
2493 DPLANE_PRIO_KERNEL,
2494 DPLANE_PROV_FLAGS_DEFAULT, NULL,
2495 kernel_dplane_process_func,
2496 NULL,
2497 NULL, NULL);
2498
2499 if (ret != AOK)
2500 zlog_err("Unable to register kernel dplane provider: %d",
2501 ret);
2502
2503 #if DPLANE_TEST_PROVIDER
2504 /* Optional test provider ... */
2505 ret = dplane_provider_register("Test",
2506 DPLANE_PRIO_PRE_KERNEL,
2507 DPLANE_PROV_FLAGS_DEFAULT, NULL,
2508 test_dplane_process_func,
2509 test_dplane_shutdown_func,
2510 NULL /* data */, NULL);
2511
2512 if (ret != AOK)
2513 zlog_err("Unable to register test dplane provider: %d",
2514 ret);
2515 #endif /* DPLANE_TEST_PROVIDER */
2516 }
2517
2518 /* Indicates zebra shutdown/exit is in progress. Some operations may be
2519 * simplified or skipped during shutdown processing.
2520 */
2521 bool dplane_is_in_shutdown(void)
2522 {
2523 return zdplane_info.dg_is_shutdown;
2524 }
2525
2526 /*
2527 * Early or pre-shutdown, de-init notification api. This runs pretty
2528 * early during zebra shutdown, as a signal to stop new work and prepare
2529 * for updates generated by shutdown/cleanup activity, as zebra tries to
2530 * remove everything it's responsible for.
2531 * NB: This runs in the main zebra pthread context.
2532 */
2533 void zebra_dplane_pre_finish(void)
2534 {
2535 if (IS_ZEBRA_DEBUG_DPLANE)
2536 zlog_debug("Zebra dataplane pre-fini called");
2537
2538 zdplane_info.dg_is_shutdown = true;
2539
2540 /* TODO -- Notify provider(s) of pending shutdown */
2541 }
2542
2543 /*
2544 * Utility to determine whether work remains enqueued within the dplane;
2545 * used during system shutdown processing.
2546 */
2547 static bool dplane_work_pending(void)
2548 {
2549 bool ret = false;
2550 struct zebra_dplane_ctx *ctx;
2551 struct zebra_dplane_provider *prov;
2552
2553 /* TODO -- just checking incoming/pending work for now, must check
2554 * providers
2555 */
2556 DPLANE_LOCK();
2557 {
2558 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
2559 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
2560 }
2561 DPLANE_UNLOCK();
2562
2563 if (ctx != NULL) {
2564 ret = true;
2565 goto done;
2566 }
2567
2568 while (prov) {
2569
2570 dplane_provider_lock(prov);
2571
2572 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
2573 if (ctx == NULL)
2574 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
2575
2576 dplane_provider_unlock(prov);
2577
2578 if (ctx != NULL)
2579 break;
2580
2581 DPLANE_LOCK();
2582 prov = TAILQ_NEXT(prov, dp_prov_link);
2583 DPLANE_UNLOCK();
2584 }
2585
2586 if (ctx != NULL)
2587 ret = true;
2588
2589 done:
2590 return ret;
2591 }
2592
2593 /*
2594 * Shutdown-time intermediate callback, used to determine when all pending
2595 * in-flight updates are done. If there's still work to do, reschedules itself.
2596 * If all work is done, schedules an event to the main zebra thread for
2597 * final zebra shutdown.
2598 * This runs in the dplane pthread context.
2599 */
2600 static int dplane_check_shutdown_status(struct thread *event)
2601 {
2602 if (IS_ZEBRA_DEBUG_DPLANE)
2603 zlog_debug("Zebra dataplane shutdown status check called");
2604
2605 if (dplane_work_pending()) {
2606 /* Reschedule dplane check on a short timer */
2607 thread_add_timer_msec(zdplane_info.dg_master,
2608 dplane_check_shutdown_status,
2609 NULL, 100,
2610 &zdplane_info.dg_t_shutdown_check);
2611
2612 /* TODO - give up and stop waiting after a short time? */
2613
2614 } else {
2615 /* We appear to be done - schedule a final callback event
2616 * for the zebra main pthread.
2617 */
2618 thread_add_event(zrouter.master, zebra_finalize, NULL, 0, NULL);
2619 }
2620
2621 return 0;
2622 }
2623
2624 /*
2625 * Shutdown, de-init api. This runs pretty late during shutdown,
2626 * after zebra has tried to free/remove/uninstall all routes during shutdown.
2627 * At this point, dplane work may still remain to be done, so we can't just
2628 * blindly terminate. If there's still work to do, we'll periodically check
2629 * and when done, we'll enqueue a task to the zebra main thread for final
2630 * termination processing.
2631 *
2632 * NB: This runs in the main zebra thread context.
2633 */
2634 void zebra_dplane_finish(void)
2635 {
2636 if (IS_ZEBRA_DEBUG_DPLANE)
2637 zlog_debug("Zebra dataplane fini called");
2638
2639 thread_add_event(zdplane_info.dg_master,
2640 dplane_check_shutdown_status, NULL, 0,
2641 &zdplane_info.dg_t_shutdown_check);
2642 }
2643
2644 /*
2645 * Main dataplane pthread event loop. The thread takes new incoming work
2646 * and offers it to the first provider. It then iterates through the
2647 * providers, taking complete work from each one and offering it
2648 * to the next in order. At each step, a limited number of updates are
2649 * processed during a cycle in order to provide some fairness.
2650 *
2651 * This loop through the providers is only run once, so that the dataplane
2652 * pthread can look for other pending work - such as i/o work on behalf of
2653 * providers.
2654 */
2655 static int dplane_thread_loop(struct thread *event)
2656 {
2657 struct dplane_ctx_q work_list;
2658 struct dplane_ctx_q error_list;
2659 struct zebra_dplane_provider *prov;
2660 struct zebra_dplane_ctx *ctx, *tctx;
2661 int limit, counter, error_counter;
2662 uint64_t curr, high;
2663
2664 /* Capture work limit per cycle */
2665 limit = zdplane_info.dg_updates_per_cycle;
2666
2667 /* Init temporary lists used to move contexts among providers */
2668 TAILQ_INIT(&work_list);
2669 TAILQ_INIT(&error_list);
2670 error_counter = 0;
2671
2672 /* Check for zebra shutdown */
2673 if (!zdplane_info.dg_run)
2674 goto done;
2675
2676 /* Dequeue some incoming work from zebra (if any) onto the temporary
2677 * working list.
2678 */
2679 DPLANE_LOCK();
2680
2681 /* Locate initial registered provider */
2682 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
2683
2684 /* Move new work from incoming list to temp list */
2685 for (counter = 0; counter < limit; counter++) {
2686 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
2687 if (ctx) {
2688 TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx,
2689 zd_q_entries);
2690
2691 ctx->zd_provider = prov->dp_id;
2692
2693 TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
2694 } else {
2695 break;
2696 }
2697 }
2698
2699 DPLANE_UNLOCK();
2700
2701 atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, counter,
2702 memory_order_relaxed);
2703
2704 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2705 zlog_debug("dplane: incoming new work counter: %d", counter);
2706
2707 /* Iterate through the registered providers, offering new incoming
2708 * work. If the provider has outgoing work in its queue, take that
2709 * work for the next provider
2710 */
2711 while (prov) {
2712
2713 /* At each iteration, the temporary work list has 'counter'
2714 * items.
2715 */
2716 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2717 zlog_debug("dplane enqueues %d new work to provider '%s'",
2718 counter, dplane_provider_get_name(prov));
2719
2720 /* Capture current provider id in each context; check for
2721 * error status.
2722 */
2723 TAILQ_FOREACH_SAFE(ctx, &work_list, zd_q_entries, tctx) {
2724 if (dplane_ctx_get_status(ctx) ==
2725 ZEBRA_DPLANE_REQUEST_SUCCESS) {
2726 ctx->zd_provider = prov->dp_id;
2727 } else {
2728 /*
2729 * TODO -- improve error-handling: recirc
2730 * errors backwards so that providers can
2731 * 'undo' their work (if they want to)
2732 */
2733
2734 /* Move to error list; will be returned
2735 * zebra main.
2736 */
2737 TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
2738 TAILQ_INSERT_TAIL(&error_list,
2739 ctx, zd_q_entries);
2740 error_counter++;
2741 }
2742 }
2743
2744 /* Enqueue new work to the provider */
2745 dplane_provider_lock(prov);
2746
2747 if (TAILQ_FIRST(&work_list))
2748 TAILQ_CONCAT(&(prov->dp_ctx_in_q), &work_list,
2749 zd_q_entries);
2750
2751 atomic_fetch_add_explicit(&prov->dp_in_counter, counter,
2752 memory_order_relaxed);
2753 atomic_fetch_add_explicit(&prov->dp_in_queued, counter,
2754 memory_order_relaxed);
2755 curr = atomic_load_explicit(&prov->dp_in_queued,
2756 memory_order_relaxed);
2757 high = atomic_load_explicit(&prov->dp_in_max,
2758 memory_order_relaxed);
2759 if (curr > high)
2760 atomic_store_explicit(&prov->dp_in_max, curr,
2761 memory_order_relaxed);
2762
2763 dplane_provider_unlock(prov);
2764
2765 /* Reset the temp list (though the 'concat' may have done this
2766 * already), and the counter
2767 */
2768 TAILQ_INIT(&work_list);
2769 counter = 0;
2770
2771 /* Call into the provider code. Note that this is
2772 * unconditional: we offer to do work even if we don't enqueue
2773 * any _new_ work.
2774 */
2775 (*prov->dp_fp)(prov);
2776
2777 /* Check for zebra shutdown */
2778 if (!zdplane_info.dg_run)
2779 break;
2780
2781 /* Dequeue completed work from the provider */
2782 dplane_provider_lock(prov);
2783
2784 while (counter < limit) {
2785 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
2786 if (ctx) {
2787 TAILQ_REMOVE(&(prov->dp_ctx_out_q), ctx,
2788 zd_q_entries);
2789
2790 TAILQ_INSERT_TAIL(&work_list,
2791 ctx, zd_q_entries);
2792 counter++;
2793 } else
2794 break;
2795 }
2796
2797 dplane_provider_unlock(prov);
2798
2799 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2800 zlog_debug("dplane dequeues %d completed work from provider %s",
2801 counter, dplane_provider_get_name(prov));
2802
2803 /* Locate next provider */
2804 DPLANE_LOCK();
2805 prov = TAILQ_NEXT(prov, dp_prov_link);
2806 DPLANE_UNLOCK();
2807 }
2808
2809 /* After all providers have been serviced, enqueue any completed
2810 * work and any errors back to zebra so it can process the results.
2811 */
2812 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2813 zlog_debug("dplane has %d completed, %d errors, for zebra main",
2814 counter, error_counter);
2815
2816 /*
2817 * Hand lists through the api to zebra main,
2818 * to reduce the number of lock/unlock cycles
2819 */
2820
2821 /* Call through to zebra main */
2822 (zdplane_info.dg_results_cb)(&error_list);
2823
2824 TAILQ_INIT(&error_list);
2825
2826 /* Call through to zebra main */
2827 (zdplane_info.dg_results_cb)(&work_list);
2828
2829 TAILQ_INIT(&work_list);
2830
2831 done:
2832 return 0;
2833 }
2834
2835 /*
2836 * Final phase of shutdown, after all work enqueued to dplane has been
2837 * processed. This is called from the zebra main pthread context.
2838 */
2839 void zebra_dplane_shutdown(void)
2840 {
2841 if (IS_ZEBRA_DEBUG_DPLANE)
2842 zlog_debug("Zebra dataplane shutdown called");
2843
2844 /* Stop dplane thread, if it's running */
2845
2846 zdplane_info.dg_run = false;
2847
2848 THREAD_OFF(zdplane_info.dg_t_update);
2849
2850 frr_pthread_stop(zdplane_info.dg_pthread, NULL);
2851
2852 /* Destroy pthread */
2853 frr_pthread_destroy(zdplane_info.dg_pthread);
2854 zdplane_info.dg_pthread = NULL;
2855 zdplane_info.dg_master = NULL;
2856
2857 /* TODO -- Notify provider(s) of final shutdown */
2858
2859 /* TODO -- Clean-up provider objects */
2860
2861 /* TODO -- Clean queue(s), free memory */
2862 }
2863
2864 /*
2865 * Initialize the dataplane module during startup, internal/private version
2866 */
2867 static void zebra_dplane_init_internal(void)
2868 {
2869 memset(&zdplane_info, 0, sizeof(zdplane_info));
2870
2871 pthread_mutex_init(&zdplane_info.dg_mutex, NULL);
2872
2873 TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
2874 TAILQ_INIT(&zdplane_info.dg_providers_q);
2875
2876 zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
2877
2878 zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED;
2879
2880 /* Register default kernel 'provider' during init */
2881 dplane_provider_init();
2882 }
2883
2884 /*
2885 * Start the dataplane pthread. This step needs to be run later than the
2886 * 'init' step, in case zebra has fork-ed.
2887 */
2888 void zebra_dplane_start(void)
2889 {
2890 struct zebra_dplane_provider *prov;
2891 struct frr_pthread_attr pattr = {
2892 .start = frr_pthread_attr_default.start,
2893 .stop = frr_pthread_attr_default.stop
2894 };
2895
2896 /* Start dataplane pthread */
2897
2898 zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
2899 "Zebra dplane");
2900
2901 zdplane_info.dg_master = zdplane_info.dg_pthread->master;
2902
2903 zdplane_info.dg_run = true;
2904
2905 /* Enqueue an initial event for the dataplane pthread */
2906 thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
2907 &zdplane_info.dg_t_update);
2908
2909 /* Call start callbacks for registered providers */
2910
2911 DPLANE_LOCK();
2912 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
2913 DPLANE_UNLOCK();
2914
2915 while (prov) {
2916
2917 if (prov->dp_start)
2918 (prov->dp_start)(prov);
2919
2920 /* Locate next provider */
2921 DPLANE_LOCK();
2922 prov = TAILQ_NEXT(prov, dp_prov_link);
2923 DPLANE_UNLOCK();
2924 }
2925
2926 frr_pthread_run(zdplane_info.dg_pthread, NULL);
2927 }
2928
2929 /*
2930 * Initialize the dataplane module at startup; called by zebra rib_init()
2931 */
2932 void zebra_dplane_init(int (*results_fp)(struct dplane_ctx_q *))
2933 {
2934 zebra_dplane_init_internal();
2935 zdplane_info.dg_results_cb = results_fp;
2936 }