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