2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2020 Alexander V. Chernikov
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include "opt_inet6.h"
32 #include "opt_route.h"
34 #include <sys/param.h>
35 #include <sys/eventhandler.h>
36 #include <sys/kernel.h>
39 #include <sys/rmlock.h>
40 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/kernel.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/sysctl.h>
49 #include <sys/syslog.h>
50 #include <sys/queue.h>
54 #include <net/if_var.h>
56 #include <netinet/in.h>
57 #include <netinet/in_var.h>
58 #include <netinet/ip.h>
59 #include <netinet/ip_var.h>
61 #include <netinet/ip6.h>
62 #include <netinet6/ip6_var.h>
65 #include <net/route.h>
66 #include <net/route/nhop.h>
67 #include <net/route/route_ctl.h>
68 #include <net/route/route_var.h>
69 #include <net/route/fib_algo.h>
71 #include <machine/stdarg.h>
74 * Fib lookup framework.
76 * This framework enables accelerated longest-prefix-match lookups for the
77 * routing tables by adding the ability to dynamically attach/detach lookup
78 * algorithms implementation to/from the datapath.
80 * flm - fib lookup modules - implementation of particular lookup algorithm
81 * fd - fib data - instance of an flm bound to specific routing table
83 * This file provides main framework functionality.
85 * The following are the features provided by the framework
87 * 1) nexhops abstraction -> provides transparent referencing, indexing
88 * and efficient idx->ptr mappings for nexthop and nexthop groups.
89 * 2) Routing table synchronisation
90 * 3) dataplane attachment points
91 * 4) automatic algorithm selection based on the provided preference.
95 * For each supported address family, there is a an allocated array of fib_dp
96 * structures, indexed by fib number. Each array entry contains callback function
97 * and its argument. This function will be called with a family-specific lookup key,
98 * scope and provided argument. This array gets re-created every time when new algo
99 * instance gets created. Please take a look at the replace_rtables_family() function
104 SYSCTL_DECL(_net_route);
105 SYSCTL_NODE(_net_route, OID_AUTO, algo, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
106 "Fib algorithm lookups");
108 VNET_DEFINE(int, fib_sync_limit) = 100;
109 #define V_fib_sync_limit VNET(fib_sync_limit)
110 SYSCTL_INT(_net_route_algo, OID_AUTO, fib_sync_limit, CTLFLAG_RW | CTLFLAG_VNET,
111 &VNET_NAME(fib_sync_limit), 0, "Guarantee synchronous fib till route limit");
114 VNET_DEFINE_STATIC(bool, algo_fixed_inet6) = false;
115 #define V_algo_fixed_inet6 VNET(algo_fixed_inet6)
116 SYSCTL_NODE(_net_route_algo, OID_AUTO, inet6, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
117 "IPv6 longest prefix match lookups");
120 VNET_DEFINE_STATIC(bool, algo_fixed_inet) = false;
121 #define V_algo_fixed_inet VNET(algo_fixed_inet)
122 SYSCTL_NODE(_net_route_algo, OID_AUTO, inet, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
123 "IPv4 longest prefix match lookups");
126 /* Fib instance counter */
127 static uint32_t fib_gen = 0;
129 struct nhop_ref_table {
135 * Data structure for the fib lookup instance tied to the particular rib.
138 uint32_t number_nhops; /* current # of nhops */
139 uint8_t hit_nhops; /* true if out of nhop limit */
140 uint8_t init_done; /* true if init is competed */
141 uint32_t fd_dead:1; /* Scheduled for deletion */
142 uint32_t fd_linked:1; /* true if linked */
143 uint32_t fd_need_rebuild:1; /* true if rebuild scheduled */
144 uint32_t fd_force_eval:1;/* true if rebuild scheduled */
145 uint8_t fd_family; /* family */
146 uint32_t fd_fibnum; /* fibnum */
147 uint32_t fd_failed_rebuilds; /* stat: failed rebuilds */
148 uint32_t fd_gen; /* instance gen# */
149 struct callout fd_callout; /* rebuild callout */
150 void *fd_algo_data; /* algorithm data */
151 struct nhop_object **nh_idx; /* nhop idx->ptr array */
152 struct nhop_ref_table *nh_ref_table; /* array with # of nhop references */
153 struct rib_head *fd_rh; /* RIB table we're attached to */
154 struct rib_subscription *fd_rs; /* storing table subscription */
155 struct fib_dp fd_dp; /* fib datapath data */
156 struct vnet *fd_vnet; /* vnet fib belongs to */
157 struct epoch_context fd_epoch_ctx; /* epoch context for deletion */
158 struct fib_lookup_module *fd_flm;/* pointer to the lookup module */
159 uint32_t fd_num_changes; /* number of changes since last callout */
160 TAILQ_ENTRY(fib_data) entries; /* list of all fds in vnet */
163 static bool rebuild_fd(struct fib_data *fd);
164 static void rebuild_fd_callout(void *_data);
165 static void destroy_fd_instance_epoch(epoch_context_t ctx);
166 static enum flm_op_result attach_datapath(struct fib_data *fd);
167 static bool is_idx_free(struct fib_data *fd, uint32_t index);
168 static void set_algo_fixed(struct rib_head *rh);
169 static bool is_algo_fixed(struct rib_head *rh);
171 static uint32_t fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh);
172 static void fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh);
174 static struct fib_lookup_module *fib_check_best_algo(struct rib_head *rh,
175 struct fib_lookup_module *orig_flm);
176 static void fib_unref_algo(struct fib_lookup_module *flm);
177 static bool flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum);
180 #define FIB_MOD_LOCK() mtx_lock(&fib_mtx)
181 #define FIB_MOD_UNLOCK() mtx_unlock(&fib_mtx)
182 #define FIB_MOD_LOCK_ASSERT() mtx_assert(&fib_mtx, MA_OWNED)
184 MTX_SYSINIT(fib_mtx, &fib_mtx, "algo list mutex", MTX_DEF);
186 /* Algorithm has to be this percent better than the current to switch */
187 #define BEST_DIFF_PERCENT (5 * 256 / 100)
188 /* Schedule algo re-evaluation X seconds after a change */
189 #define ALGO_EVAL_DELAY_MS 30000
190 /* Force algo re-evaluation after X changes */
191 #define ALGO_EVAL_NUM_ROUTES 100
192 /* Try to setup algorithm X times */
193 #define FIB_MAX_TRIES 32
194 /* Max amount of supported nexthops */
195 #define FIB_MAX_NHOPS 262144
196 #define FIB_CALLOUT_DELAY_MS 50
199 static int flm_debug_level = LOG_NOTICE;
200 SYSCTL_INT(_net_route_algo, OID_AUTO, debug_level, CTLFLAG_RW | CTLFLAG_RWTUN,
201 &flm_debug_level, 0, "debuglevel");
202 #define FLM_MAX_DEBUG_LEVEL LOG_DEBUG
207 #define _PASS_MSG(_l) (flm_debug_level >= (_l))
208 #define ALGO_PRINTF(_fmt, ...) printf("[fib_algo] %s: " _fmt "\n", __func__, ##__VA_ARGS__)
209 #define _ALGO_PRINTF(_fib, _fam, _aname, _gen, _func, _fmt, ...) \
210 printf("[fib_algo] %s.%u (%s#%u) %s: " _fmt "\n",\
211 print_family(_fam), _fib, _aname, _gen, _func, ## __VA_ARGS__)
212 #define _RH_PRINTF(_fib, _fam, _func, _fmt, ...) \
213 printf("[fib_algo] %s.%u %s: " _fmt "\n", print_family(_fam), _fib, _func, ## __VA_ARGS__)
214 #define RH_PRINTF(_l, _rh, _fmt, ...) if (_PASS_MSG(_l)) { \
215 _RH_PRINTF(_rh->rib_fibnum, _rh->rib_family, __func__, _fmt, ## __VA_ARGS__);\
217 #define FD_PRINTF(_l, _fd, _fmt, ...) FD_PRINTF_##_l(_l, _fd, _fmt, ## __VA_ARGS__)
218 #define _FD_PRINTF(_l, _fd, _fmt, ...) if (_PASS_MSG(_l)) { \
219 _ALGO_PRINTF(_fd->fd_fibnum, _fd->fd_family, _fd->fd_flm->flm_name, \
220 _fd->fd_gen, __func__, _fmt, ## __VA_ARGS__); \
222 #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG2
223 #define FD_PRINTF_LOG_DEBUG2 _FD_PRINTF
225 #define FD_PRINTF_LOG_DEBUG2(_l, _fd, _fmt, ...)
227 #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG
228 #define FD_PRINTF_LOG_DEBUG _FD_PRINTF
230 #define FD_PRINTF_LOG_DEBUG()
232 #if FLM_MAX_DEBUG_LEVEL>=LOG_INFO
233 #define FD_PRINTF_LOG_INFO _FD_PRINTF
235 #define FD_PRINTF_LOG_INFO()
237 #define FD_PRINTF_LOG_NOTICE _FD_PRINTF
238 #define FD_PRINTF_LOG_ERR _FD_PRINTF
239 #define FD_PRINTF_LOG_WARNING _FD_PRINTF
242 /* List of all registered lookup algorithms */
243 static TAILQ_HEAD(, fib_lookup_module) all_algo_list = TAILQ_HEAD_INITIALIZER(all_algo_list);
245 /* List of all fib lookup instances in the vnet */
246 VNET_DEFINE_STATIC(TAILQ_HEAD(fib_data_head, fib_data), fib_data_list);
247 #define V_fib_data_list VNET(fib_data_list)
249 /* Datastructure for storing non-transient fib lookup module failures */
252 uint32_t fe_fibnum; /* failed rtable */
253 struct fib_lookup_module *fe_flm; /* failed module */
254 TAILQ_ENTRY(fib_error) entries;/* list of all errored entries */
256 VNET_DEFINE_STATIC(TAILQ_HEAD(fib_error_head, fib_error), fib_error_list);
257 #define V_fib_error_list VNET(fib_error_list)
259 /* Per-family array of fibnum -> {func, arg} mappings used in datapath */
260 struct fib_dp_header {
261 struct epoch_context fdh_epoch_ctx;
262 uint32_t fdh_num_tables;
263 struct fib_dp fdh_idx[0];
267 * Tries to add new non-transient algorithm error to the list of
269 * Returns true on success.
272 flm_error_add(struct fib_lookup_module *flm, uint32_t fibnum)
274 struct fib_error *fe;
276 fe = malloc(sizeof(struct fib_error), M_TEMP, M_NOWAIT | M_ZERO);
280 fe->fe_family = flm->flm_family;
281 fe->fe_fibnum = fibnum;
284 /* Avoid duplicates by checking if error already exists first */
285 if (flm_error_check(flm, fibnum)) {
290 TAILQ_INSERT_HEAD(&V_fib_error_list, fe, entries);
297 * True if non-transient error has been registered for @flm in @fibnum.
300 flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum)
302 const struct fib_error *fe;
304 TAILQ_FOREACH(fe, &V_fib_error_list, entries) {
305 if ((fe->fe_flm == flm) && (fe->fe_fibnum == fibnum))
313 * Clear all errors of algo specified by @flm.
316 fib_error_clear_flm(struct fib_lookup_module *flm)
318 struct fib_error *fe, *fe_tmp;
320 FIB_MOD_LOCK_ASSERT();
322 TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
323 if (fe->fe_flm == flm) {
324 TAILQ_REMOVE(&V_fib_error_list, fe, entries);
331 * Clears all errors in current VNET.
336 struct fib_error *fe, *fe_tmp;
338 FIB_MOD_LOCK_ASSERT();
340 TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
341 TAILQ_REMOVE(&V_fib_error_list, fe, entries);
347 print_op_result(enum flm_op_result result)
362 print_family(int family)
365 if (family == AF_INET)
367 else if (family == AF_INET6)
374 * Debug function used by lookup algorithms.
375 * Outputs message denoted by @fmt, prepended by "[fib_algo] inetX.Y (algo) "
378 fib_printf(int level, struct fib_data *fd, const char *func, char *fmt, ...)
383 if (level > flm_debug_level)
387 vsnprintf(buf, sizeof(buf), fmt, ap);
390 _ALGO_PRINTF(fd->fd_fibnum, fd->fd_family, fd->fd_flm->flm_name,
391 fd->fd_gen, func, "%s", buf);
395 * Outputs list of algorithms supported by the provided address family.
398 print_algos_sysctl(struct sysctl_req *req, int family)
400 struct fib_lookup_module *flm;
402 int error, count = 0;
404 error = sysctl_wire_old_buffer(req, 0);
406 sbuf_new_for_sysctl(&sbuf, NULL, 512, req);
407 TAILQ_FOREACH(flm, &all_algo_list, entries) {
408 if (flm->flm_family == family) {
410 sbuf_cat(&sbuf, ", ");
411 sbuf_cat(&sbuf, flm->flm_name);
414 error = sbuf_finish(&sbuf);
422 print_algos_sysctl_inet6(SYSCTL_HANDLER_ARGS)
425 return (print_algos_sysctl(req, AF_INET6));
427 SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo_list,
428 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
429 print_algos_sysctl_inet6, "A", "List of IPv6 lookup algorithms");
434 print_algos_sysctl_inet(SYSCTL_HANDLER_ARGS)
437 return (print_algos_sysctl(req, AF_INET));
439 SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo_list,
440 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
441 print_algos_sysctl_inet, "A", "List of IPv4 lookup algorithms");
445 * Calculate delay between repeated failures.
446 * Returns current delay in milliseconds.
449 callout_calc_delay_ms(struct fib_data *fd)
453 if (fd->fd_failed_rebuilds > 10)
456 shift = fd->fd_failed_rebuilds;
458 return ((1 << shift) * FIB_CALLOUT_DELAY_MS);
462 schedule_callout(struct fib_data *fd, int delay_ms)
465 callout_reset_sbt(&fd->fd_callout, SBT_1MS * delay_ms, 0,
466 rebuild_fd_callout, fd, 0);
470 schedule_fd_rebuild(struct fib_data *fd, const char *reason)
473 RIB_WLOCK_ASSERT(fd->fd_rh);
475 if (!fd->fd_need_rebuild) {
476 fd->fd_need_rebuild = true;
479 * Potentially re-schedules pending callout
480 * initiated by schedule_algo_eval.
482 FD_PRINTF(LOG_INFO, fd, "Scheduling rebuild: %s (failures=%d)",
483 reason, fd->fd_failed_rebuilds);
484 schedule_callout(fd, callout_calc_delay_ms(fd));
489 schedule_algo_eval(struct fib_data *fd)
492 RIB_WLOCK_ASSERT(fd->fd_rh);
494 if (fd->fd_num_changes++ == 0) {
495 /* Start callout to consider switch */
496 if (!callout_pending(&fd->fd_callout))
497 schedule_callout(fd, ALGO_EVAL_DELAY_MS);
498 } else if (fd->fd_num_changes > ALGO_EVAL_NUM_ROUTES && !fd->fd_force_eval) {
499 /* Reset callout to exec immediately */
500 if (!fd->fd_need_rebuild) {
501 fd->fd_force_eval = true;
502 schedule_callout(fd, 1);
508 need_immediate_rebuild(struct fib_data *fd, struct rib_cmd_info *rc)
510 struct nhop_object *nh;
512 if ((V_fib_sync_limit == 0) || (fd->fd_rh->rnh_prefixes <= V_fib_sync_limit))
515 /* Sync addition/removal of interface routes */
516 switch (rc->rc_cmd) {
519 if (!NH_IS_NHGRP(nh) && (!(nh->nh_flags & NHF_GATEWAY)))
524 if (!NH_IS_NHGRP(nh) && (!(nh->nh_flags & NHF_GATEWAY)))
533 * Rib subscription handler. Checks if the algorithm is ready to
534 * receive updates, handles nexthop refcounting and passes change
535 * data to the algorithm callback.
538 handle_rtable_change_cb(struct rib_head *rnh, struct rib_cmd_info *rc,
541 struct fib_data *fd = (struct fib_data *)_data;
542 enum flm_op_result result;
544 RIB_WLOCK_ASSERT(rnh);
547 * There is a small gap between subscribing for route changes
548 * and initiating rtable dump. Avoid receiving route changes
549 * prior to finishing rtable dump by checking `init_done`.
554 * If algo requested rebuild, stop sending updates by default.
555 * This simplifies nexthop refcount handling logic.
557 if (fd->fd_need_rebuild)
560 /* Consider scheduling algorithm re-evaluation */
561 schedule_algo_eval(fd);
564 * Maintain guarantee that every nexthop returned by the dataplane
565 * lookup has > 0 refcount, so can be safely referenced within current
568 if (rc->rc_nh_new != NULL) {
569 if (fib_ref_nhop(fd, rc->rc_nh_new) == 0) {
570 /* ran out of indexes */
571 schedule_fd_rebuild(fd, "ran out of nhop indexes");
576 result = fd->fd_flm->flm_change_rib_item_cb(rnh, rc, fd->fd_algo_data);
580 /* Unref old nexthop on success */
581 if (rc->rc_nh_old != NULL)
582 fib_unref_nhop(fd, rc->rc_nh_old);
587 * Algo is not able to apply the update.
588 * Schedule algo rebuild.
590 if (!need_immediate_rebuild(fd, rc)) {
591 schedule_fd_rebuild(fd, "algo requested rebuild");
595 fd->fd_need_rebuild = true;
596 FD_PRINTF(LOG_INFO, fd, "running sync rebuild");
598 schedule_fd_rebuild(fd, "sync rebuild failed");
603 * Algo reported a non-recoverable error.
604 * Record the error and schedule rebuild, which will
605 * trigger best algo selection.
607 FD_PRINTF(LOG_ERR, fd, "algo reported non-recoverable error");
608 if (!flm_error_add(fd->fd_flm, fd->fd_fibnum))
609 FD_PRINTF(LOG_ERR, fd, "failed to ban algo");
610 schedule_fd_rebuild(fd, "algo reported non-recoverable error");
615 estimate_nhop_scale(const struct fib_data *old_fd, struct fib_data *fd)
618 if (old_fd == NULL) {
619 // TODO: read from rtable
620 fd->number_nhops = 16;
624 if (old_fd->hit_nhops && old_fd->number_nhops < FIB_MAX_NHOPS)
625 fd->number_nhops = 2 * old_fd->number_nhops;
627 fd->number_nhops = old_fd->number_nhops;
633 enum flm_op_result result;
637 * Handler called after all rtenties have been dumped.
638 * Performs post-dump framework checks and calls
639 * algo:flm_dump_end_cb().
641 * Updates walk_cbdata result.
644 sync_algo_end_cb(struct rib_head *rnh, enum rib_walk_hook stage, void *_data)
646 struct walk_cbdata *w = (struct walk_cbdata *)_data;
647 struct fib_data *fd = w->fd;
649 RIB_WLOCK_ASSERT(w->fd->fd_rh);
651 if (rnh->rib_dying) {
652 w->result = FLM_ERROR;
657 FD_PRINTF(LOG_INFO, fd, "ran out of nexthops at %u nhops",
658 fd->nh_ref_table->count);
659 if (w->result == FLM_SUCCESS)
660 w->result = FLM_REBUILD;
664 if (stage != RIB_WALK_HOOK_POST || w->result != FLM_SUCCESS)
667 /* Post-dump hook, dump successful */
668 w->result = fd->fd_flm->flm_dump_end_cb(fd->fd_algo_data, &fd->fd_dp);
670 if (w->result == FLM_SUCCESS) {
671 /* Mark init as done to allow routing updates */
677 * Callback for each entry in rib.
678 * Calls algo:flm_dump_rib_item_cb func as a part of initial
679 * route table synchronisation.
682 sync_algo_cb(struct rtentry *rt, void *_data)
684 struct walk_cbdata *w = (struct walk_cbdata *)_data;
686 RIB_WLOCK_ASSERT(w->fd->fd_rh);
688 if (w->result == FLM_SUCCESS && w->func) {
691 * Reference nexthops to maintain guarantee that
692 * each nexthop returned by datapath has > 0 references
693 * and can be safely referenced within current epoch.
695 struct nhop_object *nh = rt_get_raw_nhop(rt);
696 if (fib_ref_nhop(w->fd, nh) != 0)
697 w->result = w->func(rt, w->fd->fd_algo_data);
699 w->result = FLM_REBUILD;
706 * Dump all routing table state to the algo instance.
708 static enum flm_op_result
709 sync_algo(struct fib_data *fd)
711 struct walk_cbdata w = {
713 .func = fd->fd_flm->flm_dump_rib_item_cb,
714 .result = FLM_SUCCESS,
717 rib_walk_ext_locked(fd->fd_rh, sync_algo_cb, sync_algo_end_cb, &w);
719 FD_PRINTF(LOG_INFO, fd,
720 "initial dump completed (rtable version: %d), result: %s",
721 fd->fd_rh->rnh_gen, print_op_result(w.result));
727 * Schedules epoch-backed @fd instance deletion.
728 * * Unlinks @fd from the list of active algo instances.
729 * * Removes rib subscription.
731 * * Schedules actual deletion.
733 * Assume @fd is already unlinked from the datapath.
736 schedule_destroy_fd_instance(struct fib_data *fd, bool in_callout)
741 RIB_WLOCK_ASSERT(fd->fd_rh);
744 is_dead = fd->fd_dead;
748 TAILQ_REMOVE(&V_fib_data_list, fd, entries);
749 fd->fd_linked = false;
755 FD_PRINTF(LOG_INFO, fd, "DETACH");
757 if (fd->fd_rs != NULL)
758 rib_unsibscribe_locked(fd->fd_rs);
761 * After rib_unsubscribe() no _new_ handle_rtable_change_cb() calls
762 * will be executed, hence no _new_ callout schedules will happen.
764 callout_stop(&fd->fd_callout);
766 epoch_call(net_epoch_preempt, destroy_fd_instance_epoch,
773 * Wipe all fd instances from the list matching rib specified by @rh.
774 * If @keep_first is set, remove all but the first record.
777 fib_cleanup_algo(struct rib_head *rh, bool keep_first, bool in_callout)
779 struct fib_data_head tmp_head = TAILQ_HEAD_INITIALIZER(tmp_head);
780 struct fib_data *fd, *fd_tmp;
781 struct epoch_tracker et;
784 TAILQ_FOREACH_SAFE(fd, &V_fib_data_list, entries, fd_tmp) {
785 if (fd->fd_rh == rh) {
790 TAILQ_REMOVE(&V_fib_data_list, fd, entries);
791 fd->fd_linked = false;
792 TAILQ_INSERT_TAIL(&tmp_head, fd, entries);
797 /* Pass 2: remove each entry */
799 TAILQ_FOREACH_SAFE(fd, &tmp_head, entries, fd_tmp) {
801 RIB_WLOCK(fd->fd_rh);
802 schedule_destroy_fd_instance(fd, in_callout);
804 RIB_WUNLOCK(fd->fd_rh);
810 fib_destroy_rib(struct rib_head *rh)
814 * rnh has `is_dying` flag set, so setup of new fd's will fail at
815 * sync_algo() stage, preventing new entries to be added to the list
816 * of active algos. Remove all existing entries for the particular rib.
818 fib_cleanup_algo(rh, false, false);
822 * Finalises fd destruction by freeing all fd resources.
825 destroy_fd_instance(struct fib_data *fd)
828 FD_PRINTF(LOG_INFO, fd, "destroy fd %p", fd);
830 /* Call destroy callback first */
831 if (fd->fd_algo_data != NULL)
832 fd->fd_flm->flm_destroy_cb(fd->fd_algo_data);
835 if ((fd->nh_idx != NULL) && (fd->nh_ref_table != NULL)) {
836 for (int i = 0; i < fd->number_nhops; i++) {
837 if (!is_idx_free(fd, i)) {
838 FD_PRINTF(LOG_DEBUG2, fd, " FREE nhop %d %p",
840 nhop_free_any(fd->nh_idx[i]);
843 free(fd->nh_idx, M_RTABLE);
845 if (fd->nh_ref_table != NULL)
846 free(fd->nh_ref_table, M_RTABLE);
848 fib_unref_algo(fd->fd_flm);
854 * Epoch callback indicating fd is safe to destroy
857 destroy_fd_instance_epoch(epoch_context_t ctx)
861 fd = __containerof(ctx, struct fib_data, fd_epoch_ctx);
863 destroy_fd_instance(fd);
867 * Tries to setup fd instance.
868 * - Allocates fd/nhop table
869 * - Runs algo:flm_init_cb algo init
870 * - Subscribes fd to the rib
872 * - Adds instance to the list of active instances.
874 * Returns: operation result. Fills in @pfd with resulting fd on success.
877 static enum flm_op_result
878 try_setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
879 struct fib_data *old_fd, struct fib_data **pfd)
883 enum flm_op_result result;
886 fd = malloc(sizeof(struct fib_data), M_RTABLE, M_NOWAIT | M_ZERO);
889 RH_PRINTF(LOG_INFO, rh, "Unable to allocate fib_data structure");
890 return (FLM_REBUILD);
894 estimate_nhop_scale(old_fd, fd);
897 fd->fd_gen = ++fib_gen;
898 fd->fd_family = rh->rib_family;
899 fd->fd_fibnum = rh->rib_fibnum;
900 callout_init_rm(&fd->fd_callout, &rh->rib_lock, 0);
901 fd->fd_vnet = curvnet;
904 FD_PRINTF(LOG_DEBUG, fd, "allocated fd %p", fd);
910 /* Allocate nhidx -> nhop_ptr table */
911 size = fd->number_nhops * sizeof(void *);
912 fd->nh_idx = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
913 if (fd->nh_idx == NULL) {
914 FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop table idx (sz:%zu)", size);
915 return (FLM_REBUILD);
918 /* Allocate nhop index refcount table */
919 size = sizeof(struct nhop_ref_table);
920 size += fd->number_nhops * sizeof(uint32_t);
921 fd->nh_ref_table = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
922 if (fd->nh_ref_table == NULL) {
923 FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop refcount table (sz:%zu)", size);
924 return (FLM_REBUILD);
926 FD_PRINTF(LOG_DEBUG, fd, "Allocated %u nhop indexes", fd->number_nhops);
928 /* Okay, we're ready for algo init */
929 void *old_algo_data = (old_fd != NULL) ? old_fd->fd_algo_data : NULL;
930 result = flm->flm_init_cb(fd->fd_fibnum, fd, old_algo_data, &fd->fd_algo_data);
931 if (result != FLM_SUCCESS) {
932 FD_PRINTF(LOG_INFO, fd, "%s algo init failed", flm->flm_name);
936 /* Try to subscribe */
937 if (flm->flm_change_rib_item_cb != NULL) {
938 fd->fd_rs = rib_subscribe_locked(fd->fd_rh,
939 handle_rtable_change_cb, fd, RIB_NOTIFY_IMMEDIATE);
940 if (fd->fd_rs == NULL) {
941 FD_PRINTF(LOG_INFO, fd, "failed to subscribe to the rib changes");
942 return (FLM_REBUILD);
947 result = sync_algo(fd);
948 if (result != FLM_SUCCESS) {
949 FD_PRINTF(LOG_INFO, fd, "rib sync failed");
952 FD_PRINTF(LOG_INFO, fd, "DUMP completed successfully.");
956 * Insert fd in the beginning of a list, to maintain invariant
957 * that first matching entry for the AF/fib is always the active
960 TAILQ_INSERT_HEAD(&V_fib_data_list, fd, entries);
961 fd->fd_linked = true;
964 return (FLM_SUCCESS);
968 * Sets up algo @flm for table @rh and links it to the datapath.
971 static enum flm_op_result
972 setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
973 struct fib_data *orig_fd, struct fib_data **pfd, bool attach)
975 struct fib_data *prev_fd, *new_fd;
976 enum flm_op_result result;
979 RIB_WLOCK_ASSERT(rh);
983 for (int i = 0; i < FIB_MAX_TRIES; i++) {
984 result = try_setup_fd_instance(flm, rh, prev_fd, &new_fd);
986 if ((result == FLM_SUCCESS) && attach)
987 result = attach_datapath(new_fd);
989 if ((prev_fd != NULL) && (prev_fd != orig_fd)) {
990 schedule_destroy_fd_instance(prev_fd, false);
994 RH_PRINTF(LOG_INFO, rh, "try %d: fib algo result: %s", i,
995 print_op_result(result));
997 if (result == FLM_REBUILD) {
1006 if (result != FLM_SUCCESS) {
1007 RH_PRINTF(LOG_WARNING, rh,
1008 "%s algo instance setup failed, failures=%d", flm->flm_name,
1009 orig_fd ? orig_fd->fd_failed_rebuilds + 1 : 0);
1010 /* update failure count */
1012 if (orig_fd != NULL)
1013 orig_fd->fd_failed_rebuilds++;
1016 /* Ban algo on non-recoverable error */
1017 if (result == FLM_ERROR)
1018 flm_error_add(flm, rh->rib_fibnum);
1020 if ((prev_fd != NULL) && (prev_fd != orig_fd))
1021 schedule_destroy_fd_instance(prev_fd, false);
1022 if (new_fd != NULL) {
1023 schedule_destroy_fd_instance(new_fd, false);
1033 * Callout for all scheduled fd-related work.
1034 * - Checks if the current algo is still the best algo
1035 * - Creates a new instance of an algo for af/fib if desired.
1038 rebuild_fd_callout(void *_data)
1040 struct fib_data *fd = (struct fib_data *)_data;
1041 struct epoch_tracker et;
1043 FD_PRINTF(LOG_INFO, fd, "running callout rebuild");
1045 NET_EPOCH_ENTER(et);
1046 CURVNET_SET(fd->fd_vnet);
1053 * Tries to create new algo instance based on @fd data.
1054 * Returns true on success.
1057 rebuild_fd(struct fib_data *fd)
1059 struct fib_data *fd_new, *fd_tmp;
1060 struct fib_lookup_module *flm_new = NULL;
1061 enum flm_op_result result;
1062 bool need_rebuild = false;
1065 RIB_WLOCK_ASSERT(fd->fd_rh);
1067 need_rebuild = fd->fd_need_rebuild;
1068 fd->fd_need_rebuild = false;
1069 fd->fd_force_eval = false;
1070 fd->fd_num_changes = 0;
1072 /* First, check if we're still OK to use this algo */
1073 if (!is_algo_fixed(fd->fd_rh))
1074 flm_new = fib_check_best_algo(fd->fd_rh, fd->fd_flm);
1075 if ((flm_new == NULL) && (!need_rebuild)) {
1076 /* Keep existing algo, no need to rebuild. */
1080 if (flm_new == NULL) {
1081 flm_new = fd->fd_flm;
1085 FD_PRINTF(LOG_NOTICE, fd, "switching algo to %s", flm_new->flm_name);
1087 result = setup_fd_instance(flm_new, fd->fd_rh, fd_tmp, &fd_new, true);
1088 if (fd_tmp == NULL) {
1089 /* fd_new represents new algo */
1090 fib_unref_algo(flm_new);
1092 if (result != FLM_SUCCESS) {
1093 FD_PRINTF(LOG_NOTICE, fd, "table rebuild failed");
1096 FD_PRINTF(LOG_INFO, fd_new, "switched to new instance");
1098 /* Remove old instance */
1099 schedule_destroy_fd_instance(fd, true);
1105 * Finds algo by name/family.
1106 * Returns referenced algo or NULL.
1108 static struct fib_lookup_module *
1109 fib_find_algo(const char *algo_name, int family)
1111 struct fib_lookup_module *flm;
1114 TAILQ_FOREACH(flm, &all_algo_list, entries) {
1115 if ((strcmp(flm->flm_name, algo_name) == 0) &&
1116 (family == flm->flm_family)) {
1117 flm->flm_refcount++;
1128 fib_unref_algo(struct fib_lookup_module *flm)
1132 flm->flm_refcount--;
1137 set_fib_algo(uint32_t fibnum, int family, struct sysctl_oid *oidp, struct sysctl_req *req)
1139 struct fib_lookup_module *flm = NULL;
1140 struct fib_data *fd = NULL;
1141 char old_algo_name[32], algo_name[32];
1142 struct rib_head *rh = NULL;
1143 enum flm_op_result result;
1144 struct epoch_tracker et;
1147 /* Fetch current algo/rib for af/family */
1149 TAILQ_FOREACH(fd, &V_fib_data_list, entries) {
1150 if ((fd->fd_family == family) && (fd->fd_fibnum == fibnum))
1158 strlcpy(old_algo_name, fd->fd_flm->flm_name,
1159 sizeof(old_algo_name));
1162 strlcpy(algo_name, old_algo_name, sizeof(algo_name));
1163 error = sysctl_handle_string(oidp, algo_name, sizeof(algo_name), req);
1164 if (error != 0 || req->newptr == NULL)
1167 if (strcmp(algo_name, old_algo_name) == 0)
1170 /* New algorithm name is different */
1171 flm = fib_find_algo(algo_name, family);
1173 RH_PRINTF(LOG_INFO, rh, "unable to find algo %s", algo_name);
1178 NET_EPOCH_ENTER(et);
1180 result = setup_fd_instance(flm, rh, NULL, &fd, true);
1183 fib_unref_algo(flm);
1184 if (result != FLM_SUCCESS)
1187 /* Disable automated jumping between algos */
1191 /* Remove old instance(s) */
1192 fib_cleanup_algo(rh, true, false);
1194 /* Drain cb so user can unload the module after userret if so desired */
1195 epoch_drain_callbacks(net_epoch_preempt);
1202 set_algo_inet_sysctl_handler(SYSCTL_HANDLER_ARGS)
1205 return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET, oidp, req));
1207 SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo,
1208 CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1209 set_algo_inet_sysctl_handler, "A", "Set IPv4 lookup algo");
1214 set_algo_inet6_sysctl_handler(SYSCTL_HANDLER_ARGS)
1217 return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET6, oidp, req));
1219 SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo,
1220 CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1221 set_algo_inet6_sysctl_handler, "A", "Set IPv6 lookup algo");
1225 destroy_fdh_epoch(epoch_context_t ctx)
1227 struct fib_dp_header *fdh;
1229 fdh = __containerof(ctx, struct fib_dp_header, fdh_epoch_ctx);
1230 free(fdh, M_RTABLE);
1233 static struct fib_dp_header *
1234 alloc_fib_dp_array(uint32_t num_tables, bool waitok)
1237 struct fib_dp_header *fdh;
1239 sz = sizeof(struct fib_dp_header);
1240 sz += sizeof(struct fib_dp) * num_tables;
1241 fdh = malloc(sz, M_RTABLE, (waitok ? M_WAITOK : M_NOWAIT) | M_ZERO);
1243 fdh->fdh_num_tables = num_tables;
1247 static struct fib_dp_header *
1248 get_fib_dp_header(struct fib_dp *dp)
1251 return (__containerof((void *)dp, struct fib_dp_header, fdh_idx));
1255 * Replace per-family index pool @pdp with a new one which
1256 * contains updated callback/algo data from @fd.
1257 * Returns 0 on success.
1259 static enum flm_op_result
1260 replace_rtables_family(struct fib_dp **pdp, struct fib_data *fd)
1262 struct fib_dp_header *new_fdh, *old_fdh;
1266 FD_PRINTF(LOG_DEBUG, fd, "[vnet %p] replace with f:%p arg:%p",
1267 curvnet, fd->fd_dp.f, fd->fd_dp.arg);
1270 old_fdh = get_fib_dp_header(*pdp);
1271 new_fdh = alloc_fib_dp_array(old_fdh->fdh_num_tables, false);
1272 FD_PRINTF(LOG_DEBUG, fd, "OLD FDH: %p NEW FDH: %p", old_fdh, new_fdh);
1273 if (new_fdh == NULL) {
1275 FD_PRINTF(LOG_WARNING, fd, "error attaching datapath");
1276 return (FLM_REBUILD);
1279 memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1280 old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1281 /* Update relevant data structure for @fd */
1282 new_fdh->fdh_idx[fd->fd_fibnum] = fd->fd_dp;
1284 /* Ensure memcpy() writes have completed */
1285 atomic_thread_fence_rel();
1286 /* Set new datapath pointer */
1287 *pdp = &new_fdh->fdh_idx[0];
1289 FD_PRINTF(LOG_DEBUG, fd, "update %p -> %p", old_fdh, new_fdh);
1291 epoch_call(net_epoch_preempt, destroy_fdh_epoch,
1292 &old_fdh->fdh_epoch_ctx);
1294 return (FLM_SUCCESS);
1297 static struct fib_dp **
1298 get_family_dp_ptr(int family)
1302 return (&V_inet_dp);
1304 return (&V_inet6_dp);
1310 * Make datapath use fib instance @fd
1312 static enum flm_op_result
1313 attach_datapath(struct fib_data *fd)
1315 struct fib_dp **pdp;
1317 pdp = get_family_dp_ptr(fd->fd_family);
1318 return (replace_rtables_family(pdp, fd));
1322 * Grow datapath pointers array.
1323 * Called from sysctl handler on growing number of routing tables.
1326 grow_rtables_family(struct fib_dp **pdp, uint32_t new_num_tables)
1328 struct fib_dp_header *new_fdh, *old_fdh = NULL;
1330 new_fdh = alloc_fib_dp_array(new_num_tables, true);
1334 old_fdh = get_fib_dp_header(*pdp);
1335 memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1336 old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1339 /* Wait till all writes completed */
1340 atomic_thread_fence_rel();
1342 *pdp = &new_fdh->fdh_idx[0];
1345 if (old_fdh != NULL)
1346 epoch_call(net_epoch_preempt, destroy_fdh_epoch,
1347 &old_fdh->fdh_epoch_ctx);
1351 * Grows per-AF arrays of datapath pointers for each supported family.
1352 * Called from fibs resize sysctl handler.
1355 fib_grow_rtables(uint32_t new_num_tables)
1359 grow_rtables_family(get_family_dp_ptr(AF_INET), new_num_tables);
1362 grow_rtables_family(get_family_dp_ptr(AF_INET6), new_num_tables);
1367 fib_get_rtable_info(struct rib_head *rh, struct rib_rtable_info *rinfo)
1370 bzero(rinfo, sizeof(struct rib_rtable_info));
1371 rinfo->num_prefixes = rh->rnh_prefixes;
1372 rinfo->num_nhops = nhops_get_count(rh);
1374 rinfo->num_nhgrp = nhgrp_get_count(rh);
1379 * Accessor to get rib instance @fd is attached to.
1382 fib_get_rh(struct fib_data *fd)
1389 * Accessor to export idx->nhop array
1391 struct nhop_object **
1392 fib_get_nhop_array(struct fib_data *fd)
1395 return (fd->nh_idx);
1399 get_nhop_idx(struct nhop_object *nh)
1402 if (NH_IS_NHGRP(nh))
1403 return (nhgrp_get_idx((struct nhgrp_object *)nh) * 2 - 1);
1405 return (nhop_get_idx(nh) * 2);
1407 return (nhop_get_idx(nh));
1412 fib_get_nhop_idx(struct fib_data *fd, struct nhop_object *nh)
1415 return (get_nhop_idx(nh));
1419 is_idx_free(struct fib_data *fd, uint32_t index)
1422 return (fd->nh_ref_table->refcnt[index] == 0);
1426 fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh)
1428 uint32_t idx = get_nhop_idx(nh);
1430 if (idx >= fd->number_nhops) {
1435 if (is_idx_free(fd, idx)) {
1437 fd->nh_idx[idx] = nh;
1438 fd->nh_ref_table->count++;
1439 FD_PRINTF(LOG_DEBUG2, fd, " REF nhop %u %p", idx, fd->nh_idx[idx]);
1441 fd->nh_ref_table->refcnt[idx]++;
1446 struct nhop_release_data {
1447 struct nhop_object *nh;
1448 struct epoch_context ctx;
1452 release_nhop_epoch(epoch_context_t ctx)
1454 struct nhop_release_data *nrd;
1456 nrd = __containerof(ctx, struct nhop_release_data, ctx);
1457 nhop_free_any(nrd->nh);
1462 * Delays nexthop refcount release.
1463 * Datapath may have the datastructures not updated yet, so the old
1464 * nexthop may still be returned till the end of current epoch. Delay
1465 * refcount removal, as we may be removing the last instance, which will
1466 * trigger nexthop deletion, rendering returned nexthop invalid.
1469 fib_schedule_release_nhop(struct fib_data *fd, struct nhop_object *nh)
1471 struct nhop_release_data *nrd;
1473 nrd = malloc(sizeof(struct nhop_release_data), M_TEMP, M_NOWAIT | M_ZERO);
1476 epoch_call(net_epoch_preempt, release_nhop_epoch, &nrd->ctx);
1479 * Unable to allocate memory. Leak nexthop to maintain guarantee
1480 * that each nhop can be referenced.
1482 FD_PRINTF(LOG_ERR, fd, "unable to schedule nhop %p deletion", nh);
1487 fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh)
1489 uint32_t idx = get_nhop_idx(nh);
1491 KASSERT((idx < fd->number_nhops), ("invalid nhop index"));
1492 KASSERT((nh == fd->nh_idx[idx]), ("index table contains whong nh"));
1494 fd->nh_ref_table->refcnt[idx]--;
1495 if (fd->nh_ref_table->refcnt[idx] == 0) {
1496 FD_PRINTF(LOG_DEBUG, fd, " FREE nhop %d %p", idx, fd->nh_idx[idx]);
1497 fib_schedule_release_nhop(fd, fd->nh_idx[idx]);
1502 set_algo_fixed(struct rib_head *rh)
1504 switch (rh->rib_family) {
1507 V_algo_fixed_inet = true;
1512 V_algo_fixed_inet6 = true;
1519 is_algo_fixed(struct rib_head *rh)
1522 switch (rh->rib_family) {
1525 return (V_algo_fixed_inet);
1529 return (V_algo_fixed_inet6);
1536 * Runs the check on what would be the best algo for rib @rh, assuming
1537 * that the current algo is the one specified by @orig_flm. Note that
1538 * it can be NULL for initial selection.
1540 * Returns referenced new algo or NULL if the current one is the best.
1542 static struct fib_lookup_module *
1543 fib_check_best_algo(struct rib_head *rh, struct fib_lookup_module *orig_flm)
1545 uint8_t preference, curr_preference = 0, best_preference = 0;
1546 struct fib_lookup_module *flm, *best_flm = NULL;
1547 struct rib_rtable_info rinfo;
1548 int candidate_algos = 0;
1550 fib_get_rtable_info(rh, &rinfo);
1553 TAILQ_FOREACH(flm, &all_algo_list, entries) {
1554 if (flm->flm_family != rh->rib_family)
1557 preference = flm->flm_get_pref(&rinfo);
1558 if (preference > best_preference) {
1559 if (!flm_error_check(flm, rh->rib_fibnum)) {
1560 best_preference = preference;
1564 if (flm == orig_flm)
1565 curr_preference = preference;
1567 if ((best_flm != NULL) && (curr_preference + BEST_DIFF_PERCENT < best_preference))
1568 best_flm->flm_refcount++;
1573 RH_PRINTF(LOG_DEBUG, rh, "candidate_algos: %d, curr: %s(%d) result: %s(%d)",
1574 candidate_algos, orig_flm ? orig_flm->flm_name : "NULL", curr_preference,
1575 best_flm ? best_flm->flm_name : (orig_flm ? orig_flm->flm_name : "NULL"),
1582 * Called when new route table is created.
1583 * Selects, allocates and attaches fib algo for the table.
1586 fib_select_algo_initial(struct rib_head *rh)
1588 struct fib_lookup_module *flm;
1589 struct fib_data *fd = NULL;
1590 enum flm_op_result result;
1591 struct epoch_tracker et;
1594 flm = fib_check_best_algo(rh, NULL);
1596 RH_PRINTF(LOG_CRIT, rh, "no algo selected");
1599 RH_PRINTF(LOG_INFO, rh, "selected algo %s", flm->flm_name);
1601 NET_EPOCH_ENTER(et);
1603 result = setup_fd_instance(flm, rh, NULL, &fd, false);
1607 RH_PRINTF(LOG_DEBUG, rh, "result=%d fd=%p", result, fd);
1608 if (result == FLM_SUCCESS) {
1611 * Attach datapath directly to avoid multiple reallocations
1614 struct fib_dp_header *fdp;
1615 struct fib_dp **pdp;
1617 pdp = get_family_dp_ptr(rh->rib_family);
1619 fdp = get_fib_dp_header(*pdp);
1620 fdp->fdh_idx[fd->fd_fibnum] = fd->fd_dp;
1621 FD_PRINTF(LOG_INFO, fd, "datapath attached");
1625 RH_PRINTF(LOG_CRIT, rh, "unable to setup algo %s", flm->flm_name);
1628 fib_unref_algo(flm);
1634 * Registers fib lookup module within the subsystem.
1637 fib_module_register(struct fib_lookup_module *flm)
1641 ALGO_PRINTF("attaching %s to %s", flm->flm_name,
1642 print_family(flm->flm_family));
1643 TAILQ_INSERT_TAIL(&all_algo_list, flm, entries);
1650 * Tries to unregister fib lookup module.
1652 * Returns 0 on success, EBUSY if module is still used
1653 * by some of the tables.
1656 fib_module_unregister(struct fib_lookup_module *flm)
1660 if (flm->flm_refcount > 0) {
1664 fib_error_clear_flm(flm);
1665 ALGO_PRINTF("detaching %s from %s", flm->flm_name,
1666 print_family(flm->flm_family));
1667 TAILQ_REMOVE(&all_algo_list, flm, entries);
1677 TAILQ_INIT(&V_fib_data_list);
1681 vnet_fib_destroy(void)