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