1f5baf8bbSAlexander V. Chernikov /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3f5baf8bbSAlexander V. Chernikov *
4f5baf8bbSAlexander V. Chernikov * Copyright (c) 2020 Alexander V. Chernikov
5f5baf8bbSAlexander V. Chernikov *
6f5baf8bbSAlexander V. Chernikov * Redistribution and use in source and binary forms, with or without
7f5baf8bbSAlexander V. Chernikov * modification, are permitted provided that the following conditions
8f5baf8bbSAlexander V. Chernikov * are met:
9f5baf8bbSAlexander V. Chernikov * 1. Redistributions of source code must retain the above copyright
10f5baf8bbSAlexander V. Chernikov * notice, this list of conditions and the following disclaimer.
11f5baf8bbSAlexander V. Chernikov * 2. Redistributions in binary form must reproduce the above copyright
12f5baf8bbSAlexander V. Chernikov * notice, this list of conditions and the following disclaimer in the
13f5baf8bbSAlexander V. Chernikov * documentation and/or other materials provided with the distribution.
14f5baf8bbSAlexander V. Chernikov *
15f5baf8bbSAlexander V. Chernikov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f5baf8bbSAlexander V. Chernikov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f5baf8bbSAlexander V. Chernikov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f5baf8bbSAlexander V. Chernikov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f5baf8bbSAlexander V. Chernikov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f5baf8bbSAlexander V. Chernikov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f5baf8bbSAlexander V. Chernikov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f5baf8bbSAlexander V. Chernikov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f5baf8bbSAlexander V. Chernikov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f5baf8bbSAlexander V. Chernikov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f5baf8bbSAlexander V. Chernikov * SUCH DAMAGE.
26f5baf8bbSAlexander V. Chernikov */
27f5baf8bbSAlexander V. Chernikov
28f5baf8bbSAlexander V. Chernikov #include <sys/cdefs.h>
29f5baf8bbSAlexander V. Chernikov #include "opt_inet.h"
30f5baf8bbSAlexander V. Chernikov #include "opt_inet6.h"
31f5baf8bbSAlexander V. Chernikov #include "opt_route.h"
32f5baf8bbSAlexander V. Chernikov
33f5baf8bbSAlexander V. Chernikov #include <sys/param.h>
34f5baf8bbSAlexander V. Chernikov #include <sys/eventhandler.h>
35f5baf8bbSAlexander V. Chernikov #include <sys/kernel.h>
36f5baf8bbSAlexander V. Chernikov #include <sys/sbuf.h>
37f5baf8bbSAlexander V. Chernikov #include <sys/lock.h>
38f5baf8bbSAlexander V. Chernikov #include <sys/rmlock.h>
39f5baf8bbSAlexander V. Chernikov #include <sys/malloc.h>
40f5baf8bbSAlexander V. Chernikov #include <sys/mbuf.h>
41f5baf8bbSAlexander V. Chernikov #include <sys/module.h>
42f5baf8bbSAlexander V. Chernikov #include <sys/kernel.h>
43f5baf8bbSAlexander V. Chernikov #include <sys/priv.h>
44f5baf8bbSAlexander V. Chernikov #include <sys/proc.h>
45f5baf8bbSAlexander V. Chernikov #include <sys/socket.h>
46f5baf8bbSAlexander V. Chernikov #include <sys/socketvar.h>
47f5baf8bbSAlexander V. Chernikov #include <sys/sysctl.h>
48f5baf8bbSAlexander V. Chernikov #include <sys/syslog.h>
49f5baf8bbSAlexander V. Chernikov #include <sys/queue.h>
50f5baf8bbSAlexander V. Chernikov #include <net/vnet.h>
51f5baf8bbSAlexander V. Chernikov
52f5baf8bbSAlexander V. Chernikov #include <net/if.h>
53f5baf8bbSAlexander V. Chernikov #include <net/if_var.h>
54f5baf8bbSAlexander V. Chernikov
55f5baf8bbSAlexander V. Chernikov #include <netinet/in.h>
56f5baf8bbSAlexander V. Chernikov #include <netinet/in_var.h>
57f5baf8bbSAlexander V. Chernikov #include <netinet/ip.h>
58f5baf8bbSAlexander V. Chernikov #include <netinet/ip_var.h>
59f5baf8bbSAlexander V. Chernikov #ifdef INET6
60f5baf8bbSAlexander V. Chernikov #include <netinet/ip6.h>
61f5baf8bbSAlexander V. Chernikov #include <netinet6/ip6_var.h>
62f5baf8bbSAlexander V. Chernikov #endif
63f5baf8bbSAlexander V. Chernikov
64f5baf8bbSAlexander V. Chernikov #include <net/route.h>
65f5baf8bbSAlexander V. Chernikov #include <net/route/nhop.h>
66f5baf8bbSAlexander V. Chernikov #include <net/route/route_ctl.h>
67f5baf8bbSAlexander V. Chernikov #include <net/route/route_var.h>
68f5baf8bbSAlexander V. Chernikov #include <net/route/fib_algo.h>
69f5baf8bbSAlexander V. Chernikov
70f5baf8bbSAlexander V. Chernikov #include <machine/stdarg.h>
71f5baf8bbSAlexander V. Chernikov
72f5baf8bbSAlexander V. Chernikov /*
73f5baf8bbSAlexander V. Chernikov * Fib lookup framework.
74f5baf8bbSAlexander V. Chernikov *
75f5baf8bbSAlexander V. Chernikov * This framework enables accelerated longest-prefix-match lookups for the
76f5baf8bbSAlexander V. Chernikov * routing tables by adding the ability to dynamically attach/detach lookup
77f5baf8bbSAlexander V. Chernikov * algorithms implementation to/from the datapath.
78f5baf8bbSAlexander V. Chernikov *
79f5baf8bbSAlexander V. Chernikov * flm - fib lookup modules - implementation of particular lookup algorithm
80f5baf8bbSAlexander V. Chernikov * fd - fib data - instance of an flm bound to specific routing table
81f5baf8bbSAlexander V. Chernikov *
82f5baf8bbSAlexander V. Chernikov * This file provides main framework functionality.
83f5baf8bbSAlexander V. Chernikov *
84f5baf8bbSAlexander V. Chernikov * The following are the features provided by the framework
85f5baf8bbSAlexander V. Chernikov *
86f5baf8bbSAlexander V. Chernikov * 1) nexhops abstraction -> provides transparent referencing, indexing
87f5baf8bbSAlexander V. Chernikov * and efficient idx->ptr mappings for nexthop and nexthop groups.
88f5baf8bbSAlexander V. Chernikov * 2) Routing table synchronisation
89f5baf8bbSAlexander V. Chernikov * 3) dataplane attachment points
90f5baf8bbSAlexander V. Chernikov * 4) automatic algorithm selection based on the provided preference.
91f5baf8bbSAlexander V. Chernikov *
92f5baf8bbSAlexander V. Chernikov *
93f5baf8bbSAlexander V. Chernikov * DATAPATH
94f5baf8bbSAlexander V. Chernikov * For each supported address family, there is a an allocated array of fib_dp
95f5baf8bbSAlexander V. Chernikov * structures, indexed by fib number. Each array entry contains callback function
96f5baf8bbSAlexander V. Chernikov * and its argument. This function will be called with a family-specific lookup key,
97f5baf8bbSAlexander V. Chernikov * scope and provided argument. This array gets re-created every time when new algo
98f5baf8bbSAlexander V. Chernikov * instance gets created. Please take a look at the replace_rtables_family() function
99f5baf8bbSAlexander V. Chernikov * for more details.
100f5baf8bbSAlexander V. Chernikov *
101f5baf8bbSAlexander V. Chernikov */
102f5baf8bbSAlexander V. Chernikov
103f5baf8bbSAlexander V. Chernikov SYSCTL_DECL(_net_route);
104f5baf8bbSAlexander V. Chernikov SYSCTL_NODE(_net_route, OID_AUTO, algo, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
105f5baf8bbSAlexander V. Chernikov "Fib algorithm lookups");
106f5baf8bbSAlexander V. Chernikov
107ee2cf2b3SAlexander V. Chernikov /* Algorithm sync policy */
108ee2cf2b3SAlexander V. Chernikov
109ee2cf2b3SAlexander V. Chernikov /* Time interval to bucket updates */
1107d222ce3SAlexander V. Chernikov VNET_DEFINE_STATIC(unsigned int, update_bucket_time_ms) = 50;
1117d222ce3SAlexander V. Chernikov #define V_update_bucket_time_ms VNET(update_bucket_time_ms)
112ee2cf2b3SAlexander V. Chernikov SYSCTL_UINT(_net_route_algo, OID_AUTO, bucket_time_ms, CTLFLAG_RW | CTLFLAG_VNET,
1137d222ce3SAlexander V. Chernikov &VNET_NAME(update_bucket_time_ms), 0, "Time interval to calculate update rate");
114ee2cf2b3SAlexander V. Chernikov
115ee2cf2b3SAlexander V. Chernikov /* Minimum update rate to delay sync */
1167d222ce3SAlexander V. Chernikov VNET_DEFINE_STATIC(unsigned int, bucket_change_threshold_rate) = 500;
117ee2cf2b3SAlexander V. Chernikov #define V_bucket_change_threshold_rate VNET(bucket_change_threshold_rate)
118ee2cf2b3SAlexander V. Chernikov SYSCTL_UINT(_net_route_algo, OID_AUTO, bucket_change_threshold_rate, CTLFLAG_RW | CTLFLAG_VNET,
119ee2cf2b3SAlexander V. Chernikov &VNET_NAME(bucket_change_threshold_rate), 0, "Minimum update rate to delay sync");
120ee2cf2b3SAlexander V. Chernikov
121ee2cf2b3SAlexander V. Chernikov /* Max allowed delay to sync */
1227d222ce3SAlexander V. Chernikov VNET_DEFINE_STATIC(unsigned int, fib_max_sync_delay_ms) = 1000;
123ee2cf2b3SAlexander V. Chernikov #define V_fib_max_sync_delay_ms VNET(fib_max_sync_delay_ms)
124ee2cf2b3SAlexander V. Chernikov SYSCTL_UINT(_net_route_algo, OID_AUTO, fib_max_sync_delay_ms, CTLFLAG_RW | CTLFLAG_VNET,
125ee2cf2b3SAlexander V. Chernikov &VNET_NAME(fib_max_sync_delay_ms), 0, "Maximum time to delay sync (ms)");
126ee2cf2b3SAlexander V. Chernikov
127151ec796SAlexander V. Chernikov
128f5baf8bbSAlexander V. Chernikov #ifdef INET6
1299d6567bcSAlexander V. Chernikov VNET_DEFINE_STATIC(bool, algo_fixed_inet6) = false;
1309d6567bcSAlexander V. Chernikov #define V_algo_fixed_inet6 VNET(algo_fixed_inet6)
131f5baf8bbSAlexander V. Chernikov SYSCTL_NODE(_net_route_algo, OID_AUTO, inet6, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
132f5baf8bbSAlexander V. Chernikov "IPv6 longest prefix match lookups");
133f5baf8bbSAlexander V. Chernikov #endif
134f5baf8bbSAlexander V. Chernikov #ifdef INET
1359d6567bcSAlexander V. Chernikov VNET_DEFINE_STATIC(bool, algo_fixed_inet) = false;
1369d6567bcSAlexander V. Chernikov #define V_algo_fixed_inet VNET(algo_fixed_inet)
137f5baf8bbSAlexander V. Chernikov SYSCTL_NODE(_net_route_algo, OID_AUTO, inet, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
138f5baf8bbSAlexander V. Chernikov "IPv4 longest prefix match lookups");
139f5baf8bbSAlexander V. Chernikov #endif
140f5baf8bbSAlexander V. Chernikov
141f8b7ebeaSAlexander V. Chernikov /* Fib instance counter */
142f8b7ebeaSAlexander V. Chernikov static uint32_t fib_gen = 0;
143f8b7ebeaSAlexander V. Chernikov
144f5baf8bbSAlexander V. Chernikov struct nhop_ref_table {
145f5baf8bbSAlexander V. Chernikov uint32_t count;
146f5baf8bbSAlexander V. Chernikov int32_t refcnt[0];
147f5baf8bbSAlexander V. Chernikov };
148f5baf8bbSAlexander V. Chernikov
149ee2cf2b3SAlexander V. Chernikov enum fib_callout_action {
150ee2cf2b3SAlexander V. Chernikov FDA_NONE, /* No callout scheduled */
151ee2cf2b3SAlexander V. Chernikov FDA_REBUILD, /* Asks to rebuild algo instance */
152ee2cf2b3SAlexander V. Chernikov FDA_EVAL, /* Asks to evaluate if the current algo is still be best */
1536b8ef0d4SAlexander V. Chernikov FDA_BATCH, /* Asks to submit batch of updates to the algo */
154ee2cf2b3SAlexander V. Chernikov };
155ee2cf2b3SAlexander V. Chernikov
156ee2cf2b3SAlexander V. Chernikov struct fib_sync_status {
157ee2cf2b3SAlexander V. Chernikov struct timeval diverge_time; /* ts when diverged */
158ee2cf2b3SAlexander V. Chernikov uint32_t num_changes; /* number of changes since sync */
159ee2cf2b3SAlexander V. Chernikov uint32_t bucket_changes; /* num changes within the current bucket */
160ee2cf2b3SAlexander V. Chernikov uint64_t bucket_id; /* 50ms bucket # */
1616b8ef0d4SAlexander V. Chernikov struct fib_change_queue fd_change_queue;/* list of scheduled entries */
162ee2cf2b3SAlexander V. Chernikov };
163ee2cf2b3SAlexander V. Chernikov
164f5baf8bbSAlexander V. Chernikov /*
165f5baf8bbSAlexander V. Chernikov * Data structure for the fib lookup instance tied to the particular rib.
166f5baf8bbSAlexander V. Chernikov */
167f5baf8bbSAlexander V. Chernikov struct fib_data {
168f5baf8bbSAlexander V. Chernikov uint32_t number_nhops; /* current # of nhops */
169f5baf8bbSAlexander V. Chernikov uint8_t hit_nhops; /* true if out of nhop limit */
170f5baf8bbSAlexander V. Chernikov uint8_t init_done; /* true if init is competed */
171f5baf8bbSAlexander V. Chernikov uint32_t fd_dead:1; /* Scheduled for deletion */
172f5baf8bbSAlexander V. Chernikov uint32_t fd_linked:1; /* true if linked */
173f5baf8bbSAlexander V. Chernikov uint32_t fd_need_rebuild:1; /* true if rebuild scheduled */
1746b8ef0d4SAlexander V. Chernikov uint32_t fd_batch:1; /* true if batched notification scheduled */
175f5baf8bbSAlexander V. Chernikov uint8_t fd_family; /* family */
176f5baf8bbSAlexander V. Chernikov uint32_t fd_fibnum; /* fibnum */
177f5baf8bbSAlexander V. Chernikov uint32_t fd_failed_rebuilds; /* stat: failed rebuilds */
178f8b7ebeaSAlexander V. Chernikov uint32_t fd_gen; /* instance gen# */
179f5baf8bbSAlexander V. Chernikov struct callout fd_callout; /* rebuild callout */
180ee2cf2b3SAlexander V. Chernikov enum fib_callout_action fd_callout_action; /* Callout action to take */
181f5baf8bbSAlexander V. Chernikov void *fd_algo_data; /* algorithm data */
182f5baf8bbSAlexander V. Chernikov struct nhop_object **nh_idx; /* nhop idx->ptr array */
183f5baf8bbSAlexander V. Chernikov struct nhop_ref_table *nh_ref_table; /* array with # of nhop references */
184f5baf8bbSAlexander V. Chernikov struct rib_head *fd_rh; /* RIB table we're attached to */
185f5baf8bbSAlexander V. Chernikov struct rib_subscription *fd_rs; /* storing table subscription */
186f5baf8bbSAlexander V. Chernikov struct fib_dp fd_dp; /* fib datapath data */
187f5baf8bbSAlexander V. Chernikov struct vnet *fd_vnet; /* vnet fib belongs to */
188f5baf8bbSAlexander V. Chernikov struct epoch_context fd_epoch_ctx; /* epoch context for deletion */
189f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *fd_flm;/* pointer to the lookup module */
190ee2cf2b3SAlexander V. Chernikov struct fib_sync_status fd_ss; /* State relevant to the rib sync */
191f5baf8bbSAlexander V. Chernikov uint32_t fd_num_changes; /* number of changes since last callout */
192f5baf8bbSAlexander V. Chernikov TAILQ_ENTRY(fib_data) entries; /* list of all fds in vnet */
193f5baf8bbSAlexander V. Chernikov };
194f5baf8bbSAlexander V. Chernikov
195ee2cf2b3SAlexander V. Chernikov static bool rebuild_fd(struct fib_data *fd, const char *reason);
196ee2cf2b3SAlexander V. Chernikov static bool rebuild_fd_flm(struct fib_data *fd, struct fib_lookup_module *flm_new);
197ee2cf2b3SAlexander V. Chernikov static void handle_fd_callout(void *_data);
198f5baf8bbSAlexander V. Chernikov static void destroy_fd_instance_epoch(epoch_context_t ctx);
199f5baf8bbSAlexander V. Chernikov static bool is_idx_free(struct fib_data *fd, uint32_t index);
200f5baf8bbSAlexander V. Chernikov static void set_algo_fixed(struct rib_head *rh);
2019d6567bcSAlexander V. Chernikov static bool is_algo_fixed(struct rib_head *rh);
202f5baf8bbSAlexander V. Chernikov
203f5baf8bbSAlexander V. Chernikov static uint32_t fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh);
204f5baf8bbSAlexander V. Chernikov static void fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh);
205f5baf8bbSAlexander V. Chernikov
206f5baf8bbSAlexander V. Chernikov static struct fib_lookup_module *fib_check_best_algo(struct rib_head *rh,
207f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *orig_flm);
208f5baf8bbSAlexander V. Chernikov static void fib_unref_algo(struct fib_lookup_module *flm);
209f5baf8bbSAlexander V. Chernikov static bool flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum);
210f5baf8bbSAlexander V. Chernikov
211f5baf8bbSAlexander V. Chernikov struct mtx fib_mtx;
212f5baf8bbSAlexander V. Chernikov #define FIB_MOD_LOCK() mtx_lock(&fib_mtx)
213f5baf8bbSAlexander V. Chernikov #define FIB_MOD_UNLOCK() mtx_unlock(&fib_mtx)
214f5baf8bbSAlexander V. Chernikov #define FIB_MOD_LOCK_ASSERT() mtx_assert(&fib_mtx, MA_OWNED)
215f5baf8bbSAlexander V. Chernikov
216685de460SAlexander V. Chernikov MTX_SYSINIT(fib_mtx, &fib_mtx, "algo list mutex", MTX_DEF);
217f5baf8bbSAlexander V. Chernikov
218f5baf8bbSAlexander V. Chernikov /* Algorithm has to be this percent better than the current to switch */
219f5baf8bbSAlexander V. Chernikov #define BEST_DIFF_PERCENT (5 * 256 / 100)
220f5baf8bbSAlexander V. Chernikov /* Schedule algo re-evaluation X seconds after a change */
221f5baf8bbSAlexander V. Chernikov #define ALGO_EVAL_DELAY_MS 30000
222f5baf8bbSAlexander V. Chernikov /* Force algo re-evaluation after X changes */
223f5baf8bbSAlexander V. Chernikov #define ALGO_EVAL_NUM_ROUTES 100
224f5baf8bbSAlexander V. Chernikov /* Try to setup algorithm X times */
225f5baf8bbSAlexander V. Chernikov #define FIB_MAX_TRIES 32
226f5baf8bbSAlexander V. Chernikov /* Max amount of supported nexthops */
227f5baf8bbSAlexander V. Chernikov #define FIB_MAX_NHOPS 262144
228f5baf8bbSAlexander V. Chernikov #define FIB_CALLOUT_DELAY_MS 50
229f5baf8bbSAlexander V. Chernikov
230ee2cf2b3SAlexander V. Chernikov
231f5baf8bbSAlexander V. Chernikov /* Debug */
232f5baf8bbSAlexander V. Chernikov static int flm_debug_level = LOG_NOTICE;
233f5baf8bbSAlexander V. Chernikov SYSCTL_INT(_net_route_algo, OID_AUTO, debug_level, CTLFLAG_RW | CTLFLAG_RWTUN,
234f5baf8bbSAlexander V. Chernikov &flm_debug_level, 0, "debuglevel");
235f5baf8bbSAlexander V. Chernikov #define FLM_MAX_DEBUG_LEVEL LOG_DEBUG
236f8b7ebeaSAlexander V. Chernikov #ifndef LOG_DEBUG2
237f8b7ebeaSAlexander V. Chernikov #define LOG_DEBUG2 8
238f8b7ebeaSAlexander V. Chernikov #endif
239f5baf8bbSAlexander V. Chernikov
240f5baf8bbSAlexander V. Chernikov #define _PASS_MSG(_l) (flm_debug_level >= (_l))
241c2338561SAlexander V. Chernikov #define ALGO_PRINTF(_l, _fmt, ...) if (_PASS_MSG(_l)) { \
242c2338561SAlexander V. Chernikov printf("[fib_algo] %s: " _fmt "\n", __func__, ##__VA_ARGS__); \
243c2338561SAlexander V. Chernikov }
244f8b7ebeaSAlexander V. Chernikov #define _ALGO_PRINTF(_fib, _fam, _aname, _gen, _func, _fmt, ...) \
245f8b7ebeaSAlexander V. Chernikov printf("[fib_algo] %s.%u (%s#%u) %s: " _fmt "\n",\
246f8b7ebeaSAlexander V. Chernikov print_family(_fam), _fib, _aname, _gen, _func, ## __VA_ARGS__)
247f5baf8bbSAlexander V. Chernikov #define _RH_PRINTF(_fib, _fam, _func, _fmt, ...) \
248f5baf8bbSAlexander V. Chernikov printf("[fib_algo] %s.%u %s: " _fmt "\n", print_family(_fam), _fib, _func, ## __VA_ARGS__)
249f5baf8bbSAlexander V. Chernikov #define RH_PRINTF(_l, _rh, _fmt, ...) if (_PASS_MSG(_l)) { \
250f5baf8bbSAlexander V. Chernikov _RH_PRINTF(_rh->rib_fibnum, _rh->rib_family, __func__, _fmt, ## __VA_ARGS__);\
251f5baf8bbSAlexander V. Chernikov }
252f5baf8bbSAlexander V. Chernikov #define FD_PRINTF(_l, _fd, _fmt, ...) FD_PRINTF_##_l(_l, _fd, _fmt, ## __VA_ARGS__)
253f5baf8bbSAlexander V. Chernikov #define _FD_PRINTF(_l, _fd, _fmt, ...) if (_PASS_MSG(_l)) { \
254f5baf8bbSAlexander V. Chernikov _ALGO_PRINTF(_fd->fd_fibnum, _fd->fd_family, _fd->fd_flm->flm_name, \
255f8b7ebeaSAlexander V. Chernikov _fd->fd_gen, __func__, _fmt, ## __VA_ARGS__); \
256f5baf8bbSAlexander V. Chernikov }
257f8b7ebeaSAlexander V. Chernikov #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG2
258f8b7ebeaSAlexander V. Chernikov #define FD_PRINTF_LOG_DEBUG2 _FD_PRINTF
259f8b7ebeaSAlexander V. Chernikov #else
260f8b7ebeaSAlexander V. Chernikov #define FD_PRINTF_LOG_DEBUG2(_l, _fd, _fmt, ...)
261f8b7ebeaSAlexander V. Chernikov #endif
262f5baf8bbSAlexander V. Chernikov #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG
263f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_DEBUG _FD_PRINTF
264f5baf8bbSAlexander V. Chernikov #else
265f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_DEBUG()
266f5baf8bbSAlexander V. Chernikov #endif
267f5baf8bbSAlexander V. Chernikov #if FLM_MAX_DEBUG_LEVEL>=LOG_INFO
268f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_INFO _FD_PRINTF
269f5baf8bbSAlexander V. Chernikov #else
270f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_INFO()
271f5baf8bbSAlexander V. Chernikov #endif
272f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_NOTICE _FD_PRINTF
273f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_ERR _FD_PRINTF
274f5baf8bbSAlexander V. Chernikov #define FD_PRINTF_LOG_WARNING _FD_PRINTF
275f5baf8bbSAlexander V. Chernikov
276f5baf8bbSAlexander V. Chernikov
277f5baf8bbSAlexander V. Chernikov /* List of all registered lookup algorithms */
278685de460SAlexander V. Chernikov static TAILQ_HEAD(, fib_lookup_module) all_algo_list = TAILQ_HEAD_INITIALIZER(all_algo_list);
279f5baf8bbSAlexander V. Chernikov
280f5baf8bbSAlexander V. Chernikov /* List of all fib lookup instances in the vnet */
281f5baf8bbSAlexander V. Chernikov VNET_DEFINE_STATIC(TAILQ_HEAD(fib_data_head, fib_data), fib_data_list);
282f5baf8bbSAlexander V. Chernikov #define V_fib_data_list VNET(fib_data_list)
283f5baf8bbSAlexander V. Chernikov
284f5baf8bbSAlexander V. Chernikov /* Datastructure for storing non-transient fib lookup module failures */
285f5baf8bbSAlexander V. Chernikov struct fib_error {
286f5baf8bbSAlexander V. Chernikov int fe_family;
287f5baf8bbSAlexander V. Chernikov uint32_t fe_fibnum; /* failed rtable */
288f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *fe_flm; /* failed module */
289f5baf8bbSAlexander V. Chernikov TAILQ_ENTRY(fib_error) entries;/* list of all errored entries */
290f5baf8bbSAlexander V. Chernikov };
291f5baf8bbSAlexander V. Chernikov VNET_DEFINE_STATIC(TAILQ_HEAD(fib_error_head, fib_error), fib_error_list);
292f5baf8bbSAlexander V. Chernikov #define V_fib_error_list VNET(fib_error_list)
293f5baf8bbSAlexander V. Chernikov
294f5baf8bbSAlexander V. Chernikov /* Per-family array of fibnum -> {func, arg} mappings used in datapath */
295f5baf8bbSAlexander V. Chernikov struct fib_dp_header {
296f5baf8bbSAlexander V. Chernikov struct epoch_context fdh_epoch_ctx;
297f5baf8bbSAlexander V. Chernikov uint32_t fdh_num_tables;
298f5baf8bbSAlexander V. Chernikov struct fib_dp fdh_idx[0];
299f5baf8bbSAlexander V. Chernikov };
300f5baf8bbSAlexander V. Chernikov
301f5baf8bbSAlexander V. Chernikov /*
302f5baf8bbSAlexander V. Chernikov * Tries to add new non-transient algorithm error to the list of
303f5baf8bbSAlexander V. Chernikov * errors.
304f5baf8bbSAlexander V. Chernikov * Returns true on success.
305f5baf8bbSAlexander V. Chernikov */
306f5baf8bbSAlexander V. Chernikov static bool
flm_error_add(struct fib_lookup_module * flm,uint32_t fibnum)307f5baf8bbSAlexander V. Chernikov flm_error_add(struct fib_lookup_module *flm, uint32_t fibnum)
308f5baf8bbSAlexander V. Chernikov {
309f5baf8bbSAlexander V. Chernikov struct fib_error *fe;
310f5baf8bbSAlexander V. Chernikov
311f5baf8bbSAlexander V. Chernikov fe = malloc(sizeof(struct fib_error), M_TEMP, M_NOWAIT | M_ZERO);
312f5baf8bbSAlexander V. Chernikov if (fe == NULL)
313f5baf8bbSAlexander V. Chernikov return (false);
314f5baf8bbSAlexander V. Chernikov fe->fe_flm = flm;
315f5baf8bbSAlexander V. Chernikov fe->fe_family = flm->flm_family;
316f5baf8bbSAlexander V. Chernikov fe->fe_fibnum = fibnum;
317f5baf8bbSAlexander V. Chernikov
318f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
319f5baf8bbSAlexander V. Chernikov /* Avoid duplicates by checking if error already exists first */
320f5baf8bbSAlexander V. Chernikov if (flm_error_check(flm, fibnum)) {
321f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
322f5baf8bbSAlexander V. Chernikov free(fe, M_TEMP);
323f5baf8bbSAlexander V. Chernikov return (true);
324f5baf8bbSAlexander V. Chernikov }
325f5baf8bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&V_fib_error_list, fe, entries);
326f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
327f5baf8bbSAlexander V. Chernikov
328f5baf8bbSAlexander V. Chernikov return (true);
329f5baf8bbSAlexander V. Chernikov }
330f5baf8bbSAlexander V. Chernikov
331f5baf8bbSAlexander V. Chernikov /*
332f5baf8bbSAlexander V. Chernikov * True if non-transient error has been registered for @flm in @fibnum.
333f5baf8bbSAlexander V. Chernikov */
334f5baf8bbSAlexander V. Chernikov static bool
flm_error_check(const struct fib_lookup_module * flm,uint32_t fibnum)335f5baf8bbSAlexander V. Chernikov flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum)
336f5baf8bbSAlexander V. Chernikov {
337f5baf8bbSAlexander V. Chernikov const struct fib_error *fe;
338f5baf8bbSAlexander V. Chernikov
339f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH(fe, &V_fib_error_list, entries) {
340f5baf8bbSAlexander V. Chernikov if ((fe->fe_flm == flm) && (fe->fe_fibnum == fibnum))
341f5baf8bbSAlexander V. Chernikov return (true);
342f5baf8bbSAlexander V. Chernikov }
343f5baf8bbSAlexander V. Chernikov
344f5baf8bbSAlexander V. Chernikov return (false);
345f5baf8bbSAlexander V. Chernikov }
346f5baf8bbSAlexander V. Chernikov
347f5baf8bbSAlexander V. Chernikov /*
348f5baf8bbSAlexander V. Chernikov * Clear all errors of algo specified by @flm.
349f5baf8bbSAlexander V. Chernikov */
350f5baf8bbSAlexander V. Chernikov static void
fib_error_clear_flm(struct fib_lookup_module * flm)351f5baf8bbSAlexander V. Chernikov fib_error_clear_flm(struct fib_lookup_module *flm)
352f5baf8bbSAlexander V. Chernikov {
353f5baf8bbSAlexander V. Chernikov struct fib_error *fe, *fe_tmp;
354f5baf8bbSAlexander V. Chernikov
355f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK_ASSERT();
356f5baf8bbSAlexander V. Chernikov
357f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
358f5baf8bbSAlexander V. Chernikov if (fe->fe_flm == flm) {
359f5baf8bbSAlexander V. Chernikov TAILQ_REMOVE(&V_fib_error_list, fe, entries);
360f5baf8bbSAlexander V. Chernikov free(fe, M_TEMP);
361f5baf8bbSAlexander V. Chernikov }
362f5baf8bbSAlexander V. Chernikov }
363f5baf8bbSAlexander V. Chernikov }
364f5baf8bbSAlexander V. Chernikov
365f5baf8bbSAlexander V. Chernikov /*
366f5baf8bbSAlexander V. Chernikov * Clears all errors in current VNET.
367f5baf8bbSAlexander V. Chernikov */
368f5baf8bbSAlexander V. Chernikov static void
fib_error_clear(void)3696409e594SStefan Eßer fib_error_clear(void)
370f5baf8bbSAlexander V. Chernikov {
371f5baf8bbSAlexander V. Chernikov struct fib_error *fe, *fe_tmp;
372f5baf8bbSAlexander V. Chernikov
373f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK_ASSERT();
374f5baf8bbSAlexander V. Chernikov
375f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
376f5baf8bbSAlexander V. Chernikov TAILQ_REMOVE(&V_fib_error_list, fe, entries);
377f5baf8bbSAlexander V. Chernikov free(fe, M_TEMP);
378f5baf8bbSAlexander V. Chernikov }
379f5baf8bbSAlexander V. Chernikov }
380f5baf8bbSAlexander V. Chernikov
381f5baf8bbSAlexander V. Chernikov static const char *
print_op_result(enum flm_op_result result)382f733d970SAlexander V. Chernikov print_op_result(enum flm_op_result result)
383f733d970SAlexander V. Chernikov {
384f733d970SAlexander V. Chernikov switch (result) {
385f733d970SAlexander V. Chernikov case FLM_SUCCESS:
386f733d970SAlexander V. Chernikov return "success";
387f733d970SAlexander V. Chernikov case FLM_REBUILD:
388f733d970SAlexander V. Chernikov return "rebuild";
3896b8ef0d4SAlexander V. Chernikov case FLM_BATCH:
3906b8ef0d4SAlexander V. Chernikov return "batch";
391f733d970SAlexander V. Chernikov case FLM_ERROR:
392f733d970SAlexander V. Chernikov return "error";
393f733d970SAlexander V. Chernikov }
394f733d970SAlexander V. Chernikov
395f733d970SAlexander V. Chernikov return "unknown";
396f733d970SAlexander V. Chernikov }
397f733d970SAlexander V. Chernikov
398f733d970SAlexander V. Chernikov static const char *
print_family(int family)399f5baf8bbSAlexander V. Chernikov print_family(int family)
400f5baf8bbSAlexander V. Chernikov {
401f5baf8bbSAlexander V. Chernikov
402f5baf8bbSAlexander V. Chernikov if (family == AF_INET)
403f5baf8bbSAlexander V. Chernikov return ("inet");
404f5baf8bbSAlexander V. Chernikov else if (family == AF_INET6)
405f5baf8bbSAlexander V. Chernikov return ("inet6");
406f5baf8bbSAlexander V. Chernikov else
407f5baf8bbSAlexander V. Chernikov return ("unknown");
408f5baf8bbSAlexander V. Chernikov }
409f5baf8bbSAlexander V. Chernikov
410f5baf8bbSAlexander V. Chernikov /*
411f5baf8bbSAlexander V. Chernikov * Debug function used by lookup algorithms.
412f5baf8bbSAlexander V. Chernikov * Outputs message denoted by @fmt, prepended by "[fib_algo] inetX.Y (algo) "
413f5baf8bbSAlexander V. Chernikov */
414f5baf8bbSAlexander V. Chernikov void
fib_printf(int level,struct fib_data * fd,const char * func,char * fmt,...)415f5baf8bbSAlexander V. Chernikov fib_printf(int level, struct fib_data *fd, const char *func, char *fmt, ...)
416f5baf8bbSAlexander V. Chernikov {
417f5baf8bbSAlexander V. Chernikov char buf[128];
418f5baf8bbSAlexander V. Chernikov va_list ap;
419f5baf8bbSAlexander V. Chernikov
420f5baf8bbSAlexander V. Chernikov if (level > flm_debug_level)
421f5baf8bbSAlexander V. Chernikov return;
422f5baf8bbSAlexander V. Chernikov
423f5baf8bbSAlexander V. Chernikov va_start(ap, fmt);
424f5baf8bbSAlexander V. Chernikov vsnprintf(buf, sizeof(buf), fmt, ap);
425f5baf8bbSAlexander V. Chernikov va_end(ap);
426f5baf8bbSAlexander V. Chernikov
427f5baf8bbSAlexander V. Chernikov _ALGO_PRINTF(fd->fd_fibnum, fd->fd_family, fd->fd_flm->flm_name,
428f8b7ebeaSAlexander V. Chernikov fd->fd_gen, func, "%s", buf);
429f5baf8bbSAlexander V. Chernikov }
430f5baf8bbSAlexander V. Chernikov
431f5baf8bbSAlexander V. Chernikov /*
432f5baf8bbSAlexander V. Chernikov * Outputs list of algorithms supported by the provided address family.
433f5baf8bbSAlexander V. Chernikov */
434f5baf8bbSAlexander V. Chernikov static int
print_algos_sysctl(struct sysctl_req * req,int family)435f5baf8bbSAlexander V. Chernikov print_algos_sysctl(struct sysctl_req *req, int family)
436f5baf8bbSAlexander V. Chernikov {
437f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *flm;
438f5baf8bbSAlexander V. Chernikov struct sbuf sbuf;
439f5baf8bbSAlexander V. Chernikov int error, count = 0;
440f5baf8bbSAlexander V. Chernikov
441f5baf8bbSAlexander V. Chernikov error = sysctl_wire_old_buffer(req, 0);
442f5baf8bbSAlexander V. Chernikov if (error == 0) {
443f5baf8bbSAlexander V. Chernikov sbuf_new_for_sysctl(&sbuf, NULL, 512, req);
444f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH(flm, &all_algo_list, entries) {
445f5baf8bbSAlexander V. Chernikov if (flm->flm_family == family) {
446f5baf8bbSAlexander V. Chernikov if (count++ > 0)
447f5baf8bbSAlexander V. Chernikov sbuf_cat(&sbuf, ", ");
448f5baf8bbSAlexander V. Chernikov sbuf_cat(&sbuf, flm->flm_name);
449f5baf8bbSAlexander V. Chernikov }
450f5baf8bbSAlexander V. Chernikov }
451f5baf8bbSAlexander V. Chernikov error = sbuf_finish(&sbuf);
452f5baf8bbSAlexander V. Chernikov sbuf_delete(&sbuf);
453f5baf8bbSAlexander V. Chernikov }
454f5baf8bbSAlexander V. Chernikov return (error);
455f5baf8bbSAlexander V. Chernikov }
456f5baf8bbSAlexander V. Chernikov
457f5baf8bbSAlexander V. Chernikov #ifdef INET6
458f5baf8bbSAlexander V. Chernikov static int
print_algos_sysctl_inet6(SYSCTL_HANDLER_ARGS)459f5baf8bbSAlexander V. Chernikov print_algos_sysctl_inet6(SYSCTL_HANDLER_ARGS)
460f5baf8bbSAlexander V. Chernikov {
461f5baf8bbSAlexander V. Chernikov
462f5baf8bbSAlexander V. Chernikov return (print_algos_sysctl(req, AF_INET6));
463f5baf8bbSAlexander V. Chernikov }
464f5baf8bbSAlexander V. Chernikov SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo_list,
465f5baf8bbSAlexander V. Chernikov CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
466f5baf8bbSAlexander V. Chernikov print_algos_sysctl_inet6, "A", "List of IPv6 lookup algorithms");
467f5baf8bbSAlexander V. Chernikov #endif
468f5baf8bbSAlexander V. Chernikov
469f5baf8bbSAlexander V. Chernikov #ifdef INET
470f5baf8bbSAlexander V. Chernikov static int
print_algos_sysctl_inet(SYSCTL_HANDLER_ARGS)471f5baf8bbSAlexander V. Chernikov print_algos_sysctl_inet(SYSCTL_HANDLER_ARGS)
472f5baf8bbSAlexander V. Chernikov {
473f5baf8bbSAlexander V. Chernikov
474f5baf8bbSAlexander V. Chernikov return (print_algos_sysctl(req, AF_INET));
475f5baf8bbSAlexander V. Chernikov }
476f5baf8bbSAlexander V. Chernikov SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo_list,
477f5baf8bbSAlexander V. Chernikov CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
478f5baf8bbSAlexander V. Chernikov print_algos_sysctl_inet, "A", "List of IPv4 lookup algorithms");
479f5baf8bbSAlexander V. Chernikov #endif
480f5baf8bbSAlexander V. Chernikov
481f5baf8bbSAlexander V. Chernikov /*
482f5baf8bbSAlexander V. Chernikov * Calculate delay between repeated failures.
483f5baf8bbSAlexander V. Chernikov * Returns current delay in milliseconds.
484f5baf8bbSAlexander V. Chernikov */
485f5baf8bbSAlexander V. Chernikov static uint32_t
callout_calc_delay_ms(struct fib_data * fd)486f5baf8bbSAlexander V. Chernikov callout_calc_delay_ms(struct fib_data *fd)
487f5baf8bbSAlexander V. Chernikov {
488f5baf8bbSAlexander V. Chernikov uint32_t shift;
489f5baf8bbSAlexander V. Chernikov
490f5baf8bbSAlexander V. Chernikov if (fd->fd_failed_rebuilds > 10)
491f5baf8bbSAlexander V. Chernikov shift = 10;
492f5baf8bbSAlexander V. Chernikov else
493f5baf8bbSAlexander V. Chernikov shift = fd->fd_failed_rebuilds;
494f5baf8bbSAlexander V. Chernikov
495f5baf8bbSAlexander V. Chernikov return ((1 << shift) * FIB_CALLOUT_DELAY_MS);
496f5baf8bbSAlexander V. Chernikov }
497f5baf8bbSAlexander V. Chernikov
498f5baf8bbSAlexander V. Chernikov static void
schedule_callout(struct fib_data * fd,enum fib_callout_action action,int delay_ms)499ee2cf2b3SAlexander V. Chernikov schedule_callout(struct fib_data *fd, enum fib_callout_action action, int delay_ms)
500f5baf8bbSAlexander V. Chernikov {
501f5baf8bbSAlexander V. Chernikov
502ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "delay=%d action=%d", delay_ms, action);
503ee2cf2b3SAlexander V. Chernikov fd->fd_callout_action = action;
504e4ac3f74SAlexander V. Chernikov callout_reset_sbt(&fd->fd_callout, SBT_1MS * delay_ms, 0,
505ee2cf2b3SAlexander V. Chernikov handle_fd_callout, fd, 0);
506f5baf8bbSAlexander V. Chernikov }
507f5baf8bbSAlexander V. Chernikov
508f5baf8bbSAlexander V. Chernikov static void
schedule_fd_rebuild(struct fib_data * fd,const char * reason)509f8b7ebeaSAlexander V. Chernikov schedule_fd_rebuild(struct fib_data *fd, const char *reason)
510f5baf8bbSAlexander V. Chernikov {
511f5baf8bbSAlexander V. Chernikov
512151ec796SAlexander V. Chernikov RIB_WLOCK_ASSERT(fd->fd_rh);
513151ec796SAlexander V. Chernikov
514f5baf8bbSAlexander V. Chernikov if (!fd->fd_need_rebuild) {
515f5baf8bbSAlexander V. Chernikov fd->fd_need_rebuild = true;
5166b8ef0d4SAlexander V. Chernikov /* Stop batch updates */
5176b8ef0d4SAlexander V. Chernikov fd->fd_batch = false;
518f5baf8bbSAlexander V. Chernikov
519f5baf8bbSAlexander V. Chernikov /*
520f5baf8bbSAlexander V. Chernikov * Potentially re-schedules pending callout
521f5baf8bbSAlexander V. Chernikov * initiated by schedule_algo_eval.
522f5baf8bbSAlexander V. Chernikov */
523f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "Scheduling rebuild: %s (failures=%d)",
524f8b7ebeaSAlexander V. Chernikov reason, fd->fd_failed_rebuilds);
525ee2cf2b3SAlexander V. Chernikov schedule_callout(fd, FDA_REBUILD, callout_calc_delay_ms(fd));
526ee2cf2b3SAlexander V. Chernikov }
527ee2cf2b3SAlexander V. Chernikov }
528ee2cf2b3SAlexander V. Chernikov
52933cb3cb2SAlexander V. Chernikov static void
sync_rib_gen(struct fib_data * fd)53033cb3cb2SAlexander V. Chernikov sync_rib_gen(struct fib_data *fd)
53133cb3cb2SAlexander V. Chernikov {
53233cb3cb2SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "Sync gen %u -> %u", fd->fd_rh->rnh_gen, fd->fd_rh->rnh_gen_rib);
53333cb3cb2SAlexander V. Chernikov fd->fd_rh->rnh_gen = fd->fd_rh->rnh_gen_rib;
53433cb3cb2SAlexander V. Chernikov }
53533cb3cb2SAlexander V. Chernikov
536ee2cf2b3SAlexander V. Chernikov static int64_t
get_tv_diff_ms(const struct timeval * old_tv,const struct timeval * new_tv)537ee2cf2b3SAlexander V. Chernikov get_tv_diff_ms(const struct timeval *old_tv, const struct timeval *new_tv)
538ee2cf2b3SAlexander V. Chernikov {
539ee2cf2b3SAlexander V. Chernikov int64_t diff = 0;
540ee2cf2b3SAlexander V. Chernikov
541ee2cf2b3SAlexander V. Chernikov diff = ((int64_t)(new_tv->tv_sec - old_tv->tv_sec)) * 1000;
542ee2cf2b3SAlexander V. Chernikov diff += (new_tv->tv_usec - old_tv->tv_usec) / 1000;
543ee2cf2b3SAlexander V. Chernikov
544ee2cf2b3SAlexander V. Chernikov return (diff);
545ee2cf2b3SAlexander V. Chernikov }
546ee2cf2b3SAlexander V. Chernikov
547ee2cf2b3SAlexander V. Chernikov static void
add_tv_diff_ms(struct timeval * tv,int ms)548ee2cf2b3SAlexander V. Chernikov add_tv_diff_ms(struct timeval *tv, int ms)
549ee2cf2b3SAlexander V. Chernikov {
550ee2cf2b3SAlexander V. Chernikov tv->tv_sec += ms / 1000;
551ee2cf2b3SAlexander V. Chernikov ms = ms % 1000;
552ee2cf2b3SAlexander V. Chernikov if (ms * 1000 + tv->tv_usec < 1000000)
553ee2cf2b3SAlexander V. Chernikov tv->tv_usec += ms * 1000;
554ee2cf2b3SAlexander V. Chernikov else {
555ee2cf2b3SAlexander V. Chernikov tv->tv_sec += 1;
556ee2cf2b3SAlexander V. Chernikov tv->tv_usec = ms * 1000 + tv->tv_usec - 1000000;
557ee2cf2b3SAlexander V. Chernikov }
558ee2cf2b3SAlexander V. Chernikov }
559ee2cf2b3SAlexander V. Chernikov
560ee2cf2b3SAlexander V. Chernikov /*
561ee2cf2b3SAlexander V. Chernikov * Marks the time when algo state diverges from the rib state.
562ee2cf2b3SAlexander V. Chernikov */
563ee2cf2b3SAlexander V. Chernikov static void
mark_diverge_time(struct fib_data * fd)564ee2cf2b3SAlexander V. Chernikov mark_diverge_time(struct fib_data *fd)
565ee2cf2b3SAlexander V. Chernikov {
566ee2cf2b3SAlexander V. Chernikov struct fib_sync_status *fd_ss = &fd->fd_ss;
567ee2cf2b3SAlexander V. Chernikov
568ee2cf2b3SAlexander V. Chernikov getmicrouptime(&fd_ss->diverge_time);
569ee2cf2b3SAlexander V. Chernikov fd_ss->bucket_id = 0;
570ee2cf2b3SAlexander V. Chernikov fd_ss->bucket_changes = 0;
571ee2cf2b3SAlexander V. Chernikov }
572ee2cf2b3SAlexander V. Chernikov
573ee2cf2b3SAlexander V. Chernikov /*
574ee2cf2b3SAlexander V. Chernikov * Calculates and updates the next algorithm sync time, based on the current activity.
575ee2cf2b3SAlexander V. Chernikov *
576ee2cf2b3SAlexander V. Chernikov * The intent is to provide reasonable balance between the update
577ee2cf2b3SAlexander V. Chernikov * latency and efficient batching when changing large amount of routes.
578ee2cf2b3SAlexander V. Chernikov *
579ee2cf2b3SAlexander V. Chernikov * High-level algorithm looks the following:
580ee2cf2b3SAlexander V. Chernikov * 1) all changes are bucketed in 50ms intervals
581ee2cf2b3SAlexander V. Chernikov * 2) If amount of changes within the bucket is greater than the threshold,
582ee2cf2b3SAlexander V. Chernikov * the update gets delayed, up to maximum delay threshold.
583ee2cf2b3SAlexander V. Chernikov */
584ee2cf2b3SAlexander V. Chernikov static void
update_rebuild_delay(struct fib_data * fd,enum fib_callout_action action)5856b8ef0d4SAlexander V. Chernikov update_rebuild_delay(struct fib_data *fd, enum fib_callout_action action)
586ee2cf2b3SAlexander V. Chernikov {
587ee2cf2b3SAlexander V. Chernikov uint32_t bucket_id, new_delay = 0;
588ee2cf2b3SAlexander V. Chernikov struct timeval tv;
589ee2cf2b3SAlexander V. Chernikov
590ee2cf2b3SAlexander V. Chernikov /* Fetch all variables at once to ensure consistent reads */
5917d222ce3SAlexander V. Chernikov uint32_t bucket_time_ms = V_update_bucket_time_ms;
592ee2cf2b3SAlexander V. Chernikov uint32_t threshold_rate = V_bucket_change_threshold_rate;
593ee2cf2b3SAlexander V. Chernikov uint32_t max_delay_ms = V_fib_max_sync_delay_ms;
594ee2cf2b3SAlexander V. Chernikov
595ee2cf2b3SAlexander V. Chernikov if (bucket_time_ms == 0)
596ee2cf2b3SAlexander V. Chernikov bucket_time_ms = 50;
597ee2cf2b3SAlexander V. Chernikov /* calculate per-bucket threshold rate */
598ee2cf2b3SAlexander V. Chernikov threshold_rate = threshold_rate * bucket_time_ms / 1000;
599ee2cf2b3SAlexander V. Chernikov
600ee2cf2b3SAlexander V. Chernikov getmicrouptime(&tv);
601ee2cf2b3SAlexander V. Chernikov
602ee2cf2b3SAlexander V. Chernikov struct fib_sync_status *fd_ss = &fd->fd_ss;
603ee2cf2b3SAlexander V. Chernikov
604ee2cf2b3SAlexander V. Chernikov bucket_id = get_tv_diff_ms(&fd_ss->diverge_time, &tv) / bucket_time_ms;
605ee2cf2b3SAlexander V. Chernikov
606ee2cf2b3SAlexander V. Chernikov if (fd_ss->bucket_id == bucket_id) {
607ee2cf2b3SAlexander V. Chernikov fd_ss->bucket_changes++;
608ee2cf2b3SAlexander V. Chernikov if (fd_ss->bucket_changes == threshold_rate) {
609ee2cf2b3SAlexander V. Chernikov new_delay = (bucket_id + 2) * bucket_time_ms;
610ee2cf2b3SAlexander V. Chernikov if (new_delay <= max_delay_ms) {
611ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd,
612ee2cf2b3SAlexander V. Chernikov "hit threshold of %u routes, delay update,"
613ee2cf2b3SAlexander V. Chernikov "bucket: %u, total delay: %u",
614ee2cf2b3SAlexander V. Chernikov threshold_rate, bucket_id + 1, new_delay);
615ee2cf2b3SAlexander V. Chernikov } else {
616ee2cf2b3SAlexander V. Chernikov new_delay = 0;
617ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd,
618ee2cf2b3SAlexander V. Chernikov "maximum sync delay (%u ms) reached", max_delay_ms);
619ee2cf2b3SAlexander V. Chernikov }
620ee2cf2b3SAlexander V. Chernikov } else if ((bucket_id == 0) && (fd_ss->bucket_changes == 1))
621ee2cf2b3SAlexander V. Chernikov new_delay = bucket_time_ms;
622ee2cf2b3SAlexander V. Chernikov } else {
623ee2cf2b3SAlexander V. Chernikov fd_ss->bucket_id = bucket_id;
624ee2cf2b3SAlexander V. Chernikov fd_ss->bucket_changes = 1;
625ee2cf2b3SAlexander V. Chernikov }
626ee2cf2b3SAlexander V. Chernikov
627ee2cf2b3SAlexander V. Chernikov if (new_delay > 0) {
628ee2cf2b3SAlexander V. Chernikov /* Calculated time has been updated */
629ee2cf2b3SAlexander V. Chernikov struct timeval new_tv = fd_ss->diverge_time;
630ee2cf2b3SAlexander V. Chernikov add_tv_diff_ms(&new_tv, new_delay);
631ee2cf2b3SAlexander V. Chernikov
632ee2cf2b3SAlexander V. Chernikov int32_t delay_ms = get_tv_diff_ms(&tv, &new_tv);
6336b8ef0d4SAlexander V. Chernikov schedule_callout(fd, action, delay_ms);
634f5baf8bbSAlexander V. Chernikov }
635f5baf8bbSAlexander V. Chernikov }
636f5baf8bbSAlexander V. Chernikov
637f5baf8bbSAlexander V. Chernikov static void
update_algo_state(struct fib_data * fd)638ee2cf2b3SAlexander V. Chernikov update_algo_state(struct fib_data *fd)
639f5baf8bbSAlexander V. Chernikov {
640f5baf8bbSAlexander V. Chernikov
641151ec796SAlexander V. Chernikov RIB_WLOCK_ASSERT(fd->fd_rh);
642151ec796SAlexander V. Chernikov
6436b8ef0d4SAlexander V. Chernikov if (fd->fd_batch || fd->fd_need_rebuild) {
6446b8ef0d4SAlexander V. Chernikov enum fib_callout_action action = fd->fd_need_rebuild ? FDA_REBUILD : FDA_BATCH;
6456b8ef0d4SAlexander V. Chernikov update_rebuild_delay(fd, action);
646ee2cf2b3SAlexander V. Chernikov return;
647ee2cf2b3SAlexander V. Chernikov }
648ee2cf2b3SAlexander V. Chernikov
649f5baf8bbSAlexander V. Chernikov if (fd->fd_num_changes++ == 0) {
650f5baf8bbSAlexander V. Chernikov /* Start callout to consider switch */
651f5baf8bbSAlexander V. Chernikov if (!callout_pending(&fd->fd_callout))
652ee2cf2b3SAlexander V. Chernikov schedule_callout(fd, FDA_EVAL, ALGO_EVAL_DELAY_MS);
653ee2cf2b3SAlexander V. Chernikov } else if (fd->fd_num_changes == ALGO_EVAL_NUM_ROUTES) {
654f5baf8bbSAlexander V. Chernikov /* Reset callout to exec immediately */
655ee2cf2b3SAlexander V. Chernikov if (fd->fd_callout_action == FDA_EVAL)
656ee2cf2b3SAlexander V. Chernikov schedule_callout(fd, FDA_EVAL, 1);
657f5baf8bbSAlexander V. Chernikov }
658f5baf8bbSAlexander V. Chernikov }
659f5baf8bbSAlexander V. Chernikov
660151ec796SAlexander V. Chernikov static bool
need_immediate_sync(struct fib_data * fd,struct rib_cmd_info * rc)661ee2cf2b3SAlexander V. Chernikov need_immediate_sync(struct fib_data *fd, struct rib_cmd_info *rc)
662151ec796SAlexander V. Chernikov {
663151ec796SAlexander V. Chernikov struct nhop_object *nh;
664151ec796SAlexander V. Chernikov
665151ec796SAlexander V. Chernikov /* Sync addition/removal of interface routes */
666151ec796SAlexander V. Chernikov switch (rc->rc_cmd) {
667151ec796SAlexander V. Chernikov case RTM_ADD:
668151ec796SAlexander V. Chernikov nh = rc->rc_nh_new;
669439d087dSAlexander V. Chernikov if (!NH_IS_NHGRP(nh)) {
670439d087dSAlexander V. Chernikov if (!(nh->nh_flags & NHF_GATEWAY))
671151ec796SAlexander V. Chernikov return (true);
672439d087dSAlexander V. Chernikov if (nhop_get_rtflags(nh) & RTF_STATIC)
673439d087dSAlexander V. Chernikov return (true);
674439d087dSAlexander V. Chernikov }
675151ec796SAlexander V. Chernikov break;
676151ec796SAlexander V. Chernikov case RTM_DELETE:
677151ec796SAlexander V. Chernikov nh = rc->rc_nh_old;
678439d087dSAlexander V. Chernikov if (!NH_IS_NHGRP(nh)) {
679439d087dSAlexander V. Chernikov if (!(nh->nh_flags & NHF_GATEWAY))
680151ec796SAlexander V. Chernikov return (true);
681439d087dSAlexander V. Chernikov if (nhop_get_rtflags(nh) & RTF_STATIC)
682439d087dSAlexander V. Chernikov return (true);
683439d087dSAlexander V. Chernikov }
684151ec796SAlexander V. Chernikov break;
685151ec796SAlexander V. Chernikov }
686151ec796SAlexander V. Chernikov
687151ec796SAlexander V. Chernikov return (false);
688151ec796SAlexander V. Chernikov }
689151ec796SAlexander V. Chernikov
6906b8ef0d4SAlexander V. Chernikov static bool
apply_rtable_changes(struct fib_data * fd)6916b8ef0d4SAlexander V. Chernikov apply_rtable_changes(struct fib_data *fd)
6926b8ef0d4SAlexander V. Chernikov {
6936b8ef0d4SAlexander V. Chernikov enum flm_op_result result;
6946b8ef0d4SAlexander V. Chernikov struct fib_change_queue *q = &fd->fd_ss.fd_change_queue;
6956b8ef0d4SAlexander V. Chernikov
6966b8ef0d4SAlexander V. Chernikov result = fd->fd_flm->flm_change_rib_items_cb(fd->fd_rh, q, fd->fd_algo_data);
6976b8ef0d4SAlexander V. Chernikov
6986b8ef0d4SAlexander V. Chernikov if (result == FLM_SUCCESS) {
69933cb3cb2SAlexander V. Chernikov sync_rib_gen(fd);
7006b8ef0d4SAlexander V. Chernikov for (int i = 0; i < q->count; i++)
7016b8ef0d4SAlexander V. Chernikov if (q->entries[i].nh_old)
7026b8ef0d4SAlexander V. Chernikov fib_unref_nhop(fd, q->entries[i].nh_old);
7036b8ef0d4SAlexander V. Chernikov q->count = 0;
7046b8ef0d4SAlexander V. Chernikov }
7056b8ef0d4SAlexander V. Chernikov fd->fd_batch = false;
7066b8ef0d4SAlexander V. Chernikov
7076b8ef0d4SAlexander V. Chernikov return (result == FLM_SUCCESS);
7086b8ef0d4SAlexander V. Chernikov }
7096b8ef0d4SAlexander V. Chernikov
7106b8ef0d4SAlexander V. Chernikov static bool
fill_change_entry(struct fib_data * fd,struct fib_change_entry * ce,struct rib_cmd_info * rc)7116b8ef0d4SAlexander V. Chernikov fill_change_entry(struct fib_data *fd, struct fib_change_entry *ce, struct rib_cmd_info *rc)
7126b8ef0d4SAlexander V. Chernikov {
7136b8ef0d4SAlexander V. Chernikov int plen = 0;
7146b8ef0d4SAlexander V. Chernikov
7156b8ef0d4SAlexander V. Chernikov switch (fd->fd_family) {
71667372fb3SAlexander V. Chernikov #ifdef INET
7176b8ef0d4SAlexander V. Chernikov case AF_INET:
7186b8ef0d4SAlexander V. Chernikov rt_get_inet_prefix_plen(rc->rc_rt, &ce->addr4, &plen, &ce->scopeid);
7196b8ef0d4SAlexander V. Chernikov break;
72067372fb3SAlexander V. Chernikov #endif
72167372fb3SAlexander V. Chernikov #ifdef INET6
7226b8ef0d4SAlexander V. Chernikov case AF_INET6:
7236b8ef0d4SAlexander V. Chernikov rt_get_inet6_prefix_plen(rc->rc_rt, &ce->addr6, &plen, &ce->scopeid);
7246b8ef0d4SAlexander V. Chernikov break;
72567372fb3SAlexander V. Chernikov #endif
7266b8ef0d4SAlexander V. Chernikov }
7276b8ef0d4SAlexander V. Chernikov
7286b8ef0d4SAlexander V. Chernikov ce->plen = plen;
7296b8ef0d4SAlexander V. Chernikov ce->nh_old = rc->rc_nh_old;
7306b8ef0d4SAlexander V. Chernikov ce->nh_new = rc->rc_nh_new;
7316b8ef0d4SAlexander V. Chernikov if (ce->nh_new != NULL) {
7326b8ef0d4SAlexander V. Chernikov if (fib_ref_nhop(fd, ce->nh_new) == 0)
7336b8ef0d4SAlexander V. Chernikov return (false);
7346b8ef0d4SAlexander V. Chernikov }
7356b8ef0d4SAlexander V. Chernikov
7366b8ef0d4SAlexander V. Chernikov return (true);
7376b8ef0d4SAlexander V. Chernikov }
7386b8ef0d4SAlexander V. Chernikov
7396b8ef0d4SAlexander V. Chernikov static bool
queue_rtable_change(struct fib_data * fd,struct rib_cmd_info * rc)7406b8ef0d4SAlexander V. Chernikov queue_rtable_change(struct fib_data *fd, struct rib_cmd_info *rc)
7416b8ef0d4SAlexander V. Chernikov {
7426b8ef0d4SAlexander V. Chernikov struct fib_change_queue *q = &fd->fd_ss.fd_change_queue;
7436b8ef0d4SAlexander V. Chernikov
7446b8ef0d4SAlexander V. Chernikov if (q->count >= q->size) {
7456b8ef0d4SAlexander V. Chernikov uint32_t q_size;
7466b8ef0d4SAlexander V. Chernikov
7476b8ef0d4SAlexander V. Chernikov if (q->size == 0)
7486b8ef0d4SAlexander V. Chernikov q_size = 256; /* ~18k memory */
7496b8ef0d4SAlexander V. Chernikov else
7506b8ef0d4SAlexander V. Chernikov q_size = q->size * 2;
7516b8ef0d4SAlexander V. Chernikov
7526b8ef0d4SAlexander V. Chernikov size_t size = q_size * sizeof(struct fib_change_entry);
7536b8ef0d4SAlexander V. Chernikov void *a = realloc(q->entries, size, M_TEMP, M_NOWAIT | M_ZERO);
7546b8ef0d4SAlexander V. Chernikov if (a == NULL) {
7556b8ef0d4SAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "Unable to realloc queue for %u elements",
7566b8ef0d4SAlexander V. Chernikov q_size);
7576b8ef0d4SAlexander V. Chernikov return (false);
7586b8ef0d4SAlexander V. Chernikov }
7596b8ef0d4SAlexander V. Chernikov q->entries = a;
7606b8ef0d4SAlexander V. Chernikov q->size = q_size;
7616b8ef0d4SAlexander V. Chernikov }
7626b8ef0d4SAlexander V. Chernikov
7636b8ef0d4SAlexander V. Chernikov return (fill_change_entry(fd, &q->entries[q->count++], rc));
7646b8ef0d4SAlexander V. Chernikov }
7656b8ef0d4SAlexander V. Chernikov
766f5baf8bbSAlexander V. Chernikov /*
767f5baf8bbSAlexander V. Chernikov * Rib subscription handler. Checks if the algorithm is ready to
768f5baf8bbSAlexander V. Chernikov * receive updates, handles nexthop refcounting and passes change
769f5baf8bbSAlexander V. Chernikov * data to the algorithm callback.
770f5baf8bbSAlexander V. Chernikov */
771f5baf8bbSAlexander V. Chernikov static void
handle_rtable_change_cb(struct rib_head * rnh,struct rib_cmd_info * rc,void * _data)772f5baf8bbSAlexander V. Chernikov handle_rtable_change_cb(struct rib_head *rnh, struct rib_cmd_info *rc,
773f5baf8bbSAlexander V. Chernikov void *_data)
774f5baf8bbSAlexander V. Chernikov {
775f5baf8bbSAlexander V. Chernikov struct fib_data *fd = (struct fib_data *)_data;
776f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
777f5baf8bbSAlexander V. Chernikov
778f5baf8bbSAlexander V. Chernikov RIB_WLOCK_ASSERT(rnh);
779f5baf8bbSAlexander V. Chernikov
780f5baf8bbSAlexander V. Chernikov /*
781f5baf8bbSAlexander V. Chernikov * There is a small gap between subscribing for route changes
782f5baf8bbSAlexander V. Chernikov * and initiating rtable dump. Avoid receiving route changes
783f5baf8bbSAlexander V. Chernikov * prior to finishing rtable dump by checking `init_done`.
784f5baf8bbSAlexander V. Chernikov */
785f5baf8bbSAlexander V. Chernikov if (!fd->init_done)
786f5baf8bbSAlexander V. Chernikov return;
787ee2cf2b3SAlexander V. Chernikov
788ee2cf2b3SAlexander V. Chernikov bool immediate_sync = need_immediate_sync(fd, rc);
789ee2cf2b3SAlexander V. Chernikov
790ee2cf2b3SAlexander V. Chernikov /* Consider scheduling algorithm re-evaluation */
791ee2cf2b3SAlexander V. Chernikov update_algo_state(fd);
792ee2cf2b3SAlexander V. Chernikov
793f5baf8bbSAlexander V. Chernikov /*
794f5baf8bbSAlexander V. Chernikov * If algo requested rebuild, stop sending updates by default.
795f5baf8bbSAlexander V. Chernikov * This simplifies nexthop refcount handling logic.
796f5baf8bbSAlexander V. Chernikov */
7976b8ef0d4SAlexander V. Chernikov if (fd->fd_need_rebuild) {
7986b8ef0d4SAlexander V. Chernikov if (immediate_sync)
7996b8ef0d4SAlexander V. Chernikov rebuild_fd(fd, "rtable change type enforced sync");
800f5baf8bbSAlexander V. Chernikov return;
8016b8ef0d4SAlexander V. Chernikov }
8026b8ef0d4SAlexander V. Chernikov
8036b8ef0d4SAlexander V. Chernikov /*
8046b8ef0d4SAlexander V. Chernikov * Algo requested updates to be delivered in batches.
8056b8ef0d4SAlexander V. Chernikov * Add the current change to the queue and return.
8066b8ef0d4SAlexander V. Chernikov */
8076b8ef0d4SAlexander V. Chernikov if (fd->fd_batch) {
8086b8ef0d4SAlexander V. Chernikov if (immediate_sync) {
8096b8ef0d4SAlexander V. Chernikov if (!queue_rtable_change(fd, rc) || !apply_rtable_changes(fd))
8106b8ef0d4SAlexander V. Chernikov rebuild_fd(fd, "batch sync failed");
8116b8ef0d4SAlexander V. Chernikov } else {
8126b8ef0d4SAlexander V. Chernikov if (!queue_rtable_change(fd, rc))
8136b8ef0d4SAlexander V. Chernikov schedule_fd_rebuild(fd, "batch queue failed");
8146b8ef0d4SAlexander V. Chernikov }
8156b8ef0d4SAlexander V. Chernikov return;
8166b8ef0d4SAlexander V. Chernikov }
817f5baf8bbSAlexander V. Chernikov
818f5baf8bbSAlexander V. Chernikov /*
819f5baf8bbSAlexander V. Chernikov * Maintain guarantee that every nexthop returned by the dataplane
820f5baf8bbSAlexander V. Chernikov * lookup has > 0 refcount, so can be safely referenced within current
821f5baf8bbSAlexander V. Chernikov * epoch.
822f5baf8bbSAlexander V. Chernikov */
823f5baf8bbSAlexander V. Chernikov if (rc->rc_nh_new != NULL) {
824f5baf8bbSAlexander V. Chernikov if (fib_ref_nhop(fd, rc->rc_nh_new) == 0) {
825f5baf8bbSAlexander V. Chernikov /* ran out of indexes */
826f8b7ebeaSAlexander V. Chernikov schedule_fd_rebuild(fd, "ran out of nhop indexes");
827f5baf8bbSAlexander V. Chernikov return;
828f5baf8bbSAlexander V. Chernikov }
829f5baf8bbSAlexander V. Chernikov }
830f5baf8bbSAlexander V. Chernikov
831f5baf8bbSAlexander V. Chernikov result = fd->fd_flm->flm_change_rib_item_cb(rnh, rc, fd->fd_algo_data);
832f5baf8bbSAlexander V. Chernikov
833f5baf8bbSAlexander V. Chernikov switch (result) {
834f5baf8bbSAlexander V. Chernikov case FLM_SUCCESS:
83533cb3cb2SAlexander V. Chernikov sync_rib_gen(fd);
836f5baf8bbSAlexander V. Chernikov /* Unref old nexthop on success */
837f5baf8bbSAlexander V. Chernikov if (rc->rc_nh_old != NULL)
838f5baf8bbSAlexander V. Chernikov fib_unref_nhop(fd, rc->rc_nh_old);
839f5baf8bbSAlexander V. Chernikov break;
8406b8ef0d4SAlexander V. Chernikov case FLM_BATCH:
8416b8ef0d4SAlexander V. Chernikov
8426b8ef0d4SAlexander V. Chernikov /*
8436b8ef0d4SAlexander V. Chernikov * Algo asks to batch the changes.
8446b8ef0d4SAlexander V. Chernikov */
8456b8ef0d4SAlexander V. Chernikov if (queue_rtable_change(fd, rc)) {
8466b8ef0d4SAlexander V. Chernikov if (!immediate_sync) {
8476b8ef0d4SAlexander V. Chernikov fd->fd_batch = true;
8486b8ef0d4SAlexander V. Chernikov mark_diverge_time(fd);
8496b8ef0d4SAlexander V. Chernikov update_rebuild_delay(fd, FDA_BATCH);
8506b8ef0d4SAlexander V. Chernikov break;
8516b8ef0d4SAlexander V. Chernikov }
8526b8ef0d4SAlexander V. Chernikov if (apply_rtable_changes(fd))
8536b8ef0d4SAlexander V. Chernikov break;
8546b8ef0d4SAlexander V. Chernikov }
8556b8ef0d4SAlexander V. Chernikov FD_PRINTF(LOG_ERR, fd, "batched sync failed, force the rebuild");
8566b8ef0d4SAlexander V. Chernikov
857f5baf8bbSAlexander V. Chernikov case FLM_REBUILD:
858f5baf8bbSAlexander V. Chernikov
859f5baf8bbSAlexander V. Chernikov /*
860f5baf8bbSAlexander V. Chernikov * Algo is not able to apply the update.
861f5baf8bbSAlexander V. Chernikov * Schedule algo rebuild.
862f5baf8bbSAlexander V. Chernikov */
863ee2cf2b3SAlexander V. Chernikov if (!immediate_sync) {
864ee2cf2b3SAlexander V. Chernikov mark_diverge_time(fd);
865f8b7ebeaSAlexander V. Chernikov schedule_fd_rebuild(fd, "algo requested rebuild");
866f5baf8bbSAlexander V. Chernikov break;
867151ec796SAlexander V. Chernikov }
868151ec796SAlexander V. Chernikov
869151ec796SAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "running sync rebuild");
870ee2cf2b3SAlexander V. Chernikov rebuild_fd(fd, "rtable change type enforced sync");
871151ec796SAlexander V. Chernikov break;
872f5baf8bbSAlexander V. Chernikov case FLM_ERROR:
873f5baf8bbSAlexander V. Chernikov
874f5baf8bbSAlexander V. Chernikov /*
875f5baf8bbSAlexander V. Chernikov * Algo reported a non-recoverable error.
876f5baf8bbSAlexander V. Chernikov * Record the error and schedule rebuild, which will
877f5baf8bbSAlexander V. Chernikov * trigger best algo selection.
878f5baf8bbSAlexander V. Chernikov */
879f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_ERR, fd, "algo reported non-recoverable error");
880f5baf8bbSAlexander V. Chernikov if (!flm_error_add(fd->fd_flm, fd->fd_fibnum))
881f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_ERR, fd, "failed to ban algo");
882f8b7ebeaSAlexander V. Chernikov schedule_fd_rebuild(fd, "algo reported non-recoverable error");
883f5baf8bbSAlexander V. Chernikov }
884f5baf8bbSAlexander V. Chernikov }
885f5baf8bbSAlexander V. Chernikov
886f5baf8bbSAlexander V. Chernikov static void
estimate_nhop_scale(const struct fib_data * old_fd,struct fib_data * fd)887f5baf8bbSAlexander V. Chernikov estimate_nhop_scale(const struct fib_data *old_fd, struct fib_data *fd)
888f5baf8bbSAlexander V. Chernikov {
889f5baf8bbSAlexander V. Chernikov
890f5baf8bbSAlexander V. Chernikov if (old_fd == NULL) {
891f5baf8bbSAlexander V. Chernikov // TODO: read from rtable
892f5baf8bbSAlexander V. Chernikov fd->number_nhops = 16;
893f5baf8bbSAlexander V. Chernikov return;
894f5baf8bbSAlexander V. Chernikov }
895f5baf8bbSAlexander V. Chernikov
896f5baf8bbSAlexander V. Chernikov if (old_fd->hit_nhops && old_fd->number_nhops < FIB_MAX_NHOPS)
897f5baf8bbSAlexander V. Chernikov fd->number_nhops = 2 * old_fd->number_nhops;
898f5baf8bbSAlexander V. Chernikov else
899f5baf8bbSAlexander V. Chernikov fd->number_nhops = old_fd->number_nhops;
900f5baf8bbSAlexander V. Chernikov }
901f5baf8bbSAlexander V. Chernikov
902f5baf8bbSAlexander V. Chernikov struct walk_cbdata {
903f5baf8bbSAlexander V. Chernikov struct fib_data *fd;
904f5baf8bbSAlexander V. Chernikov flm_dump_t *func;
905f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
906f5baf8bbSAlexander V. Chernikov };
907f5baf8bbSAlexander V. Chernikov
908f5baf8bbSAlexander V. Chernikov /*
909f5baf8bbSAlexander V. Chernikov * Handler called after all rtenties have been dumped.
910f5baf8bbSAlexander V. Chernikov * Performs post-dump framework checks and calls
911f5baf8bbSAlexander V. Chernikov * algo:flm_dump_end_cb().
912f5baf8bbSAlexander V. Chernikov *
913f5baf8bbSAlexander V. Chernikov * Updates walk_cbdata result.
914f5baf8bbSAlexander V. Chernikov */
915f5baf8bbSAlexander V. Chernikov static void
sync_algo_end_cb(struct rib_head * rnh,enum rib_walk_hook stage,void * _data)916f5baf8bbSAlexander V. Chernikov sync_algo_end_cb(struct rib_head *rnh, enum rib_walk_hook stage, void *_data)
917f5baf8bbSAlexander V. Chernikov {
918f5baf8bbSAlexander V. Chernikov struct walk_cbdata *w = (struct walk_cbdata *)_data;
919f5baf8bbSAlexander V. Chernikov struct fib_data *fd = w->fd;
920f5baf8bbSAlexander V. Chernikov
921f5baf8bbSAlexander V. Chernikov RIB_WLOCK_ASSERT(w->fd->fd_rh);
922f5baf8bbSAlexander V. Chernikov
923f5baf8bbSAlexander V. Chernikov if (rnh->rib_dying) {
924f5baf8bbSAlexander V. Chernikov w->result = FLM_ERROR;
925f5baf8bbSAlexander V. Chernikov return;
926f5baf8bbSAlexander V. Chernikov }
927f5baf8bbSAlexander V. Chernikov
928f5baf8bbSAlexander V. Chernikov if (fd->hit_nhops) {
929f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "ran out of nexthops at %u nhops",
930f5baf8bbSAlexander V. Chernikov fd->nh_ref_table->count);
931f733d970SAlexander V. Chernikov if (w->result == FLM_SUCCESS)
932f5baf8bbSAlexander V. Chernikov w->result = FLM_REBUILD;
933f5baf8bbSAlexander V. Chernikov return;
934f5baf8bbSAlexander V. Chernikov }
935f5baf8bbSAlexander V. Chernikov
936f733d970SAlexander V. Chernikov if (stage != RIB_WALK_HOOK_POST || w->result != FLM_SUCCESS)
937f733d970SAlexander V. Chernikov return;
938f733d970SAlexander V. Chernikov
939f733d970SAlexander V. Chernikov /* Post-dump hook, dump successful */
940f5baf8bbSAlexander V. Chernikov w->result = fd->fd_flm->flm_dump_end_cb(fd->fd_algo_data, &fd->fd_dp);
941f5baf8bbSAlexander V. Chernikov
942f5baf8bbSAlexander V. Chernikov if (w->result == FLM_SUCCESS) {
943f5baf8bbSAlexander V. Chernikov /* Mark init as done to allow routing updates */
944f5baf8bbSAlexander V. Chernikov fd->init_done = 1;
945f5baf8bbSAlexander V. Chernikov }
946f5baf8bbSAlexander V. Chernikov }
947f5baf8bbSAlexander V. Chernikov
948f5baf8bbSAlexander V. Chernikov /*
949f5baf8bbSAlexander V. Chernikov * Callback for each entry in rib.
950f5baf8bbSAlexander V. Chernikov * Calls algo:flm_dump_rib_item_cb func as a part of initial
951f5baf8bbSAlexander V. Chernikov * route table synchronisation.
952f5baf8bbSAlexander V. Chernikov */
953f5baf8bbSAlexander V. Chernikov static int
sync_algo_cb(struct rtentry * rt,void * _data)954f5baf8bbSAlexander V. Chernikov sync_algo_cb(struct rtentry *rt, void *_data)
955f5baf8bbSAlexander V. Chernikov {
956f5baf8bbSAlexander V. Chernikov struct walk_cbdata *w = (struct walk_cbdata *)_data;
957f5baf8bbSAlexander V. Chernikov
958f5baf8bbSAlexander V. Chernikov RIB_WLOCK_ASSERT(w->fd->fd_rh);
959f5baf8bbSAlexander V. Chernikov
960f5baf8bbSAlexander V. Chernikov if (w->result == FLM_SUCCESS && w->func) {
961f5baf8bbSAlexander V. Chernikov
962f5baf8bbSAlexander V. Chernikov /*
963f5baf8bbSAlexander V. Chernikov * Reference nexthops to maintain guarantee that
964f5baf8bbSAlexander V. Chernikov * each nexthop returned by datapath has > 0 references
965f5baf8bbSAlexander V. Chernikov * and can be safely referenced within current epoch.
966f5baf8bbSAlexander V. Chernikov */
967f5baf8bbSAlexander V. Chernikov struct nhop_object *nh = rt_get_raw_nhop(rt);
968f5baf8bbSAlexander V. Chernikov if (fib_ref_nhop(w->fd, nh) != 0)
969f5baf8bbSAlexander V. Chernikov w->result = w->func(rt, w->fd->fd_algo_data);
970f5baf8bbSAlexander V. Chernikov else
971f5baf8bbSAlexander V. Chernikov w->result = FLM_REBUILD;
972f5baf8bbSAlexander V. Chernikov }
973f5baf8bbSAlexander V. Chernikov
974f5baf8bbSAlexander V. Chernikov return (0);
975f5baf8bbSAlexander V. Chernikov }
976f5baf8bbSAlexander V. Chernikov
977f5baf8bbSAlexander V. Chernikov /*
978f5baf8bbSAlexander V. Chernikov * Dump all routing table state to the algo instance.
979f5baf8bbSAlexander V. Chernikov */
980f5baf8bbSAlexander V. Chernikov static enum flm_op_result
sync_algo(struct fib_data * fd)981f5baf8bbSAlexander V. Chernikov sync_algo(struct fib_data *fd)
982f5baf8bbSAlexander V. Chernikov {
983f5baf8bbSAlexander V. Chernikov struct walk_cbdata w = {
984f5baf8bbSAlexander V. Chernikov .fd = fd,
985f5baf8bbSAlexander V. Chernikov .func = fd->fd_flm->flm_dump_rib_item_cb,
986f5baf8bbSAlexander V. Chernikov .result = FLM_SUCCESS,
987f5baf8bbSAlexander V. Chernikov };
988f5baf8bbSAlexander V. Chernikov
989151ec796SAlexander V. Chernikov rib_walk_ext_locked(fd->fd_rh, sync_algo_cb, sync_algo_end_cb, &w);
990f5baf8bbSAlexander V. Chernikov
991f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd,
992f8b7ebeaSAlexander V. Chernikov "initial dump completed (rtable version: %d), result: %s",
993f8b7ebeaSAlexander V. Chernikov fd->fd_rh->rnh_gen, print_op_result(w.result));
994f5baf8bbSAlexander V. Chernikov
995f5baf8bbSAlexander V. Chernikov return (w.result);
996f5baf8bbSAlexander V. Chernikov }
997f5baf8bbSAlexander V. Chernikov
998f5baf8bbSAlexander V. Chernikov /*
999f5baf8bbSAlexander V. Chernikov * Schedules epoch-backed @fd instance deletion.
1000f5baf8bbSAlexander V. Chernikov * * Unlinks @fd from the list of active algo instances.
1001f5baf8bbSAlexander V. Chernikov * * Removes rib subscription.
1002f5baf8bbSAlexander V. Chernikov * * Stops callout.
1003f5baf8bbSAlexander V. Chernikov * * Schedules actual deletion.
1004f5baf8bbSAlexander V. Chernikov *
1005f5baf8bbSAlexander V. Chernikov * Assume @fd is already unlinked from the datapath.
1006f5baf8bbSAlexander V. Chernikov */
1007f5baf8bbSAlexander V. Chernikov static int
schedule_destroy_fd_instance(struct fib_data * fd,bool in_callout)1008f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(struct fib_data *fd, bool in_callout)
1009f5baf8bbSAlexander V. Chernikov {
1010f5baf8bbSAlexander V. Chernikov bool is_dead;
1011f5baf8bbSAlexander V. Chernikov
1012f5baf8bbSAlexander V. Chernikov NET_EPOCH_ASSERT();
1013151ec796SAlexander V. Chernikov RIB_WLOCK_ASSERT(fd->fd_rh);
1014f5baf8bbSAlexander V. Chernikov
1015f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1016f5baf8bbSAlexander V. Chernikov is_dead = fd->fd_dead;
1017f5baf8bbSAlexander V. Chernikov if (!is_dead)
1018f5baf8bbSAlexander V. Chernikov fd->fd_dead = true;
1019f5baf8bbSAlexander V. Chernikov if (fd->fd_linked) {
1020f5baf8bbSAlexander V. Chernikov TAILQ_REMOVE(&V_fib_data_list, fd, entries);
1021f5baf8bbSAlexander V. Chernikov fd->fd_linked = false;
1022f5baf8bbSAlexander V. Chernikov }
1023f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1024f5baf8bbSAlexander V. Chernikov if (is_dead)
1025f5baf8bbSAlexander V. Chernikov return (0);
1026f5baf8bbSAlexander V. Chernikov
1027f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "DETACH");
1028f5baf8bbSAlexander V. Chernikov
1029f5baf8bbSAlexander V. Chernikov if (fd->fd_rs != NULL)
10305b42b494SAlexander V. Chernikov rib_unsubscribe_locked(fd->fd_rs);
1031f5baf8bbSAlexander V. Chernikov
1032f5baf8bbSAlexander V. Chernikov /*
1033f5baf8bbSAlexander V. Chernikov * After rib_unsubscribe() no _new_ handle_rtable_change_cb() calls
1034f5baf8bbSAlexander V. Chernikov * will be executed, hence no _new_ callout schedules will happen.
1035f5baf8bbSAlexander V. Chernikov */
1036f5baf8bbSAlexander V. Chernikov callout_stop(&fd->fd_callout);
1037f5baf8bbSAlexander V. Chernikov
1038e2f79d9eSAlexander V. Chernikov fib_epoch_call(destroy_fd_instance_epoch, &fd->fd_epoch_ctx);
1039f5baf8bbSAlexander V. Chernikov
1040f5baf8bbSAlexander V. Chernikov return (0);
1041f5baf8bbSAlexander V. Chernikov }
1042f5baf8bbSAlexander V. Chernikov
1043f5baf8bbSAlexander V. Chernikov /*
1044f5baf8bbSAlexander V. Chernikov * Wipe all fd instances from the list matching rib specified by @rh.
1045f5baf8bbSAlexander V. Chernikov * If @keep_first is set, remove all but the first record.
1046f5baf8bbSAlexander V. Chernikov */
1047f5baf8bbSAlexander V. Chernikov static void
fib_cleanup_algo(struct rib_head * rh,bool keep_first,bool in_callout)1048f5baf8bbSAlexander V. Chernikov fib_cleanup_algo(struct rib_head *rh, bool keep_first, bool in_callout)
1049f5baf8bbSAlexander V. Chernikov {
1050f5baf8bbSAlexander V. Chernikov struct fib_data_head tmp_head = TAILQ_HEAD_INITIALIZER(tmp_head);
1051f5baf8bbSAlexander V. Chernikov struct fib_data *fd, *fd_tmp;
1052f5baf8bbSAlexander V. Chernikov struct epoch_tracker et;
1053f5baf8bbSAlexander V. Chernikov
1054f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1055f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(fd, &V_fib_data_list, entries, fd_tmp) {
1056f5baf8bbSAlexander V. Chernikov if (fd->fd_rh == rh) {
1057f5baf8bbSAlexander V. Chernikov if (keep_first) {
1058f5baf8bbSAlexander V. Chernikov keep_first = false;
1059f5baf8bbSAlexander V. Chernikov continue;
1060f5baf8bbSAlexander V. Chernikov }
1061f5baf8bbSAlexander V. Chernikov TAILQ_REMOVE(&V_fib_data_list, fd, entries);
1062f5baf8bbSAlexander V. Chernikov fd->fd_linked = false;
1063f5baf8bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&tmp_head, fd, entries);
1064f5baf8bbSAlexander V. Chernikov }
1065f5baf8bbSAlexander V. Chernikov }
1066f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1067f5baf8bbSAlexander V. Chernikov
1068f5baf8bbSAlexander V. Chernikov /* Pass 2: remove each entry */
1069f5baf8bbSAlexander V. Chernikov NET_EPOCH_ENTER(et);
1070f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(fd, &tmp_head, entries, fd_tmp) {
1071151ec796SAlexander V. Chernikov if (!in_callout)
1072151ec796SAlexander V. Chernikov RIB_WLOCK(fd->fd_rh);
1073f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(fd, in_callout);
1074151ec796SAlexander V. Chernikov if (!in_callout)
1075151ec796SAlexander V. Chernikov RIB_WUNLOCK(fd->fd_rh);
1076f5baf8bbSAlexander V. Chernikov }
1077f5baf8bbSAlexander V. Chernikov NET_EPOCH_EXIT(et);
1078f5baf8bbSAlexander V. Chernikov }
1079f5baf8bbSAlexander V. Chernikov
1080f5baf8bbSAlexander V. Chernikov void
fib_destroy_rib(struct rib_head * rh)1081f5baf8bbSAlexander V. Chernikov fib_destroy_rib(struct rib_head *rh)
1082f5baf8bbSAlexander V. Chernikov {
1083f5baf8bbSAlexander V. Chernikov
1084f5baf8bbSAlexander V. Chernikov /*
1085f5baf8bbSAlexander V. Chernikov * rnh has `is_dying` flag set, so setup of new fd's will fail at
1086f5baf8bbSAlexander V. Chernikov * sync_algo() stage, preventing new entries to be added to the list
1087f5baf8bbSAlexander V. Chernikov * of active algos. Remove all existing entries for the particular rib.
1088f5baf8bbSAlexander V. Chernikov */
1089f5baf8bbSAlexander V. Chernikov fib_cleanup_algo(rh, false, false);
1090f5baf8bbSAlexander V. Chernikov }
1091f5baf8bbSAlexander V. Chernikov
1092f5baf8bbSAlexander V. Chernikov /*
1093f5baf8bbSAlexander V. Chernikov * Finalises fd destruction by freeing all fd resources.
1094f5baf8bbSAlexander V. Chernikov */
1095f5baf8bbSAlexander V. Chernikov static void
destroy_fd_instance(struct fib_data * fd)1096f5baf8bbSAlexander V. Chernikov destroy_fd_instance(struct fib_data *fd)
1097f5baf8bbSAlexander V. Chernikov {
1098f5baf8bbSAlexander V. Chernikov
1099f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "destroy fd %p", fd);
1100f5baf8bbSAlexander V. Chernikov
1101f5baf8bbSAlexander V. Chernikov /* Call destroy callback first */
1102f5baf8bbSAlexander V. Chernikov if (fd->fd_algo_data != NULL)
1103f5baf8bbSAlexander V. Chernikov fd->fd_flm->flm_destroy_cb(fd->fd_algo_data);
1104f5baf8bbSAlexander V. Chernikov
1105f5baf8bbSAlexander V. Chernikov /* Nhop table */
1106f5baf8bbSAlexander V. Chernikov if ((fd->nh_idx != NULL) && (fd->nh_ref_table != NULL)) {
1107f5baf8bbSAlexander V. Chernikov for (int i = 0; i < fd->number_nhops; i++) {
1108f5baf8bbSAlexander V. Chernikov if (!is_idx_free(fd, i)) {
1109f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG2, fd, " FREE nhop %d %p",
1110f5baf8bbSAlexander V. Chernikov i, fd->nh_idx[i]);
1111f5baf8bbSAlexander V. Chernikov nhop_free_any(fd->nh_idx[i]);
1112f5baf8bbSAlexander V. Chernikov }
1113f5baf8bbSAlexander V. Chernikov }
1114f5baf8bbSAlexander V. Chernikov free(fd->nh_idx, M_RTABLE);
1115f5baf8bbSAlexander V. Chernikov }
1116f5baf8bbSAlexander V. Chernikov if (fd->nh_ref_table != NULL)
1117f5baf8bbSAlexander V. Chernikov free(fd->nh_ref_table, M_RTABLE);
1118f5baf8bbSAlexander V. Chernikov
11196b8ef0d4SAlexander V. Chernikov if (fd->fd_ss.fd_change_queue.entries != NULL)
11206b8ef0d4SAlexander V. Chernikov free(fd->fd_ss.fd_change_queue.entries, M_TEMP);
11216b8ef0d4SAlexander V. Chernikov
1122f5baf8bbSAlexander V. Chernikov fib_unref_algo(fd->fd_flm);
1123f5baf8bbSAlexander V. Chernikov
1124f5baf8bbSAlexander V. Chernikov free(fd, M_RTABLE);
1125f5baf8bbSAlexander V. Chernikov }
1126f5baf8bbSAlexander V. Chernikov
1127f5baf8bbSAlexander V. Chernikov /*
1128f5baf8bbSAlexander V. Chernikov * Epoch callback indicating fd is safe to destroy
1129f5baf8bbSAlexander V. Chernikov */
1130f5baf8bbSAlexander V. Chernikov static void
destroy_fd_instance_epoch(epoch_context_t ctx)1131f5baf8bbSAlexander V. Chernikov destroy_fd_instance_epoch(epoch_context_t ctx)
1132f5baf8bbSAlexander V. Chernikov {
1133f5baf8bbSAlexander V. Chernikov struct fib_data *fd;
1134f5baf8bbSAlexander V. Chernikov
1135f5baf8bbSAlexander V. Chernikov fd = __containerof(ctx, struct fib_data, fd_epoch_ctx);
1136f5baf8bbSAlexander V. Chernikov
113793dd3adaSAlexander V. Chernikov CURVNET_SET(fd->fd_vnet);
1138f5baf8bbSAlexander V. Chernikov destroy_fd_instance(fd);
113993dd3adaSAlexander V. Chernikov CURVNET_RESTORE();
1140f5baf8bbSAlexander V. Chernikov }
1141f5baf8bbSAlexander V. Chernikov
1142f5baf8bbSAlexander V. Chernikov /*
1143f5baf8bbSAlexander V. Chernikov * Tries to setup fd instance.
1144f5baf8bbSAlexander V. Chernikov * - Allocates fd/nhop table
1145f5baf8bbSAlexander V. Chernikov * - Runs algo:flm_init_cb algo init
1146f5baf8bbSAlexander V. Chernikov * - Subscribes fd to the rib
1147f5baf8bbSAlexander V. Chernikov * - Runs rtable dump
1148f5baf8bbSAlexander V. Chernikov * - Adds instance to the list of active instances.
1149f5baf8bbSAlexander V. Chernikov *
1150f5baf8bbSAlexander V. Chernikov * Returns: operation result. Fills in @pfd with resulting fd on success.
1151f5baf8bbSAlexander V. Chernikov *
1152f5baf8bbSAlexander V. Chernikov */
1153f5baf8bbSAlexander V. Chernikov static enum flm_op_result
try_setup_fd_instance(struct fib_lookup_module * flm,struct rib_head * rh,struct fib_data * old_fd,struct fib_data ** pfd)1154f5baf8bbSAlexander V. Chernikov try_setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
1155f5baf8bbSAlexander V. Chernikov struct fib_data *old_fd, struct fib_data **pfd)
1156f5baf8bbSAlexander V. Chernikov {
1157f5baf8bbSAlexander V. Chernikov struct fib_data *fd;
1158f5baf8bbSAlexander V. Chernikov size_t size;
1159f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
1160f5baf8bbSAlexander V. Chernikov
1161f5baf8bbSAlexander V. Chernikov /* Allocate */
1162f5baf8bbSAlexander V. Chernikov fd = malloc(sizeof(struct fib_data), M_RTABLE, M_NOWAIT | M_ZERO);
1163f5baf8bbSAlexander V. Chernikov if (fd == NULL) {
1164f5baf8bbSAlexander V. Chernikov *pfd = NULL;
1165f8b7ebeaSAlexander V. Chernikov RH_PRINTF(LOG_INFO, rh, "Unable to allocate fib_data structure");
1166f5baf8bbSAlexander V. Chernikov return (FLM_REBUILD);
1167f5baf8bbSAlexander V. Chernikov }
1168f5baf8bbSAlexander V. Chernikov *pfd = fd;
1169f5baf8bbSAlexander V. Chernikov
1170f5baf8bbSAlexander V. Chernikov estimate_nhop_scale(old_fd, fd);
1171f5baf8bbSAlexander V. Chernikov
1172f5baf8bbSAlexander V. Chernikov fd->fd_rh = rh;
1173f5baf8bbSAlexander V. Chernikov fd->fd_family = rh->rib_family;
1174f5baf8bbSAlexander V. Chernikov fd->fd_fibnum = rh->rib_fibnum;
1175151ec796SAlexander V. Chernikov callout_init_rm(&fd->fd_callout, &rh->rib_lock, 0);
1176f5baf8bbSAlexander V. Chernikov fd->fd_vnet = curvnet;
1177f5baf8bbSAlexander V. Chernikov fd->fd_flm = flm;
1178f5baf8bbSAlexander V. Chernikov
1179f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1180f5baf8bbSAlexander V. Chernikov flm->flm_refcount++;
118141ce0e34SAlexander V. Chernikov fd->fd_gen = ++fib_gen;
1182f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1183f5baf8bbSAlexander V. Chernikov
118441ce0e34SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "allocated fd %p", fd);
118541ce0e34SAlexander V. Chernikov
1186f5baf8bbSAlexander V. Chernikov /* Allocate nhidx -> nhop_ptr table */
1187f5baf8bbSAlexander V. Chernikov size = fd->number_nhops * sizeof(void *);
1188f5baf8bbSAlexander V. Chernikov fd->nh_idx = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
1189f5baf8bbSAlexander V. Chernikov if (fd->nh_idx == NULL) {
1190f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop table idx (sz:%zu)", size);
1191f5baf8bbSAlexander V. Chernikov return (FLM_REBUILD);
1192f5baf8bbSAlexander V. Chernikov }
1193f5baf8bbSAlexander V. Chernikov
1194f5baf8bbSAlexander V. Chernikov /* Allocate nhop index refcount table */
1195f5baf8bbSAlexander V. Chernikov size = sizeof(struct nhop_ref_table);
1196f5baf8bbSAlexander V. Chernikov size += fd->number_nhops * sizeof(uint32_t);
1197f5baf8bbSAlexander V. Chernikov fd->nh_ref_table = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
1198f5baf8bbSAlexander V. Chernikov if (fd->nh_ref_table == NULL) {
1199f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop refcount table (sz:%zu)", size);
1200f5baf8bbSAlexander V. Chernikov return (FLM_REBUILD);
1201f5baf8bbSAlexander V. Chernikov }
1202f733d970SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "Allocated %u nhop indexes", fd->number_nhops);
1203f5baf8bbSAlexander V. Chernikov
1204f5baf8bbSAlexander V. Chernikov /* Okay, we're ready for algo init */
1205f5baf8bbSAlexander V. Chernikov void *old_algo_data = (old_fd != NULL) ? old_fd->fd_algo_data : NULL;
1206f5baf8bbSAlexander V. Chernikov result = flm->flm_init_cb(fd->fd_fibnum, fd, old_algo_data, &fd->fd_algo_data);
1207f8b7ebeaSAlexander V. Chernikov if (result != FLM_SUCCESS) {
1208f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "%s algo init failed", flm->flm_name);
1209f5baf8bbSAlexander V. Chernikov return (result);
1210f8b7ebeaSAlexander V. Chernikov }
1211f5baf8bbSAlexander V. Chernikov
1212f5baf8bbSAlexander V. Chernikov /* Try to subscribe */
1213f5baf8bbSAlexander V. Chernikov if (flm->flm_change_rib_item_cb != NULL) {
1214151ec796SAlexander V. Chernikov fd->fd_rs = rib_subscribe_locked(fd->fd_rh,
1215151ec796SAlexander V. Chernikov handle_rtable_change_cb, fd, RIB_NOTIFY_IMMEDIATE);
1216f8b7ebeaSAlexander V. Chernikov if (fd->fd_rs == NULL) {
1217f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "failed to subscribe to the rib changes");
1218f5baf8bbSAlexander V. Chernikov return (FLM_REBUILD);
1219f5baf8bbSAlexander V. Chernikov }
1220f8b7ebeaSAlexander V. Chernikov }
1221f5baf8bbSAlexander V. Chernikov
1222f5baf8bbSAlexander V. Chernikov /* Dump */
1223f5baf8bbSAlexander V. Chernikov result = sync_algo(fd);
1224f8b7ebeaSAlexander V. Chernikov if (result != FLM_SUCCESS) {
1225f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "rib sync failed");
1226f5baf8bbSAlexander V. Chernikov return (result);
1227f8b7ebeaSAlexander V. Chernikov }
1228f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "DUMP completed successfully.");
1229f5baf8bbSAlexander V. Chernikov
1230f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1231f5baf8bbSAlexander V. Chernikov /*
1232f5baf8bbSAlexander V. Chernikov * Insert fd in the beginning of a list, to maintain invariant
1233f5baf8bbSAlexander V. Chernikov * that first matching entry for the AF/fib is always the active
1234f5baf8bbSAlexander V. Chernikov * one.
1235f5baf8bbSAlexander V. Chernikov */
1236f5baf8bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&V_fib_data_list, fd, entries);
1237f5baf8bbSAlexander V. Chernikov fd->fd_linked = true;
1238f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1239f5baf8bbSAlexander V. Chernikov
1240f5baf8bbSAlexander V. Chernikov return (FLM_SUCCESS);
1241f5baf8bbSAlexander V. Chernikov }
1242f5baf8bbSAlexander V. Chernikov
1243f5baf8bbSAlexander V. Chernikov /*
1244f5baf8bbSAlexander V. Chernikov * Sets up algo @flm for table @rh and links it to the datapath.
1245f5baf8bbSAlexander V. Chernikov *
1246f5baf8bbSAlexander V. Chernikov */
1247f5baf8bbSAlexander V. Chernikov static enum flm_op_result
setup_fd_instance(struct fib_lookup_module * flm,struct rib_head * rh,struct fib_data * orig_fd,struct fib_data ** pfd,bool attach)1248f5baf8bbSAlexander V. Chernikov setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
1249f5baf8bbSAlexander V. Chernikov struct fib_data *orig_fd, struct fib_data **pfd, bool attach)
1250f5baf8bbSAlexander V. Chernikov {
1251f5baf8bbSAlexander V. Chernikov struct fib_data *prev_fd, *new_fd;
1252f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
1253f5baf8bbSAlexander V. Chernikov
1254151ec796SAlexander V. Chernikov NET_EPOCH_ASSERT();
1255151ec796SAlexander V. Chernikov RIB_WLOCK_ASSERT(rh);
1256151ec796SAlexander V. Chernikov
1257f5baf8bbSAlexander V. Chernikov prev_fd = orig_fd;
1258f5baf8bbSAlexander V. Chernikov new_fd = NULL;
1259f5baf8bbSAlexander V. Chernikov for (int i = 0; i < FIB_MAX_TRIES; i++) {
1260f5baf8bbSAlexander V. Chernikov result = try_setup_fd_instance(flm, rh, prev_fd, &new_fd);
1261f5baf8bbSAlexander V. Chernikov
1262e2f79d9eSAlexander V. Chernikov if ((result == FLM_SUCCESS) && attach) {
126333cb3cb2SAlexander V. Chernikov if (fib_set_datapath_ptr(new_fd, &new_fd->fd_dp))
126433cb3cb2SAlexander V. Chernikov sync_rib_gen(new_fd);
126533cb3cb2SAlexander V. Chernikov else
1266e2f79d9eSAlexander V. Chernikov result = FLM_REBUILD;
1267e2f79d9eSAlexander V. Chernikov }
1268f5baf8bbSAlexander V. Chernikov
1269f5baf8bbSAlexander V. Chernikov if ((prev_fd != NULL) && (prev_fd != orig_fd)) {
1270f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(prev_fd, false);
1271f5baf8bbSAlexander V. Chernikov prev_fd = NULL;
1272f5baf8bbSAlexander V. Chernikov }
1273f5baf8bbSAlexander V. Chernikov
1274f733d970SAlexander V. Chernikov RH_PRINTF(LOG_INFO, rh, "try %d: fib algo result: %s", i,
1275f733d970SAlexander V. Chernikov print_op_result(result));
1276f5baf8bbSAlexander V. Chernikov
1277f5baf8bbSAlexander V. Chernikov if (result == FLM_REBUILD) {
1278f5baf8bbSAlexander V. Chernikov prev_fd = new_fd;
1279f5baf8bbSAlexander V. Chernikov new_fd = NULL;
1280f5baf8bbSAlexander V. Chernikov continue;
1281f5baf8bbSAlexander V. Chernikov }
1282f5baf8bbSAlexander V. Chernikov
1283f5baf8bbSAlexander V. Chernikov break;
1284f5baf8bbSAlexander V. Chernikov }
1285f5baf8bbSAlexander V. Chernikov
1286f5baf8bbSAlexander V. Chernikov if (result != FLM_SUCCESS) {
1287f8b7ebeaSAlexander V. Chernikov RH_PRINTF(LOG_WARNING, rh,
1288f8b7ebeaSAlexander V. Chernikov "%s algo instance setup failed, failures=%d", flm->flm_name,
1289f8b7ebeaSAlexander V. Chernikov orig_fd ? orig_fd->fd_failed_rebuilds + 1 : 0);
1290f5baf8bbSAlexander V. Chernikov /* update failure count */
1291f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1292f5baf8bbSAlexander V. Chernikov if (orig_fd != NULL)
1293f5baf8bbSAlexander V. Chernikov orig_fd->fd_failed_rebuilds++;
1294f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1295f5baf8bbSAlexander V. Chernikov
1296f5baf8bbSAlexander V. Chernikov /* Ban algo on non-recoverable error */
1297f5baf8bbSAlexander V. Chernikov if (result == FLM_ERROR)
1298f5baf8bbSAlexander V. Chernikov flm_error_add(flm, rh->rib_fibnum);
1299f5baf8bbSAlexander V. Chernikov
1300f5baf8bbSAlexander V. Chernikov if ((prev_fd != NULL) && (prev_fd != orig_fd))
1301f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(prev_fd, false);
1302f5baf8bbSAlexander V. Chernikov if (new_fd != NULL) {
1303f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(new_fd, false);
1304f5baf8bbSAlexander V. Chernikov new_fd = NULL;
1305f5baf8bbSAlexander V. Chernikov }
1306f5baf8bbSAlexander V. Chernikov }
1307f5baf8bbSAlexander V. Chernikov
1308f5baf8bbSAlexander V. Chernikov *pfd = new_fd;
1309f5baf8bbSAlexander V. Chernikov return (result);
1310f5baf8bbSAlexander V. Chernikov }
1311f5baf8bbSAlexander V. Chernikov
1312f5baf8bbSAlexander V. Chernikov /*
1313ee2cf2b3SAlexander V. Chernikov * Tries to sync algo with the current rtable state, either
1314ee2cf2b3SAlexander V. Chernikov * by executing batch update or rebuilding.
1315ee2cf2b3SAlexander V. Chernikov * Returns true on success.
1316ee2cf2b3SAlexander V. Chernikov */
1317ee2cf2b3SAlexander V. Chernikov static bool
execute_callout_action(struct fib_data * fd)1318ee2cf2b3SAlexander V. Chernikov execute_callout_action(struct fib_data *fd)
1319ee2cf2b3SAlexander V. Chernikov {
1320ee2cf2b3SAlexander V. Chernikov enum fib_callout_action action = fd->fd_callout_action;
1321ee2cf2b3SAlexander V. Chernikov struct fib_lookup_module *flm_new = NULL;
1322ee2cf2b3SAlexander V. Chernikov bool result = true;
1323ee2cf2b3SAlexander V. Chernikov
1324ee2cf2b3SAlexander V. Chernikov NET_EPOCH_ASSERT();
1325ee2cf2b3SAlexander V. Chernikov RIB_WLOCK_ASSERT(fd->fd_rh);
1326ee2cf2b3SAlexander V. Chernikov
1327ee2cf2b3SAlexander V. Chernikov fd->fd_need_rebuild = false;
13286b8ef0d4SAlexander V. Chernikov fd->fd_batch = false;
1329ee2cf2b3SAlexander V. Chernikov fd->fd_num_changes = 0;
1330ee2cf2b3SAlexander V. Chernikov
1331ee2cf2b3SAlexander V. Chernikov /* First, check if we're still OK to use this algo */
1332ee2cf2b3SAlexander V. Chernikov if (!is_algo_fixed(fd->fd_rh))
1333ee2cf2b3SAlexander V. Chernikov flm_new = fib_check_best_algo(fd->fd_rh, fd->fd_flm);
1334ee2cf2b3SAlexander V. Chernikov if (flm_new != NULL)
1335ee2cf2b3SAlexander V. Chernikov action = FDA_REBUILD;
1336ee2cf2b3SAlexander V. Chernikov
13376b8ef0d4SAlexander V. Chernikov if (action == FDA_BATCH) {
13386b8ef0d4SAlexander V. Chernikov /* Try to sync */
13396b8ef0d4SAlexander V. Chernikov if (!apply_rtable_changes(fd))
13406b8ef0d4SAlexander V. Chernikov action = FDA_REBUILD;
13416b8ef0d4SAlexander V. Chernikov }
13426b8ef0d4SAlexander V. Chernikov
1343ee2cf2b3SAlexander V. Chernikov if (action == FDA_REBUILD)
1344ee2cf2b3SAlexander V. Chernikov result = rebuild_fd_flm(fd, flm_new != NULL ? flm_new : fd->fd_flm);
1345ee2cf2b3SAlexander V. Chernikov if (flm_new != NULL)
1346ee2cf2b3SAlexander V. Chernikov fib_unref_algo(flm_new);
1347ee2cf2b3SAlexander V. Chernikov
1348ee2cf2b3SAlexander V. Chernikov return (result);
1349ee2cf2b3SAlexander V. Chernikov }
1350ee2cf2b3SAlexander V. Chernikov
1351ee2cf2b3SAlexander V. Chernikov /*
1352f5baf8bbSAlexander V. Chernikov * Callout for all scheduled fd-related work.
1353f5baf8bbSAlexander V. Chernikov * - Checks if the current algo is still the best algo
13546b8ef0d4SAlexander V. Chernikov * - Synchronises algo instance to the rtable (batch usecase)
1355f5baf8bbSAlexander V. Chernikov * - Creates a new instance of an algo for af/fib if desired.
1356f5baf8bbSAlexander V. Chernikov */
1357f5baf8bbSAlexander V. Chernikov static void
handle_fd_callout(void * _data)1358ee2cf2b3SAlexander V. Chernikov handle_fd_callout(void *_data)
1359f5baf8bbSAlexander V. Chernikov {
1360ab6d9aaeSAlexander V. Chernikov struct fib_data *fd = (struct fib_data *)_data;
1361151ec796SAlexander V. Chernikov struct epoch_tracker et;
1362ab6d9aaeSAlexander V. Chernikov
1363ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "running callout type=%d", fd->fd_callout_action);
1364ab6d9aaeSAlexander V. Chernikov
1365151ec796SAlexander V. Chernikov NET_EPOCH_ENTER(et);
1366ab6d9aaeSAlexander V. Chernikov CURVNET_SET(fd->fd_vnet);
1367ee2cf2b3SAlexander V. Chernikov execute_callout_action(fd);
1368ab6d9aaeSAlexander V. Chernikov CURVNET_RESTORE();
1369151ec796SAlexander V. Chernikov NET_EPOCH_EXIT(et);
1370ab6d9aaeSAlexander V. Chernikov }
1371ab6d9aaeSAlexander V. Chernikov
1372ab6d9aaeSAlexander V. Chernikov /*
1373ab6d9aaeSAlexander V. Chernikov * Tries to create new algo instance based on @fd data.
1374ab6d9aaeSAlexander V. Chernikov * Returns true on success.
1375ab6d9aaeSAlexander V. Chernikov */
1376ab6d9aaeSAlexander V. Chernikov static bool
rebuild_fd_flm(struct fib_data * fd,struct fib_lookup_module * flm_new)1377ee2cf2b3SAlexander V. Chernikov rebuild_fd_flm(struct fib_data *fd, struct fib_lookup_module *flm_new)
1378ab6d9aaeSAlexander V. Chernikov {
1379ee2cf2b3SAlexander V. Chernikov struct fib_data *fd_new, *fd_tmp = NULL;
1380ee2cf2b3SAlexander V. Chernikov bool result;
1381f5baf8bbSAlexander V. Chernikov
1382ee2cf2b3SAlexander V. Chernikov if (flm_new == fd->fd_flm)
1383f5baf8bbSAlexander V. Chernikov fd_tmp = fd;
1384ee2cf2b3SAlexander V. Chernikov else
1385*ed81a155SMarius Strobl FD_PRINTF(LOG_INFO, fd, "switching algo to %s", flm_new->flm_name);
1386ee2cf2b3SAlexander V. Chernikov
1387f5baf8bbSAlexander V. Chernikov result = setup_fd_instance(flm_new, fd->fd_rh, fd_tmp, &fd_new, true);
1388f5baf8bbSAlexander V. Chernikov if (result != FLM_SUCCESS) {
1389f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_NOTICE, fd, "table rebuild failed");
1390ab6d9aaeSAlexander V. Chernikov return (false);
1391f5baf8bbSAlexander V. Chernikov }
1392f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd_new, "switched to new instance");
1393f5baf8bbSAlexander V. Chernikov
1394ab6d9aaeSAlexander V. Chernikov /* Remove old instance */
1395f5baf8bbSAlexander V. Chernikov schedule_destroy_fd_instance(fd, true);
1396f5baf8bbSAlexander V. Chernikov
1397ab6d9aaeSAlexander V. Chernikov return (true);
1398f5baf8bbSAlexander V. Chernikov }
1399f5baf8bbSAlexander V. Chernikov
1400ee2cf2b3SAlexander V. Chernikov static bool
rebuild_fd(struct fib_data * fd,const char * reason)1401ee2cf2b3SAlexander V. Chernikov rebuild_fd(struct fib_data *fd, const char *reason)
1402ee2cf2b3SAlexander V. Chernikov {
1403ee2cf2b3SAlexander V. Chernikov struct fib_lookup_module *flm_new = NULL;
1404ee2cf2b3SAlexander V. Chernikov bool result;
1405ee2cf2b3SAlexander V. Chernikov
1406ee2cf2b3SAlexander V. Chernikov if (!is_algo_fixed(fd->fd_rh))
1407ee2cf2b3SAlexander V. Chernikov flm_new = fib_check_best_algo(fd->fd_rh, fd->fd_flm);
1408ee2cf2b3SAlexander V. Chernikov
1409ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_INFO, fd, "running sync rebuild: %s", reason);
1410ee2cf2b3SAlexander V. Chernikov result = rebuild_fd_flm(fd, flm_new != NULL ? flm_new : fd->fd_flm);
1411ee2cf2b3SAlexander V. Chernikov if (flm_new != NULL)
1412ee2cf2b3SAlexander V. Chernikov fib_unref_algo(flm_new);
1413ee2cf2b3SAlexander V. Chernikov
1414ee2cf2b3SAlexander V. Chernikov if (!result) {
1415ee2cf2b3SAlexander V. Chernikov FD_PRINTF(LOG_ERR, fd, "sync rebuild failed");
1416ee2cf2b3SAlexander V. Chernikov schedule_fd_rebuild(fd, "sync rebuild failed");
1417ee2cf2b3SAlexander V. Chernikov }
1418ee2cf2b3SAlexander V. Chernikov
1419ee2cf2b3SAlexander V. Chernikov return (result);
1420ee2cf2b3SAlexander V. Chernikov }
1421ee2cf2b3SAlexander V. Chernikov
1422f5baf8bbSAlexander V. Chernikov /*
1423f5baf8bbSAlexander V. Chernikov * Finds algo by name/family.
1424f5baf8bbSAlexander V. Chernikov * Returns referenced algo or NULL.
1425f5baf8bbSAlexander V. Chernikov */
1426f5baf8bbSAlexander V. Chernikov static struct fib_lookup_module *
fib_find_algo(const char * algo_name,int family)1427f5baf8bbSAlexander V. Chernikov fib_find_algo(const char *algo_name, int family)
1428f5baf8bbSAlexander V. Chernikov {
1429f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *flm;
1430f5baf8bbSAlexander V. Chernikov
1431f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1432f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH(flm, &all_algo_list, entries) {
1433f5baf8bbSAlexander V. Chernikov if ((strcmp(flm->flm_name, algo_name) == 0) &&
1434f5baf8bbSAlexander V. Chernikov (family == flm->flm_family)) {
1435f5baf8bbSAlexander V. Chernikov flm->flm_refcount++;
1436f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1437f5baf8bbSAlexander V. Chernikov return (flm);
1438f5baf8bbSAlexander V. Chernikov }
1439f5baf8bbSAlexander V. Chernikov }
1440f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1441f5baf8bbSAlexander V. Chernikov
1442f5baf8bbSAlexander V. Chernikov return (NULL);
1443f5baf8bbSAlexander V. Chernikov }
1444f5baf8bbSAlexander V. Chernikov
1445f5baf8bbSAlexander V. Chernikov static void
fib_unref_algo(struct fib_lookup_module * flm)1446f5baf8bbSAlexander V. Chernikov fib_unref_algo(struct fib_lookup_module *flm)
1447f5baf8bbSAlexander V. Chernikov {
1448f5baf8bbSAlexander V. Chernikov
1449f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1450f5baf8bbSAlexander V. Chernikov flm->flm_refcount--;
1451f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1452f5baf8bbSAlexander V. Chernikov }
1453f5baf8bbSAlexander V. Chernikov
1454f5baf8bbSAlexander V. Chernikov static int
set_fib_algo(uint32_t fibnum,int family,struct sysctl_oid * oidp,struct sysctl_req * req)1455f5baf8bbSAlexander V. Chernikov set_fib_algo(uint32_t fibnum, int family, struct sysctl_oid *oidp, struct sysctl_req *req)
1456f5baf8bbSAlexander V. Chernikov {
1457f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *flm = NULL;
1458f5baf8bbSAlexander V. Chernikov struct fib_data *fd = NULL;
1459f5baf8bbSAlexander V. Chernikov char old_algo_name[32], algo_name[32];
1460f5baf8bbSAlexander V. Chernikov struct rib_head *rh = NULL;
1461f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
1462151ec796SAlexander V. Chernikov struct epoch_tracker et;
1463f5baf8bbSAlexander V. Chernikov int error;
1464f5baf8bbSAlexander V. Chernikov
1465f5baf8bbSAlexander V. Chernikov /* Fetch current algo/rib for af/family */
1466f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1467f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH(fd, &V_fib_data_list, entries) {
1468f5baf8bbSAlexander V. Chernikov if ((fd->fd_family == family) && (fd->fd_fibnum == fibnum))
1469f5baf8bbSAlexander V. Chernikov break;
1470f5baf8bbSAlexander V. Chernikov }
1471f5baf8bbSAlexander V. Chernikov if (fd == NULL) {
1472f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1473f5baf8bbSAlexander V. Chernikov return (ENOENT);
1474f5baf8bbSAlexander V. Chernikov }
1475f5baf8bbSAlexander V. Chernikov rh = fd->fd_rh;
1476f5baf8bbSAlexander V. Chernikov strlcpy(old_algo_name, fd->fd_flm->flm_name,
1477f5baf8bbSAlexander V. Chernikov sizeof(old_algo_name));
1478f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1479f5baf8bbSAlexander V. Chernikov
1480f5baf8bbSAlexander V. Chernikov strlcpy(algo_name, old_algo_name, sizeof(algo_name));
1481f5baf8bbSAlexander V. Chernikov error = sysctl_handle_string(oidp, algo_name, sizeof(algo_name), req);
1482f5baf8bbSAlexander V. Chernikov if (error != 0 || req->newptr == NULL)
1483f5baf8bbSAlexander V. Chernikov return (error);
1484f5baf8bbSAlexander V. Chernikov
1485f5baf8bbSAlexander V. Chernikov if (strcmp(algo_name, old_algo_name) == 0)
1486f5baf8bbSAlexander V. Chernikov return (0);
1487f5baf8bbSAlexander V. Chernikov
1488f5baf8bbSAlexander V. Chernikov /* New algorithm name is different */
1489f5baf8bbSAlexander V. Chernikov flm = fib_find_algo(algo_name, family);
1490f5baf8bbSAlexander V. Chernikov if (flm == NULL) {
1491f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_INFO, rh, "unable to find algo %s", algo_name);
1492f5baf8bbSAlexander V. Chernikov return (ESRCH);
1493f5baf8bbSAlexander V. Chernikov }
1494f5baf8bbSAlexander V. Chernikov
1495f5baf8bbSAlexander V. Chernikov fd = NULL;
1496151ec796SAlexander V. Chernikov NET_EPOCH_ENTER(et);
1497151ec796SAlexander V. Chernikov RIB_WLOCK(rh);
1498f5baf8bbSAlexander V. Chernikov result = setup_fd_instance(flm, rh, NULL, &fd, true);
1499151ec796SAlexander V. Chernikov RIB_WUNLOCK(rh);
1500151ec796SAlexander V. Chernikov NET_EPOCH_EXIT(et);
1501f5baf8bbSAlexander V. Chernikov fib_unref_algo(flm);
1502f5baf8bbSAlexander V. Chernikov if (result != FLM_SUCCESS)
1503f5baf8bbSAlexander V. Chernikov return (EINVAL);
1504f5baf8bbSAlexander V. Chernikov
1505f5baf8bbSAlexander V. Chernikov /* Disable automated jumping between algos */
1506f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1507f5baf8bbSAlexander V. Chernikov set_algo_fixed(rh);
1508f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1509f5baf8bbSAlexander V. Chernikov /* Remove old instance(s) */
1510f5baf8bbSAlexander V. Chernikov fib_cleanup_algo(rh, true, false);
1511f5baf8bbSAlexander V. Chernikov
1512f5baf8bbSAlexander V. Chernikov /* Drain cb so user can unload the module after userret if so desired */
1513150486f6SZhenlei Huang NET_EPOCH_DRAIN_CALLBACKS();
1514f5baf8bbSAlexander V. Chernikov
1515f5baf8bbSAlexander V. Chernikov return (0);
1516f5baf8bbSAlexander V. Chernikov }
1517f5baf8bbSAlexander V. Chernikov
1518f5baf8bbSAlexander V. Chernikov #ifdef INET
1519f5baf8bbSAlexander V. Chernikov static int
set_algo_inet_sysctl_handler(SYSCTL_HANDLER_ARGS)1520f5baf8bbSAlexander V. Chernikov set_algo_inet_sysctl_handler(SYSCTL_HANDLER_ARGS)
1521f5baf8bbSAlexander V. Chernikov {
1522f5baf8bbSAlexander V. Chernikov
152378c93a17SAlexander V. Chernikov return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET, oidp, req));
1524f5baf8bbSAlexander V. Chernikov }
1525f5baf8bbSAlexander V. Chernikov SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo,
15269d6567bcSAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1527f5baf8bbSAlexander V. Chernikov set_algo_inet_sysctl_handler, "A", "Set IPv4 lookup algo");
1528f5baf8bbSAlexander V. Chernikov #endif
1529f5baf8bbSAlexander V. Chernikov
1530f5baf8bbSAlexander V. Chernikov #ifdef INET6
1531f5baf8bbSAlexander V. Chernikov static int
set_algo_inet6_sysctl_handler(SYSCTL_HANDLER_ARGS)1532f5baf8bbSAlexander V. Chernikov set_algo_inet6_sysctl_handler(SYSCTL_HANDLER_ARGS)
1533f5baf8bbSAlexander V. Chernikov {
1534f5baf8bbSAlexander V. Chernikov
153578c93a17SAlexander V. Chernikov return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET6, oidp, req));
1536f5baf8bbSAlexander V. Chernikov }
1537f5baf8bbSAlexander V. Chernikov SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo,
15389d6567bcSAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1539f5baf8bbSAlexander V. Chernikov set_algo_inet6_sysctl_handler, "A", "Set IPv6 lookup algo");
1540f5baf8bbSAlexander V. Chernikov #endif
1541f5baf8bbSAlexander V. Chernikov
15428a0d57baSAlexander V. Chernikov static struct nhop_object *
dummy_lookup(void * algo_data,const struct flm_lookup_key key,uint32_t scopeid)15438a0d57baSAlexander V. Chernikov dummy_lookup(void *algo_data, const struct flm_lookup_key key, uint32_t scopeid)
15448a0d57baSAlexander V. Chernikov {
15458a0d57baSAlexander V. Chernikov return (NULL);
15468a0d57baSAlexander V. Chernikov }
15478a0d57baSAlexander V. Chernikov
1548f5baf8bbSAlexander V. Chernikov static void
destroy_fdh_epoch(epoch_context_t ctx)1549f5baf8bbSAlexander V. Chernikov destroy_fdh_epoch(epoch_context_t ctx)
1550f5baf8bbSAlexander V. Chernikov {
1551f5baf8bbSAlexander V. Chernikov struct fib_dp_header *fdh;
1552f5baf8bbSAlexander V. Chernikov
1553f5baf8bbSAlexander V. Chernikov fdh = __containerof(ctx, struct fib_dp_header, fdh_epoch_ctx);
1554f5baf8bbSAlexander V. Chernikov free(fdh, M_RTABLE);
1555f5baf8bbSAlexander V. Chernikov }
1556f5baf8bbSAlexander V. Chernikov
1557f5baf8bbSAlexander V. Chernikov static struct fib_dp_header *
alloc_fib_dp_array(uint32_t num_tables,bool waitok)1558f5baf8bbSAlexander V. Chernikov alloc_fib_dp_array(uint32_t num_tables, bool waitok)
1559f5baf8bbSAlexander V. Chernikov {
1560f5baf8bbSAlexander V. Chernikov size_t sz;
1561f5baf8bbSAlexander V. Chernikov struct fib_dp_header *fdh;
1562f5baf8bbSAlexander V. Chernikov
1563f5baf8bbSAlexander V. Chernikov sz = sizeof(struct fib_dp_header);
1564f5baf8bbSAlexander V. Chernikov sz += sizeof(struct fib_dp) * num_tables;
1565f5baf8bbSAlexander V. Chernikov fdh = malloc(sz, M_RTABLE, (waitok ? M_WAITOK : M_NOWAIT) | M_ZERO);
15668a0d57baSAlexander V. Chernikov if (fdh != NULL) {
1567f5baf8bbSAlexander V. Chernikov fdh->fdh_num_tables = num_tables;
15688a0d57baSAlexander V. Chernikov /*
15698a0d57baSAlexander V. Chernikov * Set dummy lookup function ptr always returning NULL, so
15708a0d57baSAlexander V. Chernikov * we can delay algo init.
15718a0d57baSAlexander V. Chernikov */
15728a0d57baSAlexander V. Chernikov for (uint32_t i = 0; i < num_tables; i++)
15738a0d57baSAlexander V. Chernikov fdh->fdh_idx[i].f = dummy_lookup;
15748a0d57baSAlexander V. Chernikov }
1575f5baf8bbSAlexander V. Chernikov return (fdh);
1576f5baf8bbSAlexander V. Chernikov }
1577f5baf8bbSAlexander V. Chernikov
1578f5baf8bbSAlexander V. Chernikov static struct fib_dp_header *
get_fib_dp_header(struct fib_dp * dp)1579f5baf8bbSAlexander V. Chernikov get_fib_dp_header(struct fib_dp *dp)
1580f5baf8bbSAlexander V. Chernikov {
1581f5baf8bbSAlexander V. Chernikov
1582f5baf8bbSAlexander V. Chernikov return (__containerof((void *)dp, struct fib_dp_header, fdh_idx));
1583f5baf8bbSAlexander V. Chernikov }
1584f5baf8bbSAlexander V. Chernikov
1585f5baf8bbSAlexander V. Chernikov /*
1586f5baf8bbSAlexander V. Chernikov * Replace per-family index pool @pdp with a new one which
1587f5baf8bbSAlexander V. Chernikov * contains updated callback/algo data from @fd.
1588e2f79d9eSAlexander V. Chernikov * Returns true on success.
1589f5baf8bbSAlexander V. Chernikov */
1590e2f79d9eSAlexander V. Chernikov static bool
replace_rtables_family(struct fib_dp ** pdp,struct fib_data * fd,struct fib_dp * dp)1591e2f79d9eSAlexander V. Chernikov replace_rtables_family(struct fib_dp **pdp, struct fib_data *fd, struct fib_dp *dp)
1592f5baf8bbSAlexander V. Chernikov {
1593f5baf8bbSAlexander V. Chernikov struct fib_dp_header *new_fdh, *old_fdh;
1594f5baf8bbSAlexander V. Chernikov
1595f5baf8bbSAlexander V. Chernikov NET_EPOCH_ASSERT();
1596f5baf8bbSAlexander V. Chernikov
1597f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "[vnet %p] replace with f:%p arg:%p",
1598e2f79d9eSAlexander V. Chernikov curvnet, dp->f, dp->arg);
1599f5baf8bbSAlexander V. Chernikov
1600f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1601f5baf8bbSAlexander V. Chernikov old_fdh = get_fib_dp_header(*pdp);
1602e2f79d9eSAlexander V. Chernikov
16030abb6ff5SAlexander V. Chernikov if (old_fdh->fdh_idx[fd->fd_fibnum].f == dp->f) {
16040abb6ff5SAlexander V. Chernikov /*
16050abb6ff5SAlexander V. Chernikov * Function is the same, data pointer needs update.
16060abb6ff5SAlexander V. Chernikov * Perform in-line replace without reallocation.
16070abb6ff5SAlexander V. Chernikov */
16080abb6ff5SAlexander V. Chernikov old_fdh->fdh_idx[fd->fd_fibnum].arg = dp->arg;
16090abb6ff5SAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "FDH %p inline update", old_fdh);
16100abb6ff5SAlexander V. Chernikov FIB_MOD_UNLOCK();
16110abb6ff5SAlexander V. Chernikov return (true);
16120abb6ff5SAlexander V. Chernikov }
16130abb6ff5SAlexander V. Chernikov
1614f5baf8bbSAlexander V. Chernikov new_fdh = alloc_fib_dp_array(old_fdh->fdh_num_tables, false);
1615f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "OLD FDH: %p NEW FDH: %p", old_fdh, new_fdh);
1616f5baf8bbSAlexander V. Chernikov if (new_fdh == NULL) {
1617f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1618f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_WARNING, fd, "error attaching datapath");
1619e2f79d9eSAlexander V. Chernikov return (false);
1620f5baf8bbSAlexander V. Chernikov }
1621f5baf8bbSAlexander V. Chernikov
1622f5baf8bbSAlexander V. Chernikov memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1623f5baf8bbSAlexander V. Chernikov old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1624f5baf8bbSAlexander V. Chernikov /* Update relevant data structure for @fd */
1625e2f79d9eSAlexander V. Chernikov new_fdh->fdh_idx[fd->fd_fibnum] = *dp;
1626f5baf8bbSAlexander V. Chernikov
1627f5baf8bbSAlexander V. Chernikov /* Ensure memcpy() writes have completed */
1628f5baf8bbSAlexander V. Chernikov atomic_thread_fence_rel();
1629f5baf8bbSAlexander V. Chernikov /* Set new datapath pointer */
1630f5baf8bbSAlexander V. Chernikov *pdp = &new_fdh->fdh_idx[0];
1631f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1632f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, "update %p -> %p", old_fdh, new_fdh);
1633f5baf8bbSAlexander V. Chernikov
1634e2f79d9eSAlexander V. Chernikov fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
1635f5baf8bbSAlexander V. Chernikov
1636e2f79d9eSAlexander V. Chernikov return (true);
1637f5baf8bbSAlexander V. Chernikov }
1638f5baf8bbSAlexander V. Chernikov
1639f5baf8bbSAlexander V. Chernikov static struct fib_dp **
get_family_dp_ptr(int family)1640f5baf8bbSAlexander V. Chernikov get_family_dp_ptr(int family)
1641f5baf8bbSAlexander V. Chernikov {
1642f5baf8bbSAlexander V. Chernikov switch (family) {
16437d222ce3SAlexander V. Chernikov #ifdef INET
1644f5baf8bbSAlexander V. Chernikov case AF_INET:
1645f5baf8bbSAlexander V. Chernikov return (&V_inet_dp);
16467d222ce3SAlexander V. Chernikov #endif
16477d222ce3SAlexander V. Chernikov #ifdef INET6
1648f5baf8bbSAlexander V. Chernikov case AF_INET6:
1649f5baf8bbSAlexander V. Chernikov return (&V_inet6_dp);
16507d222ce3SAlexander V. Chernikov #endif
1651f5baf8bbSAlexander V. Chernikov }
1652f5baf8bbSAlexander V. Chernikov return (NULL);
1653f5baf8bbSAlexander V. Chernikov }
1654f5baf8bbSAlexander V. Chernikov
1655f5baf8bbSAlexander V. Chernikov /*
1656f5baf8bbSAlexander V. Chernikov * Make datapath use fib instance @fd
1657f5baf8bbSAlexander V. Chernikov */
1658e2f79d9eSAlexander V. Chernikov bool
fib_set_datapath_ptr(struct fib_data * fd,struct fib_dp * dp)1659e2f79d9eSAlexander V. Chernikov fib_set_datapath_ptr(struct fib_data *fd, struct fib_dp *dp)
1660f5baf8bbSAlexander V. Chernikov {
1661f5baf8bbSAlexander V. Chernikov struct fib_dp **pdp;
1662f5baf8bbSAlexander V. Chernikov
1663f5baf8bbSAlexander V. Chernikov pdp = get_family_dp_ptr(fd->fd_family);
1664e2f79d9eSAlexander V. Chernikov return (replace_rtables_family(pdp, fd, dp));
1665f5baf8bbSAlexander V. Chernikov }
1666f5baf8bbSAlexander V. Chernikov
1667f5baf8bbSAlexander V. Chernikov /*
1668f5baf8bbSAlexander V. Chernikov * Grow datapath pointers array.
1669f5baf8bbSAlexander V. Chernikov * Called from sysctl handler on growing number of routing tables.
1670f5baf8bbSAlexander V. Chernikov */
1671f5baf8bbSAlexander V. Chernikov static void
grow_rtables_family(struct fib_dp ** pdp,uint32_t new_num_tables)1672f5baf8bbSAlexander V. Chernikov grow_rtables_family(struct fib_dp **pdp, uint32_t new_num_tables)
1673f5baf8bbSAlexander V. Chernikov {
1674f5baf8bbSAlexander V. Chernikov struct fib_dp_header *new_fdh, *old_fdh = NULL;
1675f5baf8bbSAlexander V. Chernikov
1676f5baf8bbSAlexander V. Chernikov new_fdh = alloc_fib_dp_array(new_num_tables, true);
1677f5baf8bbSAlexander V. Chernikov
1678f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1679f5baf8bbSAlexander V. Chernikov if (*pdp != NULL) {
1680f5baf8bbSAlexander V. Chernikov old_fdh = get_fib_dp_header(*pdp);
1681f5baf8bbSAlexander V. Chernikov memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1682f5baf8bbSAlexander V. Chernikov old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1683f5baf8bbSAlexander V. Chernikov }
1684f5baf8bbSAlexander V. Chernikov
1685f5baf8bbSAlexander V. Chernikov /* Wait till all writes completed */
1686f5baf8bbSAlexander V. Chernikov atomic_thread_fence_rel();
1687f5baf8bbSAlexander V. Chernikov
1688f5baf8bbSAlexander V. Chernikov *pdp = &new_fdh->fdh_idx[0];
1689f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1690f5baf8bbSAlexander V. Chernikov
1691f5baf8bbSAlexander V. Chernikov if (old_fdh != NULL)
1692e2f79d9eSAlexander V. Chernikov fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
1693f5baf8bbSAlexander V. Chernikov }
1694f5baf8bbSAlexander V. Chernikov
1695f5baf8bbSAlexander V. Chernikov /*
1696f5baf8bbSAlexander V. Chernikov * Grows per-AF arrays of datapath pointers for each supported family.
1697f5baf8bbSAlexander V. Chernikov * Called from fibs resize sysctl handler.
1698f5baf8bbSAlexander V. Chernikov */
1699f5baf8bbSAlexander V. Chernikov void
fib_grow_rtables(uint32_t new_num_tables)1700f5baf8bbSAlexander V. Chernikov fib_grow_rtables(uint32_t new_num_tables)
1701f5baf8bbSAlexander V. Chernikov {
1702f5baf8bbSAlexander V. Chernikov
1703f5baf8bbSAlexander V. Chernikov #ifdef INET
1704f5baf8bbSAlexander V. Chernikov grow_rtables_family(get_family_dp_ptr(AF_INET), new_num_tables);
1705f5baf8bbSAlexander V. Chernikov #endif
1706f5baf8bbSAlexander V. Chernikov #ifdef INET6
1707f5baf8bbSAlexander V. Chernikov grow_rtables_family(get_family_dp_ptr(AF_INET6), new_num_tables);
1708f5baf8bbSAlexander V. Chernikov #endif
1709f5baf8bbSAlexander V. Chernikov }
1710f5baf8bbSAlexander V. Chernikov
1711f5baf8bbSAlexander V. Chernikov void
fib_get_rtable_info(struct rib_head * rh,struct rib_rtable_info * rinfo)1712f5baf8bbSAlexander V. Chernikov fib_get_rtable_info(struct rib_head *rh, struct rib_rtable_info *rinfo)
1713f5baf8bbSAlexander V. Chernikov {
1714f5baf8bbSAlexander V. Chernikov
1715f5baf8bbSAlexander V. Chernikov bzero(rinfo, sizeof(struct rib_rtable_info));
1716f5baf8bbSAlexander V. Chernikov rinfo->num_prefixes = rh->rnh_prefixes;
1717f5baf8bbSAlexander V. Chernikov rinfo->num_nhops = nhops_get_count(rh);
1718f5baf8bbSAlexander V. Chernikov #ifdef ROUTE_MPATH
1719f5baf8bbSAlexander V. Chernikov rinfo->num_nhgrp = nhgrp_get_count(rh);
1720f5baf8bbSAlexander V. Chernikov #endif
1721f5baf8bbSAlexander V. Chernikov }
1722f5baf8bbSAlexander V. Chernikov
1723f5baf8bbSAlexander V. Chernikov /*
1724e2f79d9eSAlexander V. Chernikov * Updates pointer to the algo data for the @fd.
1725e2f79d9eSAlexander V. Chernikov */
1726e2f79d9eSAlexander V. Chernikov void
fib_set_algo_ptr(struct fib_data * fd,void * algo_data)1727e2f79d9eSAlexander V. Chernikov fib_set_algo_ptr(struct fib_data *fd, void *algo_data)
1728e2f79d9eSAlexander V. Chernikov {
1729e2f79d9eSAlexander V. Chernikov RIB_WLOCK_ASSERT(fd->fd_rh);
1730e2f79d9eSAlexander V. Chernikov
1731e2f79d9eSAlexander V. Chernikov fd->fd_algo_data = algo_data;
1732e2f79d9eSAlexander V. Chernikov }
1733e2f79d9eSAlexander V. Chernikov
1734e2f79d9eSAlexander V. Chernikov /*
1735e2f79d9eSAlexander V. Chernikov * Calls @callback with @ctx after the end of a current epoch.
1736e2f79d9eSAlexander V. Chernikov */
1737e2f79d9eSAlexander V. Chernikov void
fib_epoch_call(epoch_callback_t callback,epoch_context_t ctx)1738e2f79d9eSAlexander V. Chernikov fib_epoch_call(epoch_callback_t callback, epoch_context_t ctx)
1739e2f79d9eSAlexander V. Chernikov {
174073336a6fSZhenlei Huang NET_EPOCH_CALL(callback, ctx);
1741e2f79d9eSAlexander V. Chernikov }
1742e2f79d9eSAlexander V. Chernikov
1743e2f79d9eSAlexander V. Chernikov /*
1744f5baf8bbSAlexander V. Chernikov * Accessor to get rib instance @fd is attached to.
1745f5baf8bbSAlexander V. Chernikov */
1746f5baf8bbSAlexander V. Chernikov struct rib_head *
fib_get_rh(struct fib_data * fd)1747f5baf8bbSAlexander V. Chernikov fib_get_rh(struct fib_data *fd)
1748f5baf8bbSAlexander V. Chernikov {
1749f5baf8bbSAlexander V. Chernikov
1750f5baf8bbSAlexander V. Chernikov return (fd->fd_rh);
1751f5baf8bbSAlexander V. Chernikov }
1752f5baf8bbSAlexander V. Chernikov
1753f5baf8bbSAlexander V. Chernikov /*
1754f5baf8bbSAlexander V. Chernikov * Accessor to export idx->nhop array
1755f5baf8bbSAlexander V. Chernikov */
1756f5baf8bbSAlexander V. Chernikov struct nhop_object **
fib_get_nhop_array(struct fib_data * fd)1757f5baf8bbSAlexander V. Chernikov fib_get_nhop_array(struct fib_data *fd)
1758f5baf8bbSAlexander V. Chernikov {
1759f5baf8bbSAlexander V. Chernikov
1760f5baf8bbSAlexander V. Chernikov return (fd->nh_idx);
1761f5baf8bbSAlexander V. Chernikov }
1762f5baf8bbSAlexander V. Chernikov
1763f5baf8bbSAlexander V. Chernikov static uint32_t
get_nhop_idx(struct nhop_object * nh)1764f5baf8bbSAlexander V. Chernikov get_nhop_idx(struct nhop_object *nh)
1765f5baf8bbSAlexander V. Chernikov {
1766f5baf8bbSAlexander V. Chernikov #ifdef ROUTE_MPATH
1767f5baf8bbSAlexander V. Chernikov if (NH_IS_NHGRP(nh))
17687e64580bSAlexander V. Chernikov return (nhgrp_get_idx((struct nhgrp_object *)nh));
1769f5baf8bbSAlexander V. Chernikov else
1770f5baf8bbSAlexander V. Chernikov #endif
17717e64580bSAlexander V. Chernikov return (nhop_get_idx(nh));
1772f5baf8bbSAlexander V. Chernikov }
1773f5baf8bbSAlexander V. Chernikov
1774f5baf8bbSAlexander V. Chernikov uint32_t
fib_get_nhop_idx(struct fib_data * fd,struct nhop_object * nh)1775f5baf8bbSAlexander V. Chernikov fib_get_nhop_idx(struct fib_data *fd, struct nhop_object *nh)
1776f5baf8bbSAlexander V. Chernikov {
1777f5baf8bbSAlexander V. Chernikov
1778f5baf8bbSAlexander V. Chernikov return (get_nhop_idx(nh));
1779f5baf8bbSAlexander V. Chernikov }
1780f5baf8bbSAlexander V. Chernikov
1781f5baf8bbSAlexander V. Chernikov static bool
is_idx_free(struct fib_data * fd,uint32_t index)1782f5baf8bbSAlexander V. Chernikov is_idx_free(struct fib_data *fd, uint32_t index)
1783f5baf8bbSAlexander V. Chernikov {
1784f5baf8bbSAlexander V. Chernikov
1785f5baf8bbSAlexander V. Chernikov return (fd->nh_ref_table->refcnt[index] == 0);
1786f5baf8bbSAlexander V. Chernikov }
1787f5baf8bbSAlexander V. Chernikov
1788f5baf8bbSAlexander V. Chernikov static uint32_t
fib_ref_nhop(struct fib_data * fd,struct nhop_object * nh)1789f5baf8bbSAlexander V. Chernikov fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh)
1790f5baf8bbSAlexander V. Chernikov {
1791f5baf8bbSAlexander V. Chernikov uint32_t idx = get_nhop_idx(nh);
1792f5baf8bbSAlexander V. Chernikov
1793f5baf8bbSAlexander V. Chernikov if (idx >= fd->number_nhops) {
1794f5baf8bbSAlexander V. Chernikov fd->hit_nhops = 1;
1795f5baf8bbSAlexander V. Chernikov return (0);
1796f5baf8bbSAlexander V. Chernikov }
1797f5baf8bbSAlexander V. Chernikov
1798f5baf8bbSAlexander V. Chernikov if (is_idx_free(fd, idx)) {
1799f5baf8bbSAlexander V. Chernikov nhop_ref_any(nh);
1800f5baf8bbSAlexander V. Chernikov fd->nh_idx[idx] = nh;
1801f5baf8bbSAlexander V. Chernikov fd->nh_ref_table->count++;
1802f8b7ebeaSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG2, fd, " REF nhop %u %p", idx, fd->nh_idx[idx]);
1803f5baf8bbSAlexander V. Chernikov }
1804f5baf8bbSAlexander V. Chernikov fd->nh_ref_table->refcnt[idx]++;
1805f5baf8bbSAlexander V. Chernikov
1806f5baf8bbSAlexander V. Chernikov return (idx);
1807f5baf8bbSAlexander V. Chernikov }
1808f5baf8bbSAlexander V. Chernikov
1809f5baf8bbSAlexander V. Chernikov struct nhop_release_data {
1810f5baf8bbSAlexander V. Chernikov struct nhop_object *nh;
1811f5baf8bbSAlexander V. Chernikov struct epoch_context ctx;
1812f5baf8bbSAlexander V. Chernikov };
1813f5baf8bbSAlexander V. Chernikov
1814f5baf8bbSAlexander V. Chernikov static void
release_nhop_epoch(epoch_context_t ctx)1815f5baf8bbSAlexander V. Chernikov release_nhop_epoch(epoch_context_t ctx)
1816f5baf8bbSAlexander V. Chernikov {
1817f5baf8bbSAlexander V. Chernikov struct nhop_release_data *nrd;
1818f5baf8bbSAlexander V. Chernikov
1819f5baf8bbSAlexander V. Chernikov nrd = __containerof(ctx, struct nhop_release_data, ctx);
1820f5baf8bbSAlexander V. Chernikov nhop_free_any(nrd->nh);
1821f5baf8bbSAlexander V. Chernikov free(nrd, M_TEMP);
1822f5baf8bbSAlexander V. Chernikov }
1823f5baf8bbSAlexander V. Chernikov
1824f5baf8bbSAlexander V. Chernikov /*
1825f5baf8bbSAlexander V. Chernikov * Delays nexthop refcount release.
1826f5baf8bbSAlexander V. Chernikov * Datapath may have the datastructures not updated yet, so the old
1827f5baf8bbSAlexander V. Chernikov * nexthop may still be returned till the end of current epoch. Delay
1828f5baf8bbSAlexander V. Chernikov * refcount removal, as we may be removing the last instance, which will
1829f5baf8bbSAlexander V. Chernikov * trigger nexthop deletion, rendering returned nexthop invalid.
1830f5baf8bbSAlexander V. Chernikov */
1831f5baf8bbSAlexander V. Chernikov static void
fib_schedule_release_nhop(struct fib_data * fd,struct nhop_object * nh)1832f5baf8bbSAlexander V. Chernikov fib_schedule_release_nhop(struct fib_data *fd, struct nhop_object *nh)
1833f5baf8bbSAlexander V. Chernikov {
1834f5baf8bbSAlexander V. Chernikov struct nhop_release_data *nrd;
1835f5baf8bbSAlexander V. Chernikov
1836f5baf8bbSAlexander V. Chernikov nrd = malloc(sizeof(struct nhop_release_data), M_TEMP, M_NOWAIT | M_ZERO);
1837f5baf8bbSAlexander V. Chernikov if (nrd != NULL) {
1838f5baf8bbSAlexander V. Chernikov nrd->nh = nh;
1839e2f79d9eSAlexander V. Chernikov fib_epoch_call(release_nhop_epoch, &nrd->ctx);
1840f5baf8bbSAlexander V. Chernikov } else {
1841f5baf8bbSAlexander V. Chernikov /*
1842f5baf8bbSAlexander V. Chernikov * Unable to allocate memory. Leak nexthop to maintain guarantee
1843f5baf8bbSAlexander V. Chernikov * that each nhop can be referenced.
1844f5baf8bbSAlexander V. Chernikov */
1845f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_ERR, fd, "unable to schedule nhop %p deletion", nh);
1846f5baf8bbSAlexander V. Chernikov }
1847f5baf8bbSAlexander V. Chernikov }
1848f5baf8bbSAlexander V. Chernikov
1849f5baf8bbSAlexander V. Chernikov static void
fib_unref_nhop(struct fib_data * fd,struct nhop_object * nh)1850f5baf8bbSAlexander V. Chernikov fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh)
1851f5baf8bbSAlexander V. Chernikov {
1852f5baf8bbSAlexander V. Chernikov uint32_t idx = get_nhop_idx(nh);
1853f5baf8bbSAlexander V. Chernikov
1854f5baf8bbSAlexander V. Chernikov KASSERT((idx < fd->number_nhops), ("invalid nhop index"));
1855f5baf8bbSAlexander V. Chernikov KASSERT((nh == fd->nh_idx[idx]), ("index table contains whong nh"));
1856f5baf8bbSAlexander V. Chernikov
1857f5baf8bbSAlexander V. Chernikov fd->nh_ref_table->refcnt[idx]--;
1858f5baf8bbSAlexander V. Chernikov if (fd->nh_ref_table->refcnt[idx] == 0) {
1859f5baf8bbSAlexander V. Chernikov FD_PRINTF(LOG_DEBUG, fd, " FREE nhop %d %p", idx, fd->nh_idx[idx]);
1860f5baf8bbSAlexander V. Chernikov fib_schedule_release_nhop(fd, fd->nh_idx[idx]);
1861f5baf8bbSAlexander V. Chernikov }
1862f5baf8bbSAlexander V. Chernikov }
1863f5baf8bbSAlexander V. Chernikov
1864f5baf8bbSAlexander V. Chernikov static void
set_algo_fixed(struct rib_head * rh)1865f5baf8bbSAlexander V. Chernikov set_algo_fixed(struct rib_head *rh)
1866f5baf8bbSAlexander V. Chernikov {
1867f5baf8bbSAlexander V. Chernikov switch (rh->rib_family) {
1868f5baf8bbSAlexander V. Chernikov #ifdef INET
1869f5baf8bbSAlexander V. Chernikov case AF_INET:
18709d6567bcSAlexander V. Chernikov V_algo_fixed_inet = true;
1871f5baf8bbSAlexander V. Chernikov break;
1872f5baf8bbSAlexander V. Chernikov #endif
1873f5baf8bbSAlexander V. Chernikov #ifdef INET6
1874f5baf8bbSAlexander V. Chernikov case AF_INET6:
18759d6567bcSAlexander V. Chernikov V_algo_fixed_inet6 = true;
1876f5baf8bbSAlexander V. Chernikov break;
1877f5baf8bbSAlexander V. Chernikov #endif
1878f5baf8bbSAlexander V. Chernikov }
1879f5baf8bbSAlexander V. Chernikov }
1880f5baf8bbSAlexander V. Chernikov
1881f5baf8bbSAlexander V. Chernikov static bool
is_algo_fixed(struct rib_head * rh)1882f5baf8bbSAlexander V. Chernikov is_algo_fixed(struct rib_head *rh)
1883f5baf8bbSAlexander V. Chernikov {
1884f5baf8bbSAlexander V. Chernikov
1885f5baf8bbSAlexander V. Chernikov switch (rh->rib_family) {
1886f5baf8bbSAlexander V. Chernikov #ifdef INET
1887f5baf8bbSAlexander V. Chernikov case AF_INET:
18889d6567bcSAlexander V. Chernikov return (V_algo_fixed_inet);
1889f5baf8bbSAlexander V. Chernikov #endif
1890f5baf8bbSAlexander V. Chernikov #ifdef INET6
1891f5baf8bbSAlexander V. Chernikov case AF_INET6:
18929d6567bcSAlexander V. Chernikov return (V_algo_fixed_inet6);
1893f5baf8bbSAlexander V. Chernikov #endif
1894f5baf8bbSAlexander V. Chernikov }
1895f5baf8bbSAlexander V. Chernikov return (false);
1896f5baf8bbSAlexander V. Chernikov }
1897f5baf8bbSAlexander V. Chernikov
1898f5baf8bbSAlexander V. Chernikov /*
1899f5baf8bbSAlexander V. Chernikov * Runs the check on what would be the best algo for rib @rh, assuming
1900f5baf8bbSAlexander V. Chernikov * that the current algo is the one specified by @orig_flm. Note that
1901f5baf8bbSAlexander V. Chernikov * it can be NULL for initial selection.
1902f5baf8bbSAlexander V. Chernikov *
1903f5baf8bbSAlexander V. Chernikov * Returns referenced new algo or NULL if the current one is the best.
1904f5baf8bbSAlexander V. Chernikov */
1905f5baf8bbSAlexander V. Chernikov static struct fib_lookup_module *
fib_check_best_algo(struct rib_head * rh,struct fib_lookup_module * orig_flm)1906f5baf8bbSAlexander V. Chernikov fib_check_best_algo(struct rib_head *rh, struct fib_lookup_module *orig_flm)
1907f5baf8bbSAlexander V. Chernikov {
1908f5baf8bbSAlexander V. Chernikov uint8_t preference, curr_preference = 0, best_preference = 0;
1909f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *flm, *best_flm = NULL;
1910f5baf8bbSAlexander V. Chernikov struct rib_rtable_info rinfo;
1911f5baf8bbSAlexander V. Chernikov int candidate_algos = 0;
1912f5baf8bbSAlexander V. Chernikov
1913f5baf8bbSAlexander V. Chernikov fib_get_rtable_info(rh, &rinfo);
1914f5baf8bbSAlexander V. Chernikov
1915f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
1916f5baf8bbSAlexander V. Chernikov TAILQ_FOREACH(flm, &all_algo_list, entries) {
1917f5baf8bbSAlexander V. Chernikov if (flm->flm_family != rh->rib_family)
1918f5baf8bbSAlexander V. Chernikov continue;
1919f5baf8bbSAlexander V. Chernikov candidate_algos++;
1920f5baf8bbSAlexander V. Chernikov preference = flm->flm_get_pref(&rinfo);
1921f5baf8bbSAlexander V. Chernikov if (preference > best_preference) {
1922f5baf8bbSAlexander V. Chernikov if (!flm_error_check(flm, rh->rib_fibnum)) {
1923f5baf8bbSAlexander V. Chernikov best_preference = preference;
1924f5baf8bbSAlexander V. Chernikov best_flm = flm;
1925f5baf8bbSAlexander V. Chernikov }
1926f5baf8bbSAlexander V. Chernikov }
1927f5baf8bbSAlexander V. Chernikov if (flm == orig_flm)
1928f5baf8bbSAlexander V. Chernikov curr_preference = preference;
1929f5baf8bbSAlexander V. Chernikov }
1930f5baf8bbSAlexander V. Chernikov if ((best_flm != NULL) && (curr_preference + BEST_DIFF_PERCENT < best_preference))
1931f5baf8bbSAlexander V. Chernikov best_flm->flm_refcount++;
1932f5baf8bbSAlexander V. Chernikov else
1933f5baf8bbSAlexander V. Chernikov best_flm = NULL;
1934f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
1935f5baf8bbSAlexander V. Chernikov
1936f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_DEBUG, rh, "candidate_algos: %d, curr: %s(%d) result: %s(%d)",
1937f5baf8bbSAlexander V. Chernikov candidate_algos, orig_flm ? orig_flm->flm_name : "NULL", curr_preference,
1938f5baf8bbSAlexander V. Chernikov best_flm ? best_flm->flm_name : (orig_flm ? orig_flm->flm_name : "NULL"),
1939f5baf8bbSAlexander V. Chernikov best_preference);
1940f5baf8bbSAlexander V. Chernikov
1941f5baf8bbSAlexander V. Chernikov return (best_flm);
1942f5baf8bbSAlexander V. Chernikov }
1943f5baf8bbSAlexander V. Chernikov
1944f5baf8bbSAlexander V. Chernikov /*
1945f5baf8bbSAlexander V. Chernikov * Called when new route table is created.
1946f5baf8bbSAlexander V. Chernikov * Selects, allocates and attaches fib algo for the table.
1947f5baf8bbSAlexander V. Chernikov */
19488a0d57baSAlexander V. Chernikov static bool
fib_select_algo_initial(struct rib_head * rh,struct fib_dp * dp)19498a0d57baSAlexander V. Chernikov fib_select_algo_initial(struct rib_head *rh, struct fib_dp *dp)
1950f5baf8bbSAlexander V. Chernikov {
1951f5baf8bbSAlexander V. Chernikov struct fib_lookup_module *flm;
1952f5baf8bbSAlexander V. Chernikov struct fib_data *fd = NULL;
1953f5baf8bbSAlexander V. Chernikov enum flm_op_result result;
1954151ec796SAlexander V. Chernikov struct epoch_tracker et;
1955f5baf8bbSAlexander V. Chernikov
1956f5baf8bbSAlexander V. Chernikov flm = fib_check_best_algo(rh, NULL);
1957f5baf8bbSAlexander V. Chernikov if (flm == NULL) {
1958f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_CRIT, rh, "no algo selected");
19598a0d57baSAlexander V. Chernikov return (false);
1960f5baf8bbSAlexander V. Chernikov }
1961f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_INFO, rh, "selected algo %s", flm->flm_name);
1962f5baf8bbSAlexander V. Chernikov
1963151ec796SAlexander V. Chernikov NET_EPOCH_ENTER(et);
1964151ec796SAlexander V. Chernikov RIB_WLOCK(rh);
1965f5baf8bbSAlexander V. Chernikov result = setup_fd_instance(flm, rh, NULL, &fd, false);
1966151ec796SAlexander V. Chernikov RIB_WUNLOCK(rh);
1967151ec796SAlexander V. Chernikov NET_EPOCH_EXIT(et);
1968151ec796SAlexander V. Chernikov
1969f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_DEBUG, rh, "result=%d fd=%p", result, fd);
19708a0d57baSAlexander V. Chernikov if (result == FLM_SUCCESS)
19718a0d57baSAlexander V. Chernikov *dp = fd->fd_dp;
19728a0d57baSAlexander V. Chernikov else
1973f5baf8bbSAlexander V. Chernikov RH_PRINTF(LOG_CRIT, rh, "unable to setup algo %s", flm->flm_name);
1974f5baf8bbSAlexander V. Chernikov
1975f5baf8bbSAlexander V. Chernikov fib_unref_algo(flm);
1976f5baf8bbSAlexander V. Chernikov
19778a0d57baSAlexander V. Chernikov return (result == FLM_SUCCESS);
19788a0d57baSAlexander V. Chernikov }
19798a0d57baSAlexander V. Chernikov
19808a0d57baSAlexander V. Chernikov /*
19818a0d57baSAlexander V. Chernikov * Sets up fib algo instances for the non-initialized RIBs in the @family.
19828a0d57baSAlexander V. Chernikov * Allocates temporary datapath index to amortize datapaint index updates
19838a0d57baSAlexander V. Chernikov * with large @num_tables.
19848a0d57baSAlexander V. Chernikov */
19858a0d57baSAlexander V. Chernikov void
fib_setup_family(int family,uint32_t num_tables)19868a0d57baSAlexander V. Chernikov fib_setup_family(int family, uint32_t num_tables)
19878a0d57baSAlexander V. Chernikov {
19888a0d57baSAlexander V. Chernikov struct fib_dp_header *new_fdh = alloc_fib_dp_array(num_tables, false);
19898a0d57baSAlexander V. Chernikov if (new_fdh == NULL) {
19908a0d57baSAlexander V. Chernikov ALGO_PRINTF(LOG_CRIT, "Unable to setup framework for %s", print_family(family));
19918a0d57baSAlexander V. Chernikov return;
19928a0d57baSAlexander V. Chernikov }
19938a0d57baSAlexander V. Chernikov
19948a0d57baSAlexander V. Chernikov for (int i = 0; i < num_tables; i++) {
19958a0d57baSAlexander V. Chernikov struct rib_head *rh = rt_tables_get_rnh(i, family);
19968a0d57baSAlexander V. Chernikov if (rh->rib_algo_init)
19978a0d57baSAlexander V. Chernikov continue;
19988a0d57baSAlexander V. Chernikov if (!fib_select_algo_initial(rh, &new_fdh->fdh_idx[i]))
19998a0d57baSAlexander V. Chernikov continue;
20008a0d57baSAlexander V. Chernikov
20018a0d57baSAlexander V. Chernikov rh->rib_algo_init = true;
20028a0d57baSAlexander V. Chernikov }
20038a0d57baSAlexander V. Chernikov
20048a0d57baSAlexander V. Chernikov FIB_MOD_LOCK();
20058a0d57baSAlexander V. Chernikov struct fib_dp **pdp = get_family_dp_ptr(family);
20068a0d57baSAlexander V. Chernikov struct fib_dp_header *old_fdh = get_fib_dp_header(*pdp);
20078a0d57baSAlexander V. Chernikov
20088a0d57baSAlexander V. Chernikov /* Update the items not touched by the new init, from the old data pointer */
20098a0d57baSAlexander V. Chernikov for (int i = 0; i < num_tables; i++) {
20108a0d57baSAlexander V. Chernikov if (new_fdh->fdh_idx[i].f == dummy_lookup)
20118a0d57baSAlexander V. Chernikov new_fdh->fdh_idx[i] = old_fdh->fdh_idx[i];
20128a0d57baSAlexander V. Chernikov }
20138a0d57baSAlexander V. Chernikov
20148a0d57baSAlexander V. Chernikov /* Ensure all index writes have completed */
20158a0d57baSAlexander V. Chernikov atomic_thread_fence_rel();
20168a0d57baSAlexander V. Chernikov /* Set new datapath pointer */
20178a0d57baSAlexander V. Chernikov *pdp = &new_fdh->fdh_idx[0];
20188a0d57baSAlexander V. Chernikov
20198a0d57baSAlexander V. Chernikov FIB_MOD_UNLOCK();
20208a0d57baSAlexander V. Chernikov
20218a0d57baSAlexander V. Chernikov fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
2022f5baf8bbSAlexander V. Chernikov }
2023f5baf8bbSAlexander V. Chernikov
2024f5baf8bbSAlexander V. Chernikov /*
2025f5baf8bbSAlexander V. Chernikov * Registers fib lookup module within the subsystem.
2026f5baf8bbSAlexander V. Chernikov */
2027f5baf8bbSAlexander V. Chernikov int
fib_module_register(struct fib_lookup_module * flm)2028f5baf8bbSAlexander V. Chernikov fib_module_register(struct fib_lookup_module *flm)
2029f5baf8bbSAlexander V. Chernikov {
2030f5baf8bbSAlexander V. Chernikov
2031f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
2032c2338561SAlexander V. Chernikov ALGO_PRINTF(LOG_INFO, "attaching %s to %s", flm->flm_name,
2033f5baf8bbSAlexander V. Chernikov print_family(flm->flm_family));
2034f5baf8bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&all_algo_list, flm, entries);
2035f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
2036f5baf8bbSAlexander V. Chernikov
2037f5baf8bbSAlexander V. Chernikov return (0);
2038f5baf8bbSAlexander V. Chernikov }
2039f5baf8bbSAlexander V. Chernikov
2040f5baf8bbSAlexander V. Chernikov /*
2041f5baf8bbSAlexander V. Chernikov * Tries to unregister fib lookup module.
2042f5baf8bbSAlexander V. Chernikov *
2043f5baf8bbSAlexander V. Chernikov * Returns 0 on success, EBUSY if module is still used
2044f5baf8bbSAlexander V. Chernikov * by some of the tables.
2045f5baf8bbSAlexander V. Chernikov */
2046f5baf8bbSAlexander V. Chernikov int
fib_module_unregister(struct fib_lookup_module * flm)2047f5baf8bbSAlexander V. Chernikov fib_module_unregister(struct fib_lookup_module *flm)
2048f5baf8bbSAlexander V. Chernikov {
2049f5baf8bbSAlexander V. Chernikov
2050f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
2051f5baf8bbSAlexander V. Chernikov if (flm->flm_refcount > 0) {
2052f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
2053f5baf8bbSAlexander V. Chernikov return (EBUSY);
2054f5baf8bbSAlexander V. Chernikov }
2055f5baf8bbSAlexander V. Chernikov fib_error_clear_flm(flm);
2056c2338561SAlexander V. Chernikov ALGO_PRINTF(LOG_INFO, "detaching %s from %s", flm->flm_name,
2057f5baf8bbSAlexander V. Chernikov print_family(flm->flm_family));
2058f5baf8bbSAlexander V. Chernikov TAILQ_REMOVE(&all_algo_list, flm, entries);
2059f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
2060f5baf8bbSAlexander V. Chernikov
2061f5baf8bbSAlexander V. Chernikov return (0);
2062f5baf8bbSAlexander V. Chernikov }
2063f5baf8bbSAlexander V. Chernikov
2064f5baf8bbSAlexander V. Chernikov void
vnet_fib_init(void)2065f5baf8bbSAlexander V. Chernikov vnet_fib_init(void)
2066f5baf8bbSAlexander V. Chernikov {
2067f5baf8bbSAlexander V. Chernikov
2068f5baf8bbSAlexander V. Chernikov TAILQ_INIT(&V_fib_data_list);
2069f5baf8bbSAlexander V. Chernikov }
2070f5baf8bbSAlexander V. Chernikov
2071f5baf8bbSAlexander V. Chernikov void
vnet_fib_destroy(void)2072f5baf8bbSAlexander V. Chernikov vnet_fib_destroy(void)
2073f5baf8bbSAlexander V. Chernikov {
2074f5baf8bbSAlexander V. Chernikov
2075f5baf8bbSAlexander V. Chernikov FIB_MOD_LOCK();
2076f5baf8bbSAlexander V. Chernikov fib_error_clear();
2077f5baf8bbSAlexander V. Chernikov FIB_MOD_UNLOCK();
2078f5baf8bbSAlexander V. Chernikov }
2079