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