]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_dplane.c
Merge pull request #3591 from donaldsharp/sharp_v6
[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
4c206c8f 160 int (*dp_fp)(struct zebra_dplane_provider *prov);
7cdb1a84 161
4c206c8f 162 int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
18c37974 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' */
4c206c8f 192 int (*dg_results_cb)(struct dplane_ctx_q *ctxlist);
7cdb1a84 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,
4c206c8f
MS
991 int (*fp)(struct zebra_dplane_provider *),
992 int (*fini_fp)(struct zebra_dplane_provider *,
993 bool early),
c831033f 994 void *data)
b8e0423d
MS
995{
996 int ret = 0;
25779064 997 struct zebra_dplane_provider *p, *last;
b8e0423d
MS
998
999 /* Validate */
1000 if (fp == NULL) {
1001 ret = EINVAL;
1002 goto done;
1003 }
1004
1005 if (prio <= DPLANE_PRIO_NONE ||
1bcea841 1006 prio > DPLANE_PRIO_LAST) {
b8e0423d
MS
1007 ret = EINVAL;
1008 goto done;
1009 }
1010
1011 /* Allocate and init new provider struct */
25779064 1012 p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider));
b8e0423d
MS
1013 if (p == NULL) {
1014 ret = ENOMEM;
1015 goto done;
1016 }
1017
c831033f
MS
1018 pthread_mutex_init(&(p->dp_mutex), NULL);
1019 TAILQ_INIT(&(p->dp_ctx_in_q));
1020 TAILQ_INIT(&(p->dp_ctx_out_q));
b8e0423d
MS
1021
1022 p->dp_priority = prio;
1023 p->dp_fp = fp;
18c37974 1024 p->dp_fini = fini_fp;
c831033f 1025 p->dp_data = data;
18c37974 1026
c831033f 1027 /* Lock - the dplane pthread may be running */
18c37974 1028 DPLANE_LOCK();
b8e0423d 1029
25779064 1030 p->dp_id = ++zdplane_info.dg_provider_id;
b8e0423d 1031
c831033f
MS
1032 if (name)
1033 strlcpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN);
1034 else
1035 snprintf(p->dp_name, DPLANE_PROVIDER_NAMELEN,
1036 "provider-%u", p->dp_id);
1037
b8e0423d 1038 /* Insert into list ordered by priority */
c831033f 1039 TAILQ_FOREACH(last, &zdplane_info.dg_providers_q, dp_prov_link) {
5709131c 1040 if (last->dp_priority > p->dp_priority)
b8e0423d 1041 break;
b8e0423d
MS
1042 }
1043
5709131c 1044 if (last)
c831033f 1045 TAILQ_INSERT_BEFORE(last, p, dp_prov_link);
5709131c 1046 else
25779064 1047 TAILQ_INSERT_TAIL(&zdplane_info.dg_providers_q, p,
c831033f 1048 dp_prov_link);
b8e0423d 1049
18c37974
MS
1050 /* And unlock */
1051 DPLANE_UNLOCK();
1052
c831033f
MS
1053 if (IS_ZEBRA_DEBUG_DPLANE)
1054 zlog_debug("dplane: registered new provider '%s' (%u), prio %d",
1055 p->dp_name, p->dp_id, p->dp_priority);
1056
b8e0423d 1057done:
5709131c 1058 return ret;
b8e0423d
MS
1059}
1060
c831033f
MS
1061/* Accessors for provider attributes */
1062const char *dplane_provider_get_name(const struct zebra_dplane_provider *prov)
1063{
1064 return prov->dp_name;
1065}
1066
1067uint32_t dplane_provider_get_id(const struct zebra_dplane_provider *prov)
1068{
1069 return prov->dp_id;
1070}
1071
1072void *dplane_provider_get_data(const struct zebra_dplane_provider *prov)
1073{
1074 return prov->dp_data;
1075}
1076
1077int dplane_provider_get_work_limit(const struct zebra_dplane_provider *prov)
1078{
1079 return zdplane_info.dg_updates_per_cycle;
1080}
1081
ad6aad4d
MS
1082/* Lock/unlock a provider's mutex - iff the provider was registered with
1083 * the THREADED flag.
1084 */
1085void dplane_provider_lock(struct zebra_dplane_provider *prov)
1086{
1087 if (dplane_provider_is_threaded(prov))
1088 DPLANE_PROV_LOCK(prov);
1089}
1090
1091void dplane_provider_unlock(struct zebra_dplane_provider *prov)
1092{
1093 if (dplane_provider_is_threaded(prov))
1094 DPLANE_PROV_UNLOCK(prov);
1095}
1096
c831033f
MS
1097/*
1098 * Dequeue and maintain associated counter
1099 */
1100struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx(
1101 struct zebra_dplane_provider *prov)
1102{
1103 struct zebra_dplane_ctx *ctx = NULL;
1104
ad6aad4d 1105 dplane_provider_lock(prov);
c831033f
MS
1106
1107 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1108 if (ctx) {
1109 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
c9d17fe8
MS
1110
1111 atomic_fetch_sub_explicit(&prov->dp_in_queued, 1,
1112 memory_order_relaxed);
c831033f
MS
1113 }
1114
ad6aad4d 1115 dplane_provider_unlock(prov);
c831033f
MS
1116
1117 return ctx;
1118}
1119
1120/*
1121 * Dequeue work to a list, return count
1122 */
1123int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
1124 struct dplane_ctx_q *listp)
1125{
1126 int limit, ret;
1127 struct zebra_dplane_ctx *ctx;
1128
1129 limit = zdplane_info.dg_updates_per_cycle;
1130
ad6aad4d 1131 dplane_provider_lock(prov);
c831033f
MS
1132
1133 for (ret = 0; ret < limit; ret++) {
1134 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1135 if (ctx) {
1136 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
1137
1138 TAILQ_INSERT_TAIL(listp, ctx, zd_q_entries);
1139 } else {
1140 break;
1141 }
1142 }
1143
c9d17fe8
MS
1144 if (ret > 0)
1145 atomic_fetch_sub_explicit(&prov->dp_in_queued, ret,
1146 memory_order_relaxed);
1147
ad6aad4d 1148 dplane_provider_unlock(prov);
c831033f
MS
1149
1150 return ret;
1151}
1152
1153/*
1154 * Enqueue and maintain associated counter
1155 */
1156void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
1157 struct zebra_dplane_ctx *ctx)
1158{
ad6aad4d 1159 dplane_provider_lock(prov);
c831033f
MS
1160
1161 TAILQ_INSERT_TAIL(&(prov->dp_ctx_out_q), ctx,
1162 zd_q_entries);
1163
ad6aad4d 1164 dplane_provider_unlock(prov);
c831033f
MS
1165
1166 atomic_fetch_add_explicit(&(prov->dp_out_counter), 1,
1167 memory_order_relaxed);
1168}
1169
62b8bb7a
MS
1170/*
1171 * Accessor for provider object
1172 */
c831033f
MS
1173bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
1174{
1175 return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
1176}
1177
62b8bb7a
MS
1178/*
1179 * Internal helper that copies information from a zebra ns object; this is
1180 * called in the zebra main pthread context as part of dplane ctx init.
1181 */
1182static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
1183 struct zebra_ns *zns)
1184{
1185 ns_info->ns_id = zns->ns_id;
1186
1187#if defined(HAVE_NETLINK)
1188 ns_info->is_cmd = true;
1189 ns_info->nls = zns->netlink_dplane;
1190#endif /* NETLINK */
1191}
1192
c831033f
MS
1193/*
1194 * Provider api to signal that work/events are available
1195 * for the dataplane pthread.
1196 */
1197int dplane_provider_work_ready(void)
1198{
e5a60d82
MS
1199 /* Note that during zebra startup, we may be offered work before
1200 * the dataplane pthread (and thread-master) are ready. We want to
1201 * enqueue the work, but the event-scheduling machinery may not be
1202 * available.
1203 */
1204 if (zdplane_info.dg_run) {
1205 thread_add_event(zdplane_info.dg_master,
1206 dplane_thread_loop, NULL, 0,
1207 &zdplane_info.dg_t_update);
1208 }
c831033f
MS
1209
1210 return AOK;
1211}
1212
7cdb1a84 1213/*
c831033f 1214 * Kernel dataplane provider
7cdb1a84 1215 */
c831033f
MS
1216
1217/*
1218 * Kernel provider callback
1219 */
1220static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
7cdb1a84 1221{
c831033f
MS
1222 enum zebra_dplane_result res;
1223 struct zebra_dplane_ctx *ctx;
1224 int counter, limit;
7cdb1a84 1225
c831033f 1226 limit = dplane_provider_get_work_limit(prov);
7cdb1a84 1227
c831033f
MS
1228 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1229 zlog_debug("dplane provider '%s': processing",
1230 dplane_provider_get_name(prov));
7cdb1a84 1231
c831033f 1232 for (counter = 0; counter < limit; counter++) {
91f16812 1233
c831033f
MS
1234 ctx = dplane_provider_dequeue_in_ctx(prov);
1235 if (ctx == NULL)
1236 break;
d8c16a95 1237
c831033f
MS
1238 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
1239 char dest_str[PREFIX_STRLEN];
b8e0423d 1240
c831033f
MS
1241 prefix2str(dplane_ctx_get_dest(ctx),
1242 dest_str, sizeof(dest_str));
1d11b21f 1243
c831033f
MS
1244 zlog_debug("%u:%s Dplane route update ctx %p op %s",
1245 dplane_ctx_get_vrf(ctx), dest_str,
1246 ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
1247 }
d8c16a95 1248
c831033f
MS
1249 /* Call into the synchronous kernel-facing code here */
1250 res = kernel_route_update(ctx);
d8c16a95 1251
c831033f
MS
1252 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
1253 atomic_fetch_add_explicit(
1254 &zdplane_info.dg_route_errors, 1,
1255 memory_order_relaxed);
d8c16a95 1256
c831033f
MS
1257 dplane_ctx_set_status(ctx, res);
1258
1259 dplane_provider_enqueue_out_ctx(prov, ctx);
1260 }
1261
1262 /* Ensure that we'll run the work loop again if there's still
1263 * more work to do.
1264 */
1265 if (counter >= limit) {
1266 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1267 zlog_debug("dplane provider '%s' reached max updates %d",
1268 dplane_provider_get_name(prov), counter);
1269
1270 atomic_fetch_add_explicit(&zdplane_info.dg_update_yields,
1271 1, memory_order_relaxed);
1272
1273 dplane_provider_work_ready();
1274 }
1275
1276 return 0;
1277}
1278
e5a60d82
MS
1279#if DPLANE_TEST_PROVIDER
1280
c831033f
MS
1281/*
1282 * Test dataplane provider plugin
1283 */
1284
1285/*
1286 * Test provider process callback
1287 */
1288static int test_dplane_process_func(struct zebra_dplane_provider *prov)
1289{
1290 struct zebra_dplane_ctx *ctx;
1291 int counter, limit;
1292
1293 /* Just moving from 'in' queue to 'out' queue */
1294
1295 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1296 zlog_debug("dplane provider '%s': processing",
1297 dplane_provider_get_name(prov));
1298
1299 limit = dplane_provider_get_work_limit(prov);
1300
1301 for (counter = 0; counter < limit; counter++) {
1302
1303 ctx = dplane_provider_dequeue_in_ctx(prov);
1304 if (ctx == NULL)
1305 break;
1306
1307 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
1308
1309 dplane_provider_enqueue_out_ctx(prov, ctx);
1310 }
1311
c9d17fe8
MS
1312 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1313 zlog_debug("dplane provider '%s': processed %d",
1314 dplane_provider_get_name(prov), counter);
1315
c831033f
MS
1316 /* Ensure that we'll run the work loop again if there's still
1317 * more work to do.
1318 */
1319 if (counter >= limit)
1320 dplane_provider_work_ready();
1321
1322 return 0;
1323}
1324
1325/*
1326 * Test provider shutdown/fini callback
1327 */
1328static int test_dplane_shutdown_func(struct zebra_dplane_provider *prov,
1329 bool early)
1330{
1331 if (IS_ZEBRA_DEBUG_DPLANE)
1332 zlog_debug("dplane provider '%s': %sshutdown",
1333 dplane_provider_get_name(prov),
1334 early ? "early " : "");
1335
1336 return 0;
1337}
e5a60d82 1338#endif /* DPLANE_TEST_PROVIDER */
c831033f
MS
1339
1340/*
1341 * Register default kernel provider
1342 */
1343static void dplane_provider_init(void)
1344{
1345 int ret;
1346
1347 ret = dplane_provider_register("Kernel",
1348 DPLANE_PRIO_KERNEL,
1349 DPLANE_PROV_FLAGS_DEFAULT,
1350 kernel_dplane_process_func,
1351 NULL,
1352 NULL);
1353
1354 if (ret != AOK)
1355 zlog_err("Unable to register kernel dplane provider: %d",
1356 ret);
1357
e5a60d82
MS
1358#if DPLANE_TEST_PROVIDER
1359 /* Optional test provider ... */
c831033f
MS
1360 ret = dplane_provider_register("Test",
1361 DPLANE_PRIO_PRE_KERNEL,
1362 DPLANE_PROV_FLAGS_DEFAULT,
1363 test_dplane_process_func,
1364 test_dplane_shutdown_func,
1365 NULL /* data */);
1366
1367 if (ret != AOK)
1368 zlog_err("Unable to register test dplane provider: %d",
1369 ret);
e5a60d82 1370#endif /* DPLANE_TEST_PROVIDER */
7cdb1a84
MS
1371}
1372
4dfd7a02
MS
1373/* Indicates zebra shutdown/exit is in progress. Some operations may be
1374 * simplified or skipped during shutdown processing.
1375 */
1376bool dplane_is_in_shutdown(void)
1377{
25779064 1378 return zdplane_info.dg_is_shutdown;
4dfd7a02
MS
1379}
1380
1381/*
1382 * Early or pre-shutdown, de-init notification api. This runs pretty
1383 * early during zebra shutdown, as a signal to stop new work and prepare
1384 * for updates generated by shutdown/cleanup activity, as zebra tries to
1385 * remove everything it's responsible for.
c9d17fe8 1386 * NB: This runs in the main zebra pthread context.
4dfd7a02
MS
1387 */
1388void zebra_dplane_pre_finish(void)
1389{
1390 if (IS_ZEBRA_DEBUG_DPLANE)
1391 zlog_debug("Zebra dataplane pre-fini called");
1392
25779064 1393 zdplane_info.dg_is_shutdown = true;
4dfd7a02 1394
c9d17fe8 1395 /* TODO -- Notify provider(s) of pending shutdown */
4dfd7a02
MS
1396}
1397
1398/*
1399 * Utility to determine whether work remains enqueued within the dplane;
1400 * used during system shutdown processing.
1401 */
1402static bool dplane_work_pending(void)
1403{
c9d17fe8 1404 bool ret = false;
25779064 1405 struct zebra_dplane_ctx *ctx;
c9d17fe8 1406 struct zebra_dplane_provider *prov;
4dfd7a02 1407
c831033f
MS
1408 /* TODO -- just checking incoming/pending work for now, must check
1409 * providers
1410 */
4dfd7a02
MS
1411 DPLANE_LOCK();
1412 {
25779064 1413 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
c9d17fe8 1414 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
4dfd7a02
MS
1415 }
1416 DPLANE_UNLOCK();
1417
c9d17fe8
MS
1418 if (ctx != NULL) {
1419 ret = true;
1420 goto done;
1421 }
1422
1423 while (prov) {
1424
ad6aad4d 1425 dplane_provider_lock(prov);
c9d17fe8
MS
1426
1427 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1428 if (ctx == NULL)
1429 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
1430
ad6aad4d 1431 dplane_provider_unlock(prov);
c9d17fe8
MS
1432
1433 if (ctx != NULL)
1434 break;
1435
1436 DPLANE_LOCK();
1437 prov = TAILQ_NEXT(prov, dp_prov_link);
1438 DPLANE_UNLOCK();
1439 }
1440
1441 if (ctx != NULL)
1442 ret = true;
1443
1444done:
1445 return ret;
4dfd7a02
MS
1446}
1447
1448/*
1449 * Shutdown-time intermediate callback, used to determine when all pending
1450 * in-flight updates are done. If there's still work to do, reschedules itself.
1451 * If all work is done, schedules an event to the main zebra thread for
1452 * final zebra shutdown.
1453 * This runs in the dplane pthread context.
1454 */
1455static int dplane_check_shutdown_status(struct thread *event)
1456{
1457 if (IS_ZEBRA_DEBUG_DPLANE)
1458 zlog_debug("Zebra dataplane shutdown status check called");
1459
1460 if (dplane_work_pending()) {
1461 /* Reschedule dplane check on a short timer */
25779064 1462 thread_add_timer_msec(zdplane_info.dg_master,
4dfd7a02
MS
1463 dplane_check_shutdown_status,
1464 NULL, 100,
25779064 1465 &zdplane_info.dg_t_shutdown_check);
4dfd7a02
MS
1466
1467 /* TODO - give up and stop waiting after a short time? */
1468
1469 } else {
1470 /* We appear to be done - schedule a final callback event
1471 * for the zebra main pthread.
1472 */
1473 thread_add_event(zebrad.master, zebra_finalize, NULL, 0, NULL);
1474 }
1475
1476 return 0;
1477}
1478
18c37974 1479/*
1d11b21f 1480 * Shutdown, de-init api. This runs pretty late during shutdown,
4dfd7a02
MS
1481 * after zebra has tried to free/remove/uninstall all routes during shutdown.
1482 * At this point, dplane work may still remain to be done, so we can't just
1483 * blindly terminate. If there's still work to do, we'll periodically check
1484 * and when done, we'll enqueue a task to the zebra main thread for final
1485 * termination processing.
1486 *
1d11b21f 1487 * NB: This runs in the main zebra thread context.
18c37974 1488 */
1d11b21f 1489void zebra_dplane_finish(void)
18c37974 1490{
4dfd7a02
MS
1491 if (IS_ZEBRA_DEBUG_DPLANE)
1492 zlog_debug("Zebra dataplane fini called");
1493
25779064 1494 thread_add_event(zdplane_info.dg_master,
4dfd7a02 1495 dplane_check_shutdown_status, NULL, 0,
25779064 1496 &zdplane_info.dg_t_shutdown_check);
4dfd7a02
MS
1497}
1498
c831033f
MS
1499/*
1500 * Main dataplane pthread event loop. The thread takes new incoming work
1501 * and offers it to the first provider. It then iterates through the
1502 * providers, taking complete work from each one and offering it
1503 * to the next in order. At each step, a limited number of updates are
1504 * processed during a cycle in order to provide some fairness.
14b0bc8e
MS
1505 *
1506 * This loop through the providers is only run once, so that the dataplane
1507 * pthread can look for other pending work - such as i/o work on behalf of
1508 * providers.
c831033f
MS
1509 */
1510static int dplane_thread_loop(struct thread *event)
1511{
1512 struct dplane_ctx_q work_list;
1513 struct dplane_ctx_q error_list;
1514 struct zebra_dplane_provider *prov;
1515 struct zebra_dplane_ctx *ctx, *tctx;
1516 int limit, counter, error_counter;
c9d17fe8 1517 uint64_t curr, high;
c831033f
MS
1518
1519 /* Capture work limit per cycle */
1520 limit = zdplane_info.dg_updates_per_cycle;
1521
14b0bc8e 1522 /* Init temporary lists used to move contexts among providers */
c831033f 1523 TAILQ_INIT(&work_list);
14b0bc8e
MS
1524 TAILQ_INIT(&error_list);
1525 error_counter = 0;
c831033f
MS
1526
1527 /* Check for zebra shutdown */
1528 if (!zdplane_info.dg_run)
1529 goto done;
1530
1531 /* Dequeue some incoming work from zebra (if any) onto the temporary
1532 * working list.
1533 */
1534 DPLANE_LOCK();
1535
1536 /* Locate initial registered provider */
1537 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1538
14b0bc8e 1539 /* Move new work from incoming list to temp list */
c831033f
MS
1540 for (counter = 0; counter < limit; counter++) {
1541 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
1542 if (ctx) {
1543 TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx,
1544 zd_q_entries);
1545
c831033f
MS
1546 ctx->zd_provider = prov->dp_id;
1547
1548 TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
1549 } else {
1550 break;
1551 }
1552 }
1553
1554 DPLANE_UNLOCK();
1555
14b0bc8e
MS
1556 atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, counter,
1557 memory_order_relaxed);
1558
c831033f
MS
1559 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1560 zlog_debug("dplane: incoming new work counter: %d", counter);
1561
1562 /* Iterate through the registered providers, offering new incoming
1563 * work. If the provider has outgoing work in its queue, take that
1564 * work for the next provider
1565 */
1566 while (prov) {
1567
14b0bc8e
MS
1568 /* At each iteration, the temporary work list has 'counter'
1569 * items.
1570 */
c831033f
MS
1571 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1572 zlog_debug("dplane enqueues %d new work to provider '%s'",
1573 counter, dplane_provider_get_name(prov));
1574
1575 /* Capture current provider id in each context; check for
1576 * error status.
1577 */
1578 TAILQ_FOREACH_SAFE(ctx, &work_list, zd_q_entries, tctx) {
1579 if (dplane_ctx_get_status(ctx) ==
1580 ZEBRA_DPLANE_REQUEST_SUCCESS) {
1581 ctx->zd_provider = prov->dp_id;
1582 } else {
1583 /*
1584 * TODO -- improve error-handling: recirc
1585 * errors backwards so that providers can
1586 * 'undo' their work (if they want to)
1587 */
1588
1589 /* Move to error list; will be returned
1590 * zebra main.
1591 */
1592 TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
1593 TAILQ_INSERT_TAIL(&error_list,
1594 ctx, zd_q_entries);
1595 error_counter++;
1596 }
1597 }
1598
1599 /* Enqueue new work to the provider */
ad6aad4d 1600 dplane_provider_lock(prov);
c831033f
MS
1601
1602 if (TAILQ_FIRST(&work_list))
1603 TAILQ_CONCAT(&(prov->dp_ctx_in_q), &work_list,
1604 zd_q_entries);
1605
c9d17fe8
MS
1606 atomic_fetch_add_explicit(&prov->dp_in_counter, counter,
1607 memory_order_relaxed);
1608 atomic_fetch_add_explicit(&prov->dp_in_queued, counter,
1609 memory_order_relaxed);
1610 curr = atomic_load_explicit(&prov->dp_in_queued,
1611 memory_order_relaxed);
1612 high = atomic_load_explicit(&prov->dp_in_max,
1613 memory_order_relaxed);
1614 if (curr > high)
1615 atomic_store_explicit(&prov->dp_in_max, curr,
c831033f
MS
1616 memory_order_relaxed);
1617
ad6aad4d 1618 dplane_provider_unlock(prov);
c831033f 1619
14b0bc8e
MS
1620 /* Reset the temp list (though the 'concat' may have done this
1621 * already), and the counter
1622 */
c831033f
MS
1623 TAILQ_INIT(&work_list);
1624 counter = 0;
1625
14b0bc8e
MS
1626 /* Call into the provider code. Note that this is
1627 * unconditional: we offer to do work even if we don't enqueue
1628 * any _new_ work.
1629 */
c831033f
MS
1630 (*prov->dp_fp)(prov);
1631
1632 /* Check for zebra shutdown */
1633 if (!zdplane_info.dg_run)
1634 break;
1635
1636 /* Dequeue completed work from the provider */
ad6aad4d 1637 dplane_provider_lock(prov);
c831033f
MS
1638
1639 while (counter < limit) {
1640 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
1641 if (ctx) {
1642 TAILQ_REMOVE(&(prov->dp_ctx_out_q), ctx,
1643 zd_q_entries);
1644
1645 TAILQ_INSERT_TAIL(&work_list,
1646 ctx, zd_q_entries);
1647 counter++;
1648 } else
1649 break;
1650 }
1651
ad6aad4d 1652 dplane_provider_unlock(prov);
c831033f
MS
1653
1654 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1655 zlog_debug("dplane dequeues %d completed work from provider %s",
1656 counter, dplane_provider_get_name(prov));
1657
1658 /* Locate next provider */
1659 DPLANE_LOCK();
1660 prov = TAILQ_NEXT(prov, dp_prov_link);
1661 DPLANE_UNLOCK();
c831033f
MS
1662 }
1663
1664 /* After all providers have been serviced, enqueue any completed
14b0bc8e 1665 * work and any errors back to zebra so it can process the results.
c831033f 1666 */
c831033f 1667 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
14b0bc8e
MS
1668 zlog_debug("dplane has %d completed, %d errors, for zebra main",
1669 counter, error_counter);
c831033f
MS
1670
1671 /*
4c206c8f 1672 * Hand lists through the api to zebra main,
c831033f
MS
1673 * to reduce the number of lock/unlock cycles
1674 */
14b0bc8e 1675
4c206c8f
MS
1676 /* Call through to zebra main */
1677 (zdplane_info.dg_results_cb)(&error_list);
14b0bc8e 1678
4c206c8f 1679 TAILQ_INIT(&error_list);
14b0bc8e 1680
c831033f 1681
4c206c8f
MS
1682 /* Call through to zebra main */
1683 (zdplane_info.dg_results_cb)(&work_list);
c831033f 1684
4c206c8f 1685 TAILQ_INIT(&work_list);
c831033f
MS
1686
1687done:
1688 return 0;
1689}
1690
4dfd7a02
MS
1691/*
1692 * Final phase of shutdown, after all work enqueued to dplane has been
1693 * processed. This is called from the zebra main pthread context.
1694 */
1695void zebra_dplane_shutdown(void)
1696{
1697 if (IS_ZEBRA_DEBUG_DPLANE)
1698 zlog_debug("Zebra dataplane shutdown called");
1d11b21f
MS
1699
1700 /* Stop dplane thread, if it's running */
1701
25779064 1702 zdplane_info.dg_run = false;
1d11b21f 1703
25779064 1704 THREAD_OFF(zdplane_info.dg_t_update);
1d11b21f 1705
d8c16a95
MS
1706 frr_pthread_stop(zdplane_info.dg_pthread, NULL);
1707
1708 /* Destroy pthread */
1709 frr_pthread_destroy(zdplane_info.dg_pthread);
1710 zdplane_info.dg_pthread = NULL;
1711 zdplane_info.dg_master = NULL;
4dfd7a02 1712
c831033f
MS
1713 /* TODO -- Notify provider(s) of final shutdown */
1714
1715 /* TODO -- Clean-up provider objects */
1716
1717 /* TODO -- Clean queue(s), free memory */
1718}
1719
1720/*
1721 * Initialize the dataplane module during startup, internal/private version
1722 */
1723static void zebra_dplane_init_internal(struct zebra_t *zebra)
1724{
1725 memset(&zdplane_info, 0, sizeof(zdplane_info));
1726
1727 pthread_mutex_init(&zdplane_info.dg_mutex, NULL);
1d11b21f 1728
c831033f
MS
1729 TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
1730 TAILQ_INIT(&zdplane_info.dg_providers_q);
1d11b21f 1731
c831033f
MS
1732 zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
1733
1734 zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED;
1735
1736 /* Register default kernel 'provider' during init */
1737 dplane_provider_init();
e5a60d82 1738}
c831033f 1739
e5a60d82
MS
1740/*
1741 * Start the dataplane pthread. This step needs to be run later than the
1742 * 'init' step, in case zebra has fork-ed.
1743 */
1744void zebra_dplane_start(void)
1745{
c831033f
MS
1746 /* Start dataplane pthread */
1747
c831033f
MS
1748 struct frr_pthread_attr pattr = {
1749 .start = frr_pthread_attr_default.start,
1750 .stop = frr_pthread_attr_default.stop
1751 };
1752
1753 zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
1754 "Zebra dplane");
1755
1756 zdplane_info.dg_master = zdplane_info.dg_pthread->master;
1757
e5a60d82
MS
1758 zdplane_info.dg_run = true;
1759
c831033f
MS
1760 /* Enqueue an initial event for the dataplane pthread */
1761 thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
1762 &zdplane_info.dg_t_update);
1763
1764 frr_pthread_run(zdplane_info.dg_pthread, NULL);
18c37974
MS
1765}
1766
7cdb1a84 1767/*
b8e0423d 1768 * Initialize the dataplane module at startup; called by zebra rib_init()
7cdb1a84 1769 */
4c206c8f 1770void zebra_dplane_init(int (*results_fp)(struct dplane_ctx_q *))
7cdb1a84
MS
1771{
1772 zebra_dplane_init_internal(&zebrad);
4c206c8f 1773 zdplane_info.dg_results_cb = results_fp;
7cdb1a84 1774}