]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_dplane.c
Merge pull request #3621 from donaldsharp/modules_doc_update
[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/zserv.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 * The context block used to exchange info about route updates across
106 * the boundary between the zebra main context (and pthread) and the
107 * dataplane layer (and pthread).
108 */
109 struct zebra_dplane_ctx {
110
111 /* Operation code */
112 enum dplane_op_e zd_op;
113
114 /* Status on return */
115 enum zebra_dplane_result zd_status;
116
117 /* Dplane provider id */
118 uint32_t zd_provider;
119
120 /* Flags - used by providers, e.g. */
121 int zd_flags;
122
123 bool zd_is_update;
124
125 uint32_t zd_seq;
126 uint32_t zd_old_seq;
127
128 /* TODO -- internal/sub-operation status? */
129 enum zebra_dplane_result zd_remote_status;
130 enum zebra_dplane_result zd_kernel_status;
131
132 vrf_id_t zd_vrf_id;
133 uint32_t zd_table_id;
134
135 /* Support info for either route or LSP update */
136 union {
137 struct dplane_route_info rinfo;
138 zebra_lsp_t lsp;
139 } u;
140
141 /* Namespace info, used especially for netlink kernel communication */
142 struct zebra_dplane_info zd_ns_info;
143
144 /* Embedded list linkage */
145 TAILQ_ENTRY(zebra_dplane_ctx) zd_q_entries;
146 };
147
148 /* Flag that can be set by a pre-kernel provider as a signal that an update
149 * should bypass the kernel.
150 */
151 #define DPLANE_CTX_FLAG_NO_KERNEL 0x01
152
153
154 /*
155 * Registration block for one dataplane provider.
156 */
157 struct zebra_dplane_provider {
158 /* Name */
159 char dp_name[DPLANE_PROVIDER_NAMELEN + 1];
160
161 /* Priority, for ordering among providers */
162 uint8_t dp_priority;
163
164 /* Id value */
165 uint32_t dp_id;
166
167 /* Mutex */
168 pthread_mutex_t dp_mutex;
169
170 /* Plugin-provided extra data */
171 void *dp_data;
172
173 /* Flags */
174 int dp_flags;
175
176 int (*dp_fp)(struct zebra_dplane_provider *prov);
177
178 int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
179
180 _Atomic uint32_t dp_in_counter;
181 _Atomic uint32_t dp_in_queued;
182 _Atomic uint32_t dp_in_max;
183 _Atomic uint32_t dp_out_counter;
184 _Atomic uint32_t dp_out_queued;
185 _Atomic uint32_t dp_out_max;
186 _Atomic uint32_t dp_error_counter;
187
188 /* Queue of contexts inbound to the provider */
189 struct dplane_ctx_q dp_ctx_in_q;
190
191 /* Queue of completed contexts outbound from the provider back
192 * towards the dataplane module.
193 */
194 struct dplane_ctx_q dp_ctx_out_q;
195
196 /* Embedded list linkage for provider objects */
197 TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link;
198 };
199
200 /*
201 * Globals
202 */
203 static struct zebra_dplane_globals {
204 /* Mutex to control access to dataplane components */
205 pthread_mutex_t dg_mutex;
206
207 /* Results callback registered by zebra 'core' */
208 int (*dg_results_cb)(struct dplane_ctx_q *ctxlist);
209
210 /* Sentinel for beginning of shutdown */
211 volatile bool dg_is_shutdown;
212
213 /* Sentinel for end of shutdown */
214 volatile bool dg_run;
215
216 /* Route-update context queue inbound to the dataplane */
217 TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q;
218
219 /* Ordered list of providers */
220 TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
221
222 /* Counter used to assign internal ids to providers */
223 uint32_t dg_provider_id;
224
225 /* Limit number of pending, unprocessed updates */
226 _Atomic uint32_t dg_max_queued_updates;
227
228 /* Limit number of new updates dequeued at once, to pace an
229 * incoming burst.
230 */
231 uint32_t dg_updates_per_cycle;
232
233 _Atomic uint32_t dg_routes_in;
234 _Atomic uint32_t dg_routes_queued;
235 _Atomic uint32_t dg_routes_queued_max;
236 _Atomic uint32_t dg_route_errors;
237 _Atomic uint32_t dg_other_errors;
238
239 _Atomic uint32_t dg_lsps_in;
240 _Atomic uint32_t dg_lsps_queued;
241 _Atomic uint32_t dg_lsps_queued_max;
242 _Atomic uint32_t dg_lsp_errors;
243
244 _Atomic uint32_t dg_update_yields;
245
246 /* Dataplane pthread */
247 struct frr_pthread *dg_pthread;
248
249 /* Event-delivery context 'master' for the dplane */
250 struct thread_master *dg_master;
251
252 /* Event/'thread' pointer for queued updates */
253 struct thread *dg_t_update;
254
255 /* Event pointer for pending shutdown check loop */
256 struct thread *dg_t_shutdown_check;
257
258 } zdplane_info;
259
260 /*
261 * Lock and unlock for interactions with the zebra 'core' pthread
262 */
263 #define DPLANE_LOCK() pthread_mutex_lock(&zdplane_info.dg_mutex)
264 #define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_info.dg_mutex)
265
266
267 /*
268 * Lock and unlock for individual providers
269 */
270 #define DPLANE_PROV_LOCK(p) pthread_mutex_lock(&((p)->dp_mutex))
271 #define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex))
272
273 /* Prototypes */
274 static int dplane_thread_loop(struct thread *event);
275 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
276 struct zebra_ns *zns);
277 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
278 enum dplane_op_e op);
279
280 /*
281 * Public APIs
282 */
283
284 /* Obtain thread_master for dataplane thread */
285 struct thread_master *dplane_get_thread_master(void)
286 {
287 return zdplane_info.dg_master;
288 }
289
290 /*
291 * Allocate a dataplane update context
292 */
293 static struct zebra_dplane_ctx *dplane_ctx_alloc(void)
294 {
295 struct zebra_dplane_ctx *p;
296
297 /* TODO -- just alloc'ing memory, but would like to maintain
298 * a pool
299 */
300 p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx));
301
302 return p;
303 }
304
305 /*
306 * Free a dataplane results context.
307 */
308 static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
309 {
310 if (pctx == NULL)
311 return;
312
313 DPLANE_CTX_VALID(*pctx);
314
315 /* TODO -- just freeing memory, but would like to maintain
316 * a pool
317 */
318
319 /* Some internal allocations may need to be freed, depending on
320 * the type of info captured in the ctx.
321 */
322 switch ((*pctx)->zd_op) {
323 case DPLANE_OP_ROUTE_INSTALL:
324 case DPLANE_OP_ROUTE_UPDATE:
325 case DPLANE_OP_ROUTE_DELETE:
326
327 /* Free allocated nexthops */
328 if ((*pctx)->u.rinfo.zd_ng.nexthop) {
329 /* This deals with recursive nexthops too */
330 nexthops_free((*pctx)->u.rinfo.zd_ng.nexthop);
331
332 (*pctx)->u.rinfo.zd_ng.nexthop = NULL;
333 }
334
335 if ((*pctx)->u.rinfo.zd_old_ng.nexthop) {
336 /* This deals with recursive nexthops too */
337 nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop);
338
339 (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL;
340 }
341
342 break;
343
344 case DPLANE_OP_LSP_INSTALL:
345 case DPLANE_OP_LSP_UPDATE:
346 case DPLANE_OP_LSP_DELETE:
347 {
348 zebra_nhlfe_t *nhlfe, *next;
349
350 /* Free allocated NHLFEs */
351 for (nhlfe = (*pctx)->u.lsp.nhlfe_list; nhlfe; nhlfe = next) {
352 next = nhlfe->next;
353
354 zebra_mpls_nhlfe_del(nhlfe);
355 }
356
357 /* Clear pointers in lsp struct, in case we're cacheing
358 * free context structs.
359 */
360 (*pctx)->u.lsp.nhlfe_list = NULL;
361 (*pctx)->u.lsp.best_nhlfe = NULL;
362
363 break;
364 }
365
366 case DPLANE_OP_NONE:
367 break;
368 }
369
370 XFREE(MTYPE_DP_CTX, *pctx);
371 *pctx = NULL;
372 }
373
374 /*
375 * Return a context block to the dplane module after processing
376 */
377 void dplane_ctx_fini(struct zebra_dplane_ctx **pctx)
378 {
379 /* TODO -- maintain pool; for now, just free */
380 dplane_ctx_free(pctx);
381 }
382
383 /* Enqueue a context block */
384 void dplane_ctx_enqueue_tail(struct dplane_ctx_q *q,
385 const struct zebra_dplane_ctx *ctx)
386 {
387 TAILQ_INSERT_TAIL(q, (struct zebra_dplane_ctx *)ctx, zd_q_entries);
388 }
389
390 /* Append a list of context blocks to another list */
391 void dplane_ctx_list_append(struct dplane_ctx_q *to_list,
392 struct dplane_ctx_q *from_list)
393 {
394 if (TAILQ_FIRST(from_list)) {
395 TAILQ_CONCAT(to_list, from_list, zd_q_entries);
396
397 /* And clear 'from' list */
398 TAILQ_INIT(from_list);
399 }
400 }
401
402 /* Dequeue a context block from the head of a list */
403 struct zebra_dplane_ctx *dplane_ctx_dequeue(struct dplane_ctx_q *q)
404 {
405 struct zebra_dplane_ctx *ctx = TAILQ_FIRST(q);
406
407 if (ctx)
408 TAILQ_REMOVE(q, ctx, zd_q_entries);
409
410 return ctx;
411 }
412
413 /*
414 * Accessors for information from the context object
415 */
416 enum zebra_dplane_result dplane_ctx_get_status(
417 const struct zebra_dplane_ctx *ctx)
418 {
419 DPLANE_CTX_VALID(ctx);
420
421 return ctx->zd_status;
422 }
423
424 void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx,
425 enum zebra_dplane_result status)
426 {
427 DPLANE_CTX_VALID(ctx);
428
429 ctx->zd_status = status;
430 }
431
432 /* Retrieve last/current provider id */
433 uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx)
434 {
435 DPLANE_CTX_VALID(ctx);
436 return ctx->zd_provider;
437 }
438
439 /* Providers run before the kernel can control whether a kernel
440 * update should be done.
441 */
442 void dplane_ctx_set_skip_kernel(struct zebra_dplane_ctx *ctx)
443 {
444 DPLANE_CTX_VALID(ctx);
445
446 SET_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
447 }
448
449 bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx)
450 {
451 DPLANE_CTX_VALID(ctx);
452
453 return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
454 }
455
456 enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx)
457 {
458 DPLANE_CTX_VALID(ctx);
459
460 return ctx->zd_op;
461 }
462
463 const char *dplane_op2str(enum dplane_op_e op)
464 {
465 const char *ret = "UNKNOWN";
466
467 switch (op) {
468 case DPLANE_OP_NONE:
469 ret = "NONE";
470 break;
471
472 /* Route update */
473 case DPLANE_OP_ROUTE_INSTALL:
474 ret = "ROUTE_INSTALL";
475 break;
476 case DPLANE_OP_ROUTE_UPDATE:
477 ret = "ROUTE_UPDATE";
478 break;
479 case DPLANE_OP_ROUTE_DELETE:
480 ret = "ROUTE_DELETE";
481 break;
482
483 case DPLANE_OP_LSP_INSTALL:
484 ret = "LSP_INSTALL";
485 break;
486 case DPLANE_OP_LSP_UPDATE:
487 ret = "LSP_UPDATE";
488 break;
489 case DPLANE_OP_LSP_DELETE:
490 ret = "LSP_DELETE";
491 break;
492
493 };
494
495 return ret;
496 }
497
498 const char *dplane_res2str(enum zebra_dplane_result res)
499 {
500 const char *ret = "<Unknown>";
501
502 switch (res) {
503 case ZEBRA_DPLANE_REQUEST_FAILURE:
504 ret = "FAILURE";
505 break;
506 case ZEBRA_DPLANE_REQUEST_QUEUED:
507 ret = "QUEUED";
508 break;
509 case ZEBRA_DPLANE_REQUEST_SUCCESS:
510 ret = "SUCCESS";
511 break;
512 };
513
514 return ret;
515 }
516
517 const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
518 {
519 DPLANE_CTX_VALID(ctx);
520
521 return &(ctx->u.rinfo.zd_dest);
522 }
523
524 /* Source prefix is a little special - return NULL for "no src prefix" */
525 const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx)
526 {
527 DPLANE_CTX_VALID(ctx);
528
529 if (ctx->u.rinfo.zd_src.prefixlen == 0 &&
530 IN6_IS_ADDR_UNSPECIFIED(&(ctx->u.rinfo.zd_src.u.prefix6))) {
531 return NULL;
532 } else {
533 return &(ctx->u.rinfo.zd_src);
534 }
535 }
536
537 bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx)
538 {
539 DPLANE_CTX_VALID(ctx);
540
541 return ctx->zd_is_update;
542 }
543
544 uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx)
545 {
546 DPLANE_CTX_VALID(ctx);
547
548 return ctx->zd_seq;
549 }
550
551 uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx)
552 {
553 DPLANE_CTX_VALID(ctx);
554
555 return ctx->zd_old_seq;
556 }
557
558 vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
559 {
560 DPLANE_CTX_VALID(ctx);
561
562 return ctx->zd_vrf_id;
563 }
564
565 int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx)
566 {
567 DPLANE_CTX_VALID(ctx);
568
569 return ctx->u.rinfo.zd_type;
570 }
571
572 int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx)
573 {
574 DPLANE_CTX_VALID(ctx);
575
576 return ctx->u.rinfo.zd_old_type;
577 }
578
579 afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
580 {
581 DPLANE_CTX_VALID(ctx);
582
583 return ctx->u.rinfo.zd_afi;
584 }
585
586 safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
587 {
588 DPLANE_CTX_VALID(ctx);
589
590 return ctx->u.rinfo.zd_safi;
591 }
592
593 uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx)
594 {
595 DPLANE_CTX_VALID(ctx);
596
597 return ctx->zd_table_id;
598 }
599
600 route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx)
601 {
602 DPLANE_CTX_VALID(ctx);
603
604 return ctx->u.rinfo.zd_tag;
605 }
606
607 route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx)
608 {
609 DPLANE_CTX_VALID(ctx);
610
611 return ctx->u.rinfo.zd_old_tag;
612 }
613
614 uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx)
615 {
616 DPLANE_CTX_VALID(ctx);
617
618 return ctx->u.rinfo.zd_instance;
619 }
620
621 uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
622 {
623 DPLANE_CTX_VALID(ctx);
624
625 return ctx->u.rinfo.zd_old_instance;
626 }
627
628 uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx)
629 {
630 DPLANE_CTX_VALID(ctx);
631
632 return ctx->u.rinfo.zd_metric;
633 }
634
635 uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx)
636 {
637 DPLANE_CTX_VALID(ctx);
638
639 return ctx->u.rinfo.zd_old_metric;
640 }
641
642 uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx)
643 {
644 DPLANE_CTX_VALID(ctx);
645
646 return ctx->u.rinfo.zd_mtu;
647 }
648
649 uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx)
650 {
651 DPLANE_CTX_VALID(ctx);
652
653 return ctx->u.rinfo.zd_nexthop_mtu;
654 }
655
656 uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx)
657 {
658 DPLANE_CTX_VALID(ctx);
659
660 return ctx->u.rinfo.zd_distance;
661 }
662
663 uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
664 {
665 DPLANE_CTX_VALID(ctx);
666
667 return ctx->u.rinfo.zd_old_distance;
668 }
669
670 const struct nexthop_group *dplane_ctx_get_ng(
671 const struct zebra_dplane_ctx *ctx)
672 {
673 DPLANE_CTX_VALID(ctx);
674
675 return &(ctx->u.rinfo.zd_ng);
676 }
677
678 const struct nexthop_group *dplane_ctx_get_old_ng(
679 const struct zebra_dplane_ctx *ctx)
680 {
681 DPLANE_CTX_VALID(ctx);
682
683 return &(ctx->u.rinfo.zd_old_ng);
684 }
685
686 const struct zebra_dplane_info *dplane_ctx_get_ns(
687 const struct zebra_dplane_ctx *ctx)
688 {
689 DPLANE_CTX_VALID(ctx);
690
691 return &(ctx->zd_ns_info);
692 }
693
694 /* Accessors for LSP information */
695
696 mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
697 {
698 DPLANE_CTX_VALID(ctx);
699
700 return ctx->u.lsp.ile.in_label;
701 }
702
703 uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
704 {
705 DPLANE_CTX_VALID(ctx);
706
707 return ctx->u.lsp.addr_family;
708 }
709
710 uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
711 {
712 DPLANE_CTX_VALID(ctx);
713
714 return ctx->u.lsp.flags;
715 }
716
717 zebra_nhlfe_t *dplane_ctx_get_nhlfe(struct zebra_dplane_ctx *ctx)
718 {
719 DPLANE_CTX_VALID(ctx);
720
721 return ctx->u.lsp.nhlfe_list;
722 }
723
724 zebra_nhlfe_t *dplane_ctx_get_best_nhlfe(struct zebra_dplane_ctx *ctx)
725 {
726 DPLANE_CTX_VALID(ctx);
727
728 return ctx->u.lsp.best_nhlfe;
729 }
730
731 uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
732 {
733 DPLANE_CTX_VALID(ctx);
734
735 return ctx->u.lsp.num_ecmp;
736 }
737
738 /*
739 * End of dplane context accessors
740 */
741
742
743 /*
744 * Retrieve the limit on the number of pending, unprocessed updates.
745 */
746 uint32_t dplane_get_in_queue_limit(void)
747 {
748 return atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
749 memory_order_relaxed);
750 }
751
752 /*
753 * Configure limit on the number of pending, queued updates.
754 */
755 void dplane_set_in_queue_limit(uint32_t limit, bool set)
756 {
757 /* Reset to default on 'unset' */
758 if (!set)
759 limit = DPLANE_DEFAULT_MAX_QUEUED;
760
761 atomic_store_explicit(&zdplane_info.dg_max_queued_updates, limit,
762 memory_order_relaxed);
763 }
764
765 /*
766 * Retrieve the current queue depth of incoming, unprocessed updates
767 */
768 uint32_t dplane_get_in_queue_len(void)
769 {
770 return atomic_load_explicit(&zdplane_info.dg_routes_queued,
771 memory_order_seq_cst);
772 }
773
774 /*
775 * Common dataplane context init with zebra namespace info.
776 */
777 static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
778 struct zebra_ns *zns,
779 bool is_update)
780 {
781 dplane_info_from_zns(&(ctx->zd_ns_info), zns);
782
783 #if defined(HAVE_NETLINK)
784 /* Increment message counter after copying to context struct - may need
785 * two messages in some 'update' cases.
786 */
787 if (is_update)
788 zns->netlink_dplane.seq += 2;
789 else
790 zns->netlink_dplane.seq++;
791 #endif /* HAVE_NETLINK */
792
793 return AOK;
794 }
795
796 /*
797 * Initialize a context block for a route update from zebra data structs.
798 */
799 static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
800 enum dplane_op_e op,
801 struct route_node *rn,
802 struct route_entry *re)
803 {
804 int ret = EINVAL;
805 const struct route_table *table = NULL;
806 const rib_table_info_t *info;
807 const struct prefix *p, *src_p;
808 struct zebra_ns *zns;
809 struct zebra_vrf *zvrf;
810 struct nexthop *nexthop;
811
812 if (!ctx || !rn || !re)
813 goto done;
814
815 ctx->zd_op = op;
816 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
817
818 ctx->u.rinfo.zd_type = re->type;
819 ctx->u.rinfo.zd_old_type = re->type;
820
821 /* Prefixes: dest, and optional source */
822 srcdest_rnode_prefixes(rn, &p, &src_p);
823
824 prefix_copy(&(ctx->u.rinfo.zd_dest), p);
825
826 if (src_p)
827 prefix_copy(&(ctx->u.rinfo.zd_src), src_p);
828 else
829 memset(&(ctx->u.rinfo.zd_src), 0, sizeof(ctx->u.rinfo.zd_src));
830
831 ctx->zd_table_id = re->table;
832
833 ctx->u.rinfo.zd_metric = re->metric;
834 ctx->u.rinfo.zd_old_metric = re->metric;
835 ctx->zd_vrf_id = re->vrf_id;
836 ctx->u.rinfo.zd_mtu = re->mtu;
837 ctx->u.rinfo.zd_nexthop_mtu = re->nexthop_mtu;
838 ctx->u.rinfo.zd_instance = re->instance;
839 ctx->u.rinfo.zd_tag = re->tag;
840 ctx->u.rinfo.zd_old_tag = re->tag;
841 ctx->u.rinfo.zd_distance = re->distance;
842
843 table = srcdest_rnode_table(rn);
844 info = table->info;
845
846 ctx->u.rinfo.zd_afi = info->afi;
847 ctx->u.rinfo.zd_safi = info->safi;
848
849 /* Extract ns info - can't use pointers to 'core' structs */
850 zvrf = vrf_info_lookup(re->vrf_id);
851 zns = zvrf->zns;
852
853 dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
854
855 /* Copy nexthops; recursive info is included too */
856 copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL);
857
858 /* TODO -- maybe use array of nexthops to avoid allocs? */
859
860 /* Ensure that the dplane's nexthops flags are clear. */
861 for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
862 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
863
864 /* Trying out the sequence number idea, so we can try to detect
865 * when a result is stale.
866 */
867 re->dplane_sequence = zebra_router_get_next_sequence();
868 ctx->zd_seq = re->dplane_sequence;
869
870 ret = AOK;
871
872 done:
873 return ret;
874 }
875
876 /*
877 * Capture information for an LSP update in a dplane context.
878 */
879 static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx,
880 enum dplane_op_e op,
881 zebra_lsp_t *lsp)
882 {
883 int ret = AOK;
884 zebra_nhlfe_t *nhlfe, *new_nhlfe;
885
886 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
887 zlog_debug("init dplane ctx %s: in-label %u ecmp# %d",
888 dplane_op2str(op), lsp->ile.in_label,
889 lsp->num_ecmp);
890
891 ctx->zd_op = op;
892 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
893
894 /* Capture namespace info */
895 dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
896 (op == DPLANE_OP_LSP_UPDATE));
897
898 memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
899
900 ctx->u.lsp.ile = lsp->ile;
901 ctx->u.lsp.addr_family = lsp->addr_family;
902 ctx->u.lsp.num_ecmp = lsp->num_ecmp;
903 ctx->u.lsp.flags = lsp->flags;
904
905 /* Copy source LSP's nhlfes, and capture 'best' nhlfe */
906 for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
907 /* Not sure if this is meaningful... */
908 if (nhlfe->nexthop == NULL)
909 continue;
910
911 new_nhlfe =
912 zebra_mpls_lsp_add_nhlfe(
913 &(ctx->u.lsp),
914 nhlfe->type,
915 nhlfe->nexthop->type,
916 &(nhlfe->nexthop->gate),
917 nhlfe->nexthop->ifindex,
918 nhlfe->nexthop->nh_label->label[0]);
919
920 if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) {
921 ret = ENOMEM;
922 break;
923 }
924
925 /* Need to copy flags too */
926 new_nhlfe->flags = nhlfe->flags;
927 new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
928
929 if (nhlfe == lsp->best_nhlfe)
930 ctx->u.lsp.best_nhlfe = new_nhlfe;
931 }
932
933 /* On error the ctx will be cleaned-up, so we don't need to
934 * deal with any allocated nhlfe or nexthop structs here.
935 */
936
937 return ret;
938 }
939
940 /*
941 * Enqueue a new route update,
942 * and ensure an event is active for the dataplane pthread.
943 */
944 static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
945 {
946 int ret = EINVAL;
947 uint32_t high, curr;
948
949 /* Enqueue for processing by the dataplane pthread */
950 DPLANE_LOCK();
951 {
952 TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx,
953 zd_q_entries);
954 }
955 DPLANE_UNLOCK();
956
957 curr = atomic_add_fetch_explicit(
958 #ifdef __clang__
959 /* TODO -- issue with the clang atomic/intrinsics currently;
960 * casting away the 'Atomic'-ness of the variable works.
961 */
962 (uint32_t *)&(zdplane_info.dg_routes_queued),
963 #else
964 &(zdplane_info.dg_routes_queued),
965 #endif
966 1, memory_order_seq_cst);
967
968 /* Maybe update high-water counter also */
969 high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
970 memory_order_seq_cst);
971 while (high < curr) {
972 if (atomic_compare_exchange_weak_explicit(
973 &zdplane_info.dg_routes_queued_max,
974 &high, curr,
975 memory_order_seq_cst,
976 memory_order_seq_cst))
977 break;
978 }
979
980 /* Ensure that an event for the dataplane thread is active */
981 ret = dplane_provider_work_ready();
982
983 return ret;
984 }
985
986 /*
987 * Utility that prepares a route update and enqueues it for processing
988 */
989 static enum zebra_dplane_result
990 dplane_route_update_internal(struct route_node *rn,
991 struct route_entry *re,
992 struct route_entry *old_re,
993 enum dplane_op_e op)
994 {
995 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
996 int ret = EINVAL;
997 struct zebra_dplane_ctx *ctx = NULL;
998
999 /* Obtain context block */
1000 ctx = dplane_ctx_alloc();
1001 if (ctx == NULL) {
1002 ret = ENOMEM;
1003 goto done;
1004 }
1005
1006 /* Init context with info from zebra data structs */
1007 ret = dplane_ctx_route_init(ctx, op, rn, re);
1008 if (ret == AOK) {
1009 /* Capture some extra info for update case
1010 * where there's a different 'old' route.
1011 */
1012 if ((op == DPLANE_OP_ROUTE_UPDATE) &&
1013 old_re && (old_re != re)) {
1014 ctx->zd_is_update = true;
1015
1016 old_re->dplane_sequence =
1017 zebra_router_get_next_sequence();
1018 ctx->zd_old_seq = old_re->dplane_sequence;
1019
1020 ctx->u.rinfo.zd_old_tag = old_re->tag;
1021 ctx->u.rinfo.zd_old_type = old_re->type;
1022 ctx->u.rinfo.zd_old_instance = old_re->instance;
1023 ctx->u.rinfo.zd_old_distance = old_re->distance;
1024 ctx->u.rinfo.zd_old_metric = old_re->metric;
1025
1026 #ifndef HAVE_NETLINK
1027 /* For bsd, capture previous re's nexthops too, sigh.
1028 * We'll need these to do per-nexthop deletes.
1029 */
1030 copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
1031 old_re->ng.nexthop, NULL);
1032 #endif /* !HAVE_NETLINK */
1033 }
1034
1035 /* Enqueue context for processing */
1036 ret = dplane_route_enqueue(ctx);
1037 }
1038
1039 done:
1040 /* Update counter */
1041 atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1,
1042 memory_order_relaxed);
1043
1044 if (ret == AOK)
1045 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1046 else {
1047 atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
1048 memory_order_relaxed);
1049 if (ctx)
1050 dplane_ctx_free(&ctx);
1051 }
1052
1053 return result;
1054 }
1055
1056 /*
1057 * Enqueue a route 'add' for the dataplane.
1058 */
1059 enum zebra_dplane_result dplane_route_add(struct route_node *rn,
1060 struct route_entry *re)
1061 {
1062 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1063
1064 if (rn == NULL || re == NULL)
1065 goto done;
1066
1067 ret = dplane_route_update_internal(rn, re, NULL,
1068 DPLANE_OP_ROUTE_INSTALL);
1069
1070 done:
1071 return ret;
1072 }
1073
1074 /*
1075 * Enqueue a route update for the dataplane.
1076 */
1077 enum zebra_dplane_result dplane_route_update(struct route_node *rn,
1078 struct route_entry *re,
1079 struct route_entry *old_re)
1080 {
1081 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1082
1083 if (rn == NULL || re == NULL)
1084 goto done;
1085
1086 ret = dplane_route_update_internal(rn, re, old_re,
1087 DPLANE_OP_ROUTE_UPDATE);
1088 done:
1089 return ret;
1090 }
1091
1092 /*
1093 * Enqueue a route removal for the dataplane.
1094 */
1095 enum zebra_dplane_result dplane_route_delete(struct route_node *rn,
1096 struct route_entry *re)
1097 {
1098 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1099
1100 if (rn == NULL || re == NULL)
1101 goto done;
1102
1103 ret = dplane_route_update_internal(rn, re, NULL,
1104 DPLANE_OP_ROUTE_DELETE);
1105
1106 done:
1107 return ret;
1108 }
1109
1110 /*
1111 * Enqueue LSP add for the dataplane.
1112 */
1113 enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp)
1114 {
1115 enum zebra_dplane_result ret =
1116 lsp_update_internal(lsp, DPLANE_OP_LSP_INSTALL);
1117
1118 return ret;
1119 }
1120
1121 /*
1122 * Enqueue LSP update for the dataplane.
1123 */
1124 enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp)
1125 {
1126 enum zebra_dplane_result ret =
1127 lsp_update_internal(lsp, DPLANE_OP_LSP_UPDATE);
1128
1129 return ret;
1130 }
1131
1132 /*
1133 * Enqueue LSP delete for the dataplane.
1134 */
1135 enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp)
1136 {
1137 enum zebra_dplane_result ret =
1138 lsp_update_internal(lsp, DPLANE_OP_LSP_DELETE);
1139
1140 return ret;
1141 }
1142
1143 /*
1144 * Common internal LSP update utility
1145 */
1146 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
1147 enum dplane_op_e op)
1148 {
1149 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1150 int ret = EINVAL;
1151 struct zebra_dplane_ctx *ctx = NULL;
1152
1153 /* Obtain context block */
1154 ctx = dplane_ctx_alloc();
1155 if (ctx == NULL) {
1156 ret = ENOMEM;
1157 goto done;
1158 }
1159
1160 ret = dplane_ctx_lsp_init(ctx, op, lsp);
1161 if (ret != AOK)
1162 goto done;
1163
1164 ret = dplane_route_enqueue(ctx);
1165
1166 done:
1167 /* Update counter */
1168 atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1,
1169 memory_order_relaxed);
1170
1171 if (ret == AOK)
1172 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1173 else {
1174 atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
1175 memory_order_relaxed);
1176 if (ctx)
1177 dplane_ctx_free(&ctx);
1178 }
1179
1180 return result;
1181 }
1182
1183 /*
1184 * Handler for 'show dplane'
1185 */
1186 int dplane_show_helper(struct vty *vty, bool detailed)
1187 {
1188 uint64_t queued, queue_max, limit, errs, incoming, yields,
1189 other_errs;
1190
1191 /* Using atomics because counters are being changed in different
1192 * pthread contexts.
1193 */
1194 incoming = atomic_load_explicit(&zdplane_info.dg_routes_in,
1195 memory_order_relaxed);
1196 limit = atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
1197 memory_order_relaxed);
1198 queued = atomic_load_explicit(&zdplane_info.dg_routes_queued,
1199 memory_order_relaxed);
1200 queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
1201 memory_order_relaxed);
1202 errs = atomic_load_explicit(&zdplane_info.dg_route_errors,
1203 memory_order_relaxed);
1204 yields = atomic_load_explicit(&zdplane_info.dg_update_yields,
1205 memory_order_relaxed);
1206 other_errs = atomic_load_explicit(&zdplane_info.dg_other_errors,
1207 memory_order_relaxed);
1208
1209 vty_out(vty, "Zebra dataplane:\nRoute updates: %"PRIu64"\n",
1210 incoming);
1211 vty_out(vty, "Route update errors: %"PRIu64"\n", errs);
1212 vty_out(vty, "Other errors : %"PRIu64"\n", other_errs);
1213 vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit);
1214 vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued);
1215 vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max);
1216 vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields);
1217
1218 return CMD_SUCCESS;
1219 }
1220
1221 /*
1222 * Handler for 'show dplane providers'
1223 */
1224 int dplane_show_provs_helper(struct vty *vty, bool detailed)
1225 {
1226 struct zebra_dplane_provider *prov;
1227 uint64_t in, in_max, out, out_max;
1228
1229 vty_out(vty, "Zebra dataplane providers:\n");
1230
1231 DPLANE_LOCK();
1232 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1233 DPLANE_UNLOCK();
1234
1235 /* Show counters, useful info from each registered provider */
1236 while (prov) {
1237
1238 in = atomic_load_explicit(&prov->dp_in_counter,
1239 memory_order_relaxed);
1240 in_max = atomic_load_explicit(&prov->dp_in_max,
1241 memory_order_relaxed);
1242 out = atomic_load_explicit(&prov->dp_out_counter,
1243 memory_order_relaxed);
1244 out_max = atomic_load_explicit(&prov->dp_out_max,
1245 memory_order_relaxed);
1246
1247 vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", "
1248 "out: %"PRIu64", q_max: %"PRIu64"\n",
1249 prov->dp_name, prov->dp_id, in, in_max, out, out_max);
1250
1251 DPLANE_LOCK();
1252 prov = TAILQ_NEXT(prov, dp_prov_link);
1253 DPLANE_UNLOCK();
1254 }
1255
1256 return CMD_SUCCESS;
1257 }
1258
1259 /*
1260 * Provider registration
1261 */
1262 int dplane_provider_register(const char *name,
1263 enum dplane_provider_prio prio,
1264 int flags,
1265 int (*fp)(struct zebra_dplane_provider *),
1266 int (*fini_fp)(struct zebra_dplane_provider *,
1267 bool early),
1268 void *data,
1269 struct zebra_dplane_provider **prov_p)
1270 {
1271 int ret = 0;
1272 struct zebra_dplane_provider *p = NULL, *last;
1273
1274 /* Validate */
1275 if (fp == NULL) {
1276 ret = EINVAL;
1277 goto done;
1278 }
1279
1280 if (prio <= DPLANE_PRIO_NONE ||
1281 prio > DPLANE_PRIO_LAST) {
1282 ret = EINVAL;
1283 goto done;
1284 }
1285
1286 /* Allocate and init new provider struct */
1287 p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider));
1288 if (p == NULL) {
1289 ret = ENOMEM;
1290 goto done;
1291 }
1292
1293 pthread_mutex_init(&(p->dp_mutex), NULL);
1294 TAILQ_INIT(&(p->dp_ctx_in_q));
1295 TAILQ_INIT(&(p->dp_ctx_out_q));
1296
1297 p->dp_priority = prio;
1298 p->dp_fp = fp;
1299 p->dp_fini = fini_fp;
1300 p->dp_data = data;
1301
1302 /* Lock - the dplane pthread may be running */
1303 DPLANE_LOCK();
1304
1305 p->dp_id = ++zdplane_info.dg_provider_id;
1306
1307 if (name)
1308 strlcpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN);
1309 else
1310 snprintf(p->dp_name, DPLANE_PROVIDER_NAMELEN,
1311 "provider-%u", p->dp_id);
1312
1313 /* Insert into list ordered by priority */
1314 TAILQ_FOREACH(last, &zdplane_info.dg_providers_q, dp_prov_link) {
1315 if (last->dp_priority > p->dp_priority)
1316 break;
1317 }
1318
1319 if (last)
1320 TAILQ_INSERT_BEFORE(last, p, dp_prov_link);
1321 else
1322 TAILQ_INSERT_TAIL(&zdplane_info.dg_providers_q, p,
1323 dp_prov_link);
1324
1325 /* And unlock */
1326 DPLANE_UNLOCK();
1327
1328 if (IS_ZEBRA_DEBUG_DPLANE)
1329 zlog_debug("dplane: registered new provider '%s' (%u), prio %d",
1330 p->dp_name, p->dp_id, p->dp_priority);
1331
1332 done:
1333 if (prov_p)
1334 *prov_p = p;
1335
1336 return ret;
1337 }
1338
1339 /* Accessors for provider attributes */
1340 const char *dplane_provider_get_name(const struct zebra_dplane_provider *prov)
1341 {
1342 return prov->dp_name;
1343 }
1344
1345 uint32_t dplane_provider_get_id(const struct zebra_dplane_provider *prov)
1346 {
1347 return prov->dp_id;
1348 }
1349
1350 void *dplane_provider_get_data(const struct zebra_dplane_provider *prov)
1351 {
1352 return prov->dp_data;
1353 }
1354
1355 int dplane_provider_get_work_limit(const struct zebra_dplane_provider *prov)
1356 {
1357 return zdplane_info.dg_updates_per_cycle;
1358 }
1359
1360 /* Lock/unlock a provider's mutex - iff the provider was registered with
1361 * the THREADED flag.
1362 */
1363 void dplane_provider_lock(struct zebra_dplane_provider *prov)
1364 {
1365 if (dplane_provider_is_threaded(prov))
1366 DPLANE_PROV_LOCK(prov);
1367 }
1368
1369 void dplane_provider_unlock(struct zebra_dplane_provider *prov)
1370 {
1371 if (dplane_provider_is_threaded(prov))
1372 DPLANE_PROV_UNLOCK(prov);
1373 }
1374
1375 /*
1376 * Dequeue and maintain associated counter
1377 */
1378 struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx(
1379 struct zebra_dplane_provider *prov)
1380 {
1381 struct zebra_dplane_ctx *ctx = NULL;
1382
1383 dplane_provider_lock(prov);
1384
1385 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1386 if (ctx) {
1387 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
1388
1389 atomic_fetch_sub_explicit(&prov->dp_in_queued, 1,
1390 memory_order_relaxed);
1391 }
1392
1393 dplane_provider_unlock(prov);
1394
1395 return ctx;
1396 }
1397
1398 /*
1399 * Dequeue work to a list, return count
1400 */
1401 int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
1402 struct dplane_ctx_q *listp)
1403 {
1404 int limit, ret;
1405 struct zebra_dplane_ctx *ctx;
1406
1407 limit = zdplane_info.dg_updates_per_cycle;
1408
1409 dplane_provider_lock(prov);
1410
1411 for (ret = 0; ret < limit; ret++) {
1412 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1413 if (ctx) {
1414 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
1415
1416 TAILQ_INSERT_TAIL(listp, ctx, zd_q_entries);
1417 } else {
1418 break;
1419 }
1420 }
1421
1422 if (ret > 0)
1423 atomic_fetch_sub_explicit(&prov->dp_in_queued, ret,
1424 memory_order_relaxed);
1425
1426 dplane_provider_unlock(prov);
1427
1428 return ret;
1429 }
1430
1431 /*
1432 * Enqueue and maintain associated counter
1433 */
1434 void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
1435 struct zebra_dplane_ctx *ctx)
1436 {
1437 dplane_provider_lock(prov);
1438
1439 TAILQ_INSERT_TAIL(&(prov->dp_ctx_out_q), ctx,
1440 zd_q_entries);
1441
1442 dplane_provider_unlock(prov);
1443
1444 atomic_fetch_add_explicit(&(prov->dp_out_counter), 1,
1445 memory_order_relaxed);
1446 }
1447
1448 /*
1449 * Accessor for provider object
1450 */
1451 bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
1452 {
1453 return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
1454 }
1455
1456 /*
1457 * Internal helper that copies information from a zebra ns object; this is
1458 * called in the zebra main pthread context as part of dplane ctx init.
1459 */
1460 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
1461 struct zebra_ns *zns)
1462 {
1463 ns_info->ns_id = zns->ns_id;
1464
1465 #if defined(HAVE_NETLINK)
1466 ns_info->is_cmd = true;
1467 ns_info->nls = zns->netlink_dplane;
1468 #endif /* NETLINK */
1469 }
1470
1471 /*
1472 * Provider api to signal that work/events are available
1473 * for the dataplane pthread.
1474 */
1475 int dplane_provider_work_ready(void)
1476 {
1477 /* Note that during zebra startup, we may be offered work before
1478 * the dataplane pthread (and thread-master) are ready. We want to
1479 * enqueue the work, but the event-scheduling machinery may not be
1480 * available.
1481 */
1482 if (zdplane_info.dg_run) {
1483 thread_add_event(zdplane_info.dg_master,
1484 dplane_thread_loop, NULL, 0,
1485 &zdplane_info.dg_t_update);
1486 }
1487
1488 return AOK;
1489 }
1490
1491 /*
1492 * Kernel dataplane provider
1493 */
1494
1495 /*
1496 * Handler for kernel LSP updates
1497 */
1498 static enum zebra_dplane_result
1499 kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
1500 {
1501 enum zebra_dplane_result res;
1502
1503 /* Call into the synchronous kernel-facing code here */
1504 res = kernel_lsp_update(ctx);
1505
1506 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
1507 atomic_fetch_add_explicit(
1508 &zdplane_info.dg_lsp_errors, 1,
1509 memory_order_relaxed);
1510
1511 return res;
1512 }
1513
1514 /*
1515 * Handler for kernel route updates
1516 */
1517 static enum zebra_dplane_result
1518 kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
1519 {
1520 enum zebra_dplane_result res;
1521
1522 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
1523 char dest_str[PREFIX_STRLEN];
1524
1525 prefix2str(dplane_ctx_get_dest(ctx),
1526 dest_str, sizeof(dest_str));
1527
1528 zlog_debug("%u:%s Dplane route update ctx %p op %s",
1529 dplane_ctx_get_vrf(ctx), dest_str,
1530 ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
1531 }
1532
1533 /* Call into the synchronous kernel-facing code here */
1534 res = kernel_route_update(ctx);
1535
1536 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
1537 atomic_fetch_add_explicit(
1538 &zdplane_info.dg_route_errors, 1,
1539 memory_order_relaxed);
1540
1541 return res;
1542 }
1543
1544 /*
1545 * Kernel provider callback
1546 */
1547 static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
1548 {
1549 enum zebra_dplane_result res;
1550 struct zebra_dplane_ctx *ctx;
1551 int counter, limit;
1552
1553 limit = dplane_provider_get_work_limit(prov);
1554
1555 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1556 zlog_debug("dplane provider '%s': processing",
1557 dplane_provider_get_name(prov));
1558
1559 for (counter = 0; counter < limit; counter++) {
1560
1561 ctx = dplane_provider_dequeue_in_ctx(prov);
1562 if (ctx == NULL)
1563 break;
1564
1565 /* Dispatch to appropriate kernel-facing apis */
1566 switch (dplane_ctx_get_op(ctx)) {
1567
1568 case DPLANE_OP_ROUTE_INSTALL:
1569 case DPLANE_OP_ROUTE_UPDATE:
1570 case DPLANE_OP_ROUTE_DELETE:
1571 res = kernel_dplane_route_update(ctx);
1572 break;
1573
1574 case DPLANE_OP_LSP_INSTALL:
1575 case DPLANE_OP_LSP_UPDATE:
1576 case DPLANE_OP_LSP_DELETE:
1577 res = kernel_dplane_lsp_update(ctx);
1578 break;
1579
1580 default:
1581 atomic_fetch_add_explicit(
1582 &zdplane_info.dg_other_errors, 1,
1583 memory_order_relaxed);
1584
1585 res = ZEBRA_DPLANE_REQUEST_FAILURE;
1586 break;
1587 }
1588
1589 dplane_ctx_set_status(ctx, res);
1590
1591 dplane_provider_enqueue_out_ctx(prov, ctx);
1592 }
1593
1594 /* Ensure that we'll run the work loop again if there's still
1595 * more work to do.
1596 */
1597 if (counter >= limit) {
1598 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1599 zlog_debug("dplane provider '%s' reached max updates %d",
1600 dplane_provider_get_name(prov), counter);
1601
1602 atomic_fetch_add_explicit(&zdplane_info.dg_update_yields,
1603 1, memory_order_relaxed);
1604
1605 dplane_provider_work_ready();
1606 }
1607
1608 return 0;
1609 }
1610
1611 #if DPLANE_TEST_PROVIDER
1612
1613 /*
1614 * Test dataplane provider plugin
1615 */
1616
1617 /*
1618 * Test provider process callback
1619 */
1620 static int test_dplane_process_func(struct zebra_dplane_provider *prov)
1621 {
1622 struct zebra_dplane_ctx *ctx;
1623 int counter, limit;
1624
1625 /* Just moving from 'in' queue to 'out' queue */
1626
1627 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1628 zlog_debug("dplane provider '%s': processing",
1629 dplane_provider_get_name(prov));
1630
1631 limit = dplane_provider_get_work_limit(prov);
1632
1633 for (counter = 0; counter < limit; counter++) {
1634
1635 ctx = dplane_provider_dequeue_in_ctx(prov);
1636 if (ctx == NULL)
1637 break;
1638
1639 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
1640
1641 dplane_provider_enqueue_out_ctx(prov, ctx);
1642 }
1643
1644 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1645 zlog_debug("dplane provider '%s': processed %d",
1646 dplane_provider_get_name(prov), counter);
1647
1648 /* Ensure that we'll run the work loop again if there's still
1649 * more work to do.
1650 */
1651 if (counter >= limit)
1652 dplane_provider_work_ready();
1653
1654 return 0;
1655 }
1656
1657 /*
1658 * Test provider shutdown/fini callback
1659 */
1660 static int test_dplane_shutdown_func(struct zebra_dplane_provider *prov,
1661 bool early)
1662 {
1663 if (IS_ZEBRA_DEBUG_DPLANE)
1664 zlog_debug("dplane provider '%s': %sshutdown",
1665 dplane_provider_get_name(prov),
1666 early ? "early " : "");
1667
1668 return 0;
1669 }
1670 #endif /* DPLANE_TEST_PROVIDER */
1671
1672 /*
1673 * Register default kernel provider
1674 */
1675 static void dplane_provider_init(void)
1676 {
1677 int ret;
1678
1679 ret = dplane_provider_register("Kernel",
1680 DPLANE_PRIO_KERNEL,
1681 DPLANE_PROV_FLAGS_DEFAULT,
1682 kernel_dplane_process_func,
1683 NULL,
1684 NULL, NULL);
1685
1686 if (ret != AOK)
1687 zlog_err("Unable to register kernel dplane provider: %d",
1688 ret);
1689
1690 #if DPLANE_TEST_PROVIDER
1691 /* Optional test provider ... */
1692 ret = dplane_provider_register("Test",
1693 DPLANE_PRIO_PRE_KERNEL,
1694 DPLANE_PROV_FLAGS_DEFAULT,
1695 test_dplane_process_func,
1696 test_dplane_shutdown_func,
1697 NULL /* data */, NULL);
1698
1699 if (ret != AOK)
1700 zlog_err("Unable to register test dplane provider: %d",
1701 ret);
1702 #endif /* DPLANE_TEST_PROVIDER */
1703 }
1704
1705 /* Indicates zebra shutdown/exit is in progress. Some operations may be
1706 * simplified or skipped during shutdown processing.
1707 */
1708 bool dplane_is_in_shutdown(void)
1709 {
1710 return zdplane_info.dg_is_shutdown;
1711 }
1712
1713 /*
1714 * Early or pre-shutdown, de-init notification api. This runs pretty
1715 * early during zebra shutdown, as a signal to stop new work and prepare
1716 * for updates generated by shutdown/cleanup activity, as zebra tries to
1717 * remove everything it's responsible for.
1718 * NB: This runs in the main zebra pthread context.
1719 */
1720 void zebra_dplane_pre_finish(void)
1721 {
1722 if (IS_ZEBRA_DEBUG_DPLANE)
1723 zlog_debug("Zebra dataplane pre-fini called");
1724
1725 zdplane_info.dg_is_shutdown = true;
1726
1727 /* TODO -- Notify provider(s) of pending shutdown */
1728 }
1729
1730 /*
1731 * Utility to determine whether work remains enqueued within the dplane;
1732 * used during system shutdown processing.
1733 */
1734 static bool dplane_work_pending(void)
1735 {
1736 bool ret = false;
1737 struct zebra_dplane_ctx *ctx;
1738 struct zebra_dplane_provider *prov;
1739
1740 /* TODO -- just checking incoming/pending work for now, must check
1741 * providers
1742 */
1743 DPLANE_LOCK();
1744 {
1745 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
1746 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1747 }
1748 DPLANE_UNLOCK();
1749
1750 if (ctx != NULL) {
1751 ret = true;
1752 goto done;
1753 }
1754
1755 while (prov) {
1756
1757 dplane_provider_lock(prov);
1758
1759 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1760 if (ctx == NULL)
1761 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
1762
1763 dplane_provider_unlock(prov);
1764
1765 if (ctx != NULL)
1766 break;
1767
1768 DPLANE_LOCK();
1769 prov = TAILQ_NEXT(prov, dp_prov_link);
1770 DPLANE_UNLOCK();
1771 }
1772
1773 if (ctx != NULL)
1774 ret = true;
1775
1776 done:
1777 return ret;
1778 }
1779
1780 /*
1781 * Shutdown-time intermediate callback, used to determine when all pending
1782 * in-flight updates are done. If there's still work to do, reschedules itself.
1783 * If all work is done, schedules an event to the main zebra thread for
1784 * final zebra shutdown.
1785 * This runs in the dplane pthread context.
1786 */
1787 static int dplane_check_shutdown_status(struct thread *event)
1788 {
1789 if (IS_ZEBRA_DEBUG_DPLANE)
1790 zlog_debug("Zebra dataplane shutdown status check called");
1791
1792 if (dplane_work_pending()) {
1793 /* Reschedule dplane check on a short timer */
1794 thread_add_timer_msec(zdplane_info.dg_master,
1795 dplane_check_shutdown_status,
1796 NULL, 100,
1797 &zdplane_info.dg_t_shutdown_check);
1798
1799 /* TODO - give up and stop waiting after a short time? */
1800
1801 } else {
1802 /* We appear to be done - schedule a final callback event
1803 * for the zebra main pthread.
1804 */
1805 thread_add_event(zebrad.master, zebra_finalize, NULL, 0, NULL);
1806 }
1807
1808 return 0;
1809 }
1810
1811 /*
1812 * Shutdown, de-init api. This runs pretty late during shutdown,
1813 * after zebra has tried to free/remove/uninstall all routes during shutdown.
1814 * At this point, dplane work may still remain to be done, so we can't just
1815 * blindly terminate. If there's still work to do, we'll periodically check
1816 * and when done, we'll enqueue a task to the zebra main thread for final
1817 * termination processing.
1818 *
1819 * NB: This runs in the main zebra thread context.
1820 */
1821 void zebra_dplane_finish(void)
1822 {
1823 if (IS_ZEBRA_DEBUG_DPLANE)
1824 zlog_debug("Zebra dataplane fini called");
1825
1826 thread_add_event(zdplane_info.dg_master,
1827 dplane_check_shutdown_status, NULL, 0,
1828 &zdplane_info.dg_t_shutdown_check);
1829 }
1830
1831 /*
1832 * Main dataplane pthread event loop. The thread takes new incoming work
1833 * and offers it to the first provider. It then iterates through the
1834 * providers, taking complete work from each one and offering it
1835 * to the next in order. At each step, a limited number of updates are
1836 * processed during a cycle in order to provide some fairness.
1837 *
1838 * This loop through the providers is only run once, so that the dataplane
1839 * pthread can look for other pending work - such as i/o work on behalf of
1840 * providers.
1841 */
1842 static int dplane_thread_loop(struct thread *event)
1843 {
1844 struct dplane_ctx_q work_list;
1845 struct dplane_ctx_q error_list;
1846 struct zebra_dplane_provider *prov;
1847 struct zebra_dplane_ctx *ctx, *tctx;
1848 int limit, counter, error_counter;
1849 uint64_t curr, high;
1850
1851 /* Capture work limit per cycle */
1852 limit = zdplane_info.dg_updates_per_cycle;
1853
1854 /* Init temporary lists used to move contexts among providers */
1855 TAILQ_INIT(&work_list);
1856 TAILQ_INIT(&error_list);
1857 error_counter = 0;
1858
1859 /* Check for zebra shutdown */
1860 if (!zdplane_info.dg_run)
1861 goto done;
1862
1863 /* Dequeue some incoming work from zebra (if any) onto the temporary
1864 * working list.
1865 */
1866 DPLANE_LOCK();
1867
1868 /* Locate initial registered provider */
1869 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1870
1871 /* Move new work from incoming list to temp list */
1872 for (counter = 0; counter < limit; counter++) {
1873 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
1874 if (ctx) {
1875 TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx,
1876 zd_q_entries);
1877
1878 ctx->zd_provider = prov->dp_id;
1879
1880 TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
1881 } else {
1882 break;
1883 }
1884 }
1885
1886 DPLANE_UNLOCK();
1887
1888 atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, counter,
1889 memory_order_relaxed);
1890
1891 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1892 zlog_debug("dplane: incoming new work counter: %d", counter);
1893
1894 /* Iterate through the registered providers, offering new incoming
1895 * work. If the provider has outgoing work in its queue, take that
1896 * work for the next provider
1897 */
1898 while (prov) {
1899
1900 /* At each iteration, the temporary work list has 'counter'
1901 * items.
1902 */
1903 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1904 zlog_debug("dplane enqueues %d new work to provider '%s'",
1905 counter, dplane_provider_get_name(prov));
1906
1907 /* Capture current provider id in each context; check for
1908 * error status.
1909 */
1910 TAILQ_FOREACH_SAFE(ctx, &work_list, zd_q_entries, tctx) {
1911 if (dplane_ctx_get_status(ctx) ==
1912 ZEBRA_DPLANE_REQUEST_SUCCESS) {
1913 ctx->zd_provider = prov->dp_id;
1914 } else {
1915 /*
1916 * TODO -- improve error-handling: recirc
1917 * errors backwards so that providers can
1918 * 'undo' their work (if they want to)
1919 */
1920
1921 /* Move to error list; will be returned
1922 * zebra main.
1923 */
1924 TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
1925 TAILQ_INSERT_TAIL(&error_list,
1926 ctx, zd_q_entries);
1927 error_counter++;
1928 }
1929 }
1930
1931 /* Enqueue new work to the provider */
1932 dplane_provider_lock(prov);
1933
1934 if (TAILQ_FIRST(&work_list))
1935 TAILQ_CONCAT(&(prov->dp_ctx_in_q), &work_list,
1936 zd_q_entries);
1937
1938 atomic_fetch_add_explicit(&prov->dp_in_counter, counter,
1939 memory_order_relaxed);
1940 atomic_fetch_add_explicit(&prov->dp_in_queued, counter,
1941 memory_order_relaxed);
1942 curr = atomic_load_explicit(&prov->dp_in_queued,
1943 memory_order_relaxed);
1944 high = atomic_load_explicit(&prov->dp_in_max,
1945 memory_order_relaxed);
1946 if (curr > high)
1947 atomic_store_explicit(&prov->dp_in_max, curr,
1948 memory_order_relaxed);
1949
1950 dplane_provider_unlock(prov);
1951
1952 /* Reset the temp list (though the 'concat' may have done this
1953 * already), and the counter
1954 */
1955 TAILQ_INIT(&work_list);
1956 counter = 0;
1957
1958 /* Call into the provider code. Note that this is
1959 * unconditional: we offer to do work even if we don't enqueue
1960 * any _new_ work.
1961 */
1962 (*prov->dp_fp)(prov);
1963
1964 /* Check for zebra shutdown */
1965 if (!zdplane_info.dg_run)
1966 break;
1967
1968 /* Dequeue completed work from the provider */
1969 dplane_provider_lock(prov);
1970
1971 while (counter < limit) {
1972 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
1973 if (ctx) {
1974 TAILQ_REMOVE(&(prov->dp_ctx_out_q), ctx,
1975 zd_q_entries);
1976
1977 TAILQ_INSERT_TAIL(&work_list,
1978 ctx, zd_q_entries);
1979 counter++;
1980 } else
1981 break;
1982 }
1983
1984 dplane_provider_unlock(prov);
1985
1986 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1987 zlog_debug("dplane dequeues %d completed work from provider %s",
1988 counter, dplane_provider_get_name(prov));
1989
1990 /* Locate next provider */
1991 DPLANE_LOCK();
1992 prov = TAILQ_NEXT(prov, dp_prov_link);
1993 DPLANE_UNLOCK();
1994 }
1995
1996 /* After all providers have been serviced, enqueue any completed
1997 * work and any errors back to zebra so it can process the results.
1998 */
1999 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2000 zlog_debug("dplane has %d completed, %d errors, for zebra main",
2001 counter, error_counter);
2002
2003 /*
2004 * Hand lists through the api to zebra main,
2005 * to reduce the number of lock/unlock cycles
2006 */
2007
2008 /* Call through to zebra main */
2009 (zdplane_info.dg_results_cb)(&error_list);
2010
2011 TAILQ_INIT(&error_list);
2012
2013
2014 /* Call through to zebra main */
2015 (zdplane_info.dg_results_cb)(&work_list);
2016
2017 TAILQ_INIT(&work_list);
2018
2019 done:
2020 return 0;
2021 }
2022
2023 /*
2024 * Final phase of shutdown, after all work enqueued to dplane has been
2025 * processed. This is called from the zebra main pthread context.
2026 */
2027 void zebra_dplane_shutdown(void)
2028 {
2029 if (IS_ZEBRA_DEBUG_DPLANE)
2030 zlog_debug("Zebra dataplane shutdown called");
2031
2032 /* Stop dplane thread, if it's running */
2033
2034 zdplane_info.dg_run = false;
2035
2036 THREAD_OFF(zdplane_info.dg_t_update);
2037
2038 frr_pthread_stop(zdplane_info.dg_pthread, NULL);
2039
2040 /* Destroy pthread */
2041 frr_pthread_destroy(zdplane_info.dg_pthread);
2042 zdplane_info.dg_pthread = NULL;
2043 zdplane_info.dg_master = NULL;
2044
2045 /* TODO -- Notify provider(s) of final shutdown */
2046
2047 /* TODO -- Clean-up provider objects */
2048
2049 /* TODO -- Clean queue(s), free memory */
2050 }
2051
2052 /*
2053 * Initialize the dataplane module during startup, internal/private version
2054 */
2055 static void zebra_dplane_init_internal(struct zebra_t *zebra)
2056 {
2057 memset(&zdplane_info, 0, sizeof(zdplane_info));
2058
2059 pthread_mutex_init(&zdplane_info.dg_mutex, NULL);
2060
2061 TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
2062 TAILQ_INIT(&zdplane_info.dg_providers_q);
2063
2064 zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
2065
2066 zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED;
2067
2068 /* Register default kernel 'provider' during init */
2069 dplane_provider_init();
2070 }
2071
2072 /*
2073 * Start the dataplane pthread. This step needs to be run later than the
2074 * 'init' step, in case zebra has fork-ed.
2075 */
2076 void zebra_dplane_start(void)
2077 {
2078 /* Start dataplane pthread */
2079
2080 struct frr_pthread_attr pattr = {
2081 .start = frr_pthread_attr_default.start,
2082 .stop = frr_pthread_attr_default.stop
2083 };
2084
2085 zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
2086 "Zebra dplane");
2087
2088 zdplane_info.dg_master = zdplane_info.dg_pthread->master;
2089
2090 zdplane_info.dg_run = true;
2091
2092 /* Enqueue an initial event for the dataplane pthread */
2093 thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
2094 &zdplane_info.dg_t_update);
2095
2096 frr_pthread_run(zdplane_info.dg_pthread, NULL);
2097 }
2098
2099 /*
2100 * Initialize the dataplane module at startup; called by zebra rib_init()
2101 */
2102 void zebra_dplane_init(int (*results_fp)(struct dplane_ctx_q *))
2103 {
2104 zebra_dplane_init_internal(&zebrad);
2105 zdplane_info.dg_results_cb = results_fp;
2106 }