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