Commit | Line | Data |
---|---|---|
9948a064 JP |
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */ | |
464dce18 IS |
3 | |
4 | #include <linux/kernel.h> | |
5 | #include <linux/types.h> | |
5e9c16cc JP |
6 | #include <linux/rhashtable.h> |
7 | #include <linux/bitops.h> | |
8 | #include <linux/in6.h> | |
c723c735 | 9 | #include <linux/notifier.h> |
df6dd79b | 10 | #include <linux/inetdevice.h> |
9db032bb | 11 | #include <linux/netdevice.h> |
03ea01e9 | 12 | #include <linux/if_bridge.h> |
b5f3e0d4 | 13 | #include <linux/socket.h> |
428b851f | 14 | #include <linux/route.h> |
eb789980 | 15 | #include <linux/gcd.h> |
af658b6a | 16 | #include <linux/random.h> |
2db99378 | 17 | #include <linux/if_macvlan.h> |
32fd4b49 | 18 | #include <linux/refcount.h> |
c723c735 | 19 | #include <net/netevent.h> |
6cf3c971 JP |
20 | #include <net/neighbour.h> |
21 | #include <net/arp.h> | |
b45f64d1 | 22 | #include <net/ip_fib.h> |
583419fd | 23 | #include <net/ip6_fib.h> |
5d7bfd14 | 24 | #include <net/fib_rules.h> |
6ddb7426 | 25 | #include <net/ip_tunnels.h> |
57837885 | 26 | #include <net/l3mdev.h> |
5ea1237f | 27 | #include <net/addrconf.h> |
d5eb89cf AS |
28 | #include <net/ndisc.h> |
29 | #include <net/ipv6.h> | |
04b1d4e5 | 30 | #include <net/fib_notifier.h> |
2db99378 | 31 | #include <net/switchdev.h> |
464dce18 IS |
32 | |
33 | #include "spectrum.h" | |
34 | #include "core.h" | |
35 | #include "reg.h" | |
e0c0afd8 AS |
36 | #include "spectrum_cnt.h" |
37 | #include "spectrum_dpipe.h" | |
38ebc0f4 | 38 | #include "spectrum_ipip.h" |
d42b0965 YG |
39 | #include "spectrum_mr.h" |
40 | #include "spectrum_mr_tcam.h" | |
e0c0afd8 | 41 | #include "spectrum_router.h" |
803335ac | 42 | #include "spectrum_span.h" |
464dce18 | 43 | |
2b52ce02 | 44 | struct mlxsw_sp_fib; |
9011b677 IS |
45 | struct mlxsw_sp_vr; |
46 | struct mlxsw_sp_lpm_tree; | |
e4f3c1c1 | 47 | struct mlxsw_sp_rif_ops; |
9011b677 IS |
48 | |
49 | struct mlxsw_sp_router { | |
50 | struct mlxsw_sp *mlxsw_sp; | |
5f9efffb | 51 | struct mlxsw_sp_rif **rifs; |
9011b677 IS |
52 | struct mlxsw_sp_vr *vrs; |
53 | struct rhashtable neigh_ht; | |
54 | struct rhashtable nexthop_group_ht; | |
55 | struct rhashtable nexthop_ht; | |
dbe4598c | 56 | struct list_head nexthop_list; |
9011b677 | 57 | struct { |
2b52ce02 IS |
58 | /* One tree for each protocol: IPv4 and IPv6 */ |
59 | struct mlxsw_sp_lpm_tree *proto_trees[2]; | |
9011b677 IS |
60 | struct mlxsw_sp_lpm_tree *trees; |
61 | unsigned int tree_count; | |
62 | } lpm; | |
63 | struct { | |
64 | struct delayed_work dw; | |
65 | unsigned long interval; /* ms */ | |
66 | } neighs_update; | |
67 | struct delayed_work nexthop_probe_dw; | |
68 | #define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */ | |
69 | struct list_head nexthop_neighs_list; | |
1012b9ac | 70 | struct list_head ipip_list; |
9011b677 | 71 | bool aborted; |
7e39d115 | 72 | struct notifier_block fib_nb; |
48fac885 | 73 | struct notifier_block netevent_nb; |
965fa8e6 IS |
74 | struct notifier_block inetaddr_nb; |
75 | struct notifier_block inet6addr_nb; | |
e4f3c1c1 | 76 | const struct mlxsw_sp_rif_ops **rif_ops_arr; |
38ebc0f4 | 77 | const struct mlxsw_sp_ipip_ops **ipip_ops_arr; |
9011b677 IS |
78 | }; |
79 | ||
4724ba56 IS |
80 | struct mlxsw_sp_rif { |
81 | struct list_head nexthop_list; | |
82 | struct list_head neigh_list; | |
73b8f493 | 83 | struct net_device *dev; /* NULL for underlay RIF */ |
a1107487 | 84 | struct mlxsw_sp_fid *fid; |
4724ba56 IS |
85 | unsigned char addr[ETH_ALEN]; |
86 | int mtu; | |
bf95233e | 87 | u16 rif_index; |
6913229e | 88 | u16 vr_id; |
e4f3c1c1 IS |
89 | const struct mlxsw_sp_rif_ops *ops; |
90 | struct mlxsw_sp *mlxsw_sp; | |
91 | ||
e0c0afd8 AS |
92 | unsigned int counter_ingress; |
93 | bool counter_ingress_valid; | |
94 | unsigned int counter_egress; | |
95 | bool counter_egress_valid; | |
4724ba56 IS |
96 | }; |
97 | ||
e4f3c1c1 IS |
98 | struct mlxsw_sp_rif_params { |
99 | struct net_device *dev; | |
100 | union { | |
101 | u16 system_port; | |
102 | u16 lag_id; | |
103 | }; | |
104 | u16 vid; | |
105 | bool lag; | |
106 | }; | |
107 | ||
4d93ceeb IS |
108 | struct mlxsw_sp_rif_subport { |
109 | struct mlxsw_sp_rif common; | |
32fd4b49 | 110 | refcount_t ref_count; |
4d93ceeb IS |
111 | union { |
112 | u16 system_port; | |
113 | u16 lag_id; | |
114 | }; | |
115 | u16 vid; | |
116 | bool lag; | |
117 | }; | |
118 | ||
6ddb7426 PM |
119 | struct mlxsw_sp_rif_ipip_lb { |
120 | struct mlxsw_sp_rif common; | |
121 | struct mlxsw_sp_rif_ipip_lb_config lb_config; | |
122 | u16 ul_vr_id; /* Reserved for Spectrum-2. */ | |
25f844dd | 123 | u16 ul_rif_id; /* Reserved for Spectrum. */ |
6ddb7426 PM |
124 | }; |
125 | ||
126 | struct mlxsw_sp_rif_params_ipip_lb { | |
127 | struct mlxsw_sp_rif_params common; | |
128 | struct mlxsw_sp_rif_ipip_lb_config lb_config; | |
129 | }; | |
130 | ||
e4f3c1c1 IS |
131 | struct mlxsw_sp_rif_ops { |
132 | enum mlxsw_sp_rif_type type; | |
133 | size_t rif_size; | |
134 | ||
135 | void (*setup)(struct mlxsw_sp_rif *rif, | |
136 | const struct mlxsw_sp_rif_params *params); | |
137 | int (*configure)(struct mlxsw_sp_rif *rif); | |
138 | void (*deconfigure)(struct mlxsw_sp_rif *rif); | |
5f15e257 PM |
139 | struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif, |
140 | struct netlink_ext_ack *extack); | |
2db99378 | 141 | void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac); |
e4f3c1c1 IS |
142 | }; |
143 | ||
32fd4b49 | 144 | static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif); |
2b52ce02 IS |
145 | static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree); |
146 | static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, | |
147 | struct mlxsw_sp_lpm_tree *lpm_tree); | |
148 | static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, | |
149 | const struct mlxsw_sp_fib *fib, | |
150 | u8 tree_id); | |
151 | static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp, | |
152 | const struct mlxsw_sp_fib *fib); | |
153 | ||
e0c0afd8 AS |
154 | static unsigned int * |
155 | mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif, | |
156 | enum mlxsw_sp_rif_counter_dir dir) | |
157 | { | |
158 | switch (dir) { | |
159 | case MLXSW_SP_RIF_COUNTER_EGRESS: | |
160 | return &rif->counter_egress; | |
161 | case MLXSW_SP_RIF_COUNTER_INGRESS: | |
162 | return &rif->counter_ingress; | |
163 | } | |
164 | return NULL; | |
165 | } | |
166 | ||
167 | static bool | |
168 | mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif, | |
169 | enum mlxsw_sp_rif_counter_dir dir) | |
170 | { | |
171 | switch (dir) { | |
172 | case MLXSW_SP_RIF_COUNTER_EGRESS: | |
173 | return rif->counter_egress_valid; | |
174 | case MLXSW_SP_RIF_COUNTER_INGRESS: | |
175 | return rif->counter_ingress_valid; | |
176 | } | |
177 | return false; | |
178 | } | |
179 | ||
180 | static void | |
181 | mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif, | |
182 | enum mlxsw_sp_rif_counter_dir dir, | |
183 | bool valid) | |
184 | { | |
185 | switch (dir) { | |
186 | case MLXSW_SP_RIF_COUNTER_EGRESS: | |
187 | rif->counter_egress_valid = valid; | |
188 | break; | |
189 | case MLXSW_SP_RIF_COUNTER_INGRESS: | |
190 | rif->counter_ingress_valid = valid; | |
191 | break; | |
192 | } | |
193 | } | |
194 | ||
195 | static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, | |
196 | unsigned int counter_index, bool enable, | |
197 | enum mlxsw_sp_rif_counter_dir dir) | |
198 | { | |
199 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
200 | bool is_egress = false; | |
201 | int err; | |
202 | ||
203 | if (dir == MLXSW_SP_RIF_COUNTER_EGRESS) | |
204 | is_egress = true; | |
205 | mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index); | |
206 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
207 | if (err) | |
208 | return err; | |
209 | ||
210 | mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable, | |
211 | is_egress); | |
212 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
213 | } | |
214 | ||
215 | int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, | |
216 | struct mlxsw_sp_rif *rif, | |
217 | enum mlxsw_sp_rif_counter_dir dir, u64 *cnt) | |
218 | { | |
219 | char ricnt_pl[MLXSW_REG_RICNT_LEN]; | |
220 | unsigned int *p_counter_index; | |
221 | bool valid; | |
222 | int err; | |
223 | ||
224 | valid = mlxsw_sp_rif_counter_valid_get(rif, dir); | |
225 | if (!valid) | |
226 | return -EINVAL; | |
227 | ||
228 | p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); | |
229 | if (!p_counter_index) | |
230 | return -EINVAL; | |
231 | mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index, | |
232 | MLXSW_REG_RICNT_OPCODE_NOP); | |
233 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); | |
234 | if (err) | |
235 | return err; | |
236 | *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl); | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp, | |
241 | unsigned int counter_index) | |
242 | { | |
243 | char ricnt_pl[MLXSW_REG_RICNT_LEN]; | |
244 | ||
245 | mlxsw_reg_ricnt_pack(ricnt_pl, counter_index, | |
246 | MLXSW_REG_RICNT_OPCODE_CLEAR); | |
247 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); | |
248 | } | |
249 | ||
250 | int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, | |
251 | struct mlxsw_sp_rif *rif, | |
252 | enum mlxsw_sp_rif_counter_dir dir) | |
253 | { | |
254 | unsigned int *p_counter_index; | |
255 | int err; | |
256 | ||
257 | p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); | |
258 | if (!p_counter_index) | |
259 | return -EINVAL; | |
260 | err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, | |
261 | p_counter_index); | |
262 | if (err) | |
263 | return err; | |
264 | ||
265 | err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index); | |
266 | if (err) | |
267 | goto err_counter_clear; | |
268 | ||
269 | err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index, | |
270 | *p_counter_index, true, dir); | |
271 | if (err) | |
272 | goto err_counter_edit; | |
273 | mlxsw_sp_rif_counter_valid_set(rif, dir, true); | |
274 | return 0; | |
275 | ||
276 | err_counter_edit: | |
277 | err_counter_clear: | |
278 | mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, | |
279 | *p_counter_index); | |
280 | return err; | |
281 | } | |
282 | ||
283 | void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, | |
284 | struct mlxsw_sp_rif *rif, | |
285 | enum mlxsw_sp_rif_counter_dir dir) | |
286 | { | |
287 | unsigned int *p_counter_index; | |
288 | ||
6b1206bb AS |
289 | if (!mlxsw_sp_rif_counter_valid_get(rif, dir)) |
290 | return; | |
291 | ||
e0c0afd8 AS |
292 | p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); |
293 | if (WARN_ON(!p_counter_index)) | |
294 | return; | |
295 | mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index, | |
296 | *p_counter_index, false, dir); | |
297 | mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, | |
298 | *p_counter_index); | |
299 | mlxsw_sp_rif_counter_valid_set(rif, dir, false); | |
300 | } | |
301 | ||
e4f3c1c1 IS |
302 | static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif) |
303 | { | |
304 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
305 | struct devlink *devlink; | |
306 | ||
307 | devlink = priv_to_devlink(mlxsw_sp->core); | |
308 | if (!devlink_dpipe_table_counter_enabled(devlink, | |
309 | MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) | |
310 | return; | |
311 | mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS); | |
312 | } | |
313 | ||
314 | static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif) | |
315 | { | |
316 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
317 | ||
318 | mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS); | |
319 | } | |
320 | ||
7dcc18ad | 321 | #define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1) |
9011b677 IS |
322 | |
323 | struct mlxsw_sp_prefix_usage { | |
324 | DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT); | |
325 | }; | |
326 | ||
53342023 JP |
327 | #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \ |
328 | for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT) | |
329 | ||
330 | static bool | |
331 | mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1, | |
332 | struct mlxsw_sp_prefix_usage *prefix_usage2) | |
333 | { | |
334 | return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1)); | |
335 | } | |
336 | ||
6b75c480 JP |
337 | static void |
338 | mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1, | |
339 | struct mlxsw_sp_prefix_usage *prefix_usage2) | |
340 | { | |
341 | memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1)); | |
342 | } | |
343 | ||
5e9c16cc JP |
344 | static void |
345 | mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage, | |
346 | unsigned char prefix_len) | |
347 | { | |
348 | set_bit(prefix_len, prefix_usage->b); | |
349 | } | |
350 | ||
351 | static void | |
352 | mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage, | |
353 | unsigned char prefix_len) | |
354 | { | |
355 | clear_bit(prefix_len, prefix_usage->b); | |
356 | } | |
357 | ||
358 | struct mlxsw_sp_fib_key { | |
359 | unsigned char addr[sizeof(struct in6_addr)]; | |
360 | unsigned char prefix_len; | |
361 | }; | |
362 | ||
61c503f9 JP |
363 | enum mlxsw_sp_fib_entry_type { |
364 | MLXSW_SP_FIB_ENTRY_TYPE_REMOTE, | |
365 | MLXSW_SP_FIB_ENTRY_TYPE_LOCAL, | |
366 | MLXSW_SP_FIB_ENTRY_TYPE_TRAP, | |
4607f6d2 PM |
367 | |
368 | /* This is a special case of local delivery, where a packet should be | |
369 | * decapsulated on reception. Note that there is no corresponding ENCAP, | |
370 | * because that's a type of next hop, not of FIB entry. (There can be | |
371 | * several next hops in a REMOTE entry, and some of them may be | |
372 | * encapsulating entries.) | |
373 | */ | |
374 | MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP, | |
0c69e0fc | 375 | MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP, |
61c503f9 JP |
376 | }; |
377 | ||
a7ff87ac JP |
378 | struct mlxsw_sp_nexthop_group; |
379 | ||
9aecce1c IS |
380 | struct mlxsw_sp_fib_node { |
381 | struct list_head entry_list; | |
b45f64d1 | 382 | struct list_head list; |
9aecce1c | 383 | struct rhash_head ht_node; |
76610ebb | 384 | struct mlxsw_sp_fib *fib; |
5e9c16cc | 385 | struct mlxsw_sp_fib_key key; |
9aecce1c IS |
386 | }; |
387 | ||
4607f6d2 PM |
388 | struct mlxsw_sp_fib_entry_decap { |
389 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
390 | u32 tunnel_index; | |
391 | }; | |
392 | ||
9aecce1c IS |
393 | struct mlxsw_sp_fib_entry { |
394 | struct list_head list; | |
395 | struct mlxsw_sp_fib_node *fib_node; | |
61c503f9 | 396 | enum mlxsw_sp_fib_entry_type type; |
a7ff87ac JP |
397 | struct list_head nexthop_group_node; |
398 | struct mlxsw_sp_nexthop_group *nh_group; | |
4607f6d2 | 399 | struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */ |
5e9c16cc JP |
400 | }; |
401 | ||
4f1c7f1f IS |
402 | struct mlxsw_sp_fib4_entry { |
403 | struct mlxsw_sp_fib_entry common; | |
404 | u32 tb_id; | |
405 | u32 prio; | |
406 | u8 tos; | |
407 | u8 type; | |
408 | }; | |
409 | ||
428b851f IS |
410 | struct mlxsw_sp_fib6_entry { |
411 | struct mlxsw_sp_fib_entry common; | |
412 | struct list_head rt6_list; | |
413 | unsigned int nrt6; | |
414 | }; | |
415 | ||
416 | struct mlxsw_sp_rt6 { | |
417 | struct list_head list; | |
8d1c802b | 418 | struct fib6_info *rt; |
428b851f IS |
419 | }; |
420 | ||
9011b677 IS |
421 | struct mlxsw_sp_lpm_tree { |
422 | u8 id; /* tree ID */ | |
423 | unsigned int ref_count; | |
424 | enum mlxsw_sp_l3proto proto; | |
2b52ce02 | 425 | unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT]; |
9011b677 IS |
426 | struct mlxsw_sp_prefix_usage prefix_usage; |
427 | }; | |
428 | ||
5e9c16cc JP |
429 | struct mlxsw_sp_fib { |
430 | struct rhashtable ht; | |
9aecce1c | 431 | struct list_head node_list; |
76610ebb IS |
432 | struct mlxsw_sp_vr *vr; |
433 | struct mlxsw_sp_lpm_tree *lpm_tree; | |
76610ebb | 434 | enum mlxsw_sp_l3proto proto; |
5e9c16cc JP |
435 | }; |
436 | ||
9011b677 IS |
437 | struct mlxsw_sp_vr { |
438 | u16 id; /* virtual router ID */ | |
439 | u32 tb_id; /* kernel fib table id */ | |
440 | unsigned int rif_count; | |
441 | struct mlxsw_sp_fib *fib4; | |
a3d9bc50 | 442 | struct mlxsw_sp_fib *fib6; |
9742f866 | 443 | struct mlxsw_sp_mr_table *mr_table[MLXSW_SP_L3_PROTO_MAX]; |
9011b677 IS |
444 | }; |
445 | ||
9aecce1c | 446 | static const struct rhashtable_params mlxsw_sp_fib_ht_params; |
5e9c16cc | 447 | |
2b52ce02 IS |
448 | static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp, |
449 | struct mlxsw_sp_vr *vr, | |
76610ebb | 450 | enum mlxsw_sp_l3proto proto) |
5e9c16cc | 451 | { |
2b52ce02 | 452 | struct mlxsw_sp_lpm_tree *lpm_tree; |
5e9c16cc JP |
453 | struct mlxsw_sp_fib *fib; |
454 | int err; | |
455 | ||
2b52ce02 | 456 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[proto]; |
5e9c16cc JP |
457 | fib = kzalloc(sizeof(*fib), GFP_KERNEL); |
458 | if (!fib) | |
459 | return ERR_PTR(-ENOMEM); | |
460 | err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params); | |
461 | if (err) | |
462 | goto err_rhashtable_init; | |
9aecce1c | 463 | INIT_LIST_HEAD(&fib->node_list); |
76610ebb IS |
464 | fib->proto = proto; |
465 | fib->vr = vr; | |
2b52ce02 IS |
466 | fib->lpm_tree = lpm_tree; |
467 | mlxsw_sp_lpm_tree_hold(lpm_tree); | |
468 | err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id); | |
469 | if (err) | |
470 | goto err_lpm_tree_bind; | |
5e9c16cc JP |
471 | return fib; |
472 | ||
2b52ce02 IS |
473 | err_lpm_tree_bind: |
474 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
5e9c16cc JP |
475 | err_rhashtable_init: |
476 | kfree(fib); | |
477 | return ERR_PTR(err); | |
478 | } | |
479 | ||
2b52ce02 IS |
480 | static void mlxsw_sp_fib_destroy(struct mlxsw_sp *mlxsw_sp, |
481 | struct mlxsw_sp_fib *fib) | |
5e9c16cc | 482 | { |
2b52ce02 IS |
483 | mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); |
484 | mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree); | |
9aecce1c | 485 | WARN_ON(!list_empty(&fib->node_list)); |
5e9c16cc JP |
486 | rhashtable_destroy(&fib->ht); |
487 | kfree(fib); | |
488 | } | |
489 | ||
53342023 | 490 | static struct mlxsw_sp_lpm_tree * |
382dbb40 | 491 | mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp) |
53342023 JP |
492 | { |
493 | static struct mlxsw_sp_lpm_tree *lpm_tree; | |
494 | int i; | |
495 | ||
9011b677 IS |
496 | for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) { |
497 | lpm_tree = &mlxsw_sp->router->lpm.trees[i]; | |
382dbb40 IS |
498 | if (lpm_tree->ref_count == 0) |
499 | return lpm_tree; | |
53342023 JP |
500 | } |
501 | return NULL; | |
502 | } | |
503 | ||
504 | static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp, | |
505 | struct mlxsw_sp_lpm_tree *lpm_tree) | |
506 | { | |
507 | char ralta_pl[MLXSW_REG_RALTA_LEN]; | |
508 | ||
1a9234e6 IS |
509 | mlxsw_reg_ralta_pack(ralta_pl, true, |
510 | (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto, | |
511 | lpm_tree->id); | |
53342023 JP |
512 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); |
513 | } | |
514 | ||
cc702670 IS |
515 | static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp, |
516 | struct mlxsw_sp_lpm_tree *lpm_tree) | |
53342023 JP |
517 | { |
518 | char ralta_pl[MLXSW_REG_RALTA_LEN]; | |
519 | ||
1a9234e6 IS |
520 | mlxsw_reg_ralta_pack(ralta_pl, false, |
521 | (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto, | |
522 | lpm_tree->id); | |
cc702670 | 523 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); |
53342023 JP |
524 | } |
525 | ||
526 | static int | |
527 | mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp, | |
528 | struct mlxsw_sp_prefix_usage *prefix_usage, | |
529 | struct mlxsw_sp_lpm_tree *lpm_tree) | |
530 | { | |
531 | char ralst_pl[MLXSW_REG_RALST_LEN]; | |
532 | u8 root_bin = 0; | |
533 | u8 prefix; | |
534 | u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD; | |
535 | ||
536 | mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) | |
537 | root_bin = prefix; | |
538 | ||
539 | mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id); | |
540 | mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) { | |
541 | if (prefix == 0) | |
542 | continue; | |
543 | mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix, | |
544 | MLXSW_REG_RALST_BIN_NO_CHILD); | |
545 | last_prefix = prefix; | |
546 | } | |
547 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl); | |
548 | } | |
549 | ||
550 | static struct mlxsw_sp_lpm_tree * | |
551 | mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp, | |
552 | struct mlxsw_sp_prefix_usage *prefix_usage, | |
382dbb40 | 553 | enum mlxsw_sp_l3proto proto) |
53342023 JP |
554 | { |
555 | struct mlxsw_sp_lpm_tree *lpm_tree; | |
556 | int err; | |
557 | ||
382dbb40 | 558 | lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp); |
53342023 JP |
559 | if (!lpm_tree) |
560 | return ERR_PTR(-EBUSY); | |
561 | lpm_tree->proto = proto; | |
562 | err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree); | |
563 | if (err) | |
564 | return ERR_PTR(err); | |
565 | ||
566 | err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage, | |
567 | lpm_tree); | |
568 | if (err) | |
569 | goto err_left_struct_set; | |
2083d367 JP |
570 | memcpy(&lpm_tree->prefix_usage, prefix_usage, |
571 | sizeof(lpm_tree->prefix_usage)); | |
2b52ce02 IS |
572 | memset(&lpm_tree->prefix_ref_count, 0, |
573 | sizeof(lpm_tree->prefix_ref_count)); | |
574 | lpm_tree->ref_count = 1; | |
53342023 JP |
575 | return lpm_tree; |
576 | ||
577 | err_left_struct_set: | |
578 | mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); | |
579 | return ERR_PTR(err); | |
580 | } | |
581 | ||
cc702670 IS |
582 | static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, |
583 | struct mlxsw_sp_lpm_tree *lpm_tree) | |
53342023 | 584 | { |
cc702670 | 585 | mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); |
53342023 JP |
586 | } |
587 | ||
588 | static struct mlxsw_sp_lpm_tree * | |
589 | mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, | |
590 | struct mlxsw_sp_prefix_usage *prefix_usage, | |
382dbb40 | 591 | enum mlxsw_sp_l3proto proto) |
53342023 JP |
592 | { |
593 | struct mlxsw_sp_lpm_tree *lpm_tree; | |
594 | int i; | |
595 | ||
9011b677 IS |
596 | for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) { |
597 | lpm_tree = &mlxsw_sp->router->lpm.trees[i]; | |
8b99becd JP |
598 | if (lpm_tree->ref_count != 0 && |
599 | lpm_tree->proto == proto && | |
53342023 | 600 | mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, |
2b52ce02 IS |
601 | prefix_usage)) { |
602 | mlxsw_sp_lpm_tree_hold(lpm_tree); | |
fc922bb0 | 603 | return lpm_tree; |
2b52ce02 | 604 | } |
53342023 | 605 | } |
fc922bb0 IS |
606 | return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto); |
607 | } | |
53342023 | 608 | |
fc922bb0 IS |
609 | static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree) |
610 | { | |
53342023 | 611 | lpm_tree->ref_count++; |
53342023 JP |
612 | } |
613 | ||
cc702670 IS |
614 | static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, |
615 | struct mlxsw_sp_lpm_tree *lpm_tree) | |
53342023 JP |
616 | { |
617 | if (--lpm_tree->ref_count == 0) | |
cc702670 | 618 | mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); |
53342023 JP |
619 | } |
620 | ||
d7a60306 | 621 | #define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */ |
8494ab06 IS |
622 | |
623 | static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) | |
53342023 | 624 | { |
2b52ce02 | 625 | struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; |
53342023 | 626 | struct mlxsw_sp_lpm_tree *lpm_tree; |
8494ab06 | 627 | u64 max_trees; |
2b52ce02 | 628 | int err, i; |
53342023 | 629 | |
8494ab06 IS |
630 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES)) |
631 | return -EIO; | |
632 | ||
633 | max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES); | |
9011b677 IS |
634 | mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN; |
635 | mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count, | |
8494ab06 IS |
636 | sizeof(struct mlxsw_sp_lpm_tree), |
637 | GFP_KERNEL); | |
9011b677 | 638 | if (!mlxsw_sp->router->lpm.trees) |
8494ab06 IS |
639 | return -ENOMEM; |
640 | ||
9011b677 IS |
641 | for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) { |
642 | lpm_tree = &mlxsw_sp->router->lpm.trees[i]; | |
53342023 JP |
643 | lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN; |
644 | } | |
8494ab06 | 645 | |
2b52ce02 IS |
646 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, |
647 | MLXSW_SP_L3_PROTO_IPV4); | |
648 | if (IS_ERR(lpm_tree)) { | |
649 | err = PTR_ERR(lpm_tree); | |
650 | goto err_ipv4_tree_get; | |
651 | } | |
652 | mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4] = lpm_tree; | |
653 | ||
654 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | |
655 | MLXSW_SP_L3_PROTO_IPV6); | |
656 | if (IS_ERR(lpm_tree)) { | |
657 | err = PTR_ERR(lpm_tree); | |
658 | goto err_ipv6_tree_get; | |
659 | } | |
660 | mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6] = lpm_tree; | |
661 | ||
8494ab06 | 662 | return 0; |
2b52ce02 IS |
663 | |
664 | err_ipv6_tree_get: | |
665 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4]; | |
666 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
667 | err_ipv4_tree_get: | |
668 | kfree(mlxsw_sp->router->lpm.trees); | |
669 | return err; | |
8494ab06 IS |
670 | } |
671 | ||
672 | static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) | |
673 | { | |
2b52ce02 IS |
674 | struct mlxsw_sp_lpm_tree *lpm_tree; |
675 | ||
676 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6]; | |
677 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
678 | ||
679 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4]; | |
680 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
681 | ||
9011b677 | 682 | kfree(mlxsw_sp->router->lpm.trees); |
53342023 JP |
683 | } |
684 | ||
76610ebb IS |
685 | static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr) |
686 | { | |
9742f866 YM |
687 | return !!vr->fib4 || !!vr->fib6 || |
688 | !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] || | |
689 | !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]; | |
76610ebb IS |
690 | } |
691 | ||
6b75c480 JP |
692 | static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) |
693 | { | |
694 | struct mlxsw_sp_vr *vr; | |
695 | int i; | |
696 | ||
c1a38311 | 697 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { |
9011b677 | 698 | vr = &mlxsw_sp->router->vrs[i]; |
76610ebb | 699 | if (!mlxsw_sp_vr_is_used(vr)) |
6b75c480 JP |
700 | return vr; |
701 | } | |
702 | return NULL; | |
703 | } | |
704 | ||
705 | static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, | |
0adb214b | 706 | const struct mlxsw_sp_fib *fib, u8 tree_id) |
6b75c480 JP |
707 | { |
708 | char raltb_pl[MLXSW_REG_RALTB_LEN]; | |
709 | ||
76610ebb IS |
710 | mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, |
711 | (enum mlxsw_reg_ralxx_protocol) fib->proto, | |
0adb214b | 712 | tree_id); |
6b75c480 JP |
713 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); |
714 | } | |
715 | ||
716 | static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp, | |
76610ebb | 717 | const struct mlxsw_sp_fib *fib) |
6b75c480 JP |
718 | { |
719 | char raltb_pl[MLXSW_REG_RALTB_LEN]; | |
720 | ||
721 | /* Bind to tree 0 which is default */ | |
76610ebb IS |
722 | mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, |
723 | (enum mlxsw_reg_ralxx_protocol) fib->proto, 0); | |
6b75c480 JP |
724 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); |
725 | } | |
726 | ||
727 | static u32 mlxsw_sp_fix_tb_id(u32 tb_id) | |
728 | { | |
7e50d435 YG |
729 | /* For our purpose, squash main, default and local tables into one */ |
730 | if (tb_id == RT_TABLE_LOCAL || tb_id == RT_TABLE_DEFAULT) | |
6b75c480 JP |
731 | tb_id = RT_TABLE_MAIN; |
732 | return tb_id; | |
733 | } | |
734 | ||
735 | static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp, | |
76610ebb | 736 | u32 tb_id) |
6b75c480 JP |
737 | { |
738 | struct mlxsw_sp_vr *vr; | |
739 | int i; | |
740 | ||
741 | tb_id = mlxsw_sp_fix_tb_id(tb_id); | |
9497c042 | 742 | |
c1a38311 | 743 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { |
9011b677 | 744 | vr = &mlxsw_sp->router->vrs[i]; |
76610ebb | 745 | if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id) |
6b75c480 JP |
746 | return vr; |
747 | } | |
748 | return NULL; | |
749 | } | |
750 | ||
88782f75 IS |
751 | int mlxsw_sp_router_tb_id_vr_id(struct mlxsw_sp *mlxsw_sp, u32 tb_id, |
752 | u16 *vr_id) | |
753 | { | |
754 | struct mlxsw_sp_vr *vr; | |
755 | ||
756 | vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id); | |
757 | if (!vr) | |
758 | return -ESRCH; | |
759 | *vr_id = vr->id; | |
760 | ||
761 | return 0; | |
762 | } | |
763 | ||
76610ebb IS |
764 | static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr, |
765 | enum mlxsw_sp_l3proto proto) | |
766 | { | |
767 | switch (proto) { | |
768 | case MLXSW_SP_L3_PROTO_IPV4: | |
769 | return vr->fib4; | |
770 | case MLXSW_SP_L3_PROTO_IPV6: | |
a3d9bc50 | 771 | return vr->fib6; |
76610ebb IS |
772 | } |
773 | return NULL; | |
774 | } | |
775 | ||
6b75c480 | 776 | static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, |
f8fa9b4e DA |
777 | u32 tb_id, |
778 | struct netlink_ext_ack *extack) | |
6b75c480 | 779 | { |
9742f866 | 780 | struct mlxsw_sp_mr_table *mr4_table, *mr6_table; |
0f2d2b27 JP |
781 | struct mlxsw_sp_fib *fib4; |
782 | struct mlxsw_sp_fib *fib6; | |
6b75c480 | 783 | struct mlxsw_sp_vr *vr; |
a3d9bc50 | 784 | int err; |
6b75c480 JP |
785 | |
786 | vr = mlxsw_sp_vr_find_unused(mlxsw_sp); | |
f8fa9b4e | 787 | if (!vr) { |
6c677750 | 788 | NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported virtual routers"); |
6b75c480 | 789 | return ERR_PTR(-EBUSY); |
f8fa9b4e | 790 | } |
0f2d2b27 JP |
791 | fib4 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); |
792 | if (IS_ERR(fib4)) | |
793 | return ERR_CAST(fib4); | |
794 | fib6 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); | |
795 | if (IS_ERR(fib6)) { | |
796 | err = PTR_ERR(fib6); | |
a3d9bc50 IS |
797 | goto err_fib6_create; |
798 | } | |
0f2d2b27 JP |
799 | mr4_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id, |
800 | MLXSW_SP_L3_PROTO_IPV4); | |
801 | if (IS_ERR(mr4_table)) { | |
802 | err = PTR_ERR(mr4_table); | |
9742f866 | 803 | goto err_mr4_table_create; |
d42b0965 | 804 | } |
9742f866 YM |
805 | mr6_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id, |
806 | MLXSW_SP_L3_PROTO_IPV6); | |
807 | if (IS_ERR(mr6_table)) { | |
808 | err = PTR_ERR(mr6_table); | |
809 | goto err_mr6_table_create; | |
810 | } | |
811 | ||
0f2d2b27 JP |
812 | vr->fib4 = fib4; |
813 | vr->fib6 = fib6; | |
9742f866 YM |
814 | vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = mr4_table; |
815 | vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = mr6_table; | |
6b75c480 | 816 | vr->tb_id = tb_id; |
6b75c480 | 817 | return vr; |
a3d9bc50 | 818 | |
9742f866 YM |
819 | err_mr6_table_create: |
820 | mlxsw_sp_mr_table_destroy(mr4_table); | |
821 | err_mr4_table_create: | |
0f2d2b27 | 822 | mlxsw_sp_fib_destroy(mlxsw_sp, fib6); |
a3d9bc50 | 823 | err_fib6_create: |
0f2d2b27 | 824 | mlxsw_sp_fib_destroy(mlxsw_sp, fib4); |
a3d9bc50 | 825 | return ERR_PTR(err); |
6b75c480 JP |
826 | } |
827 | ||
2b52ce02 IS |
828 | static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp, |
829 | struct mlxsw_sp_vr *vr) | |
6b75c480 | 830 | { |
9742f866 YM |
831 | mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]); |
832 | vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = NULL; | |
833 | mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]); | |
834 | vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = NULL; | |
2b52ce02 | 835 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6); |
a3d9bc50 | 836 | vr->fib6 = NULL; |
2b52ce02 | 837 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4); |
76610ebb | 838 | vr->fib4 = NULL; |
6b75c480 JP |
839 | } |
840 | ||
f8fa9b4e DA |
841 | static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, |
842 | struct netlink_ext_ack *extack) | |
6b75c480 JP |
843 | { |
844 | struct mlxsw_sp_vr *vr; | |
6b75c480 JP |
845 | |
846 | tb_id = mlxsw_sp_fix_tb_id(tb_id); | |
76610ebb IS |
847 | vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id); |
848 | if (!vr) | |
f8fa9b4e | 849 | vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id, extack); |
6b75c480 JP |
850 | return vr; |
851 | } | |
852 | ||
2b52ce02 | 853 | static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr) |
6b75c480 | 854 | { |
a3d9bc50 | 855 | if (!vr->rif_count && list_empty(&vr->fib4->node_list) && |
d42b0965 | 856 | list_empty(&vr->fib6->node_list) && |
9742f866 YM |
857 | mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]) && |
858 | mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6])) | |
2b52ce02 | 859 | mlxsw_sp_vr_destroy(mlxsw_sp, vr); |
6b75c480 JP |
860 | } |
861 | ||
fc922bb0 IS |
862 | static bool |
863 | mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr, | |
864 | enum mlxsw_sp_l3proto proto, u8 tree_id) | |
865 | { | |
866 | struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); | |
867 | ||
868 | if (!mlxsw_sp_vr_is_used(vr)) | |
869 | return false; | |
2b52ce02 | 870 | if (fib->lpm_tree->id == tree_id) |
fc922bb0 IS |
871 | return true; |
872 | return false; | |
873 | } | |
874 | ||
875 | static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, | |
876 | struct mlxsw_sp_fib *fib, | |
877 | struct mlxsw_sp_lpm_tree *new_tree) | |
878 | { | |
879 | struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; | |
880 | int err; | |
881 | ||
fc922bb0 IS |
882 | fib->lpm_tree = new_tree; |
883 | mlxsw_sp_lpm_tree_hold(new_tree); | |
ed604c5d IS |
884 | err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); |
885 | if (err) | |
886 | goto err_tree_bind; | |
fc922bb0 IS |
887 | mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree); |
888 | return 0; | |
ed604c5d IS |
889 | |
890 | err_tree_bind: | |
891 | mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree); | |
892 | fib->lpm_tree = old_tree; | |
893 | return err; | |
fc922bb0 IS |
894 | } |
895 | ||
896 | static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, | |
897 | struct mlxsw_sp_fib *fib, | |
898 | struct mlxsw_sp_lpm_tree *new_tree) | |
899 | { | |
fc922bb0 | 900 | enum mlxsw_sp_l3proto proto = fib->proto; |
2b52ce02 | 901 | struct mlxsw_sp_lpm_tree *old_tree; |
fc922bb0 IS |
902 | u8 old_id, new_id = new_tree->id; |
903 | struct mlxsw_sp_vr *vr; | |
904 | int i, err; | |
905 | ||
2b52ce02 | 906 | old_tree = mlxsw_sp->router->lpm.proto_trees[proto]; |
fc922bb0 IS |
907 | old_id = old_tree->id; |
908 | ||
909 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { | |
910 | vr = &mlxsw_sp->router->vrs[i]; | |
911 | if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id)) | |
912 | continue; | |
913 | err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, | |
914 | mlxsw_sp_vr_fib(vr, proto), | |
915 | new_tree); | |
916 | if (err) | |
917 | goto err_tree_replace; | |
918 | } | |
919 | ||
2b52ce02 IS |
920 | memcpy(new_tree->prefix_ref_count, old_tree->prefix_ref_count, |
921 | sizeof(new_tree->prefix_ref_count)); | |
922 | mlxsw_sp->router->lpm.proto_trees[proto] = new_tree; | |
923 | mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree); | |
924 | ||
fc922bb0 IS |
925 | return 0; |
926 | ||
927 | err_tree_replace: | |
928 | for (i--; i >= 0; i--) { | |
929 | if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) | |
930 | continue; | |
931 | mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, | |
932 | mlxsw_sp_vr_fib(vr, proto), | |
933 | old_tree); | |
934 | } | |
935 | return err; | |
fc922bb0 IS |
936 | } |
937 | ||
9497c042 | 938 | static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) |
6b75c480 JP |
939 | { |
940 | struct mlxsw_sp_vr *vr; | |
c1a38311 | 941 | u64 max_vrs; |
6b75c480 JP |
942 | int i; |
943 | ||
c1a38311 | 944 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS)) |
9497c042 NF |
945 | return -EIO; |
946 | ||
c1a38311 | 947 | max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); |
9011b677 IS |
948 | mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr), |
949 | GFP_KERNEL); | |
950 | if (!mlxsw_sp->router->vrs) | |
9497c042 NF |
951 | return -ENOMEM; |
952 | ||
c1a38311 | 953 | for (i = 0; i < max_vrs; i++) { |
9011b677 | 954 | vr = &mlxsw_sp->router->vrs[i]; |
6b75c480 JP |
955 | vr->id = i; |
956 | } | |
9497c042 NF |
957 | |
958 | return 0; | |
959 | } | |
960 | ||
ac571de9 IS |
961 | static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp); |
962 | ||
9497c042 NF |
963 | static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp) |
964 | { | |
3057224e IS |
965 | /* At this stage we're guaranteed not to have new incoming |
966 | * FIB notifications and the work queue is free from FIBs | |
967 | * sitting on top of mlxsw netdevs. However, we can still | |
968 | * have other FIBs queued. Flush the queue before flushing | |
969 | * the device's tables. No need for locks, as we're the only | |
970 | * writer. | |
971 | */ | |
972 | mlxsw_core_flush_owq(); | |
ac571de9 | 973 | mlxsw_sp_router_fib_flush(mlxsw_sp); |
9011b677 | 974 | kfree(mlxsw_sp->router->vrs); |
6b75c480 JP |
975 | } |
976 | ||
6ddb7426 PM |
977 | static struct net_device * |
978 | __mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev) | |
979 | { | |
980 | struct ip_tunnel *tun = netdev_priv(ol_dev); | |
981 | struct net *net = dev_net(ol_dev); | |
982 | ||
983 | return __dev_get_by_index(net, tun->parms.link); | |
984 | } | |
985 | ||
4cf04f3f | 986 | u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev) |
6ddb7426 PM |
987 | { |
988 | struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); | |
989 | ||
990 | if (d) | |
991 | return l3mdev_fib_table(d) ? : RT_TABLE_MAIN; | |
992 | else | |
993 | return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN; | |
994 | } | |
995 | ||
1012b9ac PM |
996 | static struct mlxsw_sp_rif * |
997 | mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, | |
f8fa9b4e DA |
998 | const struct mlxsw_sp_rif_params *params, |
999 | struct netlink_ext_ack *extack); | |
1012b9ac PM |
1000 | |
1001 | static struct mlxsw_sp_rif_ipip_lb * | |
1002 | mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp, | |
1003 | enum mlxsw_sp_ipip_type ipipt, | |
7e75af63 PM |
1004 | struct net_device *ol_dev, |
1005 | struct netlink_ext_ack *extack) | |
1012b9ac PM |
1006 | { |
1007 | struct mlxsw_sp_rif_params_ipip_lb lb_params; | |
1008 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
1009 | struct mlxsw_sp_rif *rif; | |
1010 | ||
1011 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; | |
1012 | lb_params = (struct mlxsw_sp_rif_params_ipip_lb) { | |
1013 | .common.dev = ol_dev, | |
1014 | .common.lag = false, | |
1015 | .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev), | |
1016 | }; | |
1017 | ||
7e75af63 | 1018 | rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common, extack); |
1012b9ac PM |
1019 | if (IS_ERR(rif)) |
1020 | return ERR_CAST(rif); | |
1021 | return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); | |
1022 | } | |
1023 | ||
1024 | static struct mlxsw_sp_ipip_entry * | |
1025 | mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp, | |
1026 | enum mlxsw_sp_ipip_type ipipt, | |
1027 | struct net_device *ol_dev) | |
1028 | { | |
e437f3b6 | 1029 | const struct mlxsw_sp_ipip_ops *ipip_ops; |
1012b9ac PM |
1030 | struct mlxsw_sp_ipip_entry *ipip_entry; |
1031 | struct mlxsw_sp_ipip_entry *ret = NULL; | |
1032 | ||
e437f3b6 | 1033 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; |
1012b9ac PM |
1034 | ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL); |
1035 | if (!ipip_entry) | |
1036 | return ERR_PTR(-ENOMEM); | |
1037 | ||
1038 | ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt, | |
7e75af63 | 1039 | ol_dev, NULL); |
1012b9ac PM |
1040 | if (IS_ERR(ipip_entry->ol_lb)) { |
1041 | ret = ERR_CAST(ipip_entry->ol_lb); | |
1042 | goto err_ol_ipip_lb_create; | |
1043 | } | |
1044 | ||
1045 | ipip_entry->ipipt = ipipt; | |
1046 | ipip_entry->ol_dev = ol_dev; | |
e437f3b6 PM |
1047 | |
1048 | switch (ipip_ops->ul_proto) { | |
1049 | case MLXSW_SP_L3_PROTO_IPV4: | |
1050 | ipip_entry->parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev); | |
1051 | break; | |
1052 | case MLXSW_SP_L3_PROTO_IPV6: | |
1053 | WARN_ON(1); | |
1054 | break; | |
1055 | } | |
1012b9ac PM |
1056 | |
1057 | return ipip_entry; | |
1058 | ||
1059 | err_ol_ipip_lb_create: | |
1060 | kfree(ipip_entry); | |
1061 | return ret; | |
1062 | } | |
1063 | ||
1064 | static void | |
4cccb737 | 1065 | mlxsw_sp_ipip_entry_dealloc(struct mlxsw_sp_ipip_entry *ipip_entry) |
1012b9ac | 1066 | { |
1012b9ac PM |
1067 | mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common); |
1068 | kfree(ipip_entry); | |
1069 | } | |
1070 | ||
1012b9ac PM |
1071 | static bool |
1072 | mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp, | |
1073 | const enum mlxsw_sp_l3proto ul_proto, | |
1074 | union mlxsw_sp_l3addr saddr, | |
1075 | u32 ul_tb_id, | |
1076 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1077 | { | |
1078 | u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); | |
1079 | enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; | |
1080 | union mlxsw_sp_l3addr tun_saddr; | |
1081 | ||
1082 | if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) | |
1083 | return false; | |
1084 | ||
1085 | tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev); | |
1086 | return tun_ul_tb_id == ul_tb_id && | |
1087 | mlxsw_sp_l3addr_eq(&tun_saddr, &saddr); | |
1088 | } | |
1089 | ||
4607f6d2 PM |
1090 | static int |
1091 | mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp, | |
1092 | struct mlxsw_sp_fib_entry *fib_entry, | |
1093 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1094 | { | |
1095 | u32 tunnel_index; | |
1096 | int err; | |
1097 | ||
4b6b1869 JP |
1098 | err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, |
1099 | 1, &tunnel_index); | |
4607f6d2 PM |
1100 | if (err) |
1101 | return err; | |
1102 | ||
1103 | ipip_entry->decap_fib_entry = fib_entry; | |
1104 | fib_entry->decap.ipip_entry = ipip_entry; | |
1105 | fib_entry->decap.tunnel_index = tunnel_index; | |
1106 | return 0; | |
1107 | } | |
1108 | ||
1109 | static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp, | |
1110 | struct mlxsw_sp_fib_entry *fib_entry) | |
1111 | { | |
1112 | /* Unlink this node from the IPIP entry that it's the decap entry of. */ | |
1113 | fib_entry->decap.ipip_entry->decap_fib_entry = NULL; | |
1114 | fib_entry->decap.ipip_entry = NULL; | |
4b6b1869 | 1115 | mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, |
0304c005 | 1116 | 1, fib_entry->decap.tunnel_index); |
4607f6d2 PM |
1117 | } |
1118 | ||
1cc38fb1 PM |
1119 | static struct mlxsw_sp_fib_node * |
1120 | mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, | |
1121 | size_t addr_len, unsigned char prefix_len); | |
4607f6d2 PM |
1122 | static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, |
1123 | struct mlxsw_sp_fib_entry *fib_entry); | |
1124 | ||
1125 | static void | |
1126 | mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp, | |
1127 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1128 | { | |
1129 | struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry; | |
1130 | ||
1131 | mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry); | |
1132 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; | |
1133 | ||
1134 | mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); | |
1135 | } | |
1136 | ||
1cc38fb1 PM |
1137 | static void |
1138 | mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp, | |
1139 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
1140 | struct mlxsw_sp_fib_entry *decap_fib_entry) | |
1141 | { | |
1142 | if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry, | |
1143 | ipip_entry)) | |
1144 | return; | |
1145 | decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; | |
1146 | ||
1147 | if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry)) | |
1148 | mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); | |
1149 | } | |
1150 | ||
0c69e0fc IS |
1151 | static struct mlxsw_sp_fib_entry * |
1152 | mlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id, | |
1153 | enum mlxsw_sp_l3proto proto, | |
1154 | const union mlxsw_sp_l3addr *addr, | |
1155 | enum mlxsw_sp_fib_entry_type type) | |
1156 | { | |
1157 | struct mlxsw_sp_fib_entry *fib_entry; | |
1158 | struct mlxsw_sp_fib_node *fib_node; | |
1159 | unsigned char addr_prefix_len; | |
1160 | struct mlxsw_sp_fib *fib; | |
1161 | struct mlxsw_sp_vr *vr; | |
1162 | const void *addrp; | |
1163 | size_t addr_len; | |
1164 | u32 addr4; | |
1165 | ||
1166 | vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id); | |
1167 | if (!vr) | |
1168 | return NULL; | |
1169 | fib = mlxsw_sp_vr_fib(vr, proto); | |
1170 | ||
1171 | switch (proto) { | |
1172 | case MLXSW_SP_L3_PROTO_IPV4: | |
1173 | addr4 = be32_to_cpu(addr->addr4); | |
1174 | addrp = &addr4; | |
1175 | addr_len = 4; | |
1176 | addr_prefix_len = 32; | |
1177 | break; | |
1178 | case MLXSW_SP_L3_PROTO_IPV6: /* fall through */ | |
1179 | default: | |
1180 | WARN_ON(1); | |
1181 | return NULL; | |
1182 | } | |
1183 | ||
1184 | fib_node = mlxsw_sp_fib_node_lookup(fib, addrp, addr_len, | |
1185 | addr_prefix_len); | |
1186 | if (!fib_node || list_empty(&fib_node->entry_list)) | |
1187 | return NULL; | |
1188 | ||
1189 | fib_entry = list_first_entry(&fib_node->entry_list, | |
1190 | struct mlxsw_sp_fib_entry, list); | |
1191 | if (fib_entry->type != type) | |
1192 | return NULL; | |
1193 | ||
1194 | return fib_entry; | |
1195 | } | |
1196 | ||
1cc38fb1 PM |
1197 | /* Given an IPIP entry, find the corresponding decap route. */ |
1198 | static struct mlxsw_sp_fib_entry * | |
1199 | mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, | |
1200 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1201 | { | |
1202 | static struct mlxsw_sp_fib_node *fib_node; | |
1203 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
1204 | struct mlxsw_sp_fib_entry *fib_entry; | |
1205 | unsigned char saddr_prefix_len; | |
1206 | union mlxsw_sp_l3addr saddr; | |
1207 | struct mlxsw_sp_fib *ul_fib; | |
1208 | struct mlxsw_sp_vr *ul_vr; | |
1209 | const void *saddrp; | |
1210 | size_t saddr_len; | |
1211 | u32 ul_tb_id; | |
1212 | u32 saddr4; | |
1213 | ||
1214 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; | |
1215 | ||
1216 | ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); | |
1217 | ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id); | |
1218 | if (!ul_vr) | |
1219 | return NULL; | |
1220 | ||
1221 | ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto); | |
1222 | saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto, | |
1223 | ipip_entry->ol_dev); | |
1224 | ||
1225 | switch (ipip_ops->ul_proto) { | |
1226 | case MLXSW_SP_L3_PROTO_IPV4: | |
1227 | saddr4 = be32_to_cpu(saddr.addr4); | |
1228 | saddrp = &saddr4; | |
1229 | saddr_len = 4; | |
1230 | saddr_prefix_len = 32; | |
1231 | break; | |
1232 | case MLXSW_SP_L3_PROTO_IPV6: | |
1233 | WARN_ON(1); | |
1234 | return NULL; | |
1235 | } | |
1236 | ||
1237 | fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len, | |
1238 | saddr_prefix_len); | |
1239 | if (!fib_node || list_empty(&fib_node->entry_list)) | |
1240 | return NULL; | |
1241 | ||
1242 | fib_entry = list_first_entry(&fib_node->entry_list, | |
1243 | struct mlxsw_sp_fib_entry, list); | |
1244 | if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP) | |
1245 | return NULL; | |
1246 | ||
1247 | return fib_entry; | |
1248 | } | |
1249 | ||
1012b9ac | 1250 | static struct mlxsw_sp_ipip_entry * |
4cccb737 PM |
1251 | mlxsw_sp_ipip_entry_create(struct mlxsw_sp *mlxsw_sp, |
1252 | enum mlxsw_sp_ipip_type ipipt, | |
1253 | struct net_device *ol_dev) | |
1012b9ac | 1254 | { |
1012b9ac | 1255 | struct mlxsw_sp_ipip_entry *ipip_entry; |
1012b9ac PM |
1256 | |
1257 | ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev); | |
1258 | if (IS_ERR(ipip_entry)) | |
1259 | return ipip_entry; | |
1260 | ||
1261 | list_add_tail(&ipip_entry->ipip_list_node, | |
1262 | &mlxsw_sp->router->ipip_list); | |
1263 | ||
1012b9ac PM |
1264 | return ipip_entry; |
1265 | } | |
1266 | ||
1267 | static void | |
4cccb737 PM |
1268 | mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp *mlxsw_sp, |
1269 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1012b9ac | 1270 | { |
4cccb737 PM |
1271 | list_del(&ipip_entry->ipip_list_node); |
1272 | mlxsw_sp_ipip_entry_dealloc(ipip_entry); | |
1012b9ac PM |
1273 | } |
1274 | ||
4607f6d2 PM |
1275 | static bool |
1276 | mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp, | |
1277 | const struct net_device *ul_dev, | |
1278 | enum mlxsw_sp_l3proto ul_proto, | |
1279 | union mlxsw_sp_l3addr ul_dip, | |
1280 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1281 | { | |
1282 | u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN; | |
1283 | enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; | |
4607f6d2 PM |
1284 | |
1285 | if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) | |
1286 | return false; | |
1287 | ||
4607f6d2 | 1288 | return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip, |
da93d291 | 1289 | ul_tb_id, ipip_entry); |
4607f6d2 PM |
1290 | } |
1291 | ||
1292 | /* Given decap parameters, find the corresponding IPIP entry. */ | |
1293 | static struct mlxsw_sp_ipip_entry * | |
1294 | mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp, | |
1295 | const struct net_device *ul_dev, | |
1296 | enum mlxsw_sp_l3proto ul_proto, | |
1297 | union mlxsw_sp_l3addr ul_dip) | |
1298 | { | |
1299 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1300 | ||
1301 | list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, | |
1302 | ipip_list_node) | |
1303 | if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev, | |
1304 | ul_proto, ul_dip, | |
1305 | ipip_entry)) | |
1306 | return ipip_entry; | |
1307 | ||
1308 | return NULL; | |
1309 | } | |
1310 | ||
6698c168 PM |
1311 | static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp, |
1312 | const struct net_device *dev, | |
1313 | enum mlxsw_sp_ipip_type *p_type) | |
1314 | { | |
1315 | struct mlxsw_sp_router *router = mlxsw_sp->router; | |
1316 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
1317 | enum mlxsw_sp_ipip_type ipipt; | |
1318 | ||
1319 | for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) { | |
1320 | ipip_ops = router->ipip_ops_arr[ipipt]; | |
1321 | if (dev->type == ipip_ops->dev_type) { | |
1322 | if (p_type) | |
1323 | *p_type = ipipt; | |
1324 | return true; | |
1325 | } | |
1326 | } | |
1327 | return false; | |
1328 | } | |
1329 | ||
796ec776 PM |
1330 | bool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp, |
1331 | const struct net_device *dev) | |
0063587d PM |
1332 | { |
1333 | return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL); | |
1334 | } | |
1335 | ||
1336 | static struct mlxsw_sp_ipip_entry * | |
1337 | mlxsw_sp_ipip_entry_find_by_ol_dev(struct mlxsw_sp *mlxsw_sp, | |
1338 | const struct net_device *ol_dev) | |
1339 | { | |
1340 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1341 | ||
1342 | list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, | |
1343 | ipip_list_node) | |
1344 | if (ipip_entry->ol_dev == ol_dev) | |
1345 | return ipip_entry; | |
1346 | ||
1347 | return NULL; | |
1348 | } | |
1349 | ||
61481f2f PM |
1350 | static struct mlxsw_sp_ipip_entry * |
1351 | mlxsw_sp_ipip_entry_find_by_ul_dev(const struct mlxsw_sp *mlxsw_sp, | |
1352 | const struct net_device *ul_dev, | |
1353 | struct mlxsw_sp_ipip_entry *start) | |
1354 | { | |
1355 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1356 | ||
1357 | ipip_entry = list_prepare_entry(start, &mlxsw_sp->router->ipip_list, | |
1358 | ipip_list_node); | |
1359 | list_for_each_entry_continue(ipip_entry, &mlxsw_sp->router->ipip_list, | |
1360 | ipip_list_node) { | |
1361 | struct net_device *ipip_ul_dev = | |
1362 | __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev); | |
1363 | ||
1364 | if (ipip_ul_dev == ul_dev) | |
1365 | return ipip_entry; | |
1366 | } | |
1367 | ||
1368 | return NULL; | |
1369 | } | |
1370 | ||
1371 | bool mlxsw_sp_netdev_is_ipip_ul(const struct mlxsw_sp *mlxsw_sp, | |
1372 | const struct net_device *dev) | |
1373 | { | |
1374 | return mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, dev, NULL); | |
1375 | } | |
1376 | ||
cafdb2a0 PM |
1377 | static bool mlxsw_sp_netdevice_ipip_can_offload(struct mlxsw_sp *mlxsw_sp, |
1378 | const struct net_device *ol_dev, | |
1379 | enum mlxsw_sp_ipip_type ipipt) | |
1380 | { | |
1381 | const struct mlxsw_sp_ipip_ops *ops | |
1382 | = mlxsw_sp->router->ipip_ops_arr[ipipt]; | |
1383 | ||
1384 | /* For deciding whether decap should be offloaded, we don't care about | |
1385 | * overlay protocol, so ask whether either one is supported. | |
1386 | */ | |
1387 | return ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV4) || | |
1388 | ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV6); | |
1389 | } | |
1390 | ||
796ec776 PM |
1391 | static int mlxsw_sp_netdevice_ipip_ol_reg_event(struct mlxsw_sp *mlxsw_sp, |
1392 | struct net_device *ol_dev) | |
0063587d | 1393 | { |
0063587d | 1394 | struct mlxsw_sp_ipip_entry *ipip_entry; |
af641713 | 1395 | enum mlxsw_sp_l3proto ul_proto; |
0063587d | 1396 | enum mlxsw_sp_ipip_type ipipt; |
af641713 PM |
1397 | union mlxsw_sp_l3addr saddr; |
1398 | u32 ul_tb_id; | |
0063587d PM |
1399 | |
1400 | mlxsw_sp_netdev_ipip_type(mlxsw_sp, ol_dev, &ipipt); | |
cafdb2a0 | 1401 | if (mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev, ipipt)) { |
af641713 PM |
1402 | ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev); |
1403 | ul_proto = mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto; | |
1404 | saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev); | |
1405 | if (!mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto, | |
1406 | saddr, ul_tb_id, | |
1407 | NULL)) { | |
1408 | ipip_entry = mlxsw_sp_ipip_entry_create(mlxsw_sp, ipipt, | |
1409 | ol_dev); | |
1410 | if (IS_ERR(ipip_entry)) | |
1411 | return PTR_ERR(ipip_entry); | |
1412 | } | |
0063587d PM |
1413 | } |
1414 | ||
1415 | return 0; | |
1416 | } | |
1417 | ||
796ec776 PM |
1418 | static void mlxsw_sp_netdevice_ipip_ol_unreg_event(struct mlxsw_sp *mlxsw_sp, |
1419 | struct net_device *ol_dev) | |
0063587d PM |
1420 | { |
1421 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1422 | ||
1423 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
1424 | if (ipip_entry) | |
4cccb737 | 1425 | mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry); |
0063587d PM |
1426 | } |
1427 | ||
47518ca5 PM |
1428 | static void |
1429 | mlxsw_sp_ipip_entry_ol_up_event(struct mlxsw_sp *mlxsw_sp, | |
1430 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1431 | { | |
1432 | struct mlxsw_sp_fib_entry *decap_fib_entry; | |
1433 | ||
1434 | decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry); | |
1435 | if (decap_fib_entry) | |
1436 | mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry, | |
1437 | decap_fib_entry); | |
1438 | } | |
1439 | ||
22b99058 | 1440 | static int |
3c747500 ND |
1441 | mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, u16 ul_vr_id, |
1442 | u16 ul_rif_id, bool enable) | |
22b99058 PM |
1443 | { |
1444 | struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config; | |
1445 | struct mlxsw_sp_rif *rif = &lb_rif->common; | |
1446 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
1447 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
1448 | u32 saddr4; | |
1449 | ||
1450 | switch (lb_cf.ul_protocol) { | |
1451 | case MLXSW_SP_L3_PROTO_IPV4: | |
1452 | saddr4 = be32_to_cpu(lb_cf.saddr.addr4); | |
1453 | mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF, | |
1454 | rif->rif_index, rif->vr_id, rif->dev->mtu); | |
1455 | mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt, | |
1456 | MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, | |
3c747500 | 1457 | ul_vr_id, ul_rif_id, saddr4, lb_cf.okey); |
22b99058 PM |
1458 | break; |
1459 | ||
1460 | case MLXSW_SP_L3_PROTO_IPV6: | |
1461 | return -EAFNOSUPPORT; | |
1462 | } | |
1463 | ||
1464 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
1465 | } | |
1466 | ||
68c3cd92 PM |
1467 | static int mlxsw_sp_netdevice_ipip_ol_update_mtu(struct mlxsw_sp *mlxsw_sp, |
1468 | struct net_device *ol_dev) | |
1469 | { | |
1470 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1471 | struct mlxsw_sp_rif_ipip_lb *lb_rif; | |
68c3cd92 PM |
1472 | int err = 0; |
1473 | ||
1474 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
1475 | if (ipip_entry) { | |
1476 | lb_rif = ipip_entry->ol_lb; | |
25f844dd ND |
1477 | err = mlxsw_sp_rif_ipip_lb_op(lb_rif, lb_rif->ul_vr_id, |
1478 | lb_rif->ul_rif_id, true); | |
68c3cd92 PM |
1479 | if (err) |
1480 | goto out; | |
1481 | lb_rif->common.mtu = ol_dev->mtu; | |
1482 | } | |
1483 | ||
1484 | out: | |
1485 | return err; | |
1486 | } | |
1487 | ||
6d4de445 PM |
1488 | static void mlxsw_sp_netdevice_ipip_ol_up_event(struct mlxsw_sp *mlxsw_sp, |
1489 | struct net_device *ol_dev) | |
0063587d | 1490 | { |
0063587d PM |
1491 | struct mlxsw_sp_ipip_entry *ipip_entry; |
1492 | ||
1493 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
47518ca5 PM |
1494 | if (ipip_entry) |
1495 | mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry); | |
0063587d PM |
1496 | } |
1497 | ||
a3fe198e PM |
1498 | static void |
1499 | mlxsw_sp_ipip_entry_ol_down_event(struct mlxsw_sp *mlxsw_sp, | |
1500 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1501 | { | |
1502 | if (ipip_entry->decap_fib_entry) | |
1503 | mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); | |
1504 | } | |
1505 | ||
796ec776 PM |
1506 | static void mlxsw_sp_netdevice_ipip_ol_down_event(struct mlxsw_sp *mlxsw_sp, |
1507 | struct net_device *ol_dev) | |
0063587d PM |
1508 | { |
1509 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1510 | ||
1511 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
a3fe198e PM |
1512 | if (ipip_entry) |
1513 | mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry); | |
0063587d PM |
1514 | } |
1515 | ||
09dbf629 PM |
1516 | static void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp, |
1517 | struct mlxsw_sp_rif *old_rif, | |
1518 | struct mlxsw_sp_rif *new_rif); | |
65a6121b PM |
1519 | static int |
1520 | mlxsw_sp_ipip_entry_ol_lb_update(struct mlxsw_sp *mlxsw_sp, | |
1521 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
0c5f1cd5 | 1522 | bool keep_encap, |
65a6121b PM |
1523 | struct netlink_ext_ack *extack) |
1524 | { | |
1525 | struct mlxsw_sp_rif_ipip_lb *old_lb_rif = ipip_entry->ol_lb; | |
1526 | struct mlxsw_sp_rif_ipip_lb *new_lb_rif; | |
1527 | ||
1528 | new_lb_rif = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, | |
1529 | ipip_entry->ipipt, | |
1530 | ipip_entry->ol_dev, | |
1531 | extack); | |
1532 | if (IS_ERR(new_lb_rif)) | |
1533 | return PTR_ERR(new_lb_rif); | |
1534 | ipip_entry->ol_lb = new_lb_rif; | |
0c5f1cd5 | 1535 | |
09dbf629 PM |
1536 | if (keep_encap) |
1537 | mlxsw_sp_nexthop_rif_migrate(mlxsw_sp, &old_lb_rif->common, | |
1538 | &new_lb_rif->common); | |
0c5f1cd5 | 1539 | |
65a6121b | 1540 | mlxsw_sp_rif_destroy(&old_lb_rif->common); |
f63ce4e5 | 1541 | |
65a6121b PM |
1542 | return 0; |
1543 | } | |
1544 | ||
09dbf629 PM |
1545 | static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp, |
1546 | struct mlxsw_sp_rif *rif); | |
1547 | ||
0c5f1cd5 PM |
1548 | /** |
1549 | * Update the offload related to an IPIP entry. This always updates decap, and | |
1550 | * in addition to that it also: | |
1551 | * @recreate_loopback: recreates the associated loopback RIF | |
1552 | * @keep_encap: updates next hops that use the tunnel netdevice. This is only | |
1553 | * relevant when recreate_loopback is true. | |
1554 | * @update_nexthops: updates next hops, keeping the current loopback RIF. This | |
1555 | * is only relevant when recreate_loopback is false. | |
1556 | */ | |
65a6121b PM |
1557 | int __mlxsw_sp_ipip_entry_update_tunnel(struct mlxsw_sp *mlxsw_sp, |
1558 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
0c5f1cd5 PM |
1559 | bool recreate_loopback, |
1560 | bool keep_encap, | |
1561 | bool update_nexthops, | |
65a6121b PM |
1562 | struct netlink_ext_ack *extack) |
1563 | { | |
1564 | int err; | |
f63ce4e5 | 1565 | |
65a6121b PM |
1566 | /* RIFs can't be edited, so to update loopback, we need to destroy and |
1567 | * recreate it. That creates a window of opportunity where RALUE and | |
1568 | * RATR registers end up referencing a RIF that's already gone. RATRs | |
1569 | * are handled in mlxsw_sp_ipip_entry_ol_lb_update(), and to take care | |
f63ce4e5 PM |
1570 | * of RALUE, demote the decap route back. |
1571 | */ | |
1572 | if (ipip_entry->decap_fib_entry) | |
1573 | mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); | |
1574 | ||
0c5f1cd5 PM |
1575 | if (recreate_loopback) { |
1576 | err = mlxsw_sp_ipip_entry_ol_lb_update(mlxsw_sp, ipip_entry, | |
1577 | keep_encap, extack); | |
1578 | if (err) | |
1579 | return err; | |
1580 | } else if (update_nexthops) { | |
1581 | mlxsw_sp_nexthop_rif_update(mlxsw_sp, | |
1582 | &ipip_entry->ol_lb->common); | |
1583 | } | |
65a6121b PM |
1584 | |
1585 | if (ipip_entry->ol_dev->flags & IFF_UP) | |
1586 | mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry); | |
f63ce4e5 PM |
1587 | |
1588 | return 0; | |
1589 | } | |
1590 | ||
65a6121b PM |
1591 | static int mlxsw_sp_netdevice_ipip_ol_vrf_event(struct mlxsw_sp *mlxsw_sp, |
1592 | struct net_device *ol_dev, | |
1593 | struct netlink_ext_ack *extack) | |
1594 | { | |
1595 | struct mlxsw_sp_ipip_entry *ipip_entry = | |
1596 | mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
cab43d9c PM |
1597 | enum mlxsw_sp_l3proto ul_proto; |
1598 | union mlxsw_sp_l3addr saddr; | |
1599 | u32 ul_tb_id; | |
65a6121b PM |
1600 | |
1601 | if (!ipip_entry) | |
1602 | return 0; | |
cab43d9c PM |
1603 | |
1604 | /* For flat configuration cases, moving overlay to a different VRF might | |
1605 | * cause local address conflict, and the conflicting tunnels need to be | |
1606 | * demoted. | |
1607 | */ | |
1608 | ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev); | |
1609 | ul_proto = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto; | |
1610 | saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev); | |
1611 | if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto, | |
1612 | saddr, ul_tb_id, | |
1613 | ipip_entry)) { | |
1614 | mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); | |
1615 | return 0; | |
1616 | } | |
1617 | ||
65a6121b | 1618 | return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry, |
0c5f1cd5 | 1619 | true, false, false, extack); |
65a6121b PM |
1620 | } |
1621 | ||
61481f2f PM |
1622 | static int |
1623 | mlxsw_sp_netdevice_ipip_ul_vrf_event(struct mlxsw_sp *mlxsw_sp, | |
1624 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
1625 | struct net_device *ul_dev, | |
1626 | struct netlink_ext_ack *extack) | |
1627 | { | |
1628 | return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry, | |
1629 | true, true, false, extack); | |
1630 | } | |
1631 | ||
44b0fff1 PM |
1632 | static int |
1633 | mlxsw_sp_netdevice_ipip_ul_up_event(struct mlxsw_sp *mlxsw_sp, | |
1634 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
1635 | struct net_device *ul_dev) | |
1636 | { | |
1637 | return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry, | |
1638 | false, false, true, NULL); | |
1639 | } | |
1640 | ||
1641 | static int | |
1642 | mlxsw_sp_netdevice_ipip_ul_down_event(struct mlxsw_sp *mlxsw_sp, | |
1643 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
1644 | struct net_device *ul_dev) | |
1645 | { | |
1646 | /* A down underlay device causes encapsulated packets to not be | |
1647 | * forwarded, but decap still works. So refresh next hops without | |
1648 | * touching anything else. | |
1649 | */ | |
1650 | return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry, | |
1651 | false, false, true, NULL); | |
1652 | } | |
1653 | ||
4cf04f3f PM |
1654 | static int |
1655 | mlxsw_sp_netdevice_ipip_ol_change_event(struct mlxsw_sp *mlxsw_sp, | |
1656 | struct net_device *ol_dev, | |
1657 | struct netlink_ext_ack *extack) | |
1658 | { | |
1659 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
1660 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
1661 | int err; | |
1662 | ||
1663 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev); | |
1664 | if (!ipip_entry) | |
1665 | /* A change might make a tunnel eligible for offloading, but | |
1666 | * that is currently not implemented. What falls to slow path | |
1667 | * stays there. | |
1668 | */ | |
1669 | return 0; | |
1670 | ||
1671 | /* A change might make a tunnel not eligible for offloading. */ | |
1672 | if (!mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev, | |
1673 | ipip_entry->ipipt)) { | |
1674 | mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); | |
1675 | return 0; | |
1676 | } | |
1677 | ||
1678 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; | |
1679 | err = ipip_ops->ol_netdev_change(mlxsw_sp, ipip_entry, extack); | |
1680 | return err; | |
1681 | } | |
1682 | ||
af641713 PM |
1683 | void mlxsw_sp_ipip_entry_demote_tunnel(struct mlxsw_sp *mlxsw_sp, |
1684 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1685 | { | |
1686 | struct net_device *ol_dev = ipip_entry->ol_dev; | |
1687 | ||
1688 | if (ol_dev->flags & IFF_UP) | |
1689 | mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry); | |
1690 | mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry); | |
1691 | } | |
1692 | ||
1693 | /* The configuration where several tunnels have the same local address in the | |
1694 | * same underlay table needs special treatment in the HW. That is currently not | |
1695 | * implemented in the driver. This function finds and demotes the first tunnel | |
1696 | * with a given source address, except the one passed in in the argument | |
1697 | * `except'. | |
1698 | */ | |
1699 | bool | |
1700 | mlxsw_sp_ipip_demote_tunnel_by_saddr(struct mlxsw_sp *mlxsw_sp, | |
1701 | enum mlxsw_sp_l3proto ul_proto, | |
1702 | union mlxsw_sp_l3addr saddr, | |
1703 | u32 ul_tb_id, | |
1704 | const struct mlxsw_sp_ipip_entry *except) | |
1705 | { | |
1706 | struct mlxsw_sp_ipip_entry *ipip_entry, *tmp; | |
1707 | ||
1708 | list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list, | |
1709 | ipip_list_node) { | |
1710 | if (ipip_entry != except && | |
1711 | mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr, | |
1712 | ul_tb_id, ipip_entry)) { | |
1713 | mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); | |
1714 | return true; | |
1715 | } | |
1716 | } | |
1717 | ||
1718 | return false; | |
1719 | } | |
1720 | ||
61481f2f PM |
1721 | static void mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(struct mlxsw_sp *mlxsw_sp, |
1722 | struct net_device *ul_dev) | |
1723 | { | |
1724 | struct mlxsw_sp_ipip_entry *ipip_entry, *tmp; | |
1725 | ||
1726 | list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list, | |
1727 | ipip_list_node) { | |
1728 | struct net_device *ipip_ul_dev = | |
1729 | __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev); | |
1730 | ||
1731 | if (ipip_ul_dev == ul_dev) | |
1732 | mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); | |
1733 | } | |
1734 | } | |
1735 | ||
7e75af63 PM |
1736 | int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp, |
1737 | struct net_device *ol_dev, | |
1738 | unsigned long event, | |
1739 | struct netdev_notifier_info *info) | |
0063587d | 1740 | { |
7e75af63 PM |
1741 | struct netdev_notifier_changeupper_info *chup; |
1742 | struct netlink_ext_ack *extack; | |
1743 | ||
0063587d PM |
1744 | switch (event) { |
1745 | case NETDEV_REGISTER: | |
796ec776 | 1746 | return mlxsw_sp_netdevice_ipip_ol_reg_event(mlxsw_sp, ol_dev); |
0063587d | 1747 | case NETDEV_UNREGISTER: |
796ec776 | 1748 | mlxsw_sp_netdevice_ipip_ol_unreg_event(mlxsw_sp, ol_dev); |
0063587d PM |
1749 | return 0; |
1750 | case NETDEV_UP: | |
6d4de445 PM |
1751 | mlxsw_sp_netdevice_ipip_ol_up_event(mlxsw_sp, ol_dev); |
1752 | return 0; | |
0063587d | 1753 | case NETDEV_DOWN: |
796ec776 | 1754 | mlxsw_sp_netdevice_ipip_ol_down_event(mlxsw_sp, ol_dev); |
0063587d | 1755 | return 0; |
f63ce4e5 | 1756 | case NETDEV_CHANGEUPPER: |
7e75af63 PM |
1757 | chup = container_of(info, typeof(*chup), info); |
1758 | extack = info->extack; | |
1759 | if (netif_is_l3_master(chup->upper_dev)) | |
796ec776 | 1760 | return mlxsw_sp_netdevice_ipip_ol_vrf_event(mlxsw_sp, |
7e75af63 PM |
1761 | ol_dev, |
1762 | extack); | |
f63ce4e5 | 1763 | return 0; |
4cf04f3f PM |
1764 | case NETDEV_CHANGE: |
1765 | extack = info->extack; | |
1766 | return mlxsw_sp_netdevice_ipip_ol_change_event(mlxsw_sp, | |
1767 | ol_dev, extack); | |
68c3cd92 PM |
1768 | case NETDEV_CHANGEMTU: |
1769 | return mlxsw_sp_netdevice_ipip_ol_update_mtu(mlxsw_sp, ol_dev); | |
0063587d PM |
1770 | } |
1771 | return 0; | |
1772 | } | |
1773 | ||
61481f2f PM |
1774 | static int |
1775 | __mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp, | |
1776 | struct mlxsw_sp_ipip_entry *ipip_entry, | |
1777 | struct net_device *ul_dev, | |
1778 | unsigned long event, | |
1779 | struct netdev_notifier_info *info) | |
1780 | { | |
1781 | struct netdev_notifier_changeupper_info *chup; | |
1782 | struct netlink_ext_ack *extack; | |
1783 | ||
1784 | switch (event) { | |
1785 | case NETDEV_CHANGEUPPER: | |
1786 | chup = container_of(info, typeof(*chup), info); | |
1787 | extack = info->extack; | |
1788 | if (netif_is_l3_master(chup->upper_dev)) | |
1789 | return mlxsw_sp_netdevice_ipip_ul_vrf_event(mlxsw_sp, | |
1790 | ipip_entry, | |
1791 | ul_dev, | |
1792 | extack); | |
1793 | break; | |
44b0fff1 PM |
1794 | |
1795 | case NETDEV_UP: | |
1796 | return mlxsw_sp_netdevice_ipip_ul_up_event(mlxsw_sp, ipip_entry, | |
1797 | ul_dev); | |
1798 | case NETDEV_DOWN: | |
1799 | return mlxsw_sp_netdevice_ipip_ul_down_event(mlxsw_sp, | |
1800 | ipip_entry, | |
1801 | ul_dev); | |
61481f2f PM |
1802 | } |
1803 | return 0; | |
1804 | } | |
1805 | ||
1806 | int | |
1807 | mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp, | |
1808 | struct net_device *ul_dev, | |
1809 | unsigned long event, | |
1810 | struct netdev_notifier_info *info) | |
1811 | { | |
1812 | struct mlxsw_sp_ipip_entry *ipip_entry = NULL; | |
1813 | int err; | |
1814 | ||
1815 | while ((ipip_entry = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, | |
1816 | ul_dev, | |
1817 | ipip_entry))) { | |
1818 | err = __mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, ipip_entry, | |
1819 | ul_dev, event, info); | |
1820 | if (err) { | |
1821 | mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(mlxsw_sp, | |
1822 | ul_dev); | |
1823 | return err; | |
1824 | } | |
1825 | } | |
1826 | ||
1827 | return 0; | |
1828 | } | |
1829 | ||
0c69e0fc IS |
1830 | int mlxsw_sp_router_nve_promote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id, |
1831 | enum mlxsw_sp_l3proto ul_proto, | |
1832 | const union mlxsw_sp_l3addr *ul_sip, | |
1833 | u32 tunnel_index) | |
1834 | { | |
1835 | enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; | |
1836 | struct mlxsw_sp_fib_entry *fib_entry; | |
1837 | int err; | |
1838 | ||
1839 | /* It is valid to create a tunnel with a local IP and only later | |
1840 | * assign this IP address to a local interface | |
1841 | */ | |
1842 | fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id, | |
1843 | ul_proto, ul_sip, | |
1844 | type); | |
1845 | if (!fib_entry) | |
1846 | return 0; | |
1847 | ||
1848 | fib_entry->decap.tunnel_index = tunnel_index; | |
1849 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP; | |
1850 | ||
1851 | err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); | |
1852 | if (err) | |
1853 | goto err_fib_entry_update; | |
1854 | ||
1855 | return 0; | |
1856 | ||
1857 | err_fib_entry_update: | |
1858 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; | |
1859 | mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); | |
1860 | return err; | |
1861 | } | |
1862 | ||
1863 | void mlxsw_sp_router_nve_demote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id, | |
1864 | enum mlxsw_sp_l3proto ul_proto, | |
1865 | const union mlxsw_sp_l3addr *ul_sip) | |
1866 | { | |
1867 | enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP; | |
1868 | struct mlxsw_sp_fib_entry *fib_entry; | |
1869 | ||
1870 | fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id, | |
1871 | ul_proto, ul_sip, | |
1872 | type); | |
1873 | if (!fib_entry) | |
1874 | return; | |
1875 | ||
1876 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; | |
1877 | mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); | |
1878 | } | |
1879 | ||
6cf3c971 | 1880 | struct mlxsw_sp_neigh_key { |
33b1341c | 1881 | struct neighbour *n; |
6cf3c971 JP |
1882 | }; |
1883 | ||
1884 | struct mlxsw_sp_neigh_entry { | |
9665b745 | 1885 | struct list_head rif_list_node; |
6cf3c971 JP |
1886 | struct rhash_head ht_node; |
1887 | struct mlxsw_sp_neigh_key key; | |
1888 | u16 rif; | |
5c8802f1 | 1889 | bool connected; |
a6bf9e93 | 1890 | unsigned char ha[ETH_ALEN]; |
a7ff87ac JP |
1891 | struct list_head nexthop_list; /* list of nexthops using |
1892 | * this neigh entry | |
1893 | */ | |
b2157149 | 1894 | struct list_head nexthop_neighs_list_node; |
7cfcbc75 AS |
1895 | unsigned int counter_index; |
1896 | bool counter_valid; | |
6cf3c971 JP |
1897 | }; |
1898 | ||
1899 | static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { | |
1900 | .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key), | |
1901 | .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node), | |
1902 | .key_len = sizeof(struct mlxsw_sp_neigh_key), | |
1903 | }; | |
1904 | ||
f17cc84d AS |
1905 | struct mlxsw_sp_neigh_entry * |
1906 | mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, | |
1907 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
1908 | { | |
1909 | if (!neigh_entry) { | |
1910 | if (list_empty(&rif->neigh_list)) | |
1911 | return NULL; | |
1912 | else | |
1913 | return list_first_entry(&rif->neigh_list, | |
1914 | typeof(*neigh_entry), | |
1915 | rif_list_node); | |
1916 | } | |
ec2437f4 | 1917 | if (list_is_last(&neigh_entry->rif_list_node, &rif->neigh_list)) |
f17cc84d AS |
1918 | return NULL; |
1919 | return list_next_entry(neigh_entry, rif_list_node); | |
1920 | } | |
1921 | ||
1922 | int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry) | |
1923 | { | |
1924 | return neigh_entry->key.n->tbl->family; | |
1925 | } | |
1926 | ||
1927 | unsigned char * | |
1928 | mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry) | |
1929 | { | |
1930 | return neigh_entry->ha; | |
1931 | } | |
1932 | ||
1933 | u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) | |
1934 | { | |
1935 | struct neighbour *n; | |
1936 | ||
1937 | n = neigh_entry->key.n; | |
1938 | return ntohl(*((__be32 *) n->primary_key)); | |
1939 | } | |
1940 | ||
0250768c AS |
1941 | struct in6_addr * |
1942 | mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) | |
1943 | { | |
1944 | struct neighbour *n; | |
1945 | ||
1946 | n = neigh_entry->key.n; | |
1947 | return (struct in6_addr *) &n->primary_key; | |
1948 | } | |
1949 | ||
7cfcbc75 AS |
1950 | int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, |
1951 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
1952 | u64 *p_counter) | |
1953 | { | |
1954 | if (!neigh_entry->counter_valid) | |
1955 | return -EINVAL; | |
1956 | ||
1957 | return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index, | |
1958 | p_counter, NULL); | |
1959 | } | |
1960 | ||
6cf3c971 | 1961 | static struct mlxsw_sp_neigh_entry * |
5c8802f1 IS |
1962 | mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, |
1963 | u16 rif) | |
6cf3c971 JP |
1964 | { |
1965 | struct mlxsw_sp_neigh_entry *neigh_entry; | |
1966 | ||
5c8802f1 | 1967 | neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL); |
6cf3c971 JP |
1968 | if (!neigh_entry) |
1969 | return NULL; | |
5c8802f1 | 1970 | |
33b1341c | 1971 | neigh_entry->key.n = n; |
6cf3c971 | 1972 | neigh_entry->rif = rif; |
a7ff87ac | 1973 | INIT_LIST_HEAD(&neigh_entry->nexthop_list); |
5c8802f1 | 1974 | |
6cf3c971 JP |
1975 | return neigh_entry; |
1976 | } | |
1977 | ||
5c8802f1 | 1978 | static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry) |
6cf3c971 JP |
1979 | { |
1980 | kfree(neigh_entry); | |
1981 | } | |
1982 | ||
5c8802f1 IS |
1983 | static int |
1984 | mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp, | |
1985 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
6cf3c971 | 1986 | { |
9011b677 | 1987 | return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht, |
5c8802f1 IS |
1988 | &neigh_entry->ht_node, |
1989 | mlxsw_sp_neigh_ht_params); | |
1990 | } | |
6cf3c971 | 1991 | |
5c8802f1 IS |
1992 | static void |
1993 | mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp, | |
1994 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
1995 | { | |
9011b677 | 1996 | rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht, |
5c8802f1 IS |
1997 | &neigh_entry->ht_node, |
1998 | mlxsw_sp_neigh_ht_params); | |
6cf3c971 JP |
1999 | } |
2000 | ||
7cfcbc75 | 2001 | static bool |
1ed5574c AS |
2002 | mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp, |
2003 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
7cfcbc75 AS |
2004 | { |
2005 | struct devlink *devlink; | |
1ed5574c AS |
2006 | const char *table_name; |
2007 | ||
2008 | switch (mlxsw_sp_neigh_entry_type(neigh_entry)) { | |
2009 | case AF_INET: | |
2010 | table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4; | |
2011 | break; | |
2012 | case AF_INET6: | |
2013 | table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6; | |
2014 | break; | |
2015 | default: | |
2016 | WARN_ON(1); | |
2017 | return false; | |
2018 | } | |
7cfcbc75 AS |
2019 | |
2020 | devlink = priv_to_devlink(mlxsw_sp->core); | |
1ed5574c | 2021 | return devlink_dpipe_table_counter_enabled(devlink, table_name); |
7cfcbc75 AS |
2022 | } |
2023 | ||
2024 | static void | |
2025 | mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp, | |
2026 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
2027 | { | |
1ed5574c | 2028 | if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry)) |
7cfcbc75 AS |
2029 | return; |
2030 | ||
2031 | if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index)) | |
2032 | return; | |
2033 | ||
2034 | neigh_entry->counter_valid = true; | |
2035 | } | |
2036 | ||
2037 | static void | |
2038 | mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp, | |
2039 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
2040 | { | |
2041 | if (!neigh_entry->counter_valid) | |
2042 | return; | |
2043 | mlxsw_sp_flow_counter_free(mlxsw_sp, | |
2044 | neigh_entry->counter_index); | |
2045 | neigh_entry->counter_valid = false; | |
2046 | } | |
2047 | ||
5c8802f1 IS |
2048 | static struct mlxsw_sp_neigh_entry * |
2049 | mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) | |
6cf3c971 | 2050 | { |
6cf3c971 | 2051 | struct mlxsw_sp_neigh_entry *neigh_entry; |
bf95233e | 2052 | struct mlxsw_sp_rif *rif; |
6cf3c971 JP |
2053 | int err; |
2054 | ||
bf95233e AS |
2055 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev); |
2056 | if (!rif) | |
5c8802f1 | 2057 | return ERR_PTR(-EINVAL); |
6cf3c971 | 2058 | |
bf95233e | 2059 | neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index); |
6cf3c971 | 2060 | if (!neigh_entry) |
5c8802f1 IS |
2061 | return ERR_PTR(-ENOMEM); |
2062 | ||
6cf3c971 JP |
2063 | err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry); |
2064 | if (err) | |
2065 | goto err_neigh_entry_insert; | |
5c8802f1 | 2066 | |
7cfcbc75 | 2067 | mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); |
bf95233e | 2068 | list_add(&neigh_entry->rif_list_node, &rif->neigh_list); |
9665b745 | 2069 | |
5c8802f1 | 2070 | return neigh_entry; |
6cf3c971 JP |
2071 | |
2072 | err_neigh_entry_insert: | |
5c8802f1 IS |
2073 | mlxsw_sp_neigh_entry_free(neigh_entry); |
2074 | return ERR_PTR(err); | |
6cf3c971 JP |
2075 | } |
2076 | ||
5c8802f1 IS |
2077 | static void |
2078 | mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp, | |
2079 | struct mlxsw_sp_neigh_entry *neigh_entry) | |
6cf3c971 | 2080 | { |
9665b745 | 2081 | list_del(&neigh_entry->rif_list_node); |
7cfcbc75 | 2082 | mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); |
5c8802f1 IS |
2083 | mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry); |
2084 | mlxsw_sp_neigh_entry_free(neigh_entry); | |
2085 | } | |
6cf3c971 | 2086 | |
5c8802f1 IS |
2087 | static struct mlxsw_sp_neigh_entry * |
2088 | mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) | |
2089 | { | |
2090 | struct mlxsw_sp_neigh_key key; | |
6cf3c971 | 2091 | |
5c8802f1 | 2092 | key.n = n; |
9011b677 | 2093 | return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht, |
5c8802f1 | 2094 | &key, mlxsw_sp_neigh_ht_params); |
6cf3c971 JP |
2095 | } |
2096 | ||
c723c735 YG |
2097 | static void |
2098 | mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) | |
2099 | { | |
a6c9b5d1 | 2100 | unsigned long interval; |
c723c735 | 2101 | |
b5f3e0d4 | 2102 | #if IS_ENABLED(CONFIG_IPV6) |
a6c9b5d1 AS |
2103 | interval = min_t(unsigned long, |
2104 | NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME), | |
2105 | NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME)); | |
b5f3e0d4 IS |
2106 | #else |
2107 | interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); | |
2108 | #endif | |
9011b677 | 2109 | mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval); |
c723c735 YG |
2110 | } |
2111 | ||
2112 | static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, | |
2113 | char *rauhtd_pl, | |
2114 | int ent_index) | |
2115 | { | |
2116 | struct net_device *dev; | |
2117 | struct neighbour *n; | |
2118 | __be32 dipn; | |
2119 | u32 dip; | |
2120 | u16 rif; | |
2121 | ||
2122 | mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip); | |
2123 | ||
5f9efffb | 2124 | if (!mlxsw_sp->router->rifs[rif]) { |
c723c735 YG |
2125 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n"); |
2126 | return; | |
2127 | } | |
2128 | ||
2129 | dipn = htonl(dip); | |
5f9efffb | 2130 | dev = mlxsw_sp->router->rifs[rif]->dev; |
c723c735 | 2131 | n = neigh_lookup(&arp_tbl, &dipn, dev); |
1ecdaea0 | 2132 | if (!n) |
c723c735 | 2133 | return; |
c723c735 YG |
2134 | |
2135 | netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip); | |
2136 | neigh_event_send(n, NULL); | |
2137 | neigh_release(n); | |
2138 | } | |
2139 | ||
df9a21f1 | 2140 | #if IS_ENABLED(CONFIG_IPV6) |
60f040ca AS |
2141 | static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, |
2142 | char *rauhtd_pl, | |
2143 | int rec_index) | |
2144 | { | |
2145 | struct net_device *dev; | |
2146 | struct neighbour *n; | |
2147 | struct in6_addr dip; | |
2148 | u16 rif; | |
2149 | ||
2150 | mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif, | |
2151 | (char *) &dip); | |
2152 | ||
2153 | if (!mlxsw_sp->router->rifs[rif]) { | |
2154 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n"); | |
2155 | return; | |
2156 | } | |
2157 | ||
2158 | dev = mlxsw_sp->router->rifs[rif]->dev; | |
2159 | n = neigh_lookup(&nd_tbl, &dip, dev); | |
1ecdaea0 | 2160 | if (!n) |
60f040ca | 2161 | return; |
60f040ca AS |
2162 | |
2163 | netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip); | |
2164 | neigh_event_send(n, NULL); | |
2165 | neigh_release(n); | |
2166 | } | |
b5f3e0d4 IS |
2167 | #else |
2168 | static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, | |
2169 | char *rauhtd_pl, | |
2170 | int rec_index) | |
2171 | { | |
2172 | } | |
2173 | #endif | |
60f040ca | 2174 | |
c723c735 YG |
2175 | static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, |
2176 | char *rauhtd_pl, | |
2177 | int rec_index) | |
2178 | { | |
2179 | u8 num_entries; | |
2180 | int i; | |
2181 | ||
2182 | num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl, | |
2183 | rec_index); | |
2184 | /* Hardware starts counting at 0, so add 1. */ | |
2185 | num_entries++; | |
2186 | ||
2187 | /* Each record consists of several neighbour entries. */ | |
2188 | for (i = 0; i < num_entries; i++) { | |
2189 | int ent_index; | |
2190 | ||
2191 | ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i; | |
2192 | mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl, | |
2193 | ent_index); | |
2194 | } | |
2195 | ||
2196 | } | |
2197 | ||
60f040ca AS |
2198 | static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp, |
2199 | char *rauhtd_pl, | |
2200 | int rec_index) | |
2201 | { | |
2202 | /* One record contains one entry. */ | |
2203 | mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl, | |
2204 | rec_index); | |
2205 | } | |
2206 | ||
c723c735 YG |
2207 | static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, |
2208 | char *rauhtd_pl, int rec_index) | |
2209 | { | |
2210 | switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) { | |
2211 | case MLXSW_REG_RAUHTD_TYPE_IPV4: | |
2212 | mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl, | |
2213 | rec_index); | |
2214 | break; | |
2215 | case MLXSW_REG_RAUHTD_TYPE_IPV6: | |
60f040ca AS |
2216 | mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl, |
2217 | rec_index); | |
c723c735 YG |
2218 | break; |
2219 | } | |
2220 | } | |
2221 | ||
42cdb338 AS |
2222 | static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl) |
2223 | { | |
2224 | u8 num_rec, last_rec_index, num_entries; | |
2225 | ||
2226 | num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl); | |
2227 | last_rec_index = num_rec - 1; | |
2228 | ||
2229 | if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM) | |
2230 | return false; | |
2231 | if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) == | |
2232 | MLXSW_REG_RAUHTD_TYPE_IPV6) | |
2233 | return true; | |
2234 | ||
2235 | num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl, | |
2236 | last_rec_index); | |
2237 | if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC) | |
2238 | return true; | |
2239 | return false; | |
2240 | } | |
2241 | ||
60f040ca AS |
2242 | static int |
2243 | __mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp, | |
2244 | char *rauhtd_pl, | |
2245 | enum mlxsw_reg_rauhtd_type type) | |
c723c735 | 2246 | { |
60f040ca AS |
2247 | int i, num_rec; |
2248 | int err; | |
c723c735 YG |
2249 | |
2250 | /* Make sure the neighbour's netdev isn't removed in the | |
2251 | * process. | |
2252 | */ | |
2253 | rtnl_lock(); | |
2254 | do { | |
60f040ca | 2255 | mlxsw_reg_rauhtd_pack(rauhtd_pl, type); |
c723c735 YG |
2256 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd), |
2257 | rauhtd_pl); | |
2258 | if (err) { | |
7ff176f8 | 2259 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour table\n"); |
c723c735 YG |
2260 | break; |
2261 | } | |
2262 | num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl); | |
2263 | for (i = 0; i < num_rec; i++) | |
2264 | mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl, | |
2265 | i); | |
42cdb338 | 2266 | } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl)); |
c723c735 YG |
2267 | rtnl_unlock(); |
2268 | ||
60f040ca AS |
2269 | return err; |
2270 | } | |
2271 | ||
2272 | static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) | |
2273 | { | |
2274 | enum mlxsw_reg_rauhtd_type type; | |
2275 | char *rauhtd_pl; | |
2276 | int err; | |
2277 | ||
2278 | rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); | |
2279 | if (!rauhtd_pl) | |
2280 | return -ENOMEM; | |
2281 | ||
2282 | type = MLXSW_REG_RAUHTD_TYPE_IPV4; | |
2283 | err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); | |
2284 | if (err) | |
2285 | goto out; | |
2286 | ||
2287 | type = MLXSW_REG_RAUHTD_TYPE_IPV6; | |
2288 | err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); | |
2289 | out: | |
c723c735 | 2290 | kfree(rauhtd_pl); |
b2157149 YG |
2291 | return err; |
2292 | } | |
2293 | ||
2294 | static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp) | |
2295 | { | |
2296 | struct mlxsw_sp_neigh_entry *neigh_entry; | |
2297 | ||
2298 | /* Take RTNL mutex here to prevent lists from changes */ | |
2299 | rtnl_lock(); | |
9011b677 | 2300 | list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list, |
8a0b7275 | 2301 | nexthop_neighs_list_node) |
b2157149 YG |
2302 | /* If this neigh have nexthops, make the kernel think this neigh |
2303 | * is active regardless of the traffic. | |
2304 | */ | |
8a0b7275 | 2305 | neigh_event_send(neigh_entry->key.n, NULL); |
b2157149 YG |
2306 | rtnl_unlock(); |
2307 | } | |
2308 | ||
2309 | static void | |
2310 | mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp) | |
2311 | { | |
9011b677 | 2312 | unsigned long interval = mlxsw_sp->router->neighs_update.interval; |
b2157149 | 2313 | |
9011b677 | 2314 | mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, |
b2157149 YG |
2315 | msecs_to_jiffies(interval)); |
2316 | } | |
2317 | ||
2318 | static void mlxsw_sp_router_neighs_update_work(struct work_struct *work) | |
2319 | { | |
9011b677 | 2320 | struct mlxsw_sp_router *router; |
b2157149 YG |
2321 | int err; |
2322 | ||
9011b677 IS |
2323 | router = container_of(work, struct mlxsw_sp_router, |
2324 | neighs_update.dw.work); | |
2325 | err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp); | |
b2157149 | 2326 | if (err) |
9011b677 | 2327 | dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity"); |
b2157149 | 2328 | |
9011b677 | 2329 | mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp); |
b2157149 | 2330 | |
9011b677 | 2331 | mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp); |
c723c735 YG |
2332 | } |
2333 | ||
0b2361d9 YG |
2334 | static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work) |
2335 | { | |
2336 | struct mlxsw_sp_neigh_entry *neigh_entry; | |
9011b677 | 2337 | struct mlxsw_sp_router *router; |
0b2361d9 | 2338 | |
9011b677 IS |
2339 | router = container_of(work, struct mlxsw_sp_router, |
2340 | nexthop_probe_dw.work); | |
0b2361d9 YG |
2341 | /* Iterate over nexthop neighbours, find those who are unresolved and |
2342 | * send arp on them. This solves the chicken-egg problem when | |
2343 | * the nexthop wouldn't get offloaded until the neighbor is resolved | |
2344 | * but it wouldn't get resolved ever in case traffic is flowing in HW | |
2345 | * using different nexthop. | |
2346 | * | |
2347 | * Take RTNL mutex here to prevent lists from changes. | |
2348 | */ | |
2349 | rtnl_lock(); | |
9011b677 | 2350 | list_for_each_entry(neigh_entry, &router->nexthop_neighs_list, |
8a0b7275 | 2351 | nexthop_neighs_list_node) |
01b1aa35 | 2352 | if (!neigh_entry->connected) |
33b1341c | 2353 | neigh_event_send(neigh_entry->key.n, NULL); |
0b2361d9 YG |
2354 | rtnl_unlock(); |
2355 | ||
9011b677 | 2356 | mlxsw_core_schedule_dw(&router->nexthop_probe_dw, |
0b2361d9 YG |
2357 | MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL); |
2358 | } | |
2359 | ||
a7ff87ac JP |
2360 | static void |
2361 | mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp, | |
2362 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
2363 | bool removing); | |
2364 | ||
5c8802f1 IS |
2365 | static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding) |
2366 | { | |
2367 | return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD : | |
2368 | MLXSW_REG_RAUHT_OP_WRITE_DELETE; | |
2369 | } | |
2370 | ||
2371 | static void | |
2372 | mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp, | |
2373 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
2374 | enum mlxsw_reg_rauht_op op) | |
a6bf9e93 | 2375 | { |
33b1341c | 2376 | struct neighbour *n = neigh_entry->key.n; |
5c8802f1 | 2377 | u32 dip = ntohl(*((__be32 *) n->primary_key)); |
a6bf9e93 | 2378 | char rauht_pl[MLXSW_REG_RAUHT_LEN]; |
5c8802f1 IS |
2379 | |
2380 | mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, | |
2381 | dip); | |
7cfcbc75 AS |
2382 | if (neigh_entry->counter_valid) |
2383 | mlxsw_reg_rauht_pack_counter(rauht_pl, | |
2384 | neigh_entry->counter_index); | |
5c8802f1 IS |
2385 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); |
2386 | } | |
2387 | ||
d5eb89cf AS |
2388 | static void |
2389 | mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp, | |
2390 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
2391 | enum mlxsw_reg_rauht_op op) | |
2392 | { | |
2393 | struct neighbour *n = neigh_entry->key.n; | |
2394 | char rauht_pl[MLXSW_REG_RAUHT_LEN]; | |
2395 | const char *dip = n->primary_key; | |
2396 | ||
2397 | mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, | |
2398 | dip); | |
7cfcbc75 AS |
2399 | if (neigh_entry->counter_valid) |
2400 | mlxsw_reg_rauht_pack_counter(rauht_pl, | |
2401 | neigh_entry->counter_index); | |
d5eb89cf AS |
2402 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); |
2403 | } | |
2404 | ||
1d1056d8 | 2405 | bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry) |
d5eb89cf | 2406 | { |
1d1056d8 AS |
2407 | struct neighbour *n = neigh_entry->key.n; |
2408 | ||
d5eb89cf AS |
2409 | /* Packets with a link-local destination address are trapped |
2410 | * after LPM lookup and never reach the neighbour table, so | |
2411 | * there is no need to program such neighbours to the device. | |
2412 | */ | |
2413 | if (ipv6_addr_type((struct in6_addr *) &n->primary_key) & | |
2414 | IPV6_ADDR_LINKLOCAL) | |
2415 | return true; | |
2416 | return false; | |
2417 | } | |
2418 | ||
5c8802f1 IS |
2419 | static void |
2420 | mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, | |
2421 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
2422 | bool adding) | |
2423 | { | |
2424 | if (!adding && !neigh_entry->connected) | |
2425 | return; | |
2426 | neigh_entry->connected = adding; | |
b5f3e0d4 | 2427 | if (neigh_entry->key.n->tbl->family == AF_INET) { |
5c8802f1 IS |
2428 | mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry, |
2429 | mlxsw_sp_rauht_op(adding)); | |
b5f3e0d4 | 2430 | } else if (neigh_entry->key.n->tbl->family == AF_INET6) { |
1d1056d8 | 2431 | if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) |
d5eb89cf AS |
2432 | return; |
2433 | mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry, | |
2434 | mlxsw_sp_rauht_op(adding)); | |
2435 | } else { | |
5c8802f1 | 2436 | WARN_ON_ONCE(1); |
d5eb89cf | 2437 | } |
5c8802f1 IS |
2438 | } |
2439 | ||
a481d713 AS |
2440 | void |
2441 | mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, | |
2442 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
2443 | bool adding) | |
2444 | { | |
2445 | if (adding) | |
2446 | mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); | |
2447 | else | |
2448 | mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); | |
2449 | mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true); | |
2450 | } | |
2451 | ||
ceb8881d | 2452 | struct mlxsw_sp_netevent_work { |
5c8802f1 IS |
2453 | struct work_struct work; |
2454 | struct mlxsw_sp *mlxsw_sp; | |
2455 | struct neighbour *n; | |
2456 | }; | |
2457 | ||
2458 | static void mlxsw_sp_router_neigh_event_work(struct work_struct *work) | |
2459 | { | |
ceb8881d IS |
2460 | struct mlxsw_sp_netevent_work *net_work = |
2461 | container_of(work, struct mlxsw_sp_netevent_work, work); | |
2462 | struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp; | |
5c8802f1 | 2463 | struct mlxsw_sp_neigh_entry *neigh_entry; |
ceb8881d | 2464 | struct neighbour *n = net_work->n; |
5c8802f1 | 2465 | unsigned char ha[ETH_ALEN]; |
a6bf9e93 | 2466 | bool entry_connected; |
93a87e5e | 2467 | u8 nud_state, dead; |
a6bf9e93 | 2468 | |
5c8802f1 IS |
2469 | /* If these parameters are changed after we release the lock, |
2470 | * then we are guaranteed to receive another event letting us | |
2471 | * know about it. | |
2472 | */ | |
a6bf9e93 | 2473 | read_lock_bh(&n->lock); |
5c8802f1 | 2474 | memcpy(ha, n->ha, ETH_ALEN); |
a6bf9e93 | 2475 | nud_state = n->nud_state; |
93a87e5e | 2476 | dead = n->dead; |
a6bf9e93 YG |
2477 | read_unlock_bh(&n->lock); |
2478 | ||
5c8802f1 | 2479 | rtnl_lock(); |
803335ac PM |
2480 | mlxsw_sp_span_respin(mlxsw_sp); |
2481 | ||
93a87e5e | 2482 | entry_connected = nud_state & NUD_VALID && !dead; |
5c8802f1 IS |
2483 | neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n); |
2484 | if (!entry_connected && !neigh_entry) | |
2485 | goto out; | |
2486 | if (!neigh_entry) { | |
2487 | neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n); | |
2488 | if (IS_ERR(neigh_entry)) | |
2489 | goto out; | |
a6bf9e93 YG |
2490 | } |
2491 | ||
5c8802f1 IS |
2492 | memcpy(neigh_entry->ha, ha, ETH_ALEN); |
2493 | mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected); | |
2494 | mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected); | |
2495 | ||
2496 | if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list)) | |
2497 | mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); | |
2498 | ||
2499 | out: | |
2500 | rtnl_unlock(); | |
a6bf9e93 | 2501 | neigh_release(n); |
ceb8881d | 2502 | kfree(net_work); |
a6bf9e93 YG |
2503 | } |
2504 | ||
28678f07 IS |
2505 | static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp); |
2506 | ||
2507 | static void mlxsw_sp_router_mp_hash_event_work(struct work_struct *work) | |
2508 | { | |
2509 | struct mlxsw_sp_netevent_work *net_work = | |
2510 | container_of(work, struct mlxsw_sp_netevent_work, work); | |
2511 | struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp; | |
2512 | ||
2513 | mlxsw_sp_mp_hash_init(mlxsw_sp); | |
2514 | kfree(net_work); | |
2515 | } | |
2516 | ||
64953423 PM |
2517 | static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp); |
2518 | ||
2519 | static void mlxsw_sp_router_update_priority_work(struct work_struct *work) | |
2520 | { | |
2521 | struct mlxsw_sp_netevent_work *net_work = | |
2522 | container_of(work, struct mlxsw_sp_netevent_work, work); | |
2523 | struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp; | |
2524 | ||
2525 | __mlxsw_sp_router_init(mlxsw_sp); | |
2526 | kfree(net_work); | |
2527 | } | |
2528 | ||
1f65a33f PM |
2529 | static int mlxsw_sp_router_schedule_work(struct net *net, |
2530 | struct notifier_block *nb, | |
2531 | void (*cb)(struct work_struct *)) | |
2532 | { | |
2533 | struct mlxsw_sp_netevent_work *net_work; | |
2534 | struct mlxsw_sp_router *router; | |
2535 | ||
2536 | if (!net_eq(net, &init_net)) | |
2537 | return NOTIFY_DONE; | |
2538 | ||
2539 | net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); | |
2540 | if (!net_work) | |
2541 | return NOTIFY_BAD; | |
2542 | ||
2543 | router = container_of(nb, struct mlxsw_sp_router, netevent_nb); | |
2544 | INIT_WORK(&net_work->work, cb); | |
2545 | net_work->mlxsw_sp = router->mlxsw_sp; | |
2546 | mlxsw_core_schedule_work(&net_work->work); | |
2547 | return NOTIFY_DONE; | |
2548 | } | |
2549 | ||
28678f07 | 2550 | static int mlxsw_sp_router_netevent_event(struct notifier_block *nb, |
48fac885 | 2551 | unsigned long event, void *ptr) |
c723c735 | 2552 | { |
ceb8881d | 2553 | struct mlxsw_sp_netevent_work *net_work; |
c723c735 YG |
2554 | struct mlxsw_sp_port *mlxsw_sp_port; |
2555 | struct mlxsw_sp *mlxsw_sp; | |
2556 | unsigned long interval; | |
2557 | struct neigh_parms *p; | |
a6bf9e93 | 2558 | struct neighbour *n; |
c723c735 YG |
2559 | |
2560 | switch (event) { | |
2561 | case NETEVENT_DELAY_PROBE_TIME_UPDATE: | |
2562 | p = ptr; | |
2563 | ||
2564 | /* We don't care about changes in the default table. */ | |
b5f3e0d4 IS |
2565 | if (!p->dev || (p->tbl->family != AF_INET && |
2566 | p->tbl->family != AF_INET6)) | |
c723c735 YG |
2567 | return NOTIFY_DONE; |
2568 | ||
2569 | /* We are in atomic context and can't take RTNL mutex, | |
2570 | * so use RCU variant to walk the device chain. | |
2571 | */ | |
2572 | mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev); | |
2573 | if (!mlxsw_sp_port) | |
2574 | return NOTIFY_DONE; | |
2575 | ||
2576 | mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
2577 | interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME)); | |
9011b677 | 2578 | mlxsw_sp->router->neighs_update.interval = interval; |
c723c735 YG |
2579 | |
2580 | mlxsw_sp_port_dev_put(mlxsw_sp_port); | |
2581 | break; | |
a6bf9e93 YG |
2582 | case NETEVENT_NEIGH_UPDATE: |
2583 | n = ptr; | |
a6bf9e93 | 2584 | |
b5f3e0d4 | 2585 | if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6) |
a6bf9e93 YG |
2586 | return NOTIFY_DONE; |
2587 | ||
5c8802f1 | 2588 | mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev); |
a6bf9e93 YG |
2589 | if (!mlxsw_sp_port) |
2590 | return NOTIFY_DONE; | |
2591 | ||
ceb8881d IS |
2592 | net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); |
2593 | if (!net_work) { | |
a6bf9e93 | 2594 | mlxsw_sp_port_dev_put(mlxsw_sp_port); |
5c8802f1 | 2595 | return NOTIFY_BAD; |
a6bf9e93 | 2596 | } |
5c8802f1 | 2597 | |
ceb8881d IS |
2598 | INIT_WORK(&net_work->work, mlxsw_sp_router_neigh_event_work); |
2599 | net_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
2600 | net_work->n = n; | |
a6bf9e93 YG |
2601 | |
2602 | /* Take a reference to ensure the neighbour won't be | |
2603 | * destructed until we drop the reference in delayed | |
2604 | * work. | |
2605 | */ | |
2606 | neigh_clone(n); | |
ceb8881d | 2607 | mlxsw_core_schedule_work(&net_work->work); |
5c8802f1 | 2608 | mlxsw_sp_port_dev_put(mlxsw_sp_port); |
a6bf9e93 | 2609 | break; |
3192dac6 | 2610 | case NETEVENT_IPV4_MPATH_HASH_UPDATE: |
5e18b9c5 | 2611 | case NETEVENT_IPV6_MPATH_HASH_UPDATE: |
1f65a33f PM |
2612 | return mlxsw_sp_router_schedule_work(ptr, nb, |
2613 | mlxsw_sp_router_mp_hash_event_work); | |
28678f07 | 2614 | |
64953423 PM |
2615 | case NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE: |
2616 | return mlxsw_sp_router_schedule_work(ptr, nb, | |
2617 | mlxsw_sp_router_update_priority_work); | |
c723c735 YG |
2618 | } |
2619 | ||
2620 | return NOTIFY_DONE; | |
2621 | } | |
2622 | ||
6cf3c971 JP |
2623 | static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp) |
2624 | { | |
c723c735 YG |
2625 | int err; |
2626 | ||
9011b677 | 2627 | err = rhashtable_init(&mlxsw_sp->router->neigh_ht, |
c723c735 YG |
2628 | &mlxsw_sp_neigh_ht_params); |
2629 | if (err) | |
2630 | return err; | |
2631 | ||
2632 | /* Initialize the polling interval according to the default | |
2633 | * table. | |
2634 | */ | |
2635 | mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp); | |
2636 | ||
0b2361d9 | 2637 | /* Create the delayed works for the activity_update */ |
9011b677 | 2638 | INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw, |
c723c735 | 2639 | mlxsw_sp_router_neighs_update_work); |
9011b677 | 2640 | INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw, |
0b2361d9 | 2641 | mlxsw_sp_router_probe_unresolved_nexthops); |
9011b677 IS |
2642 | mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0); |
2643 | mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0); | |
c723c735 | 2644 | return 0; |
6cf3c971 JP |
2645 | } |
2646 | ||
2647 | static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) | |
2648 | { | |
9011b677 IS |
2649 | cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw); |
2650 | cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw); | |
2651 | rhashtable_destroy(&mlxsw_sp->router->neigh_ht); | |
6cf3c971 JP |
2652 | } |
2653 | ||
9665b745 | 2654 | static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, |
bf95233e | 2655 | struct mlxsw_sp_rif *rif) |
9665b745 IS |
2656 | { |
2657 | struct mlxsw_sp_neigh_entry *neigh_entry, *tmp; | |
2658 | ||
bf95233e | 2659 | list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list, |
8ba6b30e PM |
2660 | rif_list_node) { |
2661 | mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false); | |
9665b745 | 2662 | mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); |
8ba6b30e | 2663 | } |
9665b745 IS |
2664 | } |
2665 | ||
35225e47 PM |
2666 | enum mlxsw_sp_nexthop_type { |
2667 | MLXSW_SP_NEXTHOP_TYPE_ETH, | |
1012b9ac | 2668 | MLXSW_SP_NEXTHOP_TYPE_IPIP, |
35225e47 PM |
2669 | }; |
2670 | ||
c53b8e1b IS |
2671 | struct mlxsw_sp_nexthop_key { |
2672 | struct fib_nh *fib_nh; | |
2673 | }; | |
2674 | ||
a7ff87ac JP |
2675 | struct mlxsw_sp_nexthop { |
2676 | struct list_head neigh_list_node; /* member of neigh entry list */ | |
9665b745 | 2677 | struct list_head rif_list_node; |
dbe4598c | 2678 | struct list_head router_list_node; |
a7ff87ac JP |
2679 | struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group |
2680 | * this belongs to | |
2681 | */ | |
c53b8e1b IS |
2682 | struct rhash_head ht_node; |
2683 | struct mlxsw_sp_nexthop_key key; | |
58adf2c4 | 2684 | unsigned char gw_addr[sizeof(struct in6_addr)]; |
e6f3b379 | 2685 | int ifindex; |
408bd946 | 2686 | int nh_weight; |
eb789980 IS |
2687 | int norm_nh_weight; |
2688 | int num_adj_entries; | |
bf95233e | 2689 | struct mlxsw_sp_rif *rif; |
a7ff87ac JP |
2690 | u8 should_offload:1, /* set indicates this neigh is connected and |
2691 | * should be put to KVD linear area of this group. | |
2692 | */ | |
2693 | offloaded:1, /* set in case the neigh is actually put into | |
2694 | * KVD linear area of this group. | |
2695 | */ | |
2696 | update:1; /* set indicates that MAC of this neigh should be | |
2697 | * updated in HW | |
2698 | */ | |
35225e47 PM |
2699 | enum mlxsw_sp_nexthop_type type; |
2700 | union { | |
2701 | struct mlxsw_sp_neigh_entry *neigh_entry; | |
1012b9ac | 2702 | struct mlxsw_sp_ipip_entry *ipip_entry; |
35225e47 | 2703 | }; |
a5390278 AS |
2704 | unsigned int counter_index; |
2705 | bool counter_valid; | |
a7ff87ac JP |
2706 | }; |
2707 | ||
2708 | struct mlxsw_sp_nexthop_group { | |
ba31d366 | 2709 | void *priv; |
e9ad5e7d | 2710 | struct rhash_head ht_node; |
a7ff87ac | 2711 | struct list_head fib_list; /* list of fib entries that use this group */ |
58adf2c4 | 2712 | struct neigh_table *neigh_tbl; |
b3e8d1eb IS |
2713 | u8 adj_index_valid:1, |
2714 | gateway:1; /* routes using the group use a gateway */ | |
a7ff87ac JP |
2715 | u32 adj_index; |
2716 | u16 ecmp_size; | |
2717 | u16 count; | |
eb789980 | 2718 | int sum_norm_weight; |
a7ff87ac | 2719 | struct mlxsw_sp_nexthop nexthops[0]; |
bf95233e | 2720 | #define nh_rif nexthops[0].rif |
a7ff87ac JP |
2721 | }; |
2722 | ||
427e652a AS |
2723 | void mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp, |
2724 | struct mlxsw_sp_nexthop *nh) | |
a5390278 AS |
2725 | { |
2726 | struct devlink *devlink; | |
2727 | ||
2728 | devlink = priv_to_devlink(mlxsw_sp->core); | |
2729 | if (!devlink_dpipe_table_counter_enabled(devlink, | |
2730 | MLXSW_SP_DPIPE_TABLE_NAME_ADJ)) | |
2731 | return; | |
2732 | ||
2733 | if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nh->counter_index)) | |
2734 | return; | |
2735 | ||
2736 | nh->counter_valid = true; | |
2737 | } | |
2738 | ||
427e652a AS |
2739 | void mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp, |
2740 | struct mlxsw_sp_nexthop *nh) | |
a5390278 AS |
2741 | { |
2742 | if (!nh->counter_valid) | |
2743 | return; | |
2744 | mlxsw_sp_flow_counter_free(mlxsw_sp, nh->counter_index); | |
2745 | nh->counter_valid = false; | |
2746 | } | |
2747 | ||
2748 | int mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp, | |
2749 | struct mlxsw_sp_nexthop *nh, u64 *p_counter) | |
2750 | { | |
2751 | if (!nh->counter_valid) | |
2752 | return -EINVAL; | |
2753 | ||
2754 | return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter_index, | |
2755 | p_counter, NULL); | |
2756 | } | |
2757 | ||
c556cd28 AS |
2758 | struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router, |
2759 | struct mlxsw_sp_nexthop *nh) | |
2760 | { | |
2761 | if (!nh) { | |
2762 | if (list_empty(&router->nexthop_list)) | |
2763 | return NULL; | |
2764 | else | |
2765 | return list_first_entry(&router->nexthop_list, | |
2766 | typeof(*nh), router_list_node); | |
2767 | } | |
2768 | if (list_is_last(&nh->router_list_node, &router->nexthop_list)) | |
2769 | return NULL; | |
2770 | return list_next_entry(nh, router_list_node); | |
2771 | } | |
2772 | ||
2773 | bool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh) | |
2774 | { | |
2775 | return nh->offloaded; | |
2776 | } | |
2777 | ||
2778 | unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh) | |
2779 | { | |
2780 | if (!nh->offloaded) | |
2781 | return NULL; | |
2782 | return nh->neigh_entry->ha; | |
2783 | } | |
2784 | ||
2785 | int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index, | |
e69cd9d7 | 2786 | u32 *p_adj_size, u32 *p_adj_hash_index) |
c556cd28 AS |
2787 | { |
2788 | struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp; | |
2789 | u32 adj_hash_index = 0; | |
2790 | int i; | |
2791 | ||
2792 | if (!nh->offloaded || !nh_grp->adj_index_valid) | |
2793 | return -EINVAL; | |
2794 | ||
2795 | *p_adj_index = nh_grp->adj_index; | |
e69cd9d7 | 2796 | *p_adj_size = nh_grp->ecmp_size; |
c556cd28 AS |
2797 | |
2798 | for (i = 0; i < nh_grp->count; i++) { | |
2799 | struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i]; | |
2800 | ||
2801 | if (nh_iter == nh) | |
2802 | break; | |
2803 | if (nh_iter->offloaded) | |
eb789980 | 2804 | adj_hash_index += nh_iter->num_adj_entries; |
c556cd28 AS |
2805 | } |
2806 | ||
2807 | *p_adj_hash_index = adj_hash_index; | |
2808 | return 0; | |
2809 | } | |
2810 | ||
2811 | struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh) | |
2812 | { | |
2813 | return nh->rif; | |
2814 | } | |
2815 | ||
2816 | bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh) | |
2817 | { | |
2818 | struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp; | |
2819 | int i; | |
2820 | ||
2821 | for (i = 0; i < nh_grp->count; i++) { | |
2822 | struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i]; | |
2823 | ||
2824 | if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP) | |
2825 | return true; | |
2826 | } | |
2827 | return false; | |
2828 | } | |
2829 | ||
ba31d366 AS |
2830 | static struct fib_info * |
2831 | mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp) | |
2832 | { | |
2833 | return nh_grp->priv; | |
2834 | } | |
2835 | ||
2836 | struct mlxsw_sp_nexthop_group_cmp_arg { | |
e6f3b379 AS |
2837 | enum mlxsw_sp_l3proto proto; |
2838 | union { | |
2839 | struct fib_info *fi; | |
2840 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
2841 | }; | |
ba31d366 AS |
2842 | }; |
2843 | ||
e6f3b379 AS |
2844 | static bool |
2845 | mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp, | |
3743d88a IS |
2846 | const struct in6_addr *gw, int ifindex, |
2847 | int weight) | |
e6f3b379 AS |
2848 | { |
2849 | int i; | |
2850 | ||
2851 | for (i = 0; i < nh_grp->count; i++) { | |
2852 | const struct mlxsw_sp_nexthop *nh; | |
2853 | ||
2854 | nh = &nh_grp->nexthops[i]; | |
3743d88a | 2855 | if (nh->ifindex == ifindex && nh->nh_weight == weight && |
e6f3b379 AS |
2856 | ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr)) |
2857 | return true; | |
2858 | } | |
2859 | ||
2860 | return false; | |
2861 | } | |
2862 | ||
2863 | static bool | |
2864 | mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp, | |
2865 | const struct mlxsw_sp_fib6_entry *fib6_entry) | |
2866 | { | |
2867 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
2868 | ||
2869 | if (nh_grp->count != fib6_entry->nrt6) | |
2870 | return false; | |
2871 | ||
2872 | list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { | |
2873 | struct in6_addr *gw; | |
3743d88a | 2874 | int ifindex, weight; |
e6f3b379 | 2875 | |
5e670d84 DA |
2876 | ifindex = mlxsw_sp_rt6->rt->fib6_nh.nh_dev->ifindex; |
2877 | weight = mlxsw_sp_rt6->rt->fib6_nh.nh_weight; | |
2878 | gw = &mlxsw_sp_rt6->rt->fib6_nh.nh_gw; | |
3743d88a IS |
2879 | if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex, |
2880 | weight)) | |
e6f3b379 AS |
2881 | return false; |
2882 | } | |
2883 | ||
2884 | return true; | |
2885 | } | |
2886 | ||
ba31d366 AS |
2887 | static int |
2888 | mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr) | |
2889 | { | |
2890 | const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key; | |
2891 | const struct mlxsw_sp_nexthop_group *nh_grp = ptr; | |
2892 | ||
e6f3b379 AS |
2893 | switch (cmp_arg->proto) { |
2894 | case MLXSW_SP_L3_PROTO_IPV4: | |
2895 | return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp); | |
2896 | case MLXSW_SP_L3_PROTO_IPV6: | |
2897 | return !mlxsw_sp_nexthop6_group_cmp(nh_grp, | |
2898 | cmp_arg->fib6_entry); | |
2899 | default: | |
2900 | WARN_ON(1); | |
2901 | return 1; | |
2902 | } | |
2903 | } | |
2904 | ||
2905 | static int | |
2906 | mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp) | |
2907 | { | |
2908 | return nh_grp->neigh_tbl->family; | |
ba31d366 AS |
2909 | } |
2910 | ||
2911 | static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed) | |
2912 | { | |
2913 | const struct mlxsw_sp_nexthop_group *nh_grp = data; | |
e6f3b379 AS |
2914 | const struct mlxsw_sp_nexthop *nh; |
2915 | struct fib_info *fi; | |
2916 | unsigned int val; | |
2917 | int i; | |
ba31d366 | 2918 | |
e6f3b379 AS |
2919 | switch (mlxsw_sp_nexthop_group_type(nh_grp)) { |
2920 | case AF_INET: | |
2921 | fi = mlxsw_sp_nexthop4_group_fi(nh_grp); | |
2922 | return jhash(&fi, sizeof(fi), seed); | |
2923 | case AF_INET6: | |
2924 | val = nh_grp->count; | |
2925 | for (i = 0; i < nh_grp->count; i++) { | |
2926 | nh = &nh_grp->nexthops[i]; | |
2927 | val ^= nh->ifindex; | |
2928 | } | |
2929 | return jhash(&val, sizeof(val), seed); | |
2930 | default: | |
2931 | WARN_ON(1); | |
2932 | return 0; | |
2933 | } | |
2934 | } | |
2935 | ||
2936 | static u32 | |
2937 | mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed) | |
2938 | { | |
2939 | unsigned int val = fib6_entry->nrt6; | |
2940 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
2941 | struct net_device *dev; | |
2942 | ||
2943 | list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { | |
5e670d84 | 2944 | dev = mlxsw_sp_rt6->rt->fib6_nh.nh_dev; |
e6f3b379 AS |
2945 | val ^= dev->ifindex; |
2946 | } | |
2947 | ||
2948 | return jhash(&val, sizeof(val), seed); | |
ba31d366 AS |
2949 | } |
2950 | ||
2951 | static u32 | |
2952 | mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed) | |
2953 | { | |
2954 | const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data; | |
2955 | ||
e6f3b379 AS |
2956 | switch (cmp_arg->proto) { |
2957 | case MLXSW_SP_L3_PROTO_IPV4: | |
2958 | return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed); | |
2959 | case MLXSW_SP_L3_PROTO_IPV6: | |
2960 | return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed); | |
2961 | default: | |
2962 | WARN_ON(1); | |
2963 | return 0; | |
2964 | } | |
ba31d366 AS |
2965 | } |
2966 | ||
e9ad5e7d | 2967 | static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { |
e9ad5e7d | 2968 | .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node), |
ba31d366 AS |
2969 | .hashfn = mlxsw_sp_nexthop_group_hash, |
2970 | .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj, | |
2971 | .obj_cmpfn = mlxsw_sp_nexthop_group_cmp, | |
e9ad5e7d IS |
2972 | }; |
2973 | ||
2974 | static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, | |
2975 | struct mlxsw_sp_nexthop_group *nh_grp) | |
2976 | { | |
e6f3b379 AS |
2977 | if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && |
2978 | !nh_grp->gateway) | |
2979 | return 0; | |
2980 | ||
9011b677 | 2981 | return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht, |
e9ad5e7d IS |
2982 | &nh_grp->ht_node, |
2983 | mlxsw_sp_nexthop_group_ht_params); | |
2984 | } | |
2985 | ||
2986 | static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp, | |
2987 | struct mlxsw_sp_nexthop_group *nh_grp) | |
2988 | { | |
e6f3b379 AS |
2989 | if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && |
2990 | !nh_grp->gateway) | |
2991 | return; | |
2992 | ||
9011b677 | 2993 | rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht, |
e9ad5e7d IS |
2994 | &nh_grp->ht_node, |
2995 | mlxsw_sp_nexthop_group_ht_params); | |
2996 | } | |
2997 | ||
2998 | static struct mlxsw_sp_nexthop_group * | |
ba31d366 AS |
2999 | mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp, |
3000 | struct fib_info *fi) | |
e9ad5e7d | 3001 | { |
ba31d366 AS |
3002 | struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; |
3003 | ||
e6f3b379 | 3004 | cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4; |
ba31d366 AS |
3005 | cmp_arg.fi = fi; |
3006 | return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, | |
3007 | &cmp_arg, | |
e9ad5e7d IS |
3008 | mlxsw_sp_nexthop_group_ht_params); |
3009 | } | |
3010 | ||
e6f3b379 AS |
3011 | static struct mlxsw_sp_nexthop_group * |
3012 | mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp, | |
3013 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
3014 | { | |
3015 | struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; | |
3016 | ||
3017 | cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6; | |
3018 | cmp_arg.fib6_entry = fib6_entry; | |
3019 | return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, | |
3020 | &cmp_arg, | |
3021 | mlxsw_sp_nexthop_group_ht_params); | |
3022 | } | |
3023 | ||
c53b8e1b IS |
3024 | static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = { |
3025 | .key_offset = offsetof(struct mlxsw_sp_nexthop, key), | |
3026 | .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node), | |
3027 | .key_len = sizeof(struct mlxsw_sp_nexthop_key), | |
3028 | }; | |
3029 | ||
3030 | static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp, | |
3031 | struct mlxsw_sp_nexthop *nh) | |
3032 | { | |
9011b677 | 3033 | return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht, |
c53b8e1b IS |
3034 | &nh->ht_node, mlxsw_sp_nexthop_ht_params); |
3035 | } | |
3036 | ||
3037 | static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp, | |
3038 | struct mlxsw_sp_nexthop *nh) | |
3039 | { | |
9011b677 | 3040 | rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node, |
c53b8e1b IS |
3041 | mlxsw_sp_nexthop_ht_params); |
3042 | } | |
3043 | ||
ad178c8e IS |
3044 | static struct mlxsw_sp_nexthop * |
3045 | mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp, | |
3046 | struct mlxsw_sp_nexthop_key key) | |
3047 | { | |
9011b677 | 3048 | return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key, |
ad178c8e IS |
3049 | mlxsw_sp_nexthop_ht_params); |
3050 | } | |
3051 | ||
a7ff87ac | 3052 | static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp, |
76610ebb | 3053 | const struct mlxsw_sp_fib *fib, |
a7ff87ac JP |
3054 | u32 adj_index, u16 ecmp_size, |
3055 | u32 new_adj_index, | |
3056 | u16 new_ecmp_size) | |
3057 | { | |
3058 | char raleu_pl[MLXSW_REG_RALEU_LEN]; | |
3059 | ||
1a9234e6 | 3060 | mlxsw_reg_raleu_pack(raleu_pl, |
76610ebb IS |
3061 | (enum mlxsw_reg_ralxx_protocol) fib->proto, |
3062 | fib->vr->id, adj_index, ecmp_size, new_adj_index, | |
1a9234e6 | 3063 | new_ecmp_size); |
a7ff87ac JP |
3064 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl); |
3065 | } | |
3066 | ||
3067 | static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp, | |
3068 | struct mlxsw_sp_nexthop_group *nh_grp, | |
3069 | u32 old_adj_index, u16 old_ecmp_size) | |
3070 | { | |
3071 | struct mlxsw_sp_fib_entry *fib_entry; | |
76610ebb | 3072 | struct mlxsw_sp_fib *fib = NULL; |
a7ff87ac JP |
3073 | int err; |
3074 | ||
3075 | list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { | |
76610ebb | 3076 | if (fib == fib_entry->fib_node->fib) |
a7ff87ac | 3077 | continue; |
76610ebb IS |
3078 | fib = fib_entry->fib_node->fib; |
3079 | err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib, | |
a7ff87ac JP |
3080 | old_adj_index, |
3081 | old_ecmp_size, | |
3082 | nh_grp->adj_index, | |
3083 | nh_grp->ecmp_size); | |
3084 | if (err) | |
3085 | return err; | |
3086 | } | |
3087 | return 0; | |
3088 | } | |
3089 | ||
eb789980 IS |
3090 | static int __mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, |
3091 | struct mlxsw_sp_nexthop *nh) | |
a7ff87ac JP |
3092 | { |
3093 | struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry; | |
3094 | char ratr_pl[MLXSW_REG_RATR_LEN]; | |
3095 | ||
3096 | mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, | |
89e41982 PM |
3097 | true, MLXSW_REG_RATR_TYPE_ETHERNET, |
3098 | adj_index, neigh_entry->rif); | |
a7ff87ac | 3099 | mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha); |
a5390278 AS |
3100 | if (nh->counter_valid) |
3101 | mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter_index, true); | |
3102 | else | |
3103 | mlxsw_reg_ratr_counter_pack(ratr_pl, 0, false); | |
3104 | ||
a7ff87ac JP |
3105 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); |
3106 | } | |
3107 | ||
eb789980 IS |
3108 | int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, |
3109 | struct mlxsw_sp_nexthop *nh) | |
3110 | { | |
3111 | int i; | |
3112 | ||
3113 | for (i = 0; i < nh->num_adj_entries; i++) { | |
3114 | int err; | |
3115 | ||
3116 | err = __mlxsw_sp_nexthop_update(mlxsw_sp, adj_index + i, nh); | |
3117 | if (err) | |
3118 | return err; | |
3119 | } | |
3120 | ||
3121 | return 0; | |
3122 | } | |
3123 | ||
3124 | static int __mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp, | |
3125 | u32 adj_index, | |
3126 | struct mlxsw_sp_nexthop *nh) | |
1012b9ac PM |
3127 | { |
3128 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
3129 | ||
3130 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt]; | |
3131 | return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry); | |
3132 | } | |
3133 | ||
eb789980 IS |
3134 | static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp, |
3135 | u32 adj_index, | |
3136 | struct mlxsw_sp_nexthop *nh) | |
3137 | { | |
3138 | int i; | |
3139 | ||
3140 | for (i = 0; i < nh->num_adj_entries; i++) { | |
3141 | int err; | |
3142 | ||
3143 | err = __mlxsw_sp_nexthop_ipip_update(mlxsw_sp, adj_index + i, | |
3144 | nh); | |
3145 | if (err) | |
3146 | return err; | |
3147 | } | |
3148 | ||
3149 | return 0; | |
3150 | } | |
3151 | ||
a7ff87ac | 3152 | static int |
35225e47 PM |
3153 | mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, |
3154 | struct mlxsw_sp_nexthop_group *nh_grp, | |
3155 | bool reallocate) | |
a7ff87ac JP |
3156 | { |
3157 | u32 adj_index = nh_grp->adj_index; /* base */ | |
3158 | struct mlxsw_sp_nexthop *nh; | |
3159 | int i; | |
3160 | int err; | |
3161 | ||
3162 | for (i = 0; i < nh_grp->count; i++) { | |
3163 | nh = &nh_grp->nexthops[i]; | |
3164 | ||
3165 | if (!nh->should_offload) { | |
3166 | nh->offloaded = 0; | |
3167 | continue; | |
3168 | } | |
3169 | ||
a59b7e02 | 3170 | if (nh->update || reallocate) { |
35225e47 PM |
3171 | switch (nh->type) { |
3172 | case MLXSW_SP_NEXTHOP_TYPE_ETH: | |
a5390278 | 3173 | err = mlxsw_sp_nexthop_update |
35225e47 PM |
3174 | (mlxsw_sp, adj_index, nh); |
3175 | break; | |
1012b9ac PM |
3176 | case MLXSW_SP_NEXTHOP_TYPE_IPIP: |
3177 | err = mlxsw_sp_nexthop_ipip_update | |
3178 | (mlxsw_sp, adj_index, nh); | |
3179 | break; | |
35225e47 | 3180 | } |
a7ff87ac JP |
3181 | if (err) |
3182 | return err; | |
3183 | nh->update = 0; | |
3184 | nh->offloaded = 1; | |
3185 | } | |
eb789980 | 3186 | adj_index += nh->num_adj_entries; |
a7ff87ac JP |
3187 | } |
3188 | return 0; | |
3189 | } | |
3190 | ||
1819ae3d IS |
3191 | static bool |
3192 | mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, | |
3193 | const struct mlxsw_sp_fib_entry *fib_entry); | |
3194 | ||
a7ff87ac JP |
3195 | static int |
3196 | mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, | |
3197 | struct mlxsw_sp_nexthop_group *nh_grp) | |
3198 | { | |
3199 | struct mlxsw_sp_fib_entry *fib_entry; | |
3200 | int err; | |
3201 | ||
3202 | list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { | |
1819ae3d IS |
3203 | if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, |
3204 | fib_entry)) | |
3205 | continue; | |
a7ff87ac JP |
3206 | err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); |
3207 | if (err) | |
3208 | return err; | |
3209 | } | |
3210 | return 0; | |
3211 | } | |
3212 | ||
77d964e6 IS |
3213 | static void |
3214 | mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, | |
3215 | enum mlxsw_reg_ralue_op op, int err); | |
3216 | ||
3217 | static void | |
3218 | mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp) | |
3219 | { | |
3220 | enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE; | |
3221 | struct mlxsw_sp_fib_entry *fib_entry; | |
3222 | ||
3223 | list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { | |
3224 | if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, | |
3225 | fib_entry)) | |
3226 | continue; | |
3227 | mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); | |
3228 | } | |
3229 | } | |
3230 | ||
425a08c6 IS |
3231 | static void mlxsw_sp_adj_grp_size_round_up(u16 *p_adj_grp_size) |
3232 | { | |
3233 | /* Valid sizes for an adjacency group are: | |
3234 | * 1-64, 512, 1024, 2048 and 4096. | |
3235 | */ | |
3236 | if (*p_adj_grp_size <= 64) | |
3237 | return; | |
3238 | else if (*p_adj_grp_size <= 512) | |
3239 | *p_adj_grp_size = 512; | |
3240 | else if (*p_adj_grp_size <= 1024) | |
3241 | *p_adj_grp_size = 1024; | |
3242 | else if (*p_adj_grp_size <= 2048) | |
3243 | *p_adj_grp_size = 2048; | |
3244 | else | |
3245 | *p_adj_grp_size = 4096; | |
3246 | } | |
3247 | ||
3248 | static void mlxsw_sp_adj_grp_size_round_down(u16 *p_adj_grp_size, | |
3249 | unsigned int alloc_size) | |
3250 | { | |
3251 | if (alloc_size >= 4096) | |
3252 | *p_adj_grp_size = 4096; | |
3253 | else if (alloc_size >= 2048) | |
3254 | *p_adj_grp_size = 2048; | |
3255 | else if (alloc_size >= 1024) | |
3256 | *p_adj_grp_size = 1024; | |
3257 | else if (alloc_size >= 512) | |
3258 | *p_adj_grp_size = 512; | |
3259 | } | |
3260 | ||
3261 | static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp, | |
3262 | u16 *p_adj_grp_size) | |
3263 | { | |
3264 | unsigned int alloc_size; | |
3265 | int err; | |
3266 | ||
3267 | /* Round up the requested group size to the next size supported | |
3268 | * by the device and make sure the request can be satisfied. | |
3269 | */ | |
3270 | mlxsw_sp_adj_grp_size_round_up(p_adj_grp_size); | |
4b6b1869 JP |
3271 | err = mlxsw_sp_kvdl_alloc_count_query(mlxsw_sp, |
3272 | MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, | |
3273 | *p_adj_grp_size, &alloc_size); | |
425a08c6 IS |
3274 | if (err) |
3275 | return err; | |
3276 | /* It is possible the allocation results in more allocated | |
3277 | * entries than requested. Try to use as much of them as | |
3278 | * possible. | |
3279 | */ | |
3280 | mlxsw_sp_adj_grp_size_round_down(p_adj_grp_size, alloc_size); | |
3281 | ||
3282 | return 0; | |
3283 | } | |
3284 | ||
eb789980 IS |
3285 | static void |
3286 | mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group *nh_grp) | |
3287 | { | |
3288 | int i, g = 0, sum_norm_weight = 0; | |
3289 | struct mlxsw_sp_nexthop *nh; | |
3290 | ||
3291 | for (i = 0; i < nh_grp->count; i++) { | |
3292 | nh = &nh_grp->nexthops[i]; | |
3293 | ||
3294 | if (!nh->should_offload) | |
3295 | continue; | |
3296 | if (g > 0) | |
3297 | g = gcd(nh->nh_weight, g); | |
3298 | else | |
3299 | g = nh->nh_weight; | |
3300 | } | |
3301 | ||
3302 | for (i = 0; i < nh_grp->count; i++) { | |
3303 | nh = &nh_grp->nexthops[i]; | |
3304 | ||
3305 | if (!nh->should_offload) | |
3306 | continue; | |
3307 | nh->norm_nh_weight = nh->nh_weight / g; | |
3308 | sum_norm_weight += nh->norm_nh_weight; | |
3309 | } | |
3310 | ||
3311 | nh_grp->sum_norm_weight = sum_norm_weight; | |
3312 | } | |
3313 | ||
3314 | static void | |
3315 | mlxsw_sp_nexthop_group_rebalance(struct mlxsw_sp_nexthop_group *nh_grp) | |
3316 | { | |
3317 | int total = nh_grp->sum_norm_weight; | |
3318 | u16 ecmp_size = nh_grp->ecmp_size; | |
3319 | int i, weight = 0, lower_bound = 0; | |
3320 | ||
3321 | for (i = 0; i < nh_grp->count; i++) { | |
3322 | struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; | |
3323 | int upper_bound; | |
3324 | ||
3325 | if (!nh->should_offload) | |
3326 | continue; | |
3327 | weight += nh->norm_nh_weight; | |
3328 | upper_bound = DIV_ROUND_CLOSEST(ecmp_size * weight, total); | |
3329 | nh->num_adj_entries = upper_bound - lower_bound; | |
3330 | lower_bound = upper_bound; | |
3331 | } | |
3332 | } | |
3333 | ||
a7ff87ac JP |
3334 | static void |
3335 | mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, | |
3336 | struct mlxsw_sp_nexthop_group *nh_grp) | |
3337 | { | |
eb789980 | 3338 | u16 ecmp_size, old_ecmp_size; |
a7ff87ac JP |
3339 | struct mlxsw_sp_nexthop *nh; |
3340 | bool offload_change = false; | |
3341 | u32 adj_index; | |
a7ff87ac JP |
3342 | bool old_adj_index_valid; |
3343 | u32 old_adj_index; | |
a7ff87ac JP |
3344 | int i; |
3345 | int err; | |
3346 | ||
b3e8d1eb IS |
3347 | if (!nh_grp->gateway) { |
3348 | mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp); | |
3349 | return; | |
3350 | } | |
3351 | ||
a7ff87ac JP |
3352 | for (i = 0; i < nh_grp->count; i++) { |
3353 | nh = &nh_grp->nexthops[i]; | |
3354 | ||
56b8a9ed | 3355 | if (nh->should_offload != nh->offloaded) { |
a7ff87ac JP |
3356 | offload_change = true; |
3357 | if (nh->should_offload) | |
3358 | nh->update = 1; | |
3359 | } | |
a7ff87ac JP |
3360 | } |
3361 | if (!offload_change) { | |
3362 | /* Nothing was added or removed, so no need to reallocate. Just | |
3363 | * update MAC on existing adjacency indexes. | |
3364 | */ | |
35225e47 | 3365 | err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false); |
a7ff87ac JP |
3366 | if (err) { |
3367 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); | |
3368 | goto set_trap; | |
3369 | } | |
3370 | return; | |
3371 | } | |
eb789980 IS |
3372 | mlxsw_sp_nexthop_group_normalize(nh_grp); |
3373 | if (!nh_grp->sum_norm_weight) | |
a7ff87ac JP |
3374 | /* No neigh of this group is connected so we just set |
3375 | * the trap and let everthing flow through kernel. | |
3376 | */ | |
3377 | goto set_trap; | |
3378 | ||
eb789980 | 3379 | ecmp_size = nh_grp->sum_norm_weight; |
425a08c6 IS |
3380 | err = mlxsw_sp_fix_adj_grp_size(mlxsw_sp, &ecmp_size); |
3381 | if (err) | |
3382 | /* No valid allocation size available. */ | |
3383 | goto set_trap; | |
3384 | ||
4b6b1869 JP |
3385 | err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, |
3386 | ecmp_size, &adj_index); | |
13124443 | 3387 | if (err) { |
a7ff87ac JP |
3388 | /* We ran out of KVD linear space, just set the |
3389 | * trap and let everything flow through kernel. | |
3390 | */ | |
3391 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n"); | |
3392 | goto set_trap; | |
3393 | } | |
a7ff87ac JP |
3394 | old_adj_index_valid = nh_grp->adj_index_valid; |
3395 | old_adj_index = nh_grp->adj_index; | |
3396 | old_ecmp_size = nh_grp->ecmp_size; | |
3397 | nh_grp->adj_index_valid = 1; | |
3398 | nh_grp->adj_index = adj_index; | |
3399 | nh_grp->ecmp_size = ecmp_size; | |
eb789980 | 3400 | mlxsw_sp_nexthop_group_rebalance(nh_grp); |
35225e47 | 3401 | err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true); |
a7ff87ac JP |
3402 | if (err) { |
3403 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); | |
3404 | goto set_trap; | |
3405 | } | |
3406 | ||
3407 | if (!old_adj_index_valid) { | |
3408 | /* The trap was set for fib entries, so we have to call | |
3409 | * fib entry update to unset it and use adjacency index. | |
3410 | */ | |
3411 | err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp); | |
3412 | if (err) { | |
3413 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n"); | |
3414 | goto set_trap; | |
3415 | } | |
3416 | return; | |
3417 | } | |
3418 | ||
3419 | err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp, | |
3420 | old_adj_index, old_ecmp_size); | |
4b6b1869 | 3421 | mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, |
0304c005 | 3422 | old_ecmp_size, old_adj_index); |
a7ff87ac JP |
3423 | if (err) { |
3424 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n"); | |
3425 | goto set_trap; | |
3426 | } | |
77d964e6 IS |
3427 | |
3428 | /* Offload state within the group changed, so update the flags. */ | |
3429 | mlxsw_sp_nexthop_fib_entries_refresh(nh_grp); | |
3430 | ||
a7ff87ac JP |
3431 | return; |
3432 | ||
3433 | set_trap: | |
3434 | old_adj_index_valid = nh_grp->adj_index_valid; | |
3435 | nh_grp->adj_index_valid = 0; | |
3436 | for (i = 0; i < nh_grp->count; i++) { | |
3437 | nh = &nh_grp->nexthops[i]; | |
3438 | nh->offloaded = 0; | |
3439 | } | |
3440 | err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp); | |
3441 | if (err) | |
3442 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n"); | |
3443 | if (old_adj_index_valid) | |
4b6b1869 | 3444 | mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, |
0304c005 | 3445 | nh_grp->ecmp_size, nh_grp->adj_index); |
a7ff87ac JP |
3446 | } |
3447 | ||
3448 | static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh, | |
3449 | bool removing) | |
3450 | { | |
213666a3 | 3451 | if (!removing) |
a7ff87ac | 3452 | nh->should_offload = 1; |
8764a826 | 3453 | else |
a7ff87ac JP |
3454 | nh->should_offload = 0; |
3455 | nh->update = 1; | |
3456 | } | |
3457 | ||
3458 | static void | |
3459 | mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp, | |
3460 | struct mlxsw_sp_neigh_entry *neigh_entry, | |
3461 | bool removing) | |
3462 | { | |
3463 | struct mlxsw_sp_nexthop *nh; | |
3464 | ||
a7ff87ac JP |
3465 | list_for_each_entry(nh, &neigh_entry->nexthop_list, |
3466 | neigh_list_node) { | |
3467 | __mlxsw_sp_nexthop_neigh_update(nh, removing); | |
3468 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); | |
3469 | } | |
a7ff87ac JP |
3470 | } |
3471 | ||
9665b745 | 3472 | static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh, |
bf95233e | 3473 | struct mlxsw_sp_rif *rif) |
9665b745 | 3474 | { |
bf95233e | 3475 | if (nh->rif) |
9665b745 IS |
3476 | return; |
3477 | ||
bf95233e AS |
3478 | nh->rif = rif; |
3479 | list_add(&nh->rif_list_node, &rif->nexthop_list); | |
9665b745 IS |
3480 | } |
3481 | ||
3482 | static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh) | |
3483 | { | |
bf95233e | 3484 | if (!nh->rif) |
9665b745 IS |
3485 | return; |
3486 | ||
3487 | list_del(&nh->rif_list_node); | |
bf95233e | 3488 | nh->rif = NULL; |
9665b745 IS |
3489 | } |
3490 | ||
a8c97014 IS |
3491 | static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, |
3492 | struct mlxsw_sp_nexthop *nh) | |
a7ff87ac JP |
3493 | { |
3494 | struct mlxsw_sp_neigh_entry *neigh_entry; | |
a7ff87ac | 3495 | struct neighbour *n; |
93a87e5e | 3496 | u8 nud_state, dead; |
c53b8e1b IS |
3497 | int err; |
3498 | ||
ad178c8e | 3499 | if (!nh->nh_grp->gateway || nh->neigh_entry) |
b8399a1e IS |
3500 | return 0; |
3501 | ||
33b1341c | 3502 | /* Take a reference of neigh here ensuring that neigh would |
8de3c178 | 3503 | * not be destructed before the nexthop entry is finished. |
33b1341c | 3504 | * The reference is taken either in neigh_lookup() or |
fd76d910 | 3505 | * in neigh_create() in case n is not found. |
33b1341c | 3506 | */ |
58adf2c4 | 3507 | n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev); |
33b1341c | 3508 | if (!n) { |
58adf2c4 IS |
3509 | n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr, |
3510 | nh->rif->dev); | |
a8c97014 IS |
3511 | if (IS_ERR(n)) |
3512 | return PTR_ERR(n); | |
a7ff87ac | 3513 | neigh_event_send(n, NULL); |
33b1341c JP |
3514 | } |
3515 | neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n); | |
3516 | if (!neigh_entry) { | |
5c8802f1 IS |
3517 | neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n); |
3518 | if (IS_ERR(neigh_entry)) { | |
c53b8e1b IS |
3519 | err = -EINVAL; |
3520 | goto err_neigh_entry_create; | |
5c8802f1 | 3521 | } |
a7ff87ac | 3522 | } |
b2157149 YG |
3523 | |
3524 | /* If that is the first nexthop connected to that neigh, add to | |
3525 | * nexthop_neighs_list | |
3526 | */ | |
3527 | if (list_empty(&neigh_entry->nexthop_list)) | |
3528 | list_add_tail(&neigh_entry->nexthop_neighs_list_node, | |
9011b677 | 3529 | &mlxsw_sp->router->nexthop_neighs_list); |
b2157149 | 3530 | |
a7ff87ac JP |
3531 | nh->neigh_entry = neigh_entry; |
3532 | list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list); | |
3533 | read_lock_bh(&n->lock); | |
3534 | nud_state = n->nud_state; | |
93a87e5e | 3535 | dead = n->dead; |
a7ff87ac | 3536 | read_unlock_bh(&n->lock); |
93a87e5e | 3537 | __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead)); |
a7ff87ac JP |
3538 | |
3539 | return 0; | |
c53b8e1b IS |
3540 | |
3541 | err_neigh_entry_create: | |
3542 | neigh_release(n); | |
c53b8e1b | 3543 | return err; |
a7ff87ac JP |
3544 | } |
3545 | ||
a8c97014 IS |
3546 | static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, |
3547 | struct mlxsw_sp_nexthop *nh) | |
a7ff87ac JP |
3548 | { |
3549 | struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry; | |
a8c97014 | 3550 | struct neighbour *n; |
a7ff87ac | 3551 | |
b8399a1e | 3552 | if (!neigh_entry) |
a8c97014 IS |
3553 | return; |
3554 | n = neigh_entry->key.n; | |
b8399a1e | 3555 | |
58312125 | 3556 | __mlxsw_sp_nexthop_neigh_update(nh, true); |
a7ff87ac | 3557 | list_del(&nh->neigh_list_node); |
e58be79e | 3558 | nh->neigh_entry = NULL; |
b2157149 YG |
3559 | |
3560 | /* If that is the last nexthop connected to that neigh, remove from | |
3561 | * nexthop_neighs_list | |
3562 | */ | |
e58be79e IS |
3563 | if (list_empty(&neigh_entry->nexthop_list)) |
3564 | list_del(&neigh_entry->nexthop_neighs_list_node); | |
b2157149 | 3565 | |
5c8802f1 IS |
3566 | if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list)) |
3567 | mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); | |
3568 | ||
3569 | neigh_release(n); | |
a8c97014 | 3570 | } |
c53b8e1b | 3571 | |
44b0fff1 PM |
3572 | static bool mlxsw_sp_ipip_netdev_ul_up(struct net_device *ol_dev) |
3573 | { | |
3574 | struct net_device *ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); | |
3575 | ||
3576 | return ul_dev ? (ul_dev->flags & IFF_UP) : true; | |
3577 | } | |
3578 | ||
d97cda5f PM |
3579 | static void mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp, |
3580 | struct mlxsw_sp_nexthop *nh, | |
3581 | struct mlxsw_sp_ipip_entry *ipip_entry) | |
1012b9ac | 3582 | { |
44b0fff1 PM |
3583 | bool removing; |
3584 | ||
1012b9ac | 3585 | if (!nh->nh_grp->gateway || nh->ipip_entry) |
d97cda5f | 3586 | return; |
1012b9ac | 3587 | |
d97cda5f PM |
3588 | nh->ipip_entry = ipip_entry; |
3589 | removing = !mlxsw_sp_ipip_netdev_ul_up(ipip_entry->ol_dev); | |
44b0fff1 | 3590 | __mlxsw_sp_nexthop_neigh_update(nh, removing); |
d97cda5f | 3591 | mlxsw_sp_nexthop_rif_init(nh, &ipip_entry->ol_lb->common); |
1012b9ac PM |
3592 | } |
3593 | ||
3594 | static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp, | |
3595 | struct mlxsw_sp_nexthop *nh) | |
3596 | { | |
3597 | struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry; | |
3598 | ||
3599 | if (!ipip_entry) | |
3600 | return; | |
3601 | ||
3602 | __mlxsw_sp_nexthop_neigh_update(nh, true); | |
1012b9ac PM |
3603 | nh->ipip_entry = NULL; |
3604 | } | |
3605 | ||
3606 | static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp, | |
3607 | const struct fib_nh *fib_nh, | |
3608 | enum mlxsw_sp_ipip_type *p_ipipt) | |
3609 | { | |
3610 | struct net_device *dev = fib_nh->nh_dev; | |
3611 | ||
3612 | return dev && | |
3613 | fib_nh->nh_parent->fib_type == RTN_UNICAST && | |
3614 | mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt); | |
3615 | } | |
3616 | ||
35225e47 PM |
3617 | static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp, |
3618 | struct mlxsw_sp_nexthop *nh) | |
3619 | { | |
3620 | switch (nh->type) { | |
3621 | case MLXSW_SP_NEXTHOP_TYPE_ETH: | |
3622 | mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); | |
3623 | mlxsw_sp_nexthop_rif_fini(nh); | |
3624 | break; | |
1012b9ac | 3625 | case MLXSW_SP_NEXTHOP_TYPE_IPIP: |
de0f43c0 | 3626 | mlxsw_sp_nexthop_rif_fini(nh); |
1012b9ac PM |
3627 | mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh); |
3628 | break; | |
35225e47 PM |
3629 | } |
3630 | } | |
3631 | ||
3632 | static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp, | |
3633 | struct mlxsw_sp_nexthop *nh, | |
3634 | struct fib_nh *fib_nh) | |
3635 | { | |
d97cda5f | 3636 | const struct mlxsw_sp_ipip_ops *ipip_ops; |
35225e47 | 3637 | struct net_device *dev = fib_nh->nh_dev; |
d97cda5f | 3638 | struct mlxsw_sp_ipip_entry *ipip_entry; |
35225e47 PM |
3639 | struct mlxsw_sp_rif *rif; |
3640 | int err; | |
3641 | ||
d97cda5f PM |
3642 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev); |
3643 | if (ipip_entry) { | |
3644 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; | |
3645 | if (ipip_ops->can_offload(mlxsw_sp, dev, | |
3646 | MLXSW_SP_L3_PROTO_IPV4)) { | |
3647 | nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; | |
3648 | mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry); | |
3649 | return 0; | |
3650 | } | |
1012b9ac PM |
3651 | } |
3652 | ||
35225e47 PM |
3653 | nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; |
3654 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | |
3655 | if (!rif) | |
3656 | return 0; | |
3657 | ||
3658 | mlxsw_sp_nexthop_rif_init(nh, rif); | |
3659 | err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); | |
3660 | if (err) | |
3661 | goto err_neigh_init; | |
3662 | ||
3663 | return 0; | |
3664 | ||
3665 | err_neigh_init: | |
3666 | mlxsw_sp_nexthop_rif_fini(nh); | |
3667 | return err; | |
3668 | } | |
3669 | ||
3670 | static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp, | |
3671 | struct mlxsw_sp_nexthop *nh) | |
3672 | { | |
3673 | mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); | |
3674 | } | |
3675 | ||
0e6ea2a4 IS |
3676 | static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, |
3677 | struct mlxsw_sp_nexthop_group *nh_grp, | |
3678 | struct mlxsw_sp_nexthop *nh, | |
3679 | struct fib_nh *fib_nh) | |
a8c97014 IS |
3680 | { |
3681 | struct net_device *dev = fib_nh->nh_dev; | |
df6dd79b | 3682 | struct in_device *in_dev; |
a8c97014 IS |
3683 | int err; |
3684 | ||
3685 | nh->nh_grp = nh_grp; | |
3686 | nh->key.fib_nh = fib_nh; | |
408bd946 IS |
3687 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
3688 | nh->nh_weight = fib_nh->nh_weight; | |
3689 | #else | |
3690 | nh->nh_weight = 1; | |
3691 | #endif | |
58adf2c4 | 3692 | memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw)); |
a8c97014 IS |
3693 | err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh); |
3694 | if (err) | |
3695 | return err; | |
3696 | ||
a5390278 | 3697 | mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh); |
dbe4598c AS |
3698 | list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list); |
3699 | ||
97989ee0 IS |
3700 | if (!dev) |
3701 | return 0; | |
3702 | ||
df6dd79b IS |
3703 | in_dev = __in_dev_get_rtnl(dev); |
3704 | if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && | |
3705 | fib_nh->nh_flags & RTNH_F_LINKDOWN) | |
3706 | return 0; | |
3707 | ||
35225e47 | 3708 | err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); |
a8c97014 IS |
3709 | if (err) |
3710 | goto err_nexthop_neigh_init; | |
3711 | ||
3712 | return 0; | |
3713 | ||
3714 | err_nexthop_neigh_init: | |
3715 | mlxsw_sp_nexthop_remove(mlxsw_sp, nh); | |
3716 | return err; | |
3717 | } | |
3718 | ||
0e6ea2a4 IS |
3719 | static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp, |
3720 | struct mlxsw_sp_nexthop *nh) | |
a8c97014 | 3721 | { |
35225e47 | 3722 | mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); |
dbe4598c | 3723 | list_del(&nh->router_list_node); |
a5390278 | 3724 | mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); |
c53b8e1b | 3725 | mlxsw_sp_nexthop_remove(mlxsw_sp, nh); |
a7ff87ac JP |
3726 | } |
3727 | ||
0e6ea2a4 IS |
3728 | static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, |
3729 | unsigned long event, struct fib_nh *fib_nh) | |
ad178c8e IS |
3730 | { |
3731 | struct mlxsw_sp_nexthop_key key; | |
3732 | struct mlxsw_sp_nexthop *nh; | |
ad178c8e | 3733 | |
9011b677 | 3734 | if (mlxsw_sp->router->aborted) |
ad178c8e IS |
3735 | return; |
3736 | ||
3737 | key.fib_nh = fib_nh; | |
3738 | nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key); | |
3739 | if (WARN_ON_ONCE(!nh)) | |
3740 | return; | |
3741 | ||
ad178c8e IS |
3742 | switch (event) { |
3743 | case FIB_EVENT_NH_ADD: | |
35225e47 | 3744 | mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); |
ad178c8e IS |
3745 | break; |
3746 | case FIB_EVENT_NH_DEL: | |
35225e47 | 3747 | mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); |
ad178c8e IS |
3748 | break; |
3749 | } | |
3750 | ||
3751 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); | |
3752 | } | |
3753 | ||
0c5f1cd5 PM |
3754 | static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp, |
3755 | struct mlxsw_sp_rif *rif) | |
3756 | { | |
3757 | struct mlxsw_sp_nexthop *nh; | |
44b0fff1 | 3758 | bool removing; |
0c5f1cd5 PM |
3759 | |
3760 | list_for_each_entry(nh, &rif->nexthop_list, rif_list_node) { | |
44b0fff1 PM |
3761 | switch (nh->type) { |
3762 | case MLXSW_SP_NEXTHOP_TYPE_ETH: | |
3763 | removing = false; | |
3764 | break; | |
3765 | case MLXSW_SP_NEXTHOP_TYPE_IPIP: | |
3766 | removing = !mlxsw_sp_ipip_netdev_ul_up(rif->dev); | |
3767 | break; | |
3768 | default: | |
3769 | WARN_ON(1); | |
3770 | continue; | |
3771 | } | |
3772 | ||
3773 | __mlxsw_sp_nexthop_neigh_update(nh, removing); | |
0c5f1cd5 PM |
3774 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); |
3775 | } | |
3776 | } | |
3777 | ||
09dbf629 PM |
3778 | static void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp, |
3779 | struct mlxsw_sp_rif *old_rif, | |
3780 | struct mlxsw_sp_rif *new_rif) | |
3781 | { | |
3782 | struct mlxsw_sp_nexthop *nh; | |
3783 | ||
3784 | list_splice_init(&old_rif->nexthop_list, &new_rif->nexthop_list); | |
3785 | list_for_each_entry(nh, &new_rif->nexthop_list, rif_list_node) | |
3786 | nh->rif = new_rif; | |
3787 | mlxsw_sp_nexthop_rif_update(mlxsw_sp, new_rif); | |
3788 | } | |
3789 | ||
9665b745 | 3790 | static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, |
bf95233e | 3791 | struct mlxsw_sp_rif *rif) |
9665b745 IS |
3792 | { |
3793 | struct mlxsw_sp_nexthop *nh, *tmp; | |
3794 | ||
bf95233e | 3795 | list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) { |
35225e47 | 3796 | mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); |
9665b745 IS |
3797 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); |
3798 | } | |
3799 | } | |
3800 | ||
9b01451a PM |
3801 | static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp, |
3802 | const struct fib_info *fi) | |
3803 | { | |
1012b9ac PM |
3804 | return fi->fib_nh->nh_scope == RT_SCOPE_LINK || |
3805 | mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL); | |
9b01451a PM |
3806 | } |
3807 | ||
a7ff87ac | 3808 | static struct mlxsw_sp_nexthop_group * |
0e6ea2a4 | 3809 | mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) |
a7ff87ac JP |
3810 | { |
3811 | struct mlxsw_sp_nexthop_group *nh_grp; | |
3812 | struct mlxsw_sp_nexthop *nh; | |
3813 | struct fib_nh *fib_nh; | |
3814 | size_t alloc_size; | |
3815 | int i; | |
3816 | int err; | |
3817 | ||
3818 | alloc_size = sizeof(*nh_grp) + | |
3819 | fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop); | |
3820 | nh_grp = kzalloc(alloc_size, GFP_KERNEL); | |
3821 | if (!nh_grp) | |
3822 | return ERR_PTR(-ENOMEM); | |
ba31d366 | 3823 | nh_grp->priv = fi; |
a7ff87ac | 3824 | INIT_LIST_HEAD(&nh_grp->fib_list); |
58adf2c4 IS |
3825 | nh_grp->neigh_tbl = &arp_tbl; |
3826 | ||
9b01451a | 3827 | nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi); |
a7ff87ac | 3828 | nh_grp->count = fi->fib_nhs; |
7387dbbc | 3829 | fib_info_hold(fi); |
a7ff87ac JP |
3830 | for (i = 0; i < nh_grp->count; i++) { |
3831 | nh = &nh_grp->nexthops[i]; | |
3832 | fib_nh = &fi->fib_nh[i]; | |
0e6ea2a4 | 3833 | err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh); |
a7ff87ac | 3834 | if (err) |
0e6ea2a4 | 3835 | goto err_nexthop4_init; |
a7ff87ac | 3836 | } |
e9ad5e7d IS |
3837 | err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); |
3838 | if (err) | |
3839 | goto err_nexthop_group_insert; | |
a7ff87ac JP |
3840 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); |
3841 | return nh_grp; | |
3842 | ||
e9ad5e7d | 3843 | err_nexthop_group_insert: |
0e6ea2a4 | 3844 | err_nexthop4_init: |
df6dd79b IS |
3845 | for (i--; i >= 0; i--) { |
3846 | nh = &nh_grp->nexthops[i]; | |
0e6ea2a4 | 3847 | mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); |
df6dd79b | 3848 | } |
ba31d366 | 3849 | fib_info_put(fi); |
a7ff87ac JP |
3850 | kfree(nh_grp); |
3851 | return ERR_PTR(err); | |
3852 | } | |
3853 | ||
3854 | static void | |
0e6ea2a4 IS |
3855 | mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp, |
3856 | struct mlxsw_sp_nexthop_group *nh_grp) | |
a7ff87ac JP |
3857 | { |
3858 | struct mlxsw_sp_nexthop *nh; | |
3859 | int i; | |
3860 | ||
e9ad5e7d | 3861 | mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); |
a7ff87ac JP |
3862 | for (i = 0; i < nh_grp->count; i++) { |
3863 | nh = &nh_grp->nexthops[i]; | |
0e6ea2a4 | 3864 | mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); |
a7ff87ac | 3865 | } |
58312125 IS |
3866 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); |
3867 | WARN_ON_ONCE(nh_grp->adj_index_valid); | |
ba31d366 | 3868 | fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp)); |
a7ff87ac JP |
3869 | kfree(nh_grp); |
3870 | } | |
3871 | ||
0e6ea2a4 IS |
3872 | static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp, |
3873 | struct mlxsw_sp_fib_entry *fib_entry, | |
3874 | struct fib_info *fi) | |
a7ff87ac JP |
3875 | { |
3876 | struct mlxsw_sp_nexthop_group *nh_grp; | |
3877 | ||
ba31d366 | 3878 | nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi); |
a7ff87ac | 3879 | if (!nh_grp) { |
0e6ea2a4 | 3880 | nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi); |
a7ff87ac JP |
3881 | if (IS_ERR(nh_grp)) |
3882 | return PTR_ERR(nh_grp); | |
3883 | } | |
3884 | list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list); | |
3885 | fib_entry->nh_group = nh_grp; | |
3886 | return 0; | |
3887 | } | |
3888 | ||
0e6ea2a4 IS |
3889 | static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp, |
3890 | struct mlxsw_sp_fib_entry *fib_entry) | |
a7ff87ac JP |
3891 | { |
3892 | struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; | |
3893 | ||
3894 | list_del(&fib_entry->nexthop_group_node); | |
3895 | if (!list_empty(&nh_grp->fib_list)) | |
3896 | return; | |
0e6ea2a4 | 3897 | mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp); |
a7ff87ac JP |
3898 | } |
3899 | ||
4f1c7f1f IS |
3900 | static bool |
3901 | mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) | |
3902 | { | |
3903 | struct mlxsw_sp_fib4_entry *fib4_entry; | |
3904 | ||
3905 | fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry, | |
3906 | common); | |
3907 | return !fib4_entry->tos; | |
3908 | } | |
3909 | ||
013b20f9 IS |
3910 | static bool |
3911 | mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) | |
3912 | { | |
3913 | struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group; | |
3914 | ||
4f1c7f1f IS |
3915 | switch (fib_entry->fib_node->fib->proto) { |
3916 | case MLXSW_SP_L3_PROTO_IPV4: | |
3917 | if (!mlxsw_sp_fib4_entry_should_offload(fib_entry)) | |
3918 | return false; | |
3919 | break; | |
3920 | case MLXSW_SP_L3_PROTO_IPV6: | |
3921 | break; | |
3922 | } | |
9aecce1c | 3923 | |
013b20f9 IS |
3924 | switch (fib_entry->type) { |
3925 | case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: | |
3926 | return !!nh_group->adj_index_valid; | |
3927 | case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: | |
70ad3506 | 3928 | return !!nh_group->nh_rif; |
4607f6d2 | 3929 | case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: |
0c69e0fc | 3930 | case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP: |
4607f6d2 | 3931 | return true; |
013b20f9 IS |
3932 | default: |
3933 | return false; | |
3934 | } | |
3935 | } | |
3936 | ||
428b851f IS |
3937 | static struct mlxsw_sp_nexthop * |
3938 | mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp, | |
3939 | const struct mlxsw_sp_rt6 *mlxsw_sp_rt6) | |
3940 | { | |
3941 | int i; | |
3942 | ||
3943 | for (i = 0; i < nh_grp->count; i++) { | |
3944 | struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; | |
8d1c802b | 3945 | struct fib6_info *rt = mlxsw_sp_rt6->rt; |
428b851f | 3946 | |
5e670d84 | 3947 | if (nh->rif && nh->rif->dev == rt->fib6_nh.nh_dev && |
428b851f | 3948 | ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr, |
5e670d84 | 3949 | &rt->fib6_nh.nh_gw)) |
428b851f IS |
3950 | return nh; |
3951 | continue; | |
3952 | } | |
3953 | ||
3954 | return NULL; | |
3955 | } | |
3956 | ||
3984d1a8 IS |
3957 | static void |
3958 | mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) | |
3959 | { | |
3960 | struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; | |
3961 | int i; | |
3962 | ||
4607f6d2 | 3963 | if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL || |
0c69e0fc IS |
3964 | fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP || |
3965 | fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP) { | |
3984d1a8 IS |
3966 | nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; |
3967 | return; | |
3968 | } | |
3969 | ||
3970 | for (i = 0; i < nh_grp->count; i++) { | |
3971 | struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; | |
3972 | ||
3973 | if (nh->offloaded) | |
3974 | nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; | |
3975 | else | |
3976 | nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; | |
3977 | } | |
3978 | } | |
3979 | ||
3980 | static void | |
3981 | mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) | |
3982 | { | |
3983 | struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; | |
3984 | int i; | |
3985 | ||
d1c95af3 IS |
3986 | if (!list_is_singular(&nh_grp->fib_list)) |
3987 | return; | |
3988 | ||
3984d1a8 IS |
3989 | for (i = 0; i < nh_grp->count; i++) { |
3990 | struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; | |
3991 | ||
3992 | nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; | |
3993 | } | |
3994 | } | |
3995 | ||
428b851f IS |
3996 | static void |
3997 | mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) | |
3998 | { | |
3999 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
4000 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
4001 | ||
4002 | fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, | |
4003 | common); | |
4004 | ||
4005 | if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { | |
4006 | list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, | |
5e670d84 | 4007 | list)->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD; |
428b851f IS |
4008 | return; |
4009 | } | |
4010 | ||
4011 | list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { | |
4012 | struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; | |
4013 | struct mlxsw_sp_nexthop *nh; | |
4014 | ||
4015 | nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6); | |
4016 | if (nh && nh->offloaded) | |
5e670d84 | 4017 | mlxsw_sp_rt6->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD; |
428b851f | 4018 | else |
5e670d84 | 4019 | mlxsw_sp_rt6->rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD; |
428b851f IS |
4020 | } |
4021 | } | |
4022 | ||
4023 | static void | |
4024 | mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) | |
4025 | { | |
4026 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
4027 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
4028 | ||
4029 | fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, | |
4030 | common); | |
4031 | list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { | |
8d1c802b | 4032 | struct fib6_info *rt = mlxsw_sp_rt6->rt; |
428b851f | 4033 | |
5e670d84 | 4034 | rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD; |
428b851f IS |
4035 | } |
4036 | } | |
4037 | ||
013b20f9 IS |
4038 | static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) |
4039 | { | |
76610ebb | 4040 | switch (fib_entry->fib_node->fib->proto) { |
013b20f9 | 4041 | case MLXSW_SP_L3_PROTO_IPV4: |
3984d1a8 | 4042 | mlxsw_sp_fib4_entry_offload_set(fib_entry); |
013b20f9 IS |
4043 | break; |
4044 | case MLXSW_SP_L3_PROTO_IPV6: | |
428b851f IS |
4045 | mlxsw_sp_fib6_entry_offload_set(fib_entry); |
4046 | break; | |
013b20f9 IS |
4047 | } |
4048 | } | |
4049 | ||
4050 | static void | |
4051 | mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) | |
4052 | { | |
76610ebb | 4053 | switch (fib_entry->fib_node->fib->proto) { |
013b20f9 | 4054 | case MLXSW_SP_L3_PROTO_IPV4: |
3984d1a8 | 4055 | mlxsw_sp_fib4_entry_offload_unset(fib_entry); |
013b20f9 IS |
4056 | break; |
4057 | case MLXSW_SP_L3_PROTO_IPV6: | |
428b851f IS |
4058 | mlxsw_sp_fib6_entry_offload_unset(fib_entry); |
4059 | break; | |
013b20f9 | 4060 | } |
013b20f9 IS |
4061 | } |
4062 | ||
4063 | static void | |
4064 | mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, | |
4065 | enum mlxsw_reg_ralue_op op, int err) | |
4066 | { | |
4067 | switch (op) { | |
4068 | case MLXSW_REG_RALUE_OP_WRITE_DELETE: | |
013b20f9 IS |
4069 | return mlxsw_sp_fib_entry_offload_unset(fib_entry); |
4070 | case MLXSW_REG_RALUE_OP_WRITE_WRITE: | |
4071 | if (err) | |
4072 | return; | |
1353ee70 | 4073 | if (mlxsw_sp_fib_entry_should_offload(fib_entry)) |
013b20f9 | 4074 | mlxsw_sp_fib_entry_offload_set(fib_entry); |
85f44a15 | 4075 | else |
013b20f9 IS |
4076 | mlxsw_sp_fib_entry_offload_unset(fib_entry); |
4077 | return; | |
4078 | default: | |
4079 | return; | |
4080 | } | |
4081 | } | |
4082 | ||
9dbf4d76 IS |
4083 | static void |
4084 | mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl, | |
4085 | const struct mlxsw_sp_fib_entry *fib_entry, | |
4086 | enum mlxsw_reg_ralue_op op) | |
a7ff87ac | 4087 | { |
76610ebb | 4088 | struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; |
9dbf4d76 IS |
4089 | enum mlxsw_reg_ralxx_protocol proto; |
4090 | u32 *p_dip; | |
4091 | ||
4092 | proto = (enum mlxsw_reg_ralxx_protocol) fib->proto; | |
4093 | ||
4094 | switch (fib->proto) { | |
4095 | case MLXSW_SP_L3_PROTO_IPV4: | |
4096 | p_dip = (u32 *) fib_entry->fib_node->key.addr; | |
4097 | mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id, | |
4098 | fib_entry->fib_node->key.prefix_len, | |
4099 | *p_dip); | |
4100 | break; | |
4101 | case MLXSW_SP_L3_PROTO_IPV6: | |
4102 | mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id, | |
4103 | fib_entry->fib_node->key.prefix_len, | |
4104 | fib_entry->fib_node->key.addr); | |
4105 | break; | |
4106 | } | |
4107 | } | |
4108 | ||
4109 | static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, | |
4110 | struct mlxsw_sp_fib_entry *fib_entry, | |
4111 | enum mlxsw_reg_ralue_op op) | |
4112 | { | |
4113 | char ralue_pl[MLXSW_REG_RALUE_LEN]; | |
a7ff87ac JP |
4114 | enum mlxsw_reg_ralue_trap_action trap_action; |
4115 | u16 trap_id = 0; | |
4116 | u32 adjacency_index = 0; | |
4117 | u16 ecmp_size = 0; | |
4118 | ||
4119 | /* In case the nexthop group adjacency index is valid, use it | |
4120 | * with provided ECMP size. Otherwise, setup trap and pass | |
4121 | * traffic to kernel. | |
4122 | */ | |
4b411477 | 4123 | if (mlxsw_sp_fib_entry_should_offload(fib_entry)) { |
a7ff87ac JP |
4124 | trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP; |
4125 | adjacency_index = fib_entry->nh_group->adj_index; | |
4126 | ecmp_size = fib_entry->nh_group->ecmp_size; | |
4127 | } else { | |
4128 | trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP; | |
4129 | trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; | |
4130 | } | |
4131 | ||
9dbf4d76 | 4132 | mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); |
a7ff87ac JP |
4133 | mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id, |
4134 | adjacency_index, ecmp_size); | |
4135 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); | |
4136 | } | |
4137 | ||
9dbf4d76 IS |
4138 | static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp, |
4139 | struct mlxsw_sp_fib_entry *fib_entry, | |
4140 | enum mlxsw_reg_ralue_op op) | |
61c503f9 | 4141 | { |
bf95233e | 4142 | struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif; |
70ad3506 | 4143 | enum mlxsw_reg_ralue_trap_action trap_action; |
61c503f9 | 4144 | char ralue_pl[MLXSW_REG_RALUE_LEN]; |
70ad3506 | 4145 | u16 trap_id = 0; |
bf95233e | 4146 | u16 rif_index = 0; |
70ad3506 IS |
4147 | |
4148 | if (mlxsw_sp_fib_entry_should_offload(fib_entry)) { | |
4149 | trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP; | |
bf95233e | 4150 | rif_index = rif->rif_index; |
70ad3506 IS |
4151 | } else { |
4152 | trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP; | |
4153 | trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; | |
4154 | } | |
61c503f9 | 4155 | |
9dbf4d76 | 4156 | mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); |
bf95233e AS |
4157 | mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, |
4158 | rif_index); | |
61c503f9 JP |
4159 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); |
4160 | } | |
4161 | ||
9dbf4d76 IS |
4162 | static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp, |
4163 | struct mlxsw_sp_fib_entry *fib_entry, | |
4164 | enum mlxsw_reg_ralue_op op) | |
61c503f9 JP |
4165 | { |
4166 | char ralue_pl[MLXSW_REG_RALUE_LEN]; | |
61c503f9 | 4167 | |
9dbf4d76 | 4168 | mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); |
61c503f9 JP |
4169 | mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); |
4170 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); | |
4171 | } | |
4172 | ||
4607f6d2 PM |
4173 | static int |
4174 | mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp, | |
4175 | struct mlxsw_sp_fib_entry *fib_entry, | |
4176 | enum mlxsw_reg_ralue_op op) | |
4177 | { | |
4178 | struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry; | |
4179 | const struct mlxsw_sp_ipip_ops *ipip_ops; | |
4180 | ||
4181 | if (WARN_ON(!ipip_entry)) | |
4182 | return -EINVAL; | |
4183 | ||
4184 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; | |
4185 | return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op, | |
4186 | fib_entry->decap.tunnel_index); | |
4187 | } | |
4188 | ||
0c69e0fc IS |
4189 | static int mlxsw_sp_fib_entry_op_nve_decap(struct mlxsw_sp *mlxsw_sp, |
4190 | struct mlxsw_sp_fib_entry *fib_entry, | |
4191 | enum mlxsw_reg_ralue_op op) | |
4192 | { | |
4193 | char ralue_pl[MLXSW_REG_RALUE_LEN]; | |
4194 | ||
4195 | mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); | |
4196 | mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl, | |
4197 | fib_entry->decap.tunnel_index); | |
4198 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); | |
4199 | } | |
4200 | ||
9dbf4d76 IS |
4201 | static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, |
4202 | struct mlxsw_sp_fib_entry *fib_entry, | |
4203 | enum mlxsw_reg_ralue_op op) | |
61c503f9 JP |
4204 | { |
4205 | switch (fib_entry->type) { | |
4206 | case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: | |
9dbf4d76 | 4207 | return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op); |
61c503f9 | 4208 | case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: |
9dbf4d76 | 4209 | return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op); |
61c503f9 | 4210 | case MLXSW_SP_FIB_ENTRY_TYPE_TRAP: |
9dbf4d76 | 4211 | return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op); |
4607f6d2 PM |
4212 | case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: |
4213 | return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp, | |
4214 | fib_entry, op); | |
0c69e0fc IS |
4215 | case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP: |
4216 | return mlxsw_sp_fib_entry_op_nve_decap(mlxsw_sp, fib_entry, op); | |
61c503f9 JP |
4217 | } |
4218 | return -EINVAL; | |
4219 | } | |
4220 | ||
4221 | static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, | |
4222 | struct mlxsw_sp_fib_entry *fib_entry, | |
4223 | enum mlxsw_reg_ralue_op op) | |
4224 | { | |
9dbf4d76 | 4225 | int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op); |
013b20f9 | 4226 | |
013b20f9 | 4227 | mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err); |
9dbf4d76 | 4228 | |
013b20f9 | 4229 | return err; |
61c503f9 JP |
4230 | } |
4231 | ||
4232 | static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, | |
4233 | struct mlxsw_sp_fib_entry *fib_entry) | |
4234 | { | |
7146da31 JP |
4235 | return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, |
4236 | MLXSW_REG_RALUE_OP_WRITE_WRITE); | |
61c503f9 JP |
4237 | } |
4238 | ||
4239 | static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp, | |
4240 | struct mlxsw_sp_fib_entry *fib_entry) | |
4241 | { | |
4242 | return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, | |
4243 | MLXSW_REG_RALUE_OP_WRITE_DELETE); | |
4244 | } | |
4245 | ||
61c503f9 | 4246 | static int |
013b20f9 IS |
4247 | mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, |
4248 | const struct fib_entry_notifier_info *fen_info, | |
4249 | struct mlxsw_sp_fib_entry *fib_entry) | |
61c503f9 | 4250 | { |
4607f6d2 | 4251 | union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) }; |
4cf178d7 | 4252 | u32 tb_id = mlxsw_sp_fix_tb_id(fen_info->tb_id); |
4607f6d2 PM |
4253 | struct net_device *dev = fen_info->fi->fib_dev; |
4254 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
b45f64d1 | 4255 | struct fib_info *fi = fen_info->fi; |
61c503f9 | 4256 | |
97989ee0 | 4257 | switch (fen_info->type) { |
97989ee0 | 4258 | case RTN_LOCAL: |
4607f6d2 PM |
4259 | ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev, |
4260 | MLXSW_SP_L3_PROTO_IPV4, dip); | |
57c77ce4 | 4261 | if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) { |
4607f6d2 PM |
4262 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; |
4263 | return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, | |
4264 | fib_entry, | |
4265 | ipip_entry); | |
4266 | } | |
4cf178d7 IS |
4267 | if (mlxsw_sp_nve_ipv4_route_is_decap(mlxsw_sp, tb_id, |
4268 | dip.addr4)) { | |
4269 | u32 t_index; | |
4270 | ||
4271 | t_index = mlxsw_sp_nve_decap_tunnel_index_get(mlxsw_sp); | |
4272 | fib_entry->decap.tunnel_index = t_index; | |
4273 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP; | |
4274 | return 0; | |
4275 | } | |
4607f6d2 PM |
4276 | /* fall through */ |
4277 | case RTN_BROADCAST: | |
61c503f9 JP |
4278 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; |
4279 | return 0; | |
97989ee0 IS |
4280 | case RTN_UNREACHABLE: /* fall through */ |
4281 | case RTN_BLACKHOLE: /* fall through */ | |
4282 | case RTN_PROHIBIT: | |
4283 | /* Packets hitting these routes need to be trapped, but | |
4284 | * can do so with a lower priority than packets directed | |
4285 | * at the host, so use action type local instead of trap. | |
4286 | */ | |
61c503f9 | 4287 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; |
97989ee0 IS |
4288 | return 0; |
4289 | case RTN_UNICAST: | |
9b01451a | 4290 | if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi)) |
97989ee0 | 4291 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; |
9b01451a PM |
4292 | else |
4293 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; | |
97989ee0 IS |
4294 | return 0; |
4295 | default: | |
4296 | return -EINVAL; | |
4297 | } | |
a7ff87ac JP |
4298 | } |
4299 | ||
4f1c7f1f | 4300 | static struct mlxsw_sp_fib4_entry * |
9aecce1c IS |
4301 | mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, |
4302 | struct mlxsw_sp_fib_node *fib_node, | |
4303 | const struct fib_entry_notifier_info *fen_info) | |
61c503f9 | 4304 | { |
4f1c7f1f | 4305 | struct mlxsw_sp_fib4_entry *fib4_entry; |
61c503f9 | 4306 | struct mlxsw_sp_fib_entry *fib_entry; |
61c503f9 JP |
4307 | int err; |
4308 | ||
4f1c7f1f IS |
4309 | fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL); |
4310 | if (!fib4_entry) | |
4311 | return ERR_PTR(-ENOMEM); | |
4312 | fib_entry = &fib4_entry->common; | |
61c503f9 | 4313 | |
013b20f9 | 4314 | err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry); |
61c503f9 | 4315 | if (err) |
013b20f9 | 4316 | goto err_fib4_entry_type_set; |
61c503f9 | 4317 | |
0e6ea2a4 | 4318 | err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi); |
b8399a1e | 4319 | if (err) |
0e6ea2a4 | 4320 | goto err_nexthop4_group_get; |
b8399a1e | 4321 | |
4f1c7f1f IS |
4322 | fib4_entry->prio = fen_info->fi->fib_priority; |
4323 | fib4_entry->tb_id = fen_info->tb_id; | |
4324 | fib4_entry->type = fen_info->type; | |
4325 | fib4_entry->tos = fen_info->tos; | |
9aecce1c IS |
4326 | |
4327 | fib_entry->fib_node = fib_node; | |
4328 | ||
4f1c7f1f | 4329 | return fib4_entry; |
5b004412 | 4330 | |
0e6ea2a4 | 4331 | err_nexthop4_group_get: |
013b20f9 | 4332 | err_fib4_entry_type_set: |
4f1c7f1f | 4333 | kfree(fib4_entry); |
5b004412 JP |
4334 | return ERR_PTR(err); |
4335 | } | |
4336 | ||
9aecce1c | 4337 | static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, |
4f1c7f1f | 4338 | struct mlxsw_sp_fib4_entry *fib4_entry) |
9aecce1c | 4339 | { |
0e6ea2a4 | 4340 | mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common); |
4f1c7f1f | 4341 | kfree(fib4_entry); |
9aecce1c IS |
4342 | } |
4343 | ||
4f1c7f1f | 4344 | static struct mlxsw_sp_fib4_entry * |
9aecce1c IS |
4345 | mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, |
4346 | const struct fib_entry_notifier_info *fen_info) | |
5b004412 | 4347 | { |
4f1c7f1f | 4348 | struct mlxsw_sp_fib4_entry *fib4_entry; |
9aecce1c | 4349 | struct mlxsw_sp_fib_node *fib_node; |
160e22aa IS |
4350 | struct mlxsw_sp_fib *fib; |
4351 | struct mlxsw_sp_vr *vr; | |
4352 | ||
4353 | vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id); | |
4354 | if (!vr) | |
4355 | return NULL; | |
4356 | fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); | |
5b004412 | 4357 | |
160e22aa IS |
4358 | fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, |
4359 | sizeof(fen_info->dst), | |
4360 | fen_info->dst_len); | |
4361 | if (!fib_node) | |
9aecce1c IS |
4362 | return NULL; |
4363 | ||
4f1c7f1f IS |
4364 | list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { |
4365 | if (fib4_entry->tb_id == fen_info->tb_id && | |
4366 | fib4_entry->tos == fen_info->tos && | |
4367 | fib4_entry->type == fen_info->type && | |
ba31d366 AS |
4368 | mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) == |
4369 | fen_info->fi) { | |
4f1c7f1f | 4370 | return fib4_entry; |
9aecce1c IS |
4371 | } |
4372 | } | |
4373 | ||
4374 | return NULL; | |
4375 | } | |
4376 | ||
4377 | static const struct rhashtable_params mlxsw_sp_fib_ht_params = { | |
4378 | .key_offset = offsetof(struct mlxsw_sp_fib_node, key), | |
4379 | .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node), | |
4380 | .key_len = sizeof(struct mlxsw_sp_fib_key), | |
4381 | .automatic_shrinking = true, | |
4382 | }; | |
4383 | ||
4384 | static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib, | |
4385 | struct mlxsw_sp_fib_node *fib_node) | |
4386 | { | |
4387 | return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node, | |
4388 | mlxsw_sp_fib_ht_params); | |
4389 | } | |
4390 | ||
4391 | static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib, | |
4392 | struct mlxsw_sp_fib_node *fib_node) | |
4393 | { | |
4394 | rhashtable_remove_fast(&fib->ht, &fib_node->ht_node, | |
4395 | mlxsw_sp_fib_ht_params); | |
4396 | } | |
4397 | ||
4398 | static struct mlxsw_sp_fib_node * | |
4399 | mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, | |
4400 | size_t addr_len, unsigned char prefix_len) | |
4401 | { | |
4402 | struct mlxsw_sp_fib_key key; | |
4403 | ||
4404 | memset(&key, 0, sizeof(key)); | |
4405 | memcpy(key.addr, addr, addr_len); | |
4406 | key.prefix_len = prefix_len; | |
4407 | return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params); | |
4408 | } | |
4409 | ||
4410 | static struct mlxsw_sp_fib_node * | |
76610ebb | 4411 | mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr, |
9aecce1c IS |
4412 | size_t addr_len, unsigned char prefix_len) |
4413 | { | |
4414 | struct mlxsw_sp_fib_node *fib_node; | |
4415 | ||
4416 | fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL); | |
4417 | if (!fib_node) | |
5b004412 JP |
4418 | return NULL; |
4419 | ||
9aecce1c | 4420 | INIT_LIST_HEAD(&fib_node->entry_list); |
76610ebb | 4421 | list_add(&fib_node->list, &fib->node_list); |
9aecce1c IS |
4422 | memcpy(fib_node->key.addr, addr, addr_len); |
4423 | fib_node->key.prefix_len = prefix_len; | |
9aecce1c IS |
4424 | |
4425 | return fib_node; | |
4426 | } | |
4427 | ||
4428 | static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node) | |
4429 | { | |
9aecce1c IS |
4430 | list_del(&fib_node->list); |
4431 | WARN_ON(!list_empty(&fib_node->entry_list)); | |
4432 | kfree(fib_node); | |
4433 | } | |
4434 | ||
4435 | static bool | |
4436 | mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, | |
4437 | const struct mlxsw_sp_fib_entry *fib_entry) | |
4438 | { | |
4439 | return list_first_entry(&fib_node->entry_list, | |
4440 | struct mlxsw_sp_fib_entry, list) == fib_entry; | |
4441 | } | |
4442 | ||
fc922bb0 | 4443 | static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, |
fc922bb0 IS |
4444 | struct mlxsw_sp_fib_node *fib_node) |
4445 | { | |
2b52ce02 | 4446 | struct mlxsw_sp_prefix_usage req_prefix_usage; |
3aad95df | 4447 | struct mlxsw_sp_fib *fib = fib_node->fib; |
fc922bb0 IS |
4448 | struct mlxsw_sp_lpm_tree *lpm_tree; |
4449 | int err; | |
4450 | ||
2b52ce02 IS |
4451 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[fib->proto]; |
4452 | if (lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0) | |
4453 | goto out; | |
fc922bb0 | 4454 | |
2b52ce02 IS |
4455 | mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage); |
4456 | mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); | |
fc922bb0 IS |
4457 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, |
4458 | fib->proto); | |
4459 | if (IS_ERR(lpm_tree)) | |
4460 | return PTR_ERR(lpm_tree); | |
4461 | ||
fc922bb0 IS |
4462 | err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); |
4463 | if (err) | |
2b52ce02 | 4464 | goto err_lpm_tree_replace; |
fc922bb0 | 4465 | |
2b52ce02 IS |
4466 | out: |
4467 | lpm_tree->prefix_ref_count[fib_node->key.prefix_len]++; | |
fc922bb0 | 4468 | return 0; |
2b52ce02 IS |
4469 | |
4470 | err_lpm_tree_replace: | |
4471 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
4472 | return err; | |
fc922bb0 IS |
4473 | } |
4474 | ||
4475 | static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp, | |
3aad95df | 4476 | struct mlxsw_sp_fib_node *fib_node) |
fc922bb0 | 4477 | { |
2b52ce02 IS |
4478 | struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree; |
4479 | struct mlxsw_sp_prefix_usage req_prefix_usage; | |
3aad95df | 4480 | struct mlxsw_sp_fib *fib = fib_node->fib; |
2b52ce02 | 4481 | int err; |
3aad95df | 4482 | |
2b52ce02 | 4483 | if (--lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0) |
fc922bb0 | 4484 | return; |
2b52ce02 IS |
4485 | /* Try to construct a new LPM tree from the current prefix usage |
4486 | * minus the unused one. If we fail, continue using the old one. | |
4fd00312 | 4487 | */ |
2b52ce02 IS |
4488 | mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage); |
4489 | mlxsw_sp_prefix_usage_clear(&req_prefix_usage, | |
4490 | fib_node->key.prefix_len); | |
4491 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | |
4492 | fib->proto); | |
4493 | if (IS_ERR(lpm_tree)) | |
4494 | return; | |
9aecce1c | 4495 | |
2b52ce02 IS |
4496 | err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); |
4497 | if (err) | |
4498 | goto err_lpm_tree_replace; | |
9aecce1c | 4499 | |
2b52ce02 | 4500 | return; |
9aecce1c | 4501 | |
2b52ce02 IS |
4502 | err_lpm_tree_replace: |
4503 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | |
5b004412 JP |
4504 | } |
4505 | ||
76610ebb IS |
4506 | static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, |
4507 | struct mlxsw_sp_fib_node *fib_node, | |
4508 | struct mlxsw_sp_fib *fib) | |
4509 | { | |
76610ebb IS |
4510 | int err; |
4511 | ||
4512 | err = mlxsw_sp_fib_node_insert(fib, fib_node); | |
4513 | if (err) | |
4514 | return err; | |
4515 | fib_node->fib = fib; | |
4516 | ||
3aad95df | 4517 | err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib_node); |
fc922bb0 IS |
4518 | if (err) |
4519 | goto err_fib_lpm_tree_link; | |
76610ebb | 4520 | |
76610ebb IS |
4521 | return 0; |
4522 | ||
fc922bb0 | 4523 | err_fib_lpm_tree_link: |
76610ebb IS |
4524 | fib_node->fib = NULL; |
4525 | mlxsw_sp_fib_node_remove(fib, fib_node); | |
4526 | return err; | |
4527 | } | |
4528 | ||
4529 | static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, | |
4530 | struct mlxsw_sp_fib_node *fib_node) | |
4531 | { | |
76610ebb IS |
4532 | struct mlxsw_sp_fib *fib = fib_node->fib; |
4533 | ||
3aad95df | 4534 | mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib_node); |
76610ebb IS |
4535 | fib_node->fib = NULL; |
4536 | mlxsw_sp_fib_node_remove(fib, fib_node); | |
4537 | } | |
4538 | ||
9aecce1c | 4539 | static struct mlxsw_sp_fib_node * |
731ea1ca IS |
4540 | mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr, |
4541 | size_t addr_len, unsigned char prefix_len, | |
4542 | enum mlxsw_sp_l3proto proto) | |
5b004412 | 4543 | { |
9aecce1c | 4544 | struct mlxsw_sp_fib_node *fib_node; |
76610ebb | 4545 | struct mlxsw_sp_fib *fib; |
9aecce1c IS |
4546 | struct mlxsw_sp_vr *vr; |
4547 | int err; | |
4548 | ||
f8fa9b4e | 4549 | vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id, NULL); |
9aecce1c IS |
4550 | if (IS_ERR(vr)) |
4551 | return ERR_CAST(vr); | |
731ea1ca | 4552 | fib = mlxsw_sp_vr_fib(vr, proto); |
9aecce1c | 4553 | |
731ea1ca | 4554 | fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len); |
9aecce1c IS |
4555 | if (fib_node) |
4556 | return fib_node; | |
5b004412 | 4557 | |
731ea1ca | 4558 | fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len); |
9aecce1c IS |
4559 | if (!fib_node) { |
4560 | err = -ENOMEM; | |
4561 | goto err_fib_node_create; | |
5b004412 | 4562 | } |
9aecce1c | 4563 | |
76610ebb IS |
4564 | err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib); |
4565 | if (err) | |
4566 | goto err_fib_node_init; | |
4567 | ||
9aecce1c IS |
4568 | return fib_node; |
4569 | ||
76610ebb IS |
4570 | err_fib_node_init: |
4571 | mlxsw_sp_fib_node_destroy(fib_node); | |
9aecce1c | 4572 | err_fib_node_create: |
2b52ce02 | 4573 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
9aecce1c | 4574 | return ERR_PTR(err); |
5b004412 JP |
4575 | } |
4576 | ||
731ea1ca IS |
4577 | static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, |
4578 | struct mlxsw_sp_fib_node *fib_node) | |
5b004412 | 4579 | { |
76610ebb | 4580 | struct mlxsw_sp_vr *vr = fib_node->fib->vr; |
5b004412 | 4581 | |
9aecce1c IS |
4582 | if (!list_empty(&fib_node->entry_list)) |
4583 | return; | |
76610ebb | 4584 | mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node); |
9aecce1c | 4585 | mlxsw_sp_fib_node_destroy(fib_node); |
2b52ce02 | 4586 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
61c503f9 JP |
4587 | } |
4588 | ||
4f1c7f1f | 4589 | static struct mlxsw_sp_fib4_entry * |
9aecce1c | 4590 | mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, |
4f1c7f1f | 4591 | const struct mlxsw_sp_fib4_entry *new4_entry) |
61c503f9 | 4592 | { |
4f1c7f1f | 4593 | struct mlxsw_sp_fib4_entry *fib4_entry; |
9aecce1c | 4594 | |
4f1c7f1f IS |
4595 | list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { |
4596 | if (fib4_entry->tb_id > new4_entry->tb_id) | |
9aecce1c | 4597 | continue; |
4f1c7f1f | 4598 | if (fib4_entry->tb_id != new4_entry->tb_id) |
9aecce1c | 4599 | break; |
4f1c7f1f | 4600 | if (fib4_entry->tos > new4_entry->tos) |
9aecce1c | 4601 | continue; |
4f1c7f1f IS |
4602 | if (fib4_entry->prio >= new4_entry->prio || |
4603 | fib4_entry->tos < new4_entry->tos) | |
4604 | return fib4_entry; | |
9aecce1c IS |
4605 | } |
4606 | ||
4607 | return NULL; | |
4608 | } | |
4609 | ||
4f1c7f1f IS |
4610 | static int |
4611 | mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry, | |
4612 | struct mlxsw_sp_fib4_entry *new4_entry) | |
4283bce5 IS |
4613 | { |
4614 | struct mlxsw_sp_fib_node *fib_node; | |
4615 | ||
4f1c7f1f | 4616 | if (WARN_ON(!fib4_entry)) |
4283bce5 IS |
4617 | return -EINVAL; |
4618 | ||
4f1c7f1f IS |
4619 | fib_node = fib4_entry->common.fib_node; |
4620 | list_for_each_entry_from(fib4_entry, &fib_node->entry_list, | |
4621 | common.list) { | |
4622 | if (fib4_entry->tb_id != new4_entry->tb_id || | |
4623 | fib4_entry->tos != new4_entry->tos || | |
4624 | fib4_entry->prio != new4_entry->prio) | |
4283bce5 IS |
4625 | break; |
4626 | } | |
4627 | ||
4f1c7f1f | 4628 | list_add_tail(&new4_entry->common.list, &fib4_entry->common.list); |
4283bce5 IS |
4629 | return 0; |
4630 | } | |
4631 | ||
9aecce1c | 4632 | static int |
9efbee6f | 4633 | mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry, |
599cf8f9 | 4634 | bool replace, bool append) |
9aecce1c | 4635 | { |
9efbee6f | 4636 | struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node; |
4f1c7f1f | 4637 | struct mlxsw_sp_fib4_entry *fib4_entry; |
9aecce1c | 4638 | |
4f1c7f1f | 4639 | fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry); |
9aecce1c | 4640 | |
4283bce5 | 4641 | if (append) |
4f1c7f1f IS |
4642 | return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry); |
4643 | if (replace && WARN_ON(!fib4_entry)) | |
599cf8f9 | 4644 | return -EINVAL; |
4283bce5 | 4645 | |
599cf8f9 IS |
4646 | /* Insert new entry before replaced one, so that we can later |
4647 | * remove the second. | |
4648 | */ | |
4f1c7f1f IS |
4649 | if (fib4_entry) { |
4650 | list_add_tail(&new4_entry->common.list, | |
4651 | &fib4_entry->common.list); | |
9aecce1c | 4652 | } else { |
4f1c7f1f | 4653 | struct mlxsw_sp_fib4_entry *last; |
9aecce1c | 4654 | |
4f1c7f1f IS |
4655 | list_for_each_entry(last, &fib_node->entry_list, common.list) { |
4656 | if (new4_entry->tb_id > last->tb_id) | |
9aecce1c | 4657 | break; |
4f1c7f1f | 4658 | fib4_entry = last; |
9aecce1c IS |
4659 | } |
4660 | ||
4f1c7f1f IS |
4661 | if (fib4_entry) |
4662 | list_add(&new4_entry->common.list, | |
4663 | &fib4_entry->common.list); | |
9aecce1c | 4664 | else |
4f1c7f1f IS |
4665 | list_add(&new4_entry->common.list, |
4666 | &fib_node->entry_list); | |
9aecce1c IS |
4667 | } |
4668 | ||
4669 | return 0; | |
4670 | } | |
4671 | ||
4672 | static void | |
4f1c7f1f | 4673 | mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) |
9aecce1c | 4674 | { |
4f1c7f1f | 4675 | list_del(&fib4_entry->common.list); |
9aecce1c IS |
4676 | } |
4677 | ||
80c238f9 IS |
4678 | static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, |
4679 | struct mlxsw_sp_fib_entry *fib_entry) | |
9aecce1c | 4680 | { |
9efbee6f IS |
4681 | struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; |
4682 | ||
9aecce1c IS |
4683 | if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) |
4684 | return 0; | |
4685 | ||
4686 | /* To prevent packet loss, overwrite the previously offloaded | |
4687 | * entry. | |
4688 | */ | |
4689 | if (!list_is_singular(&fib_node->entry_list)) { | |
4690 | enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE; | |
4691 | struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list); | |
4692 | ||
4693 | mlxsw_sp_fib_entry_offload_refresh(n, op, 0); | |
4694 | } | |
4695 | ||
4696 | return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); | |
4697 | } | |
4698 | ||
80c238f9 IS |
4699 | static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, |
4700 | struct mlxsw_sp_fib_entry *fib_entry) | |
9aecce1c | 4701 | { |
9efbee6f IS |
4702 | struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; |
4703 | ||
9aecce1c IS |
4704 | if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) |
4705 | return; | |
4706 | ||
4707 | /* Promote the next entry by overwriting the deleted entry */ | |
4708 | if (!list_is_singular(&fib_node->entry_list)) { | |
4709 | struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list); | |
4710 | enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE; | |
4711 | ||
4712 | mlxsw_sp_fib_entry_update(mlxsw_sp, n); | |
4713 | mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); | |
4714 | return; | |
4715 | } | |
4716 | ||
4717 | mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry); | |
4718 | } | |
4719 | ||
4720 | static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, | |
4f1c7f1f | 4721 | struct mlxsw_sp_fib4_entry *fib4_entry, |
599cf8f9 | 4722 | bool replace, bool append) |
9aecce1c | 4723 | { |
9aecce1c IS |
4724 | int err; |
4725 | ||
9efbee6f | 4726 | err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append); |
9aecce1c IS |
4727 | if (err) |
4728 | return err; | |
4729 | ||
80c238f9 | 4730 | err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common); |
9aecce1c | 4731 | if (err) |
80c238f9 | 4732 | goto err_fib_node_entry_add; |
9aecce1c | 4733 | |
9aecce1c IS |
4734 | return 0; |
4735 | ||
80c238f9 | 4736 | err_fib_node_entry_add: |
4f1c7f1f | 4737 | mlxsw_sp_fib4_node_list_remove(fib4_entry); |
9aecce1c IS |
4738 | return err; |
4739 | } | |
4740 | ||
4741 | static void | |
4742 | mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, | |
4f1c7f1f | 4743 | struct mlxsw_sp_fib4_entry *fib4_entry) |
9aecce1c | 4744 | { |
80c238f9 | 4745 | mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); |
4f1c7f1f | 4746 | mlxsw_sp_fib4_node_list_remove(fib4_entry); |
4607f6d2 PM |
4747 | |
4748 | if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) | |
4749 | mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common); | |
9aecce1c IS |
4750 | } |
4751 | ||
599cf8f9 | 4752 | static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, |
4f1c7f1f | 4753 | struct mlxsw_sp_fib4_entry *fib4_entry, |
599cf8f9 IS |
4754 | bool replace) |
4755 | { | |
4f1c7f1f IS |
4756 | struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; |
4757 | struct mlxsw_sp_fib4_entry *replaced; | |
599cf8f9 IS |
4758 | |
4759 | if (!replace) | |
4760 | return; | |
4761 | ||
4762 | /* We inserted the new entry before replaced one */ | |
4f1c7f1f | 4763 | replaced = list_next_entry(fib4_entry, common.list); |
599cf8f9 IS |
4764 | |
4765 | mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced); | |
4766 | mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); | |
731ea1ca | 4767 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); |
599cf8f9 IS |
4768 | } |
4769 | ||
9aecce1c IS |
4770 | static int |
4771 | mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, | |
4283bce5 | 4772 | const struct fib_entry_notifier_info *fen_info, |
599cf8f9 | 4773 | bool replace, bool append) |
9aecce1c | 4774 | { |
4f1c7f1f | 4775 | struct mlxsw_sp_fib4_entry *fib4_entry; |
9aecce1c | 4776 | struct mlxsw_sp_fib_node *fib_node; |
61c503f9 JP |
4777 | int err; |
4778 | ||
9011b677 | 4779 | if (mlxsw_sp->router->aborted) |
b45f64d1 JP |
4780 | return 0; |
4781 | ||
731ea1ca IS |
4782 | fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id, |
4783 | &fen_info->dst, sizeof(fen_info->dst), | |
4784 | fen_info->dst_len, | |
4785 | MLXSW_SP_L3_PROTO_IPV4); | |
9aecce1c IS |
4786 | if (IS_ERR(fib_node)) { |
4787 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n"); | |
4788 | return PTR_ERR(fib_node); | |
b45f64d1 | 4789 | } |
61c503f9 | 4790 | |
4f1c7f1f IS |
4791 | fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info); |
4792 | if (IS_ERR(fib4_entry)) { | |
9aecce1c | 4793 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n"); |
4f1c7f1f | 4794 | err = PTR_ERR(fib4_entry); |
9aecce1c IS |
4795 | goto err_fib4_entry_create; |
4796 | } | |
5b004412 | 4797 | |
4f1c7f1f | 4798 | err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace, |
599cf8f9 | 4799 | append); |
b45f64d1 | 4800 | if (err) { |
9aecce1c IS |
4801 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); |
4802 | goto err_fib4_node_entry_link; | |
b45f64d1 | 4803 | } |
9aecce1c | 4804 | |
4f1c7f1f | 4805 | mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace); |
599cf8f9 | 4806 | |
61c503f9 JP |
4807 | return 0; |
4808 | ||
9aecce1c | 4809 | err_fib4_node_entry_link: |
4f1c7f1f | 4810 | mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); |
9aecce1c | 4811 | err_fib4_entry_create: |
731ea1ca | 4812 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); |
61c503f9 JP |
4813 | return err; |
4814 | } | |
4815 | ||
37956d78 JP |
4816 | static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, |
4817 | struct fib_entry_notifier_info *fen_info) | |
61c503f9 | 4818 | { |
4f1c7f1f | 4819 | struct mlxsw_sp_fib4_entry *fib4_entry; |
9aecce1c | 4820 | struct mlxsw_sp_fib_node *fib_node; |
61c503f9 | 4821 | |
9011b677 | 4822 | if (mlxsw_sp->router->aborted) |
37956d78 | 4823 | return; |
b45f64d1 | 4824 | |
4f1c7f1f IS |
4825 | fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info); |
4826 | if (WARN_ON(!fib4_entry)) | |
37956d78 | 4827 | return; |
4f1c7f1f | 4828 | fib_node = fib4_entry->common.fib_node; |
5b004412 | 4829 | |
4f1c7f1f IS |
4830 | mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); |
4831 | mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); | |
731ea1ca | 4832 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); |
61c503f9 | 4833 | } |
b45f64d1 | 4834 | |
8d1c802b | 4835 | static bool mlxsw_sp_fib6_rt_should_ignore(const struct fib6_info *rt) |
428b851f IS |
4836 | { |
4837 | /* Packets with link-local destination IP arriving to the router | |
4838 | * are trapped to the CPU, so no need to program specific routes | |
4839 | * for them. | |
4840 | */ | |
93c2fb25 | 4841 | if (ipv6_addr_type(&rt->fib6_dst.addr) & IPV6_ADDR_LINKLOCAL) |
428b851f IS |
4842 | return true; |
4843 | ||
4844 | /* Multicast routes aren't supported, so ignore them. Neighbour | |
4845 | * Discovery packets are specifically trapped. | |
4846 | */ | |
93c2fb25 | 4847 | if (ipv6_addr_type(&rt->fib6_dst.addr) & IPV6_ADDR_MULTICAST) |
428b851f IS |
4848 | return true; |
4849 | ||
4850 | /* Cloned routes are irrelevant in the forwarding path. */ | |
93c2fb25 | 4851 | if (rt->fib6_flags & RTF_CACHE) |
428b851f IS |
4852 | return true; |
4853 | ||
4854 | return false; | |
4855 | } | |
4856 | ||
8d1c802b | 4857 | static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct fib6_info *rt) |
428b851f IS |
4858 | { |
4859 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
4860 | ||
4861 | mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL); | |
4862 | if (!mlxsw_sp_rt6) | |
4863 | return ERR_PTR(-ENOMEM); | |
4864 | ||
4865 | /* In case of route replace, replaced route is deleted with | |
4866 | * no notification. Take reference to prevent accessing freed | |
4867 | * memory. | |
4868 | */ | |
4869 | mlxsw_sp_rt6->rt = rt; | |
8d1c802b | 4870 | fib6_info_hold(rt); |
428b851f IS |
4871 | |
4872 | return mlxsw_sp_rt6; | |
4873 | } | |
4874 | ||
4875 | #if IS_ENABLED(CONFIG_IPV6) | |
8d1c802b | 4876 | static void mlxsw_sp_rt6_release(struct fib6_info *rt) |
428b851f | 4877 | { |
8d1c802b | 4878 | fib6_info_release(rt); |
428b851f IS |
4879 | } |
4880 | #else | |
8d1c802b | 4881 | static void mlxsw_sp_rt6_release(struct fib6_info *rt) |
428b851f IS |
4882 | { |
4883 | } | |
4884 | #endif | |
4885 | ||
4886 | static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6) | |
4887 | { | |
4888 | mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt); | |
4889 | kfree(mlxsw_sp_rt6); | |
4890 | } | |
4891 | ||
33bd5ac5 DA |
4892 | static bool mlxsw_sp_fib6_rt_can_mp(const struct fib6_info *rt) |
4893 | { | |
4894 | /* RTF_CACHE routes are ignored */ | |
4895 | return (rt->fib6_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY; | |
4896 | } | |
4897 | ||
8d1c802b | 4898 | static struct fib6_info * |
428b851f IS |
4899 | mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) |
4900 | { | |
4901 | return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, | |
4902 | list)->rt; | |
4903 | } | |
4904 | ||
4905 | static struct mlxsw_sp_fib6_entry * | |
4906 | mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, | |
33bd5ac5 | 4907 | const struct fib6_info *nrt, bool replace) |
428b851f IS |
4908 | { |
4909 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
4910 | ||
33bd5ac5 | 4911 | if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace) |
428b851f IS |
4912 | return NULL; |
4913 | ||
4914 | list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { | |
8d1c802b | 4915 | struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); |
428b851f IS |
4916 | |
4917 | /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same | |
4918 | * virtual router. | |
4919 | */ | |
93c2fb25 | 4920 | if (rt->fib6_table->tb6_id > nrt->fib6_table->tb6_id) |
428b851f | 4921 | continue; |
93c2fb25 | 4922 | if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id) |
428b851f | 4923 | break; |
93c2fb25 | 4924 | if (rt->fib6_metric < nrt->fib6_metric) |
428b851f | 4925 | continue; |
33bd5ac5 DA |
4926 | if (rt->fib6_metric == nrt->fib6_metric && |
4927 | mlxsw_sp_fib6_rt_can_mp(rt)) | |
428b851f | 4928 | return fib6_entry; |
93c2fb25 | 4929 | if (rt->fib6_metric > nrt->fib6_metric) |
428b851f IS |
4930 | break; |
4931 | } | |
4932 | ||
4933 | return NULL; | |
4934 | } | |
4935 | ||
4936 | static struct mlxsw_sp_rt6 * | |
4937 | mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, | |
8d1c802b | 4938 | const struct fib6_info *rt) |
428b851f IS |
4939 | { |
4940 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
4941 | ||
4942 | list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { | |
4943 | if (mlxsw_sp_rt6->rt == rt) | |
4944 | return mlxsw_sp_rt6; | |
4945 | } | |
4946 | ||
4947 | return NULL; | |
4948 | } | |
4949 | ||
8f28a309 | 4950 | static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp, |
8d1c802b | 4951 | const struct fib6_info *rt, |
8f28a309 PM |
4952 | enum mlxsw_sp_ipip_type *ret) |
4953 | { | |
5e670d84 DA |
4954 | return rt->fib6_nh.nh_dev && |
4955 | mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh.nh_dev, ret); | |
8f28a309 PM |
4956 | } |
4957 | ||
35225e47 PM |
4958 | static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp, |
4959 | struct mlxsw_sp_nexthop_group *nh_grp, | |
4960 | struct mlxsw_sp_nexthop *nh, | |
8d1c802b | 4961 | const struct fib6_info *rt) |
428b851f | 4962 | { |
d97cda5f PM |
4963 | const struct mlxsw_sp_ipip_ops *ipip_ops; |
4964 | struct mlxsw_sp_ipip_entry *ipip_entry; | |
5e670d84 | 4965 | struct net_device *dev = rt->fib6_nh.nh_dev; |
428b851f IS |
4966 | struct mlxsw_sp_rif *rif; |
4967 | int err; | |
4968 | ||
d97cda5f PM |
4969 | ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev); |
4970 | if (ipip_entry) { | |
4971 | ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; | |
4972 | if (ipip_ops->can_offload(mlxsw_sp, dev, | |
4973 | MLXSW_SP_L3_PROTO_IPV6)) { | |
4974 | nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; | |
4975 | mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry); | |
4976 | return 0; | |
4977 | } | |
8f28a309 PM |
4978 | } |
4979 | ||
35225e47 | 4980 | nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; |
428b851f IS |
4981 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); |
4982 | if (!rif) | |
4983 | return 0; | |
4984 | mlxsw_sp_nexthop_rif_init(nh, rif); | |
4985 | ||
4986 | err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); | |
4987 | if (err) | |
4988 | goto err_nexthop_neigh_init; | |
4989 | ||
4990 | return 0; | |
4991 | ||
4992 | err_nexthop_neigh_init: | |
4993 | mlxsw_sp_nexthop_rif_fini(nh); | |
4994 | return err; | |
4995 | } | |
4996 | ||
35225e47 PM |
4997 | static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp, |
4998 | struct mlxsw_sp_nexthop *nh) | |
4999 | { | |
5000 | mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); | |
5001 | } | |
5002 | ||
5003 | static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, | |
5004 | struct mlxsw_sp_nexthop_group *nh_grp, | |
5005 | struct mlxsw_sp_nexthop *nh, | |
8d1c802b | 5006 | const struct fib6_info *rt) |
35225e47 | 5007 | { |
5e670d84 | 5008 | struct net_device *dev = rt->fib6_nh.nh_dev; |
35225e47 PM |
5009 | |
5010 | nh->nh_grp = nh_grp; | |
5e670d84 DA |
5011 | nh->nh_weight = rt->fib6_nh.nh_weight; |
5012 | memcpy(&nh->gw_addr, &rt->fib6_nh.nh_gw, sizeof(nh->gw_addr)); | |
a5390278 | 5013 | mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh); |
35225e47 | 5014 | |
dbe4598c AS |
5015 | list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list); |
5016 | ||
35225e47 PM |
5017 | if (!dev) |
5018 | return 0; | |
5019 | nh->ifindex = dev->ifindex; | |
5020 | ||
5021 | return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt); | |
5022 | } | |
5023 | ||
428b851f IS |
5024 | static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, |
5025 | struct mlxsw_sp_nexthop *nh) | |
5026 | { | |
35225e47 | 5027 | mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh); |
dbe4598c | 5028 | list_del(&nh->router_list_node); |
a5390278 | 5029 | mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); |
428b851f IS |
5030 | } |
5031 | ||
f6050ee6 | 5032 | static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, |
8d1c802b | 5033 | const struct fib6_info *rt) |
f6050ee6 | 5034 | { |
93c2fb25 | 5035 | return rt->fib6_flags & RTF_GATEWAY || |
8f28a309 | 5036 | mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL); |
f6050ee6 PM |
5037 | } |
5038 | ||
428b851f IS |
5039 | static struct mlxsw_sp_nexthop_group * |
5040 | mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, | |
5041 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
5042 | { | |
5043 | struct mlxsw_sp_nexthop_group *nh_grp; | |
5044 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
5045 | struct mlxsw_sp_nexthop *nh; | |
5046 | size_t alloc_size; | |
5047 | int i = 0; | |
5048 | int err; | |
5049 | ||
5050 | alloc_size = sizeof(*nh_grp) + | |
5051 | fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop); | |
5052 | nh_grp = kzalloc(alloc_size, GFP_KERNEL); | |
5053 | if (!nh_grp) | |
5054 | return ERR_PTR(-ENOMEM); | |
5055 | INIT_LIST_HEAD(&nh_grp->fib_list); | |
5056 | #if IS_ENABLED(CONFIG_IPV6) | |
5057 | nh_grp->neigh_tbl = &nd_tbl; | |
5058 | #endif | |
5059 | mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list, | |
5060 | struct mlxsw_sp_rt6, list); | |
f6050ee6 | 5061 | nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt); |
428b851f IS |
5062 | nh_grp->count = fib6_entry->nrt6; |
5063 | for (i = 0; i < nh_grp->count; i++) { | |
8d1c802b | 5064 | struct fib6_info *rt = mlxsw_sp_rt6->rt; |
428b851f IS |
5065 | |
5066 | nh = &nh_grp->nexthops[i]; | |
5067 | err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt); | |
5068 | if (err) | |
5069 | goto err_nexthop6_init; | |
5070 | mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list); | |
5071 | } | |
e6f3b379 AS |
5072 | |
5073 | err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); | |
5074 | if (err) | |
5075 | goto err_nexthop_group_insert; | |
5076 | ||
428b851f IS |
5077 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); |
5078 | return nh_grp; | |
5079 | ||
e6f3b379 | 5080 | err_nexthop_group_insert: |
428b851f IS |
5081 | err_nexthop6_init: |
5082 | for (i--; i >= 0; i--) { | |
5083 | nh = &nh_grp->nexthops[i]; | |
5084 | mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); | |
5085 | } | |
5086 | kfree(nh_grp); | |
5087 | return ERR_PTR(err); | |
5088 | } | |
5089 | ||
5090 | static void | |
5091 | mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp, | |
5092 | struct mlxsw_sp_nexthop_group *nh_grp) | |
5093 | { | |
5094 | struct mlxsw_sp_nexthop *nh; | |
5095 | int i = nh_grp->count; | |
5096 | ||
e6f3b379 | 5097 | mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); |
428b851f IS |
5098 | for (i--; i >= 0; i--) { |
5099 | nh = &nh_grp->nexthops[i]; | |
5100 | mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); | |
5101 | } | |
5102 | mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); | |
5103 | WARN_ON(nh_grp->adj_index_valid); | |
5104 | kfree(nh_grp); | |
5105 | } | |
5106 | ||
5107 | static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp, | |
5108 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
5109 | { | |
5110 | struct mlxsw_sp_nexthop_group *nh_grp; | |
5111 | ||
e6f3b379 AS |
5112 | nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry); |
5113 | if (!nh_grp) { | |
5114 | nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry); | |
5115 | if (IS_ERR(nh_grp)) | |
5116 | return PTR_ERR(nh_grp); | |
5117 | } | |
428b851f IS |
5118 | |
5119 | list_add_tail(&fib6_entry->common.nexthop_group_node, | |
5120 | &nh_grp->fib_list); | |
5121 | fib6_entry->common.nh_group = nh_grp; | |
5122 | ||
5123 | return 0; | |
5124 | } | |
5125 | ||
5126 | static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp, | |
5127 | struct mlxsw_sp_fib_entry *fib_entry) | |
5128 | { | |
5129 | struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; | |
5130 | ||
5131 | list_del(&fib_entry->nexthop_group_node); | |
5132 | if (!list_empty(&nh_grp->fib_list)) | |
5133 | return; | |
5134 | mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp); | |
5135 | } | |
5136 | ||
5137 | static int | |
5138 | mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp, | |
5139 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
5140 | { | |
5141 | struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group; | |
5142 | int err; | |
5143 | ||
5144 | fib6_entry->common.nh_group = NULL; | |
5145 | list_del(&fib6_entry->common.nexthop_group_node); | |
5146 | ||
5147 | err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); | |
5148 | if (err) | |
5149 | goto err_nexthop6_group_get; | |
5150 | ||
5151 | /* In case this entry is offloaded, then the adjacency index | |
5152 | * currently associated with it in the device's table is that | |
5153 | * of the old group. Start using the new one instead. | |
5154 | */ | |
5155 | err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); | |
5156 | if (err) | |
5157 | goto err_fib_node_entry_add; | |
5158 | ||
5159 | if (list_empty(&old_nh_grp->fib_list)) | |
5160 | mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp); | |
5161 | ||
5162 | return 0; | |
5163 | ||
5164 | err_fib_node_entry_add: | |
5165 | mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); | |
5166 | err_nexthop6_group_get: | |
5167 | list_add_tail(&fib6_entry->common.nexthop_group_node, | |
5168 | &old_nh_grp->fib_list); | |
5169 | fib6_entry->common.nh_group = old_nh_grp; | |
5170 | return err; | |
5171 | } | |
5172 | ||
5173 | static int | |
5174 | mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp, | |
5175 | struct mlxsw_sp_fib6_entry *fib6_entry, | |
8d1c802b | 5176 | struct fib6_info *rt) |
428b851f IS |
5177 | { |
5178 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
5179 | int err; | |
5180 | ||
5181 | mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); | |
5182 | if (IS_ERR(mlxsw_sp_rt6)) | |
5183 | return PTR_ERR(mlxsw_sp_rt6); | |
5184 | ||
5185 | list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); | |
5186 | fib6_entry->nrt6++; | |
5187 | ||
5188 | err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); | |
5189 | if (err) | |
5190 | goto err_nexthop6_group_update; | |
5191 | ||
5192 | return 0; | |
5193 | ||
5194 | err_nexthop6_group_update: | |
5195 | fib6_entry->nrt6--; | |
5196 | list_del(&mlxsw_sp_rt6->list); | |
5197 | mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); | |
5198 | return err; | |
5199 | } | |
5200 | ||
5201 | static void | |
5202 | mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp, | |
5203 | struct mlxsw_sp_fib6_entry *fib6_entry, | |
8d1c802b | 5204 | struct fib6_info *rt) |
428b851f IS |
5205 | { |
5206 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
5207 | ||
5208 | mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt); | |
5209 | if (WARN_ON(!mlxsw_sp_rt6)) | |
5210 | return; | |
5211 | ||
5212 | fib6_entry->nrt6--; | |
5213 | list_del(&mlxsw_sp_rt6->list); | |
5214 | mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); | |
5215 | mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); | |
5216 | } | |
5217 | ||
f6050ee6 PM |
5218 | static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, |
5219 | struct mlxsw_sp_fib_entry *fib_entry, | |
8d1c802b | 5220 | const struct fib6_info *rt) |
428b851f IS |
5221 | { |
5222 | /* Packets hitting RTF_REJECT routes need to be discarded by the | |
5223 | * stack. We can rely on their destination device not having a | |
5224 | * RIF (it's the loopback device) and can thus use action type | |
5225 | * local, which will cause them to be trapped with a lower | |
5226 | * priority than packets that need to be locally received. | |
5227 | */ | |
93c2fb25 | 5228 | if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) |
428b851f | 5229 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; |
93c2fb25 | 5230 | else if (rt->fib6_flags & RTF_REJECT) |
428b851f | 5231 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; |
f6050ee6 | 5232 | else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt)) |
428b851f IS |
5233 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; |
5234 | else | |
5235 | fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; | |
5236 | } | |
5237 | ||
5238 | static void | |
5239 | mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry) | |
5240 | { | |
5241 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp; | |
5242 | ||
5243 | list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list, | |
5244 | list) { | |
5245 | fib6_entry->nrt6--; | |
5246 | list_del(&mlxsw_sp_rt6->list); | |
5247 | mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); | |
5248 | } | |
5249 | } | |
5250 | ||
5251 | static struct mlxsw_sp_fib6_entry * | |
5252 | mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp, | |
5253 | struct mlxsw_sp_fib_node *fib_node, | |
8d1c802b | 5254 | struct fib6_info *rt) |
428b851f IS |
5255 | { |
5256 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
5257 | struct mlxsw_sp_fib_entry *fib_entry; | |
5258 | struct mlxsw_sp_rt6 *mlxsw_sp_rt6; | |
5259 | int err; | |
5260 | ||
5261 | fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL); | |
5262 | if (!fib6_entry) | |
5263 | return ERR_PTR(-ENOMEM); | |
5264 | fib_entry = &fib6_entry->common; | |
5265 | ||
5266 | mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); | |
5267 | if (IS_ERR(mlxsw_sp_rt6)) { | |
5268 | err = PTR_ERR(mlxsw_sp_rt6); | |
5269 | goto err_rt6_create; | |
5270 | } | |
5271 | ||
f6050ee6 | 5272 | mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt); |
428b851f IS |
5273 | |
5274 | INIT_LIST_HEAD(&fib6_entry->rt6_list); | |
5275 | list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); | |
5276 | fib6_entry->nrt6 = 1; | |
5277 | err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); | |
5278 | if (err) | |
5279 | goto err_nexthop6_group_get; | |
5280 | ||
5281 | fib_entry->fib_node = fib_node; | |
5282 | ||
5283 | return fib6_entry; | |
5284 | ||
5285 | err_nexthop6_group_get: | |
5286 | list_del(&mlxsw_sp_rt6->list); | |
5287 | mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); | |
5288 | err_rt6_create: | |
5289 | kfree(fib6_entry); | |
5290 | return ERR_PTR(err); | |
5291 | } | |
5292 | ||
5293 | static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, | |
5294 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
5295 | { | |
5296 | mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); | |
5297 | mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry); | |
5298 | WARN_ON(fib6_entry->nrt6); | |
5299 | kfree(fib6_entry); | |
5300 | } | |
5301 | ||
5302 | static struct mlxsw_sp_fib6_entry * | |
5303 | mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, | |
8d1c802b | 5304 | const struct fib6_info *nrt, bool replace) |
428b851f | 5305 | { |
33bd5ac5 | 5306 | struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL; |
428b851f IS |
5307 | |
5308 | list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { | |
8d1c802b | 5309 | struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); |
428b851f | 5310 | |
93c2fb25 | 5311 | if (rt->fib6_table->tb6_id > nrt->fib6_table->tb6_id) |
428b851f | 5312 | continue; |
93c2fb25 | 5313 | if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id) |
428b851f | 5314 | break; |
33bd5ac5 DA |
5315 | if (replace && rt->fib6_metric == nrt->fib6_metric) { |
5316 | if (mlxsw_sp_fib6_rt_can_mp(rt) == | |
5317 | mlxsw_sp_fib6_rt_can_mp(nrt)) | |
5318 | return fib6_entry; | |
5319 | if (mlxsw_sp_fib6_rt_can_mp(nrt)) | |
5320 | fallback = fallback ?: fib6_entry; | |
5321 | } | |
93c2fb25 | 5322 | if (rt->fib6_metric > nrt->fib6_metric) |
33bd5ac5 | 5323 | return fallback ?: fib6_entry; |
428b851f IS |
5324 | } |
5325 | ||
33bd5ac5 | 5326 | return fallback; |
428b851f IS |
5327 | } |
5328 | ||
5329 | static int | |
0a7fd1ac IS |
5330 | mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry, |
5331 | bool replace) | |
428b851f IS |
5332 | { |
5333 | struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node; | |
8d1c802b | 5334 | struct fib6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry); |
428b851f IS |
5335 | struct mlxsw_sp_fib6_entry *fib6_entry; |
5336 | ||
0a7fd1ac IS |
5337 | fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace); |
5338 | ||
5339 | if (replace && WARN_ON(!fib6_entry)) | |
5340 | return -EINVAL; | |
428b851f IS |
5341 | |
5342 | if (fib6_entry) { | |
5343 | list_add_tail(&new6_entry->common.list, | |
5344 | &fib6_entry->common.list); | |
5345 | } else { | |
5346 | struct mlxsw_sp_fib6_entry *last; | |
5347 | ||
5348 | list_for_each_entry(last, &fib_node->entry_list, common.list) { | |
8d1c802b | 5349 | struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(last); |
428b851f | 5350 | |
93c2fb25 | 5351 | if (nrt->fib6_table->tb6_id > rt->fib6_table->tb6_id) |
428b851f IS |
5352 | break; |
5353 | fib6_entry = last; | |
5354 | } | |
5355 | ||
5356 | if (fib6_entry) | |
5357 | list_add(&new6_entry->common.list, | |
5358 | &fib6_entry->common.list); | |
5359 | else | |
5360 | list_add(&new6_entry->common.list, | |
5361 | &fib_node->entry_list); | |
5362 | } | |
5363 | ||
5364 | return 0; | |
5365 | } | |
5366 | ||
5367 | static void | |
5368 | mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry) | |
5369 | { | |
5370 | list_del(&fib6_entry->common.list); | |
5371 | } | |
5372 | ||
5373 | static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, | |
0a7fd1ac IS |
5374 | struct mlxsw_sp_fib6_entry *fib6_entry, |
5375 | bool replace) | |
428b851f IS |
5376 | { |
5377 | int err; | |
5378 | ||
0a7fd1ac | 5379 | err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace); |
428b851f IS |
5380 | if (err) |
5381 | return err; | |
5382 | ||
5383 | err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); | |
5384 | if (err) | |
5385 | goto err_fib_node_entry_add; | |
5386 | ||
5387 | return 0; | |
5388 | ||
5389 | err_fib_node_entry_add: | |
5390 | mlxsw_sp_fib6_node_list_remove(fib6_entry); | |
5391 | return err; | |
5392 | } | |
5393 | ||
5394 | static void | |
5395 | mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, | |
5396 | struct mlxsw_sp_fib6_entry *fib6_entry) | |
5397 | { | |
5398 | mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common); | |
5399 | mlxsw_sp_fib6_node_list_remove(fib6_entry); | |
5400 | } | |
5401 | ||
5402 | static struct mlxsw_sp_fib6_entry * | |
5403 | mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, | |
8d1c802b | 5404 | const struct fib6_info *rt) |
428b851f IS |
5405 | { |
5406 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
5407 | struct mlxsw_sp_fib_node *fib_node; | |
5408 | struct mlxsw_sp_fib *fib; | |
5409 | struct mlxsw_sp_vr *vr; | |
5410 | ||
93c2fb25 | 5411 | vr = mlxsw_sp_vr_find(mlxsw_sp, rt->fib6_table->tb6_id); |
428b851f IS |
5412 | if (!vr) |
5413 | return NULL; | |
5414 | fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6); | |
5415 | ||
93c2fb25 DA |
5416 | fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->fib6_dst.addr, |
5417 | sizeof(rt->fib6_dst.addr), | |
5418 | rt->fib6_dst.plen); | |
428b851f IS |
5419 | if (!fib_node) |
5420 | return NULL; | |
5421 | ||
5422 | list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { | |
8d1c802b | 5423 | struct fib6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry); |
428b851f | 5424 | |
93c2fb25 DA |
5425 | if (rt->fib6_table->tb6_id == iter_rt->fib6_table->tb6_id && |
5426 | rt->fib6_metric == iter_rt->fib6_metric && | |
428b851f IS |
5427 | mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt)) |
5428 | return fib6_entry; | |
5429 | } | |
5430 | ||
5431 | return NULL; | |
5432 | } | |
5433 | ||
0a7fd1ac IS |
5434 | static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, |
5435 | struct mlxsw_sp_fib6_entry *fib6_entry, | |
5436 | bool replace) | |
5437 | { | |
5438 | struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; | |
5439 | struct mlxsw_sp_fib6_entry *replaced; | |
5440 | ||
5441 | if (!replace) | |
5442 | return; | |
5443 | ||
5444 | replaced = list_next_entry(fib6_entry, common.list); | |
5445 | ||
5446 | mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced); | |
5447 | mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced); | |
5448 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); | |
5449 | } | |
5450 | ||
428b851f | 5451 | static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, |
33bd5ac5 | 5452 | struct fib6_info *rt, bool replace) |
428b851f IS |
5453 | { |
5454 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
5455 | struct mlxsw_sp_fib_node *fib_node; | |
5456 | int err; | |
5457 | ||
5458 | if (mlxsw_sp->router->aborted) | |
5459 | return 0; | |
5460 | ||
93c2fb25 | 5461 | if (rt->fib6_src.plen) |
f36f5ac6 IS |
5462 | return -EINVAL; |
5463 | ||
428b851f IS |
5464 | if (mlxsw_sp_fib6_rt_should_ignore(rt)) |
5465 | return 0; | |
5466 | ||
93c2fb25 DA |
5467 | fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id, |
5468 | &rt->fib6_dst.addr, | |
5469 | sizeof(rt->fib6_dst.addr), | |
5470 | rt->fib6_dst.plen, | |
428b851f IS |
5471 | MLXSW_SP_L3_PROTO_IPV6); |
5472 | if (IS_ERR(fib_node)) | |
5473 | return PTR_ERR(fib_node); | |
5474 | ||
5475 | /* Before creating a new entry, try to append route to an existing | |
5476 | * multipath entry. | |
5477 | */ | |
33bd5ac5 | 5478 | fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace); |
428b851f IS |
5479 | if (fib6_entry) { |
5480 | err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt); | |
5481 | if (err) | |
5482 | goto err_fib6_entry_nexthop_add; | |
5483 | return 0; | |
5484 | } | |
5485 | ||
5486 | fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt); | |
5487 | if (IS_ERR(fib6_entry)) { | |
5488 | err = PTR_ERR(fib6_entry); | |
5489 | goto err_fib6_entry_create; | |
5490 | } | |
5491 | ||
0a7fd1ac | 5492 | err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace); |
428b851f IS |
5493 | if (err) |
5494 | goto err_fib6_node_entry_link; | |
5495 | ||
0a7fd1ac IS |
5496 | mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace); |
5497 | ||
428b851f IS |
5498 | return 0; |
5499 | ||
5500 | err_fib6_node_entry_link: | |
5501 | mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); | |
5502 | err_fib6_entry_create: | |
5503 | err_fib6_entry_nexthop_add: | |
5504 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); | |
5505 | return err; | |
5506 | } | |
5507 | ||
5508 | static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp, | |
8d1c802b | 5509 | struct fib6_info *rt) |
428b851f IS |
5510 | { |
5511 | struct mlxsw_sp_fib6_entry *fib6_entry; | |
5512 | struct mlxsw_sp_fib_node *fib_node; | |
5513 | ||
5514 | if (mlxsw_sp->router->aborted) | |
5515 | return; | |
5516 | ||
5517 | if (mlxsw_sp_fib6_rt_should_ignore(rt)) | |
5518 | return; | |
5519 | ||
5520 | fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt); | |
5521 | if (WARN_ON(!fib6_entry)) | |
5522 | return; | |
5523 | ||
5524 | /* If route is part of a multipath entry, but not the last one | |
5525 | * removed, then only reduce its nexthop group. | |
5526 | */ | |
5527 | if (!list_is_singular(&fib6_entry->rt6_list)) { | |
5528 | mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt); | |
5529 | return; | |
5530 | } | |
5531 | ||
5532 | fib_node = fib6_entry->common.fib_node; | |
5533 | ||
5534 | mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); | |
5535 | mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); | |
5536 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); | |
5537 | } | |
5538 | ||
bc65a8a4 IS |
5539 | static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp, |
5540 | enum mlxsw_reg_ralxx_protocol proto, | |
5541 | u8 tree_id) | |
b45f64d1 JP |
5542 | { |
5543 | char ralta_pl[MLXSW_REG_RALTA_LEN]; | |
5544 | char ralst_pl[MLXSW_REG_RALST_LEN]; | |
b5d90e6d | 5545 | int i, err; |
b45f64d1 | 5546 | |
bc65a8a4 | 5547 | mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id); |
b45f64d1 JP |
5548 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); |
5549 | if (err) | |
5550 | return err; | |
5551 | ||
bc65a8a4 | 5552 | mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id); |
b45f64d1 JP |
5553 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl); |
5554 | if (err) | |
5555 | return err; | |
5556 | ||
b5d90e6d | 5557 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { |
9011b677 | 5558 | struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i]; |
b5d90e6d IS |
5559 | char raltb_pl[MLXSW_REG_RALTB_LEN]; |
5560 | char ralue_pl[MLXSW_REG_RALUE_LEN]; | |
b45f64d1 | 5561 | |
bc65a8a4 | 5562 | mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id); |
b5d90e6d IS |
5563 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), |
5564 | raltb_pl); | |
5565 | if (err) | |
5566 | return err; | |
5567 | ||
bc65a8a4 IS |
5568 | mlxsw_reg_ralue_pack(ralue_pl, proto, |
5569 | MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0); | |
b5d90e6d IS |
5570 | mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); |
5571 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), | |
5572 | ralue_pl); | |
5573 | if (err) | |
5574 | return err; | |
5575 | } | |
5576 | ||
5577 | return 0; | |
b45f64d1 JP |
5578 | } |
5579 | ||
eb35da0c YM |
5580 | static struct mlxsw_sp_mr_table * |
5581 | mlxsw_sp_router_fibmr_family_to_table(struct mlxsw_sp_vr *vr, int family) | |
5582 | { | |
64ed1b9e | 5583 | if (family == RTNL_FAMILY_IPMR) |
eb35da0c | 5584 | return vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]; |
64ed1b9e YM |
5585 | else |
5586 | return vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]; | |
eb35da0c YM |
5587 | } |
5588 | ||
d42b0965 YG |
5589 | static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp, |
5590 | struct mfc_entry_notifier_info *men_info, | |
5591 | bool replace) | |
5592 | { | |
eb35da0c | 5593 | struct mlxsw_sp_mr_table *mrt; |
d42b0965 YG |
5594 | struct mlxsw_sp_vr *vr; |
5595 | ||
5596 | if (mlxsw_sp->router->aborted) | |
5597 | return 0; | |
5598 | ||
f8fa9b4e | 5599 | vr = mlxsw_sp_vr_get(mlxsw_sp, men_info->tb_id, NULL); |
d42b0965 YG |
5600 | if (IS_ERR(vr)) |
5601 | return PTR_ERR(vr); | |
5602 | ||
eb35da0c YM |
5603 | mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family); |
5604 | return mlxsw_sp_mr_route_add(mrt, men_info->mfc, replace); | |
d42b0965 YG |
5605 | } |
5606 | ||
5607 | static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp, | |
5608 | struct mfc_entry_notifier_info *men_info) | |
5609 | { | |
eb35da0c | 5610 | struct mlxsw_sp_mr_table *mrt; |
d42b0965 YG |
5611 | struct mlxsw_sp_vr *vr; |
5612 | ||
5613 | if (mlxsw_sp->router->aborted) | |
5614 | return; | |
5615 | ||
5616 | vr = mlxsw_sp_vr_find(mlxsw_sp, men_info->tb_id); | |
5617 | if (WARN_ON(!vr)) | |
5618 | return; | |
5619 | ||
eb35da0c YM |
5620 | mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family); |
5621 | mlxsw_sp_mr_route_del(mrt, men_info->mfc); | |
2b52ce02 | 5622 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
d42b0965 YG |
5623 | } |
5624 | ||
5625 | static int | |
5626 | mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp, | |
5627 | struct vif_entry_notifier_info *ven_info) | |
5628 | { | |
eb35da0c | 5629 | struct mlxsw_sp_mr_table *mrt; |
d42b0965 YG |
5630 | struct mlxsw_sp_rif *rif; |
5631 | struct mlxsw_sp_vr *vr; | |
5632 | ||
5633 | if (mlxsw_sp->router->aborted) | |
5634 | return 0; | |
5635 | ||
f8fa9b4e | 5636 | vr = mlxsw_sp_vr_get(mlxsw_sp, ven_info->tb_id, NULL); |
d42b0965 YG |
5637 | if (IS_ERR(vr)) |
5638 | return PTR_ERR(vr); | |
5639 | ||
eb35da0c | 5640 | mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family); |
d42b0965 | 5641 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev); |
eb35da0c | 5642 | return mlxsw_sp_mr_vif_add(mrt, ven_info->dev, |
d42b0965 YG |
5643 | ven_info->vif_index, |
5644 | ven_info->vif_flags, rif); | |
5645 | } | |
5646 | ||
5647 | static void | |
5648 | mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp, | |
5649 | struct vif_entry_notifier_info *ven_info) | |
5650 | { | |
eb35da0c | 5651 | struct mlxsw_sp_mr_table *mrt; |
d42b0965 YG |
5652 | struct mlxsw_sp_vr *vr; |
5653 | ||
5654 | if (mlxsw_sp->router->aborted) | |
5655 | return; | |
5656 | ||
5657 | vr = mlxsw_sp_vr_find(mlxsw_sp, ven_info->tb_id); | |
5658 | if (WARN_ON(!vr)) | |
5659 | return; | |
5660 | ||
eb35da0c YM |
5661 | mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family); |
5662 | mlxsw_sp_mr_vif_del(mrt, ven_info->vif_index); | |
2b52ce02 | 5663 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
d42b0965 YG |
5664 | } |
5665 | ||
bc65a8a4 IS |
5666 | static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) |
5667 | { | |
5668 | enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4; | |
5669 | int err; | |
5670 | ||
5671 | err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, | |
5672 | MLXSW_SP_LPM_TREE_MIN); | |
5673 | if (err) | |
5674 | return err; | |
5675 | ||
d42b0965 YG |
5676 | /* The multicast router code does not need an abort trap as by default, |
5677 | * packets that don't match any routes are trapped to the CPU. | |
5678 | */ | |
5679 | ||
bc65a8a4 IS |
5680 | proto = MLXSW_REG_RALXX_PROTOCOL_IPV6; |
5681 | return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, | |
5682 | MLXSW_SP_LPM_TREE_MIN + 1); | |
5683 | } | |
5684 | ||
9aecce1c IS |
5685 | static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, |
5686 | struct mlxsw_sp_fib_node *fib_node) | |
5687 | { | |
4f1c7f1f | 5688 | struct mlxsw_sp_fib4_entry *fib4_entry, *tmp; |
9aecce1c | 5689 | |
4f1c7f1f IS |
5690 | list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list, |
5691 | common.list) { | |
5692 | bool do_break = &tmp->common.list == &fib_node->entry_list; | |
9aecce1c | 5693 | |
4f1c7f1f IS |
5694 | mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); |
5695 | mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); | |
731ea1ca | 5696 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); |
9aecce1c IS |
5697 | /* Break when entry list is empty and node was freed. |
5698 | * Otherwise, we'll access freed memory in the next | |
5699 | * iteration. | |
5700 | */ | |
5701 | if (do_break) | |
5702 | break; | |
5703 | } | |
5704 | } | |
5705 | ||
428b851f IS |
5706 | static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp, |
5707 | struct mlxsw_sp_fib_node *fib_node) | |
5708 | { | |
5709 | struct mlxsw_sp_fib6_entry *fib6_entry, *tmp; | |
5710 | ||
5711 | list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list, | |
5712 | common.list) { | |
5713 | bool do_break = &tmp->common.list == &fib_node->entry_list; | |
5714 | ||
5715 | mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); | |
5716 | mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); | |
5717 | mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); | |
5718 | if (do_break) | |
5719 | break; | |
5720 | } | |
5721 | } | |
5722 | ||
9aecce1c IS |
5723 | static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, |
5724 | struct mlxsw_sp_fib_node *fib_node) | |
5725 | { | |
76610ebb | 5726 | switch (fib_node->fib->proto) { |
9aecce1c IS |
5727 | case MLXSW_SP_L3_PROTO_IPV4: |
5728 | mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node); | |
5729 | break; | |
5730 | case MLXSW_SP_L3_PROTO_IPV6: | |
428b851f | 5731 | mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node); |
9aecce1c IS |
5732 | break; |
5733 | } | |
5734 | } | |
5735 | ||
76610ebb IS |
5736 | static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp, |
5737 | struct mlxsw_sp_vr *vr, | |
5738 | enum mlxsw_sp_l3proto proto) | |
b45f64d1 | 5739 | { |
76610ebb | 5740 | struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); |
9aecce1c | 5741 | struct mlxsw_sp_fib_node *fib_node, *tmp; |
76610ebb IS |
5742 | |
5743 | list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) { | |
5744 | bool do_break = &tmp->list == &fib->node_list; | |
5745 | ||
5746 | mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node); | |
5747 | if (do_break) | |
5748 | break; | |
5749 | } | |
5750 | } | |
5751 | ||
5752 | static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) | |
5753 | { | |
9742f866 | 5754 | int i, j; |
b45f64d1 | 5755 | |
c1a38311 | 5756 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { |
9011b677 | 5757 | struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i]; |
ac571de9 | 5758 | |
76610ebb | 5759 | if (!mlxsw_sp_vr_is_used(vr)) |
b45f64d1 | 5760 | continue; |
d42b0965 | 5761 | |
9742f866 YM |
5762 | for (j = 0; j < MLXSW_SP_L3_PROTO_MAX; j++) |
5763 | mlxsw_sp_mr_table_flush(vr->mr_table[j]); | |
76610ebb | 5764 | mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); |
a3d9bc50 IS |
5765 | |
5766 | /* If virtual router was only used for IPv4, then it's no | |
5767 | * longer used. | |
5768 | */ | |
5769 | if (!mlxsw_sp_vr_is_used(vr)) | |
5770 | continue; | |
5771 | mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); | |
b45f64d1 | 5772 | } |
ac571de9 IS |
5773 | } |
5774 | ||
bc65a8a4 | 5775 | static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp) |
ac571de9 IS |
5776 | { |
5777 | int err; | |
5778 | ||
9011b677 | 5779 | if (mlxsw_sp->router->aborted) |
d331d303 IS |
5780 | return; |
5781 | dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n"); | |
ac571de9 | 5782 | mlxsw_sp_router_fib_flush(mlxsw_sp); |
9011b677 | 5783 | mlxsw_sp->router->aborted = true; |
b45f64d1 JP |
5784 | err = mlxsw_sp_router_set_abort_trap(mlxsw_sp); |
5785 | if (err) | |
5786 | dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n"); | |
5787 | } | |
5788 | ||
3057224e | 5789 | struct mlxsw_sp_fib_event_work { |
a0e4761d | 5790 | struct work_struct work; |
ad178c8e | 5791 | union { |
428b851f | 5792 | struct fib6_entry_notifier_info fen6_info; |
ad178c8e | 5793 | struct fib_entry_notifier_info fen_info; |
5d7bfd14 | 5794 | struct fib_rule_notifier_info fr_info; |
ad178c8e | 5795 | struct fib_nh_notifier_info fnh_info; |
d42b0965 YG |
5796 | struct mfc_entry_notifier_info men_info; |
5797 | struct vif_entry_notifier_info ven_info; | |
ad178c8e | 5798 | }; |
3057224e IS |
5799 | struct mlxsw_sp *mlxsw_sp; |
5800 | unsigned long event; | |
5801 | }; | |
5802 | ||
66a5763a | 5803 | static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) |
b45f64d1 | 5804 | { |
3057224e | 5805 | struct mlxsw_sp_fib_event_work *fib_work = |
a0e4761d | 5806 | container_of(work, struct mlxsw_sp_fib_event_work, work); |
3057224e | 5807 | struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; |
599cf8f9 | 5808 | bool replace, append; |
b45f64d1 JP |
5809 | int err; |
5810 | ||
3057224e IS |
5811 | /* Protect internal structures from changes */ |
5812 | rtnl_lock(); | |
803335ac PM |
5813 | mlxsw_sp_span_respin(mlxsw_sp); |
5814 | ||
3057224e | 5815 | switch (fib_work->event) { |
599cf8f9 | 5816 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ |
4283bce5 | 5817 | case FIB_EVENT_ENTRY_APPEND: /* fall through */ |
b45f64d1 | 5818 | case FIB_EVENT_ENTRY_ADD: |
599cf8f9 | 5819 | replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; |
4283bce5 IS |
5820 | append = fib_work->event == FIB_EVENT_ENTRY_APPEND; |
5821 | err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info, | |
599cf8f9 | 5822 | replace, append); |
b45f64d1 | 5823 | if (err) |
bc65a8a4 | 5824 | mlxsw_sp_router_fib_abort(mlxsw_sp); |
3057224e | 5825 | fib_info_put(fib_work->fen_info.fi); |
b45f64d1 JP |
5826 | break; |
5827 | case FIB_EVENT_ENTRY_DEL: | |
3057224e IS |
5828 | mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info); |
5829 | fib_info_put(fib_work->fen_info.fi); | |
b45f64d1 | 5830 | break; |
1f279233 DA |
5831 | case FIB_EVENT_RULE_ADD: |
5832 | /* if we get here, a rule was added that we do not support. | |
5833 | * just do the fib_abort | |
5834 | */ | |
5835 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
b45f64d1 | 5836 | break; |
ad178c8e IS |
5837 | case FIB_EVENT_NH_ADD: /* fall through */ |
5838 | case FIB_EVENT_NH_DEL: | |
0e6ea2a4 IS |
5839 | mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event, |
5840 | fib_work->fnh_info.fib_nh); | |
ad178c8e IS |
5841 | fib_info_put(fib_work->fnh_info.fib_nh->nh_parent); |
5842 | break; | |
b45f64d1 | 5843 | } |
3057224e IS |
5844 | rtnl_unlock(); |
5845 | kfree(fib_work); | |
5846 | } | |
5847 | ||
66a5763a IS |
5848 | static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) |
5849 | { | |
583419fd IS |
5850 | struct mlxsw_sp_fib_event_work *fib_work = |
5851 | container_of(work, struct mlxsw_sp_fib_event_work, work); | |
5852 | struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; | |
33bd5ac5 | 5853 | bool replace; |
428b851f | 5854 | int err; |
583419fd IS |
5855 | |
5856 | rtnl_lock(); | |
803335ac PM |
5857 | mlxsw_sp_span_respin(mlxsw_sp); |
5858 | ||
583419fd | 5859 | switch (fib_work->event) { |
0a7fd1ac | 5860 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ |
5a15a1b0 | 5861 | case FIB_EVENT_ENTRY_APPEND: /* fall through */ |
428b851f | 5862 | case FIB_EVENT_ENTRY_ADD: |
0a7fd1ac | 5863 | replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; |
428b851f | 5864 | err = mlxsw_sp_router_fib6_add(mlxsw_sp, |
33bd5ac5 | 5865 | fib_work->fen6_info.rt, replace); |
428b851f IS |
5866 | if (err) |
5867 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
5868 | mlxsw_sp_rt6_release(fib_work->fen6_info.rt); | |
5869 | break; | |
5870 | case FIB_EVENT_ENTRY_DEL: | |
5871 | mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt); | |
5872 | mlxsw_sp_rt6_release(fib_work->fen6_info.rt); | |
5873 | break; | |
1f279233 DA |
5874 | case FIB_EVENT_RULE_ADD: |
5875 | /* if we get here, a rule was added that we do not support. | |
5876 | * just do the fib_abort | |
5877 | */ | |
5878 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
583419fd IS |
5879 | break; |
5880 | } | |
5881 | rtnl_unlock(); | |
5882 | kfree(fib_work); | |
66a5763a IS |
5883 | } |
5884 | ||
d42b0965 YG |
5885 | static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work) |
5886 | { | |
5887 | struct mlxsw_sp_fib_event_work *fib_work = | |
5888 | container_of(work, struct mlxsw_sp_fib_event_work, work); | |
5889 | struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; | |
d42b0965 YG |
5890 | bool replace; |
5891 | int err; | |
5892 | ||
5893 | rtnl_lock(); | |
5894 | switch (fib_work->event) { | |
5895 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ | |
5896 | case FIB_EVENT_ENTRY_ADD: | |
5897 | replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; | |
5898 | ||
5899 | err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_work->men_info, | |
5900 | replace); | |
5901 | if (err) | |
5902 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
8c13af2a | 5903 | mr_cache_put(fib_work->men_info.mfc); |
d42b0965 YG |
5904 | break; |
5905 | case FIB_EVENT_ENTRY_DEL: | |
5906 | mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info); | |
8c13af2a | 5907 | mr_cache_put(fib_work->men_info.mfc); |
d42b0965 YG |
5908 | break; |
5909 | case FIB_EVENT_VIF_ADD: | |
5910 | err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp, | |
5911 | &fib_work->ven_info); | |
5912 | if (err) | |
5913 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
5914 | dev_put(fib_work->ven_info.dev); | |
5915 | break; | |
5916 | case FIB_EVENT_VIF_DEL: | |
5917 | mlxsw_sp_router_fibmr_vif_del(mlxsw_sp, | |
5918 | &fib_work->ven_info); | |
5919 | dev_put(fib_work->ven_info.dev); | |
5920 | break; | |
1f279233 DA |
5921 | case FIB_EVENT_RULE_ADD: |
5922 | /* if we get here, a rule was added that we do not support. | |
5923 | * just do the fib_abort | |
5924 | */ | |
5925 | mlxsw_sp_router_fib_abort(mlxsw_sp); | |
d42b0965 YG |
5926 | break; |
5927 | } | |
5928 | rtnl_unlock(); | |
5929 | kfree(fib_work); | |
5930 | } | |
5931 | ||
66a5763a IS |
5932 | static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, |
5933 | struct fib_notifier_info *info) | |
5934 | { | |
3c75f9b1 | 5935 | struct fib_entry_notifier_info *fen_info; |
3c75f9b1 DA |
5936 | struct fib_nh_notifier_info *fnh_info; |
5937 | ||
66a5763a IS |
5938 | switch (fib_work->event) { |
5939 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ | |
5940 | case FIB_EVENT_ENTRY_APPEND: /* fall through */ | |
5941 | case FIB_EVENT_ENTRY_ADD: /* fall through */ | |
5942 | case FIB_EVENT_ENTRY_DEL: | |
3c75f9b1 DA |
5943 | fen_info = container_of(info, struct fib_entry_notifier_info, |
5944 | info); | |
5945 | fib_work->fen_info = *fen_info; | |
5946 | /* Take reference on fib_info to prevent it from being | |
66a5763a IS |
5947 | * freed while work is queued. Release it afterwards. |
5948 | */ | |
5949 | fib_info_hold(fib_work->fen_info.fi); | |
5950 | break; | |
66a5763a IS |
5951 | case FIB_EVENT_NH_ADD: /* fall through */ |
5952 | case FIB_EVENT_NH_DEL: | |
3c75f9b1 DA |
5953 | fnh_info = container_of(info, struct fib_nh_notifier_info, |
5954 | info); | |
5955 | fib_work->fnh_info = *fnh_info; | |
66a5763a IS |
5956 | fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent); |
5957 | break; | |
5958 | } | |
5959 | } | |
5960 | ||
5961 | static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, | |
5962 | struct fib_notifier_info *info) | |
5963 | { | |
3c75f9b1 | 5964 | struct fib6_entry_notifier_info *fen6_info; |
3c75f9b1 | 5965 | |
583419fd | 5966 | switch (fib_work->event) { |
0a7fd1ac | 5967 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ |
5a15a1b0 | 5968 | case FIB_EVENT_ENTRY_APPEND: /* fall through */ |
428b851f IS |
5969 | case FIB_EVENT_ENTRY_ADD: /* fall through */ |
5970 | case FIB_EVENT_ENTRY_DEL: | |
3c75f9b1 DA |
5971 | fen6_info = container_of(info, struct fib6_entry_notifier_info, |
5972 | info); | |
5973 | fib_work->fen6_info = *fen6_info; | |
8d1c802b | 5974 | fib6_info_hold(fib_work->fen6_info.rt); |
428b851f | 5975 | break; |
583419fd | 5976 | } |
66a5763a IS |
5977 | } |
5978 | ||
d42b0965 YG |
5979 | static void |
5980 | mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work, | |
5981 | struct fib_notifier_info *info) | |
5982 | { | |
5983 | switch (fib_work->event) { | |
5984 | case FIB_EVENT_ENTRY_REPLACE: /* fall through */ | |
5985 | case FIB_EVENT_ENTRY_ADD: /* fall through */ | |
5986 | case FIB_EVENT_ENTRY_DEL: | |
5987 | memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info)); | |
8c13af2a | 5988 | mr_cache_hold(fib_work->men_info.mfc); |
d42b0965 YG |
5989 | break; |
5990 | case FIB_EVENT_VIF_ADD: /* fall through */ | |
5991 | case FIB_EVENT_VIF_DEL: | |
5992 | memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info)); | |
5993 | dev_hold(fib_work->ven_info.dev); | |
5994 | break; | |
1f279233 DA |
5995 | } |
5996 | } | |
5997 | ||
5998 | static int mlxsw_sp_router_fib_rule_event(unsigned long event, | |
5999 | struct fib_notifier_info *info, | |
6000 | struct mlxsw_sp *mlxsw_sp) | |
6001 | { | |
6002 | struct netlink_ext_ack *extack = info->extack; | |
6003 | struct fib_rule_notifier_info *fr_info; | |
6004 | struct fib_rule *rule; | |
6005 | int err = 0; | |
6006 | ||
6007 | /* nothing to do at the moment */ | |
6008 | if (event == FIB_EVENT_RULE_DEL) | |
6009 | return 0; | |
6010 | ||
6011 | if (mlxsw_sp->router->aborted) | |
6012 | return 0; | |
6013 | ||
6014 | fr_info = container_of(info, struct fib_rule_notifier_info, info); | |
6015 | rule = fr_info->rule; | |
6016 | ||
6017 | switch (info->family) { | |
6018 | case AF_INET: | |
6019 | if (!fib4_rule_default(rule) && !rule->l3mdev) | |
6290182b | 6020 | err = -EOPNOTSUPP; |
1f279233 DA |
6021 | break; |
6022 | case AF_INET6: | |
6023 | if (!fib6_rule_default(rule) && !rule->l3mdev) | |
6290182b | 6024 | err = -EOPNOTSUPP; |
1f279233 DA |
6025 | break; |
6026 | case RTNL_FAMILY_IPMR: | |
6027 | if (!ipmr_rule_default(rule) && !rule->l3mdev) | |
6290182b | 6028 | err = -EOPNOTSUPP; |
d42b0965 | 6029 | break; |
64ed1b9e YM |
6030 | case RTNL_FAMILY_IP6MR: |
6031 | if (!ip6mr_rule_default(rule) && !rule->l3mdev) | |
6290182b | 6032 | err = -EOPNOTSUPP; |
64ed1b9e | 6033 | break; |
d42b0965 | 6034 | } |
1f279233 DA |
6035 | |
6036 | if (err < 0) | |
6290182b | 6037 | NL_SET_ERR_MSG_MOD(extack, "FIB rules not supported"); |
1f279233 DA |
6038 | |
6039 | return err; | |
d42b0965 YG |
6040 | } |
6041 | ||
3057224e IS |
6042 | /* Called with rcu_read_lock() */ |
6043 | static int mlxsw_sp_router_fib_event(struct notifier_block *nb, | |
6044 | unsigned long event, void *ptr) | |
6045 | { | |
3057224e IS |
6046 | struct mlxsw_sp_fib_event_work *fib_work; |
6047 | struct fib_notifier_info *info = ptr; | |
7e39d115 | 6048 | struct mlxsw_sp_router *router; |
1f279233 | 6049 | int err; |
3057224e | 6050 | |
8e29f979 | 6051 | if (!net_eq(info->net, &init_net) || |
664375e9 | 6052 | (info->family != AF_INET && info->family != AF_INET6 && |
64ed1b9e YM |
6053 | info->family != RTNL_FAMILY_IPMR && |
6054 | info->family != RTNL_FAMILY_IP6MR)) | |
3057224e IS |
6055 | return NOTIFY_DONE; |
6056 | ||
1f279233 DA |
6057 | router = container_of(nb, struct mlxsw_sp_router, fib_nb); |
6058 | ||
6059 | switch (event) { | |
6060 | case FIB_EVENT_RULE_ADD: /* fall through */ | |
6061 | case FIB_EVENT_RULE_DEL: | |
6062 | err = mlxsw_sp_router_fib_rule_event(event, info, | |
6063 | router->mlxsw_sp); | |
6290182b IS |
6064 | if (!err || info->extack) |
6065 | return notifier_from_errno(err); | |
50d10711 IS |
6066 | break; |
6067 | case FIB_EVENT_ENTRY_ADD: | |
6068 | if (router->aborted) { | |
6069 | NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route"); | |
6070 | return notifier_from_errno(-EINVAL); | |
6071 | } | |
6072 | break; | |
1f279233 DA |
6073 | } |
6074 | ||
3057224e IS |
6075 | fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); |
6076 | if (WARN_ON(!fib_work)) | |
6077 | return NOTIFY_BAD; | |
6078 | ||
7e39d115 | 6079 | fib_work->mlxsw_sp = router->mlxsw_sp; |
3057224e IS |
6080 | fib_work->event = event; |
6081 | ||
66a5763a IS |
6082 | switch (info->family) { |
6083 | case AF_INET: | |
6084 | INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work); | |
6085 | mlxsw_sp_router_fib4_event(fib_work, info); | |
3057224e | 6086 | break; |
66a5763a IS |
6087 | case AF_INET6: |
6088 | INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work); | |
6089 | mlxsw_sp_router_fib6_event(fib_work, info); | |
ad178c8e | 6090 | break; |
64ed1b9e | 6091 | case RTNL_FAMILY_IP6MR: |
d42b0965 YG |
6092 | case RTNL_FAMILY_IPMR: |
6093 | INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work); | |
6094 | mlxsw_sp_router_fibmr_event(fib_work, info); | |
6095 | break; | |
3057224e IS |
6096 | } |
6097 | ||
a0e4761d | 6098 | mlxsw_core_schedule_work(&fib_work->work); |
3057224e | 6099 | |
b45f64d1 JP |
6100 | return NOTIFY_DONE; |
6101 | } | |
6102 | ||
0c41292b | 6103 | struct mlxsw_sp_rif * |
4724ba56 IS |
6104 | mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, |
6105 | const struct net_device *dev) | |
6106 | { | |
6107 | int i; | |
6108 | ||
6109 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) | |
5f9efffb IS |
6110 | if (mlxsw_sp->router->rifs[i] && |
6111 | mlxsw_sp->router->rifs[i]->dev == dev) | |
6112 | return mlxsw_sp->router->rifs[i]; | |
4724ba56 IS |
6113 | |
6114 | return NULL; | |
6115 | } | |
6116 | ||
6117 | static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif) | |
6118 | { | |
6119 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
6120 | int err; | |
6121 | ||
6122 | mlxsw_reg_ritr_rif_pack(ritr_pl, rif); | |
6123 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
6124 | if (WARN_ON_ONCE(err)) | |
6125 | return err; | |
6126 | ||
6127 | mlxsw_reg_ritr_enable_set(ritr_pl, false); | |
6128 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
6129 | } | |
6130 | ||
6131 | static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, | |
bf95233e | 6132 | struct mlxsw_sp_rif *rif) |
4724ba56 | 6133 | { |
bf95233e AS |
6134 | mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index); |
6135 | mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif); | |
6136 | mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif); | |
4724ba56 IS |
6137 | } |
6138 | ||
5ea1237f AS |
6139 | static bool |
6140 | mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev, | |
6141 | unsigned long event) | |
4724ba56 | 6142 | { |
5ea1237f AS |
6143 | struct inet6_dev *inet6_dev; |
6144 | bool addr_list_empty = true; | |
6145 | struct in_device *idev; | |
6146 | ||
4724ba56 IS |
6147 | switch (event) { |
6148 | case NETDEV_UP: | |
f1b1f273 | 6149 | return rif == NULL; |
4724ba56 | 6150 | case NETDEV_DOWN: |
5ea1237f AS |
6151 | idev = __in_dev_get_rtnl(dev); |
6152 | if (idev && idev->ifa_list) | |
6153 | addr_list_empty = false; | |
6154 | ||
6155 | inet6_dev = __in6_dev_get(dev); | |
6156 | if (addr_list_empty && inet6_dev && | |
6157 | !list_empty(&inet6_dev->addr_list)) | |
6158 | addr_list_empty = false; | |
6159 | ||
2db99378 IS |
6160 | /* macvlans do not have a RIF, but rather piggy back on the |
6161 | * RIF of their lower device. | |
6162 | */ | |
6163 | if (netif_is_macvlan(dev) && addr_list_empty) | |
6164 | return true; | |
6165 | ||
5ea1237f | 6166 | if (rif && addr_list_empty && |
bf95233e | 6167 | !netif_is_l3_slave(rif->dev)) |
4724ba56 IS |
6168 | return true; |
6169 | /* It is possible we already removed the RIF ourselves | |
6170 | * if it was assigned to a netdev that is now a bridge | |
6171 | * or LAG slave. | |
6172 | */ | |
6173 | return false; | |
6174 | } | |
6175 | ||
6176 | return false; | |
6177 | } | |
6178 | ||
e4f3c1c1 IS |
6179 | static enum mlxsw_sp_rif_type |
6180 | mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp, | |
6181 | const struct net_device *dev) | |
6182 | { | |
6183 | enum mlxsw_sp_fid_type type; | |
6184 | ||
6ddb7426 PM |
6185 | if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL)) |
6186 | return MLXSW_SP_RIF_TYPE_IPIP_LB; | |
6187 | ||
6188 | /* Otherwise RIF type is derived from the type of the underlying FID. */ | |
e4f3c1c1 IS |
6189 | if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev))) |
6190 | type = MLXSW_SP_FID_TYPE_8021Q; | |
6191 | else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev)) | |
6192 | type = MLXSW_SP_FID_TYPE_8021Q; | |
6193 | else if (netif_is_bridge_master(dev)) | |
6194 | type = MLXSW_SP_FID_TYPE_8021D; | |
6195 | else | |
6196 | type = MLXSW_SP_FID_TYPE_RFID; | |
6197 | ||
6198 | return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type); | |
6199 | } | |
6200 | ||
de5ed99e | 6201 | static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index) |
4724ba56 IS |
6202 | { |
6203 | int i; | |
6204 | ||
de5ed99e IS |
6205 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { |
6206 | if (!mlxsw_sp->router->rifs[i]) { | |
6207 | *p_rif_index = i; | |
6208 | return 0; | |
6209 | } | |
6210 | } | |
4724ba56 | 6211 | |
de5ed99e | 6212 | return -ENOBUFS; |
4724ba56 IS |
6213 | } |
6214 | ||
e4f3c1c1 IS |
6215 | static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index, |
6216 | u16 vr_id, | |
6217 | struct net_device *l3_dev) | |
4724ba56 | 6218 | { |
bf95233e | 6219 | struct mlxsw_sp_rif *rif; |
4724ba56 | 6220 | |
e4f3c1c1 | 6221 | rif = kzalloc(rif_size, GFP_KERNEL); |
bf95233e | 6222 | if (!rif) |
4724ba56 IS |
6223 | return NULL; |
6224 | ||
bf95233e AS |
6225 | INIT_LIST_HEAD(&rif->nexthop_list); |
6226 | INIT_LIST_HEAD(&rif->neigh_list); | |
73b8f493 ND |
6227 | if (l3_dev) { |
6228 | ether_addr_copy(rif->addr, l3_dev->dev_addr); | |
6229 | rif->mtu = l3_dev->mtu; | |
6230 | rif->dev = l3_dev; | |
6231 | } | |
bf95233e | 6232 | rif->vr_id = vr_id; |
bf95233e | 6233 | rif->rif_index = rif_index; |
4724ba56 | 6234 | |
bf95233e | 6235 | return rif; |
4724ba56 IS |
6236 | } |
6237 | ||
5f9efffb IS |
6238 | struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp, |
6239 | u16 rif_index) | |
6240 | { | |
6241 | return mlxsw_sp->router->rifs[rif_index]; | |
6242 | } | |
6243 | ||
fd1b9d41 AS |
6244 | u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif) |
6245 | { | |
6246 | return rif->rif_index; | |
6247 | } | |
6248 | ||
92107cfb PM |
6249 | u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif) |
6250 | { | |
6251 | return lb_rif->common.rif_index; | |
6252 | } | |
6253 | ||
6254 | u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif) | |
6255 | { | |
33c04afe ND |
6256 | u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(lb_rif->common.dev); |
6257 | struct mlxsw_sp_vr *ul_vr; | |
6258 | ||
6259 | ul_vr = mlxsw_sp_vr_get(lb_rif->common.mlxsw_sp, ul_tb_id, NULL); | |
6260 | if (WARN_ON(IS_ERR(ul_vr))) | |
6261 | return 0; | |
6262 | ||
6263 | return ul_vr->id; | |
92107cfb PM |
6264 | } |
6265 | ||
311596f5 ND |
6266 | u16 mlxsw_sp_ipip_lb_ul_rif_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif) |
6267 | { | |
6268 | return lb_rif->ul_rif_id; | |
6269 | } | |
6270 | ||
fd1b9d41 AS |
6271 | int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif) |
6272 | { | |
6273 | return rif->dev->ifindex; | |
6274 | } | |
6275 | ||
91e4d59a YG |
6276 | const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif) |
6277 | { | |
6278 | return rif->dev; | |
6279 | } | |
6280 | ||
a28b1ebe PM |
6281 | struct mlxsw_sp_fid *mlxsw_sp_rif_fid(const struct mlxsw_sp_rif *rif) |
6282 | { | |
6283 | return rif->fid; | |
6284 | } | |
6285 | ||
4724ba56 | 6286 | static struct mlxsw_sp_rif * |
e4f3c1c1 | 6287 | mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, |
f8fa9b4e DA |
6288 | const struct mlxsw_sp_rif_params *params, |
6289 | struct netlink_ext_ack *extack) | |
4724ba56 | 6290 | { |
e4f3c1c1 IS |
6291 | u32 tb_id = l3mdev_fib_table(params->dev); |
6292 | const struct mlxsw_sp_rif_ops *ops; | |
010cadf9 | 6293 | struct mlxsw_sp_fid *fid = NULL; |
e4f3c1c1 | 6294 | enum mlxsw_sp_rif_type type; |
bf95233e | 6295 | struct mlxsw_sp_rif *rif; |
a1107487 IS |
6296 | struct mlxsw_sp_vr *vr; |
6297 | u16 rif_index; | |
9742f866 | 6298 | int i, err; |
4724ba56 | 6299 | |
e4f3c1c1 | 6300 | type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev); |
1f5b2303 | 6301 | ops = mlxsw_sp->rif_ops_arr[type]; |
e4f3c1c1 | 6302 | |
f8fa9b4e | 6303 | vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN, extack); |
c9ec53f0 IS |
6304 | if (IS_ERR(vr)) |
6305 | return ERR_CAST(vr); | |
28a04c7b | 6306 | vr->rif_count++; |
c9ec53f0 | 6307 | |
de5ed99e | 6308 | err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index); |
f8fa9b4e | 6309 | if (err) { |
6c677750 | 6310 | NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces"); |
de5ed99e | 6311 | goto err_rif_index_alloc; |
f8fa9b4e | 6312 | } |
4724ba56 | 6313 | |
e4f3c1c1 | 6314 | rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev); |
a13a594d IS |
6315 | if (!rif) { |
6316 | err = -ENOMEM; | |
6317 | goto err_rif_alloc; | |
6318 | } | |
b61cd7c6 | 6319 | dev_hold(rif->dev); |
a04563e4 | 6320 | mlxsw_sp->router->rifs[rif_index] = rif; |
e4f3c1c1 IS |
6321 | rif->mlxsw_sp = mlxsw_sp; |
6322 | rif->ops = ops; | |
a13a594d | 6323 | |
010cadf9 | 6324 | if (ops->fid_get) { |
5f15e257 | 6325 | fid = ops->fid_get(rif, extack); |
010cadf9 PM |
6326 | if (IS_ERR(fid)) { |
6327 | err = PTR_ERR(fid); | |
6328 | goto err_fid_get; | |
6329 | } | |
6330 | rif->fid = fid; | |
4d93ceeb IS |
6331 | } |
6332 | ||
e4f3c1c1 IS |
6333 | if (ops->setup) |
6334 | ops->setup(rif, params); | |
6335 | ||
6336 | err = ops->configure(rif); | |
4724ba56 | 6337 | if (err) |
e4f3c1c1 | 6338 | goto err_configure; |
4724ba56 | 6339 | |
9742f866 YM |
6340 | for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) { |
6341 | err = mlxsw_sp_mr_rif_add(vr->mr_table[i], rif); | |
6342 | if (err) | |
6343 | goto err_mr_rif_add; | |
6344 | } | |
d42b0965 | 6345 | |
e4f3c1c1 | 6346 | mlxsw_sp_rif_counters_alloc(rif); |
4724ba56 | 6347 | |
bf95233e | 6348 | return rif; |
4724ba56 | 6349 | |
d42b0965 | 6350 | err_mr_rif_add: |
9742f866 YM |
6351 | for (i--; i >= 0; i--) |
6352 | mlxsw_sp_mr_rif_del(vr->mr_table[i], rif); | |
d42b0965 | 6353 | ops->deconfigure(rif); |
e4f3c1c1 | 6354 | err_configure: |
010cadf9 PM |
6355 | if (fid) |
6356 | mlxsw_sp_fid_put(fid); | |
a1107487 | 6357 | err_fid_get: |
a04563e4 | 6358 | mlxsw_sp->router->rifs[rif_index] = NULL; |
b61cd7c6 | 6359 | dev_put(rif->dev); |
e4f3c1c1 IS |
6360 | kfree(rif); |
6361 | err_rif_alloc: | |
de5ed99e | 6362 | err_rif_index_alloc: |
28a04c7b | 6363 | vr->rif_count--; |
2b52ce02 | 6364 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
4724ba56 IS |
6365 | return ERR_PTR(err); |
6366 | } | |
6367 | ||
32fd4b49 | 6368 | static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) |
4724ba56 | 6369 | { |
e4f3c1c1 IS |
6370 | const struct mlxsw_sp_rif_ops *ops = rif->ops; |
6371 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
a1107487 | 6372 | struct mlxsw_sp_fid *fid = rif->fid; |
e4f3c1c1 | 6373 | struct mlxsw_sp_vr *vr; |
9742f866 | 6374 | int i; |
4724ba56 | 6375 | |
bf95233e | 6376 | mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); |
e4f3c1c1 | 6377 | vr = &mlxsw_sp->router->vrs[rif->vr_id]; |
e0c0afd8 | 6378 | |
e4f3c1c1 | 6379 | mlxsw_sp_rif_counters_free(rif); |
9742f866 YM |
6380 | for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) |
6381 | mlxsw_sp_mr_rif_del(vr->mr_table[i], rif); | |
e4f3c1c1 | 6382 | ops->deconfigure(rif); |
010cadf9 PM |
6383 | if (fid) |
6384 | /* Loopback RIFs are not associated with a FID. */ | |
6385 | mlxsw_sp_fid_put(fid); | |
a04563e4 | 6386 | mlxsw_sp->router->rifs[rif->rif_index] = NULL; |
b61cd7c6 | 6387 | dev_put(rif->dev); |
e4f3c1c1 | 6388 | kfree(rif); |
28a04c7b | 6389 | vr->rif_count--; |
2b52ce02 | 6390 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
4724ba56 IS |
6391 | } |
6392 | ||
602b74ed IS |
6393 | void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp, |
6394 | struct net_device *dev) | |
6395 | { | |
6396 | struct mlxsw_sp_rif *rif; | |
6397 | ||
6398 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | |
6399 | if (!rif) | |
6400 | return; | |
6401 | mlxsw_sp_rif_destroy(rif); | |
6402 | } | |
6403 | ||
e4f3c1c1 IS |
6404 | static void |
6405 | mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params, | |
6406 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) | |
6407 | { | |
6408 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; | |
6409 | ||
6410 | params->vid = mlxsw_sp_port_vlan->vid; | |
6411 | params->lag = mlxsw_sp_port->lagged; | |
6412 | if (params->lag) | |
6413 | params->lag_id = mlxsw_sp_port->lag_id; | |
6414 | else | |
6415 | params->system_port = mlxsw_sp_port->local_port; | |
6416 | } | |
6417 | ||
32fd4b49 IS |
6418 | static struct mlxsw_sp_rif_subport * |
6419 | mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif) | |
6420 | { | |
6421 | return container_of(rif, struct mlxsw_sp_rif_subport, common); | |
6422 | } | |
6423 | ||
6424 | static struct mlxsw_sp_rif * | |
6425 | mlxsw_sp_rif_subport_get(struct mlxsw_sp *mlxsw_sp, | |
6426 | const struct mlxsw_sp_rif_params *params, | |
6427 | struct netlink_ext_ack *extack) | |
6428 | { | |
6429 | struct mlxsw_sp_rif_subport *rif_subport; | |
6430 | struct mlxsw_sp_rif *rif; | |
6431 | ||
6432 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, params->dev); | |
6433 | if (!rif) | |
6434 | return mlxsw_sp_rif_create(mlxsw_sp, params, extack); | |
6435 | ||
6436 | rif_subport = mlxsw_sp_rif_subport_rif(rif); | |
6437 | refcount_inc(&rif_subport->ref_count); | |
6438 | return rif; | |
6439 | } | |
6440 | ||
6441 | static void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif) | |
6442 | { | |
6443 | struct mlxsw_sp_rif_subport *rif_subport; | |
6444 | ||
6445 | rif_subport = mlxsw_sp_rif_subport_rif(rif); | |
6446 | if (!refcount_dec_and_test(&rif_subport->ref_count)) | |
6447 | return; | |
6448 | ||
6449 | mlxsw_sp_rif_destroy(rif); | |
6450 | } | |
6451 | ||
7cbecf24 | 6452 | static int |
a1107487 | 6453 | mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, |
f8fa9b4e DA |
6454 | struct net_device *l3_dev, |
6455 | struct netlink_ext_ack *extack) | |
4724ba56 | 6456 | { |
7cbecf24 | 6457 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; |
1b8f09a0 | 6458 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
32fd4b49 IS |
6459 | struct mlxsw_sp_rif_params params = { |
6460 | .dev = l3_dev, | |
6461 | }; | |
7cbecf24 | 6462 | u16 vid = mlxsw_sp_port_vlan->vid; |
bf95233e | 6463 | struct mlxsw_sp_rif *rif; |
a1107487 | 6464 | struct mlxsw_sp_fid *fid; |
03ea01e9 | 6465 | int err; |
4724ba56 | 6466 | |
32fd4b49 IS |
6467 | mlxsw_sp_rif_subport_params_init(¶ms, mlxsw_sp_port_vlan); |
6468 | rif = mlxsw_sp_rif_subport_get(mlxsw_sp, ¶ms, extack); | |
6469 | if (IS_ERR(rif)) | |
6470 | return PTR_ERR(rif); | |
4724ba56 | 6471 | |
a1107487 | 6472 | /* FID was already created, just take a reference */ |
5f15e257 | 6473 | fid = rif->ops->fid_get(rif, extack); |
a1107487 IS |
6474 | err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid); |
6475 | if (err) | |
6476 | goto err_fid_port_vid_map; | |
6477 | ||
7cbecf24 | 6478 | err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); |
03ea01e9 IS |
6479 | if (err) |
6480 | goto err_port_vid_learning_set; | |
6481 | ||
7cbecf24 | 6482 | err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, |
03ea01e9 IS |
6483 | BR_STATE_FORWARDING); |
6484 | if (err) | |
6485 | goto err_port_vid_stp_set; | |
6486 | ||
a1107487 | 6487 | mlxsw_sp_port_vlan->fid = fid; |
4724ba56 | 6488 | |
4724ba56 | 6489 | return 0; |
03ea01e9 IS |
6490 | |
6491 | err_port_vid_stp_set: | |
7cbecf24 | 6492 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); |
03ea01e9 | 6493 | err_port_vid_learning_set: |
a1107487 IS |
6494 | mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid); |
6495 | err_fid_port_vid_map: | |
6496 | mlxsw_sp_fid_put(fid); | |
32fd4b49 | 6497 | mlxsw_sp_rif_subport_put(rif); |
03ea01e9 | 6498 | return err; |
4724ba56 IS |
6499 | } |
6500 | ||
a1107487 IS |
6501 | void |
6502 | mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) | |
4724ba56 | 6503 | { |
ce95e154 | 6504 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; |
7cbecf24 | 6505 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; |
32fd4b49 | 6506 | struct mlxsw_sp_rif *rif = mlxsw_sp_fid_rif(fid); |
ce95e154 | 6507 | u16 vid = mlxsw_sp_port_vlan->vid; |
ce95e154 | 6508 | |
a1107487 IS |
6509 | if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID)) |
6510 | return; | |
4aafc368 | 6511 | |
a1107487 | 6512 | mlxsw_sp_port_vlan->fid = NULL; |
7cbecf24 IS |
6513 | mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING); |
6514 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); | |
a1107487 | 6515 | mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid); |
a1107487 | 6516 | mlxsw_sp_fid_put(fid); |
32fd4b49 | 6517 | mlxsw_sp_rif_subport_put(rif); |
4724ba56 IS |
6518 | } |
6519 | ||
7cbecf24 IS |
6520 | static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev, |
6521 | struct net_device *port_dev, | |
f8fa9b4e DA |
6522 | unsigned long event, u16 vid, |
6523 | struct netlink_ext_ack *extack) | |
4724ba56 IS |
6524 | { |
6525 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev); | |
ce95e154 | 6526 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
4724ba56 | 6527 | |
ce95e154 | 6528 | mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); |
7cbecf24 IS |
6529 | if (WARN_ON(!mlxsw_sp_port_vlan)) |
6530 | return -EINVAL; | |
4724ba56 IS |
6531 | |
6532 | switch (event) { | |
6533 | case NETDEV_UP: | |
a1107487 | 6534 | return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan, |
f8fa9b4e | 6535 | l3_dev, extack); |
4724ba56 | 6536 | case NETDEV_DOWN: |
a1107487 | 6537 | mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan); |
4724ba56 IS |
6538 | break; |
6539 | } | |
6540 | ||
6541 | return 0; | |
6542 | } | |
6543 | ||
6544 | static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev, | |
f8fa9b4e DA |
6545 | unsigned long event, |
6546 | struct netlink_ext_ack *extack) | |
4724ba56 | 6547 | { |
2b94e58d JP |
6548 | if (netif_is_bridge_port(port_dev) || |
6549 | netif_is_lag_port(port_dev) || | |
6550 | netif_is_ovs_port(port_dev)) | |
4724ba56 IS |
6551 | return 0; |
6552 | ||
a2d2a205 IS |
6553 | return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, |
6554 | MLXSW_SP_DEFAULT_VID, extack); | |
4724ba56 IS |
6555 | } |
6556 | ||
6557 | static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev, | |
6558 | struct net_device *lag_dev, | |
f8fa9b4e DA |
6559 | unsigned long event, u16 vid, |
6560 | struct netlink_ext_ack *extack) | |
4724ba56 IS |
6561 | { |
6562 | struct net_device *port_dev; | |
6563 | struct list_head *iter; | |
6564 | int err; | |
6565 | ||
6566 | netdev_for_each_lower_dev(lag_dev, port_dev, iter) { | |
6567 | if (mlxsw_sp_port_dev_check(port_dev)) { | |
7cbecf24 IS |
6568 | err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev, |
6569 | port_dev, | |
f8fa9b4e DA |
6570 | event, vid, |
6571 | extack); | |
4724ba56 IS |
6572 | if (err) |
6573 | return err; | |
6574 | } | |
6575 | } | |
6576 | ||
6577 | return 0; | |
6578 | } | |
6579 | ||
6580 | static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev, | |
f8fa9b4e DA |
6581 | unsigned long event, |
6582 | struct netlink_ext_ack *extack) | |
4724ba56 IS |
6583 | { |
6584 | if (netif_is_bridge_port(lag_dev)) | |
6585 | return 0; | |
6586 | ||
a2d2a205 IS |
6587 | return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, |
6588 | MLXSW_SP_DEFAULT_VID, extack); | |
4724ba56 IS |
6589 | } |
6590 | ||
21ffedb6 IS |
6591 | static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp, |
6592 | struct net_device *l3_dev, | |
f8fa9b4e DA |
6593 | unsigned long event, |
6594 | struct netlink_ext_ack *extack) | |
4724ba56 | 6595 | { |
e4f3c1c1 IS |
6596 | struct mlxsw_sp_rif_params params = { |
6597 | .dev = l3_dev, | |
6598 | }; | |
a1107487 | 6599 | struct mlxsw_sp_rif *rif; |
4724ba56 IS |
6600 | |
6601 | switch (event) { | |
6602 | case NETDEV_UP: | |
f8fa9b4e | 6603 | rif = mlxsw_sp_rif_create(mlxsw_sp, ¶ms, extack); |
e4f3c1c1 IS |
6604 | if (IS_ERR(rif)) |
6605 | return PTR_ERR(rif); | |
6606 | break; | |
4724ba56 | 6607 | case NETDEV_DOWN: |
a1107487 | 6608 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); |
e4f3c1c1 | 6609 | mlxsw_sp_rif_destroy(rif); |
4724ba56 IS |
6610 | break; |
6611 | } | |
6612 | ||
6613 | return 0; | |
6614 | } | |
6615 | ||
21ffedb6 IS |
6616 | static int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp, |
6617 | struct net_device *vlan_dev, | |
f8fa9b4e DA |
6618 | unsigned long event, |
6619 | struct netlink_ext_ack *extack) | |
4724ba56 IS |
6620 | { |
6621 | struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); | |
4724ba56 IS |
6622 | u16 vid = vlan_dev_vlan_id(vlan_dev); |
6623 | ||
6b27c8ad IS |
6624 | if (netif_is_bridge_port(vlan_dev)) |
6625 | return 0; | |
6626 | ||
4724ba56 | 6627 | if (mlxsw_sp_port_dev_check(real_dev)) |
7cbecf24 | 6628 | return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev, |
f8fa9b4e | 6629 | event, vid, extack); |
4724ba56 IS |
6630 | else if (netif_is_lag_master(real_dev)) |
6631 | return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event, | |
f8fa9b4e | 6632 | vid, extack); |
c57529e1 | 6633 | else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev)) |
21ffedb6 IS |
6634 | return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev, event, |
6635 | extack); | |
4724ba56 IS |
6636 | |
6637 | return 0; | |
6638 | } | |
6639 | ||
c3a49540 IS |
6640 | static bool mlxsw_sp_rif_macvlan_is_vrrp4(const u8 *mac) |
6641 | { | |
6642 | u8 vrrp4[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x01, 0x00 }; | |
6643 | u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; | |
6644 | ||
6645 | return ether_addr_equal_masked(mac, vrrp4, mask); | |
6646 | } | |
6647 | ||
6648 | static bool mlxsw_sp_rif_macvlan_is_vrrp6(const u8 *mac) | |
6649 | { | |
6650 | u8 vrrp6[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x02, 0x00 }; | |
6651 | u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; | |
6652 | ||
6653 | return ether_addr_equal_masked(mac, vrrp6, mask); | |
6654 | } | |
6655 | ||
6656 | static int mlxsw_sp_rif_vrrp_op(struct mlxsw_sp *mlxsw_sp, u16 rif_index, | |
6657 | const u8 *mac, bool adding) | |
6658 | { | |
6659 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
6660 | u8 vrrp_id = adding ? mac[5] : 0; | |
6661 | int err; | |
6662 | ||
6663 | if (!mlxsw_sp_rif_macvlan_is_vrrp4(mac) && | |
6664 | !mlxsw_sp_rif_macvlan_is_vrrp6(mac)) | |
6665 | return 0; | |
6666 | ||
6667 | mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index); | |
6668 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
6669 | if (err) | |
6670 | return err; | |
6671 | ||
6672 | if (mlxsw_sp_rif_macvlan_is_vrrp4(mac)) | |
6673 | mlxsw_reg_ritr_if_vrrp_id_ipv4_set(ritr_pl, vrrp_id); | |
6674 | else | |
6675 | mlxsw_reg_ritr_if_vrrp_id_ipv6_set(ritr_pl, vrrp_id); | |
6676 | ||
6677 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
6678 | } | |
6679 | ||
2db99378 IS |
6680 | static int mlxsw_sp_rif_macvlan_add(struct mlxsw_sp *mlxsw_sp, |
6681 | const struct net_device *macvlan_dev, | |
6682 | struct netlink_ext_ack *extack) | |
6683 | { | |
6684 | struct macvlan_dev *vlan = netdev_priv(macvlan_dev); | |
6685 | struct mlxsw_sp_rif *rif; | |
6686 | int err; | |
6687 | ||
6688 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev); | |
6689 | if (!rif) { | |
6690 | NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces"); | |
6691 | return -EOPNOTSUPP; | |
6692 | } | |
6693 | ||
6694 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr, | |
6695 | mlxsw_sp_fid_index(rif->fid), true); | |
6696 | if (err) | |
6697 | return err; | |
6698 | ||
c3a49540 IS |
6699 | err = mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index, |
6700 | macvlan_dev->dev_addr, true); | |
6701 | if (err) | |
6702 | goto err_rif_vrrp_add; | |
6703 | ||
2db99378 IS |
6704 | /* Make sure the bridge driver does not have this MAC pointing at |
6705 | * some other port. | |
6706 | */ | |
6707 | if (rif->ops->fdb_del) | |
6708 | rif->ops->fdb_del(rif, macvlan_dev->dev_addr); | |
6709 | ||
6710 | return 0; | |
c3a49540 IS |
6711 | |
6712 | err_rif_vrrp_add: | |
6713 | mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr, | |
6714 | mlxsw_sp_fid_index(rif->fid), false); | |
6715 | return err; | |
2db99378 IS |
6716 | } |
6717 | ||
6718 | void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp, | |
6719 | const struct net_device *macvlan_dev) | |
6720 | { | |
6721 | struct macvlan_dev *vlan = netdev_priv(macvlan_dev); | |
6722 | struct mlxsw_sp_rif *rif; | |
6723 | ||
6724 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev); | |
6725 | /* If we do not have a RIF, then we already took care of | |
6726 | * removing the macvlan's MAC during RIF deletion. | |
6727 | */ | |
6728 | if (!rif) | |
6729 | return; | |
c3a49540 IS |
6730 | mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index, macvlan_dev->dev_addr, |
6731 | false); | |
2db99378 IS |
6732 | mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr, |
6733 | mlxsw_sp_fid_index(rif->fid), false); | |
6734 | } | |
6735 | ||
21ffedb6 IS |
6736 | static int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp, |
6737 | struct net_device *macvlan_dev, | |
2db99378 IS |
6738 | unsigned long event, |
6739 | struct netlink_ext_ack *extack) | |
6740 | { | |
2db99378 IS |
6741 | switch (event) { |
6742 | case NETDEV_UP: | |
6743 | return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack); | |
6744 | case NETDEV_DOWN: | |
6745 | mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev); | |
6746 | break; | |
6747 | } | |
6748 | ||
6749 | return 0; | |
6750 | } | |
6751 | ||
74bc9939 PM |
6752 | static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp, |
6753 | struct net_device *dev, | |
6754 | const unsigned char *dev_addr, | |
6755 | struct netlink_ext_ack *extack) | |
6756 | { | |
6757 | struct mlxsw_sp_rif *rif; | |
6758 | int i; | |
6759 | ||
6760 | /* A RIF is not created for macvlan netdevs. Their MAC is used to | |
6761 | * populate the FDB | |
6762 | */ | |
6763 | if (netif_is_macvlan(dev)) | |
6764 | return 0; | |
6765 | ||
6766 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { | |
6767 | rif = mlxsw_sp->router->rifs[i]; | |
73b8f493 | 6768 | if (rif && rif->dev && rif->dev != dev && |
74bc9939 PM |
6769 | !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr, |
6770 | mlxsw_sp->mac_mask)) { | |
6771 | NL_SET_ERR_MSG_MOD(extack, "All router interface MAC addresses must have the same prefix"); | |
6772 | return -EINVAL; | |
6773 | } | |
6774 | } | |
6775 | ||
6776 | return 0; | |
6777 | } | |
6778 | ||
21ffedb6 IS |
6779 | static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp, |
6780 | struct net_device *dev, | |
f8fa9b4e DA |
6781 | unsigned long event, |
6782 | struct netlink_ext_ack *extack) | |
b1e45526 IS |
6783 | { |
6784 | if (mlxsw_sp_port_dev_check(dev)) | |
f8fa9b4e | 6785 | return mlxsw_sp_inetaddr_port_event(dev, event, extack); |
b1e45526 | 6786 | else if (netif_is_lag_master(dev)) |
f8fa9b4e | 6787 | return mlxsw_sp_inetaddr_lag_event(dev, event, extack); |
b1e45526 | 6788 | else if (netif_is_bridge_master(dev)) |
21ffedb6 IS |
6789 | return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, event, |
6790 | extack); | |
b1e45526 | 6791 | else if (is_vlan_dev(dev)) |
21ffedb6 IS |
6792 | return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event, |
6793 | extack); | |
2db99378 | 6794 | else if (netif_is_macvlan(dev)) |
21ffedb6 IS |
6795 | return mlxsw_sp_inetaddr_macvlan_event(mlxsw_sp, dev, event, |
6796 | extack); | |
b1e45526 IS |
6797 | else |
6798 | return 0; | |
6799 | } | |
6800 | ||
965fa8e6 IS |
6801 | static int mlxsw_sp_inetaddr_event(struct notifier_block *nb, |
6802 | unsigned long event, void *ptr) | |
4724ba56 IS |
6803 | { |
6804 | struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; | |
6805 | struct net_device *dev = ifa->ifa_dev->dev; | |
965fa8e6 | 6806 | struct mlxsw_sp_router *router; |
bf95233e | 6807 | struct mlxsw_sp_rif *rif; |
4724ba56 IS |
6808 | int err = 0; |
6809 | ||
89d5dd2e DA |
6810 | /* NETDEV_UP event is handled by mlxsw_sp_inetaddr_valid_event */ |
6811 | if (event == NETDEV_UP) | |
6812 | goto out; | |
6813 | ||
965fa8e6 IS |
6814 | router = container_of(nb, struct mlxsw_sp_router, inetaddr_nb); |
6815 | rif = mlxsw_sp_rif_find_by_dev(router->mlxsw_sp, dev); | |
89d5dd2e DA |
6816 | if (!mlxsw_sp_rif_should_config(rif, dev, event)) |
6817 | goto out; | |
6818 | ||
965fa8e6 | 6819 | err = __mlxsw_sp_inetaddr_event(router->mlxsw_sp, dev, event, NULL); |
89d5dd2e DA |
6820 | out: |
6821 | return notifier_from_errno(err); | |
6822 | } | |
6823 | ||
6824 | int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused, | |
6825 | unsigned long event, void *ptr) | |
6826 | { | |
6827 | struct in_validator_info *ivi = (struct in_validator_info *) ptr; | |
6828 | struct net_device *dev = ivi->ivi_dev->dev; | |
6829 | struct mlxsw_sp *mlxsw_sp; | |
6830 | struct mlxsw_sp_rif *rif; | |
6831 | int err = 0; | |
6832 | ||
4724ba56 IS |
6833 | mlxsw_sp = mlxsw_sp_lower_get(dev); |
6834 | if (!mlxsw_sp) | |
6835 | goto out; | |
6836 | ||
bf95233e | 6837 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); |
5ea1237f | 6838 | if (!mlxsw_sp_rif_should_config(rif, dev, event)) |
4724ba56 IS |
6839 | goto out; |
6840 | ||
74bc9939 PM |
6841 | err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr, |
6842 | ivi->extack); | |
6843 | if (err) | |
6844 | goto out; | |
6845 | ||
21ffedb6 | 6846 | err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, ivi->extack); |
4724ba56 IS |
6847 | out: |
6848 | return notifier_from_errno(err); | |
6849 | } | |
6850 | ||
5ea1237f AS |
6851 | struct mlxsw_sp_inet6addr_event_work { |
6852 | struct work_struct work; | |
965fa8e6 | 6853 | struct mlxsw_sp *mlxsw_sp; |
5ea1237f AS |
6854 | struct net_device *dev; |
6855 | unsigned long event; | |
6856 | }; | |
6857 | ||
6858 | static void mlxsw_sp_inet6addr_event_work(struct work_struct *work) | |
6859 | { | |
6860 | struct mlxsw_sp_inet6addr_event_work *inet6addr_work = | |
6861 | container_of(work, struct mlxsw_sp_inet6addr_event_work, work); | |
965fa8e6 | 6862 | struct mlxsw_sp *mlxsw_sp = inet6addr_work->mlxsw_sp; |
5ea1237f AS |
6863 | struct net_device *dev = inet6addr_work->dev; |
6864 | unsigned long event = inet6addr_work->event; | |
5ea1237f AS |
6865 | struct mlxsw_sp_rif *rif; |
6866 | ||
6867 | rtnl_lock(); | |
5ea1237f AS |
6868 | |
6869 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | |
6870 | if (!mlxsw_sp_rif_should_config(rif, dev, event)) | |
6871 | goto out; | |
6872 | ||
21ffedb6 | 6873 | __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, NULL); |
5ea1237f AS |
6874 | out: |
6875 | rtnl_unlock(); | |
6876 | dev_put(dev); | |
6877 | kfree(inet6addr_work); | |
6878 | } | |
6879 | ||
6880 | /* Called with rcu_read_lock() */ | |
965fa8e6 IS |
6881 | static int mlxsw_sp_inet6addr_event(struct notifier_block *nb, |
6882 | unsigned long event, void *ptr) | |
5ea1237f AS |
6883 | { |
6884 | struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr; | |
6885 | struct mlxsw_sp_inet6addr_event_work *inet6addr_work; | |
6886 | struct net_device *dev = if6->idev->dev; | |
965fa8e6 | 6887 | struct mlxsw_sp_router *router; |
5ea1237f | 6888 | |
89d5dd2e DA |
6889 | /* NETDEV_UP event is handled by mlxsw_sp_inet6addr_valid_event */ |
6890 | if (event == NETDEV_UP) | |
6891 | return NOTIFY_DONE; | |
6892 | ||
5ea1237f AS |
6893 | inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC); |
6894 | if (!inet6addr_work) | |
6895 | return NOTIFY_BAD; | |
6896 | ||
965fa8e6 | 6897 | router = container_of(nb, struct mlxsw_sp_router, inet6addr_nb); |
5ea1237f | 6898 | INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work); |
965fa8e6 | 6899 | inet6addr_work->mlxsw_sp = router->mlxsw_sp; |
5ea1237f AS |
6900 | inet6addr_work->dev = dev; |
6901 | inet6addr_work->event = event; | |
6902 | dev_hold(dev); | |
6903 | mlxsw_core_schedule_work(&inet6addr_work->work); | |
6904 | ||
6905 | return NOTIFY_DONE; | |
6906 | } | |
6907 | ||
89d5dd2e DA |
6908 | int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused, |
6909 | unsigned long event, void *ptr) | |
6910 | { | |
6911 | struct in6_validator_info *i6vi = (struct in6_validator_info *) ptr; | |
6912 | struct net_device *dev = i6vi->i6vi_dev->dev; | |
6913 | struct mlxsw_sp *mlxsw_sp; | |
6914 | struct mlxsw_sp_rif *rif; | |
6915 | int err = 0; | |
6916 | ||
6917 | mlxsw_sp = mlxsw_sp_lower_get(dev); | |
6918 | if (!mlxsw_sp) | |
6919 | goto out; | |
6920 | ||
6921 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | |
6922 | if (!mlxsw_sp_rif_should_config(rif, dev, event)) | |
6923 | goto out; | |
6924 | ||
74bc9939 PM |
6925 | err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr, |
6926 | i6vi->extack); | |
6927 | if (err) | |
6928 | goto out; | |
6929 | ||
21ffedb6 | 6930 | err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, i6vi->extack); |
89d5dd2e DA |
6931 | out: |
6932 | return notifier_from_errno(err); | |
6933 | } | |
6934 | ||
bf95233e | 6935 | static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, |
4724ba56 IS |
6936 | const char *mac, int mtu) |
6937 | { | |
6938 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
6939 | int err; | |
6940 | ||
bf95233e | 6941 | mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index); |
4724ba56 IS |
6942 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); |
6943 | if (err) | |
6944 | return err; | |
6945 | ||
6946 | mlxsw_reg_ritr_mtu_set(ritr_pl, mtu); | |
6947 | mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac); | |
6948 | mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE); | |
6949 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
6950 | } | |
6951 | ||
9735f2d2 PM |
6952 | static int |
6953 | mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp, | |
6954 | struct mlxsw_sp_rif *rif) | |
4724ba56 | 6955 | { |
9735f2d2 | 6956 | struct net_device *dev = rif->dev; |
a1107487 | 6957 | u16 fid_index; |
4724ba56 IS |
6958 | int err; |
6959 | ||
a1107487 | 6960 | fid_index = mlxsw_sp_fid_index(rif->fid); |
4724ba56 | 6961 | |
a1107487 | 6962 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false); |
4724ba56 IS |
6963 | if (err) |
6964 | return err; | |
6965 | ||
bf95233e AS |
6966 | err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr, |
6967 | dev->mtu); | |
4724ba56 IS |
6968 | if (err) |
6969 | goto err_rif_edit; | |
6970 | ||
a1107487 | 6971 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true); |
4724ba56 IS |
6972 | if (err) |
6973 | goto err_rif_fdb_op; | |
6974 | ||
fd890fe9 YG |
6975 | if (rif->mtu != dev->mtu) { |
6976 | struct mlxsw_sp_vr *vr; | |
9742f866 | 6977 | int i; |
fd890fe9 YG |
6978 | |
6979 | /* The RIF is relevant only to its mr_table instance, as unlike | |
6980 | * unicast routing, in multicast routing a RIF cannot be shared | |
6981 | * between several multicast routing tables. | |
6982 | */ | |
6983 | vr = &mlxsw_sp->router->vrs[rif->vr_id]; | |
9742f866 YM |
6984 | for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) |
6985 | mlxsw_sp_mr_rif_mtu_update(vr->mr_table[i], | |
6986 | rif, dev->mtu); | |
fd890fe9 YG |
6987 | } |
6988 | ||
bf95233e AS |
6989 | ether_addr_copy(rif->addr, dev->dev_addr); |
6990 | rif->mtu = dev->mtu; | |
4724ba56 | 6991 | |
bf95233e | 6992 | netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index); |
4724ba56 IS |
6993 | |
6994 | return 0; | |
6995 | ||
6996 | err_rif_fdb_op: | |
bf95233e | 6997 | mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu); |
4724ba56 | 6998 | err_rif_edit: |
a1107487 | 6999 | mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true); |
4724ba56 IS |
7000 | return err; |
7001 | } | |
7002 | ||
74bc9939 PM |
7003 | static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif, |
7004 | struct netdev_notifier_pre_changeaddr_info *info) | |
7005 | { | |
7006 | struct netlink_ext_ack *extack; | |
7007 | ||
7008 | extack = netdev_notifier_info_to_extack(&info->info); | |
7009 | return mlxsw_sp_router_port_check_rif_addr(rif->mlxsw_sp, rif->dev, | |
7010 | info->dev_addr, extack); | |
7011 | } | |
7012 | ||
9735f2d2 PM |
7013 | int mlxsw_sp_netdevice_router_port_event(struct net_device *dev, |
7014 | unsigned long event, void *ptr) | |
7015 | { | |
7016 | struct mlxsw_sp *mlxsw_sp; | |
7017 | struct mlxsw_sp_rif *rif; | |
7018 | ||
7019 | mlxsw_sp = mlxsw_sp_lower_get(dev); | |
7020 | if (!mlxsw_sp) | |
7021 | return 0; | |
7022 | ||
7023 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | |
7024 | if (!rif) | |
7025 | return 0; | |
7026 | ||
7027 | switch (event) { | |
7028 | case NETDEV_CHANGEMTU: /* fall through */ | |
7029 | case NETDEV_CHANGEADDR: | |
7030 | return mlxsw_sp_router_port_change_event(mlxsw_sp, rif); | |
74bc9939 PM |
7031 | case NETDEV_PRE_CHANGEADDR: |
7032 | return mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr); | |
9735f2d2 PM |
7033 | } |
7034 | ||
7035 | return 0; | |
7036 | } | |
7037 | ||
b1e45526 | 7038 | static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp, |
f8fa9b4e DA |
7039 | struct net_device *l3_dev, |
7040 | struct netlink_ext_ack *extack) | |
7179eb5a | 7041 | { |
b1e45526 | 7042 | struct mlxsw_sp_rif *rif; |
7179eb5a | 7043 | |
b1e45526 IS |
7044 | /* If netdev is already associated with a RIF, then we need to |
7045 | * destroy it and create a new one with the new virtual router ID. | |
7179eb5a | 7046 | */ |
b1e45526 IS |
7047 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); |
7048 | if (rif) | |
21ffedb6 IS |
7049 | __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, |
7050 | extack); | |
7179eb5a | 7051 | |
21ffedb6 | 7052 | return __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_UP, extack); |
7179eb5a IS |
7053 | } |
7054 | ||
b1e45526 IS |
7055 | static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp, |
7056 | struct net_device *l3_dev) | |
7179eb5a | 7057 | { |
b1e45526 | 7058 | struct mlxsw_sp_rif *rif; |
7179eb5a | 7059 | |
b1e45526 IS |
7060 | rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); |
7061 | if (!rif) | |
7179eb5a | 7062 | return; |
21ffedb6 | 7063 | __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, NULL); |
7179eb5a IS |
7064 | } |
7065 | ||
b1e45526 IS |
7066 | int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, |
7067 | struct netdev_notifier_changeupper_info *info) | |
3d70e458 | 7068 | { |
b1e45526 IS |
7069 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev); |
7070 | int err = 0; | |
3d70e458 | 7071 | |
c5516185 IS |
7072 | /* We do not create a RIF for a macvlan, but only use it to |
7073 | * direct more MAC addresses to the router. | |
7074 | */ | |
7075 | if (!mlxsw_sp || netif_is_macvlan(l3_dev)) | |
b1e45526 | 7076 | return 0; |
3d70e458 | 7077 | |
b1e45526 IS |
7078 | switch (event) { |
7079 | case NETDEV_PRECHANGEUPPER: | |
7080 | return 0; | |
7081 | case NETDEV_CHANGEUPPER: | |
f8fa9b4e DA |
7082 | if (info->linking) { |
7083 | struct netlink_ext_ack *extack; | |
7084 | ||
7085 | extack = netdev_notifier_info_to_extack(&info->info); | |
7086 | err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev, extack); | |
7087 | } else { | |
b1e45526 | 7088 | mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev); |
f8fa9b4e | 7089 | } |
b1e45526 IS |
7090 | break; |
7091 | } | |
3d70e458 | 7092 | |
b1e45526 | 7093 | return err; |
3d70e458 IS |
7094 | } |
7095 | ||
2db99378 IS |
7096 | static int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev, void *data) |
7097 | { | |
7098 | struct mlxsw_sp_rif *rif = data; | |
7099 | ||
7100 | if (!netif_is_macvlan(dev)) | |
7101 | return 0; | |
7102 | ||
7103 | return mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr, | |
7104 | mlxsw_sp_fid_index(rif->fid), false); | |
7105 | } | |
7106 | ||
7107 | static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif) | |
7108 | { | |
7109 | if (!netif_is_macvlan_port(rif->dev)) | |
7110 | return 0; | |
7111 | ||
7112 | netdev_warn(rif->dev, "Router interface is deleted. Upper macvlans will not work\n"); | |
7113 | return netdev_walk_all_upper_dev_rcu(rif->dev, | |
7114 | __mlxsw_sp_rif_macvlan_flush, rif); | |
7115 | } | |
7116 | ||
e4f3c1c1 IS |
7117 | static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif, |
7118 | const struct mlxsw_sp_rif_params *params) | |
7119 | { | |
7120 | struct mlxsw_sp_rif_subport *rif_subport; | |
7121 | ||
7122 | rif_subport = mlxsw_sp_rif_subport_rif(rif); | |
32fd4b49 | 7123 | refcount_set(&rif_subport->ref_count, 1); |
e4f3c1c1 IS |
7124 | rif_subport->vid = params->vid; |
7125 | rif_subport->lag = params->lag; | |
7126 | if (params->lag) | |
7127 | rif_subport->lag_id = params->lag_id; | |
a1107487 | 7128 | else |
e4f3c1c1 IS |
7129 | rif_subport->system_port = params->system_port; |
7130 | } | |
7131 | ||
7132 | static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) | |
7133 | { | |
7134 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7135 | struct mlxsw_sp_rif_subport *rif_subport; | |
7136 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
7137 | ||
7138 | rif_subport = mlxsw_sp_rif_subport_rif(rif); | |
7139 | mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF, | |
9571e828 PM |
7140 | rif->rif_index, rif->vr_id, rif->dev->mtu); |
7141 | mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); | |
e4f3c1c1 IS |
7142 | mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag, |
7143 | rif_subport->lag ? rif_subport->lag_id : | |
7144 | rif_subport->system_port, | |
7145 | rif_subport->vid); | |
7146 | ||
7147 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
7148 | } | |
7149 | ||
7150 | static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif) | |
7151 | { | |
010cadf9 PM |
7152 | int err; |
7153 | ||
7154 | err = mlxsw_sp_rif_subport_op(rif, true); | |
7155 | if (err) | |
7156 | return err; | |
7157 | ||
7158 | err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, | |
7159 | mlxsw_sp_fid_index(rif->fid), true); | |
7160 | if (err) | |
7161 | goto err_rif_fdb_op; | |
7162 | ||
7163 | mlxsw_sp_fid_rif_set(rif->fid, rif); | |
7164 | return 0; | |
7165 | ||
7166 | err_rif_fdb_op: | |
7167 | mlxsw_sp_rif_subport_op(rif, false); | |
7168 | return err; | |
a1107487 IS |
7169 | } |
7170 | ||
e4f3c1c1 IS |
7171 | static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif) |
7172 | { | |
010cadf9 PM |
7173 | struct mlxsw_sp_fid *fid = rif->fid; |
7174 | ||
7175 | mlxsw_sp_fid_rif_set(fid, NULL); | |
7176 | mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, | |
7177 | mlxsw_sp_fid_index(fid), false); | |
2db99378 | 7178 | mlxsw_sp_rif_macvlan_flush(rif); |
e4f3c1c1 IS |
7179 | mlxsw_sp_rif_subport_op(rif, false); |
7180 | } | |
7181 | ||
7182 | static struct mlxsw_sp_fid * | |
5f15e257 PM |
7183 | mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif, |
7184 | struct netlink_ext_ack *extack) | |
e4f3c1c1 IS |
7185 | { |
7186 | return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index); | |
7187 | } | |
7188 | ||
7189 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = { | |
7190 | .type = MLXSW_SP_RIF_TYPE_SUBPORT, | |
7191 | .rif_size = sizeof(struct mlxsw_sp_rif_subport), | |
7192 | .setup = mlxsw_sp_rif_subport_setup, | |
7193 | .configure = mlxsw_sp_rif_subport_configure, | |
7194 | .deconfigure = mlxsw_sp_rif_subport_deconfigure, | |
7195 | .fid_get = mlxsw_sp_rif_subport_fid_get, | |
7196 | }; | |
7197 | ||
7198 | static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif, | |
7199 | enum mlxsw_reg_ritr_if_type type, | |
7200 | u16 vid_fid, bool enable) | |
7201 | { | |
7202 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7203 | char ritr_pl[MLXSW_REG_RITR_LEN]; | |
7204 | ||
7205 | mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id, | |
9571e828 PM |
7206 | rif->dev->mtu); |
7207 | mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); | |
e4f3c1c1 IS |
7208 | mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid); |
7209 | ||
7210 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | |
7211 | } | |
7212 | ||
b35750f1 | 7213 | u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp) |
e4f3c1c1 IS |
7214 | { |
7215 | return mlxsw_core_max_ports(mlxsw_sp->core) + 1; | |
7216 | } | |
7217 | ||
7218 | static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif) | |
7219 | { | |
7220 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7221 | u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid); | |
7222 | int err; | |
7223 | ||
7224 | err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true); | |
7225 | if (err) | |
7226 | return err; | |
7227 | ||
0d284818 IS |
7228 | err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7229 | mlxsw_sp_router_port(mlxsw_sp), true); | |
7230 | if (err) | |
7231 | goto err_fid_mc_flood_set; | |
7232 | ||
e4f3c1c1 IS |
7233 | err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, |
7234 | mlxsw_sp_router_port(mlxsw_sp), true); | |
7235 | if (err) | |
7236 | goto err_fid_bc_flood_set; | |
7237 | ||
010cadf9 PM |
7238 | err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, |
7239 | mlxsw_sp_fid_index(rif->fid), true); | |
7240 | if (err) | |
7241 | goto err_rif_fdb_op; | |
7242 | ||
7243 | mlxsw_sp_fid_rif_set(rif->fid, rif); | |
e4f3c1c1 IS |
7244 | return 0; |
7245 | ||
010cadf9 PM |
7246 | err_rif_fdb_op: |
7247 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, | |
7248 | mlxsw_sp_router_port(mlxsw_sp), false); | |
e4f3c1c1 | 7249 | err_fid_bc_flood_set: |
0d284818 IS |
7250 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7251 | mlxsw_sp_router_port(mlxsw_sp), false); | |
7252 | err_fid_mc_flood_set: | |
e4f3c1c1 IS |
7253 | mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); |
7254 | return err; | |
7255 | } | |
7256 | ||
7257 | static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif) | |
7258 | { | |
e4f3c1c1 | 7259 | u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid); |
010cadf9 PM |
7260 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; |
7261 | struct mlxsw_sp_fid *fid = rif->fid; | |
e4f3c1c1 | 7262 | |
010cadf9 PM |
7263 | mlxsw_sp_fid_rif_set(fid, NULL); |
7264 | mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, | |
7265 | mlxsw_sp_fid_index(fid), false); | |
2db99378 | 7266 | mlxsw_sp_rif_macvlan_flush(rif); |
e4f3c1c1 IS |
7267 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, |
7268 | mlxsw_sp_router_port(mlxsw_sp), false); | |
0d284818 IS |
7269 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7270 | mlxsw_sp_router_port(mlxsw_sp), false); | |
e4f3c1c1 IS |
7271 | mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); |
7272 | } | |
7273 | ||
7274 | static struct mlxsw_sp_fid * | |
5f15e257 PM |
7275 | mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif, |
7276 | struct netlink_ext_ack *extack) | |
e4f3c1c1 | 7277 | { |
f40be47a | 7278 | struct net_device *br_dev = rif->dev; |
e6f1960a PM |
7279 | u16 vid; |
7280 | int err; | |
7281 | ||
7282 | if (is_vlan_dev(rif->dev)) { | |
7283 | vid = vlan_dev_vlan_id(rif->dev); | |
f40be47a IS |
7284 | br_dev = vlan_dev_real_dev(rif->dev); |
7285 | if (WARN_ON(!netif_is_bridge_master(br_dev))) | |
7286 | return ERR_PTR(-EINVAL); | |
e6f1960a PM |
7287 | } else { |
7288 | err = br_vlan_get_pvid(rif->dev, &vid); | |
be9c64b1 | 7289 | if (err < 0 || !vid) { |
e6f1960a | 7290 | NL_SET_ERR_MSG_MOD(extack, "Couldn't determine bridge PVID"); |
be9c64b1 | 7291 | return ERR_PTR(-EINVAL); |
e6f1960a PM |
7292 | } |
7293 | } | |
e4f3c1c1 | 7294 | |
f40be47a | 7295 | return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, br_dev, vid, extack); |
e4f3c1c1 IS |
7296 | } |
7297 | ||
2db99378 IS |
7298 | static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac) |
7299 | { | |
7300 | u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid); | |
7301 | struct switchdev_notifier_fdb_info info; | |
7302 | struct net_device *br_dev; | |
7303 | struct net_device *dev; | |
7304 | ||
7305 | br_dev = is_vlan_dev(rif->dev) ? vlan_dev_real_dev(rif->dev) : rif->dev; | |
7306 | dev = br_fdb_find_port(br_dev, mac, vid); | |
7307 | if (!dev) | |
7308 | return; | |
7309 | ||
7310 | info.addr = mac; | |
7311 | info.vid = vid; | |
6685987c PM |
7312 | call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info, |
7313 | NULL); | |
2db99378 IS |
7314 | } |
7315 | ||
e4f3c1c1 IS |
7316 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = { |
7317 | .type = MLXSW_SP_RIF_TYPE_VLAN, | |
7318 | .rif_size = sizeof(struct mlxsw_sp_rif), | |
7319 | .configure = mlxsw_sp_rif_vlan_configure, | |
7320 | .deconfigure = mlxsw_sp_rif_vlan_deconfigure, | |
7321 | .fid_get = mlxsw_sp_rif_vlan_fid_get, | |
2db99378 | 7322 | .fdb_del = mlxsw_sp_rif_vlan_fdb_del, |
e4f3c1c1 IS |
7323 | }; |
7324 | ||
7325 | static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) | |
7326 | { | |
7327 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7328 | u16 fid_index = mlxsw_sp_fid_index(rif->fid); | |
7329 | int err; | |
7330 | ||
7331 | err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, | |
7332 | true); | |
7333 | if (err) | |
7334 | return err; | |
7335 | ||
0d284818 IS |
7336 | err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7337 | mlxsw_sp_router_port(mlxsw_sp), true); | |
7338 | if (err) | |
7339 | goto err_fid_mc_flood_set; | |
7340 | ||
e4f3c1c1 IS |
7341 | err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, |
7342 | mlxsw_sp_router_port(mlxsw_sp), true); | |
7343 | if (err) | |
7344 | goto err_fid_bc_flood_set; | |
7345 | ||
010cadf9 PM |
7346 | err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, |
7347 | mlxsw_sp_fid_index(rif->fid), true); | |
7348 | if (err) | |
7349 | goto err_rif_fdb_op; | |
7350 | ||
7351 | mlxsw_sp_fid_rif_set(rif->fid, rif); | |
e4f3c1c1 IS |
7352 | return 0; |
7353 | ||
010cadf9 PM |
7354 | err_rif_fdb_op: |
7355 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, | |
7356 | mlxsw_sp_router_port(mlxsw_sp), false); | |
e4f3c1c1 | 7357 | err_fid_bc_flood_set: |
0d284818 IS |
7358 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7359 | mlxsw_sp_router_port(mlxsw_sp), false); | |
7360 | err_fid_mc_flood_set: | |
e4f3c1c1 IS |
7361 | mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); |
7362 | return err; | |
7363 | } | |
7364 | ||
7365 | static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif) | |
7366 | { | |
e4f3c1c1 | 7367 | u16 fid_index = mlxsw_sp_fid_index(rif->fid); |
010cadf9 PM |
7368 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; |
7369 | struct mlxsw_sp_fid *fid = rif->fid; | |
e4f3c1c1 | 7370 | |
010cadf9 PM |
7371 | mlxsw_sp_fid_rif_set(fid, NULL); |
7372 | mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, | |
7373 | mlxsw_sp_fid_index(fid), false); | |
2db99378 | 7374 | mlxsw_sp_rif_macvlan_flush(rif); |
e4f3c1c1 IS |
7375 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, |
7376 | mlxsw_sp_router_port(mlxsw_sp), false); | |
0d284818 IS |
7377 | mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, |
7378 | mlxsw_sp_router_port(mlxsw_sp), false); | |
e4f3c1c1 IS |
7379 | mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); |
7380 | } | |
7381 | ||
7382 | static struct mlxsw_sp_fid * | |
5f15e257 PM |
7383 | mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif, |
7384 | struct netlink_ext_ack *extack) | |
e4f3c1c1 | 7385 | { |
f40be47a | 7386 | return mlxsw_sp_bridge_fid_get(rif->mlxsw_sp, rif->dev, 0, extack); |
e4f3c1c1 IS |
7387 | } |
7388 | ||
2db99378 IS |
7389 | static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac) |
7390 | { | |
7391 | struct switchdev_notifier_fdb_info info; | |
7392 | struct net_device *dev; | |
7393 | ||
7394 | dev = br_fdb_find_port(rif->dev, mac, 0); | |
7395 | if (!dev) | |
7396 | return; | |
7397 | ||
7398 | info.addr = mac; | |
7399 | info.vid = 0; | |
6685987c PM |
7400 | call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info, |
7401 | NULL); | |
2db99378 IS |
7402 | } |
7403 | ||
e4f3c1c1 IS |
7404 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = { |
7405 | .type = MLXSW_SP_RIF_TYPE_FID, | |
7406 | .rif_size = sizeof(struct mlxsw_sp_rif), | |
7407 | .configure = mlxsw_sp_rif_fid_configure, | |
7408 | .deconfigure = mlxsw_sp_rif_fid_deconfigure, | |
7409 | .fid_get = mlxsw_sp_rif_fid_fid_get, | |
2db99378 | 7410 | .fdb_del = mlxsw_sp_rif_fid_fdb_del, |
e4f3c1c1 IS |
7411 | }; |
7412 | ||
ba6da02a IS |
7413 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_emu_ops = { |
7414 | .type = MLXSW_SP_RIF_TYPE_VLAN, | |
7415 | .rif_size = sizeof(struct mlxsw_sp_rif), | |
7416 | .configure = mlxsw_sp_rif_fid_configure, | |
7417 | .deconfigure = mlxsw_sp_rif_fid_deconfigure, | |
7418 | .fid_get = mlxsw_sp_rif_vlan_fid_get, | |
7419 | .fdb_del = mlxsw_sp_rif_vlan_fdb_del, | |
7420 | }; | |
7421 | ||
6ddb7426 PM |
7422 | static struct mlxsw_sp_rif_ipip_lb * |
7423 | mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif) | |
7424 | { | |
7425 | return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); | |
7426 | } | |
7427 | ||
7428 | static void | |
7429 | mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif, | |
7430 | const struct mlxsw_sp_rif_params *params) | |
7431 | { | |
7432 | struct mlxsw_sp_rif_params_ipip_lb *params_lb; | |
7433 | struct mlxsw_sp_rif_ipip_lb *rif_lb; | |
7434 | ||
7435 | params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb, | |
7436 | common); | |
7437 | rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif); | |
7438 | rif_lb->lb_config = params_lb->lb_config; | |
7439 | } | |
7440 | ||
6ddb7426 | 7441 | static int |
99974468 | 7442 | mlxsw_sp1_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) |
6ddb7426 PM |
7443 | { |
7444 | struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); | |
7445 | u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev); | |
7446 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7447 | struct mlxsw_sp_vr *ul_vr; | |
7448 | int err; | |
7449 | ||
f8fa9b4e | 7450 | ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id, NULL); |
6ddb7426 PM |
7451 | if (IS_ERR(ul_vr)) |
7452 | return PTR_ERR(ul_vr); | |
7453 | ||
3c747500 | 7454 | err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, true); |
6ddb7426 PM |
7455 | if (err) |
7456 | goto err_loopback_op; | |
7457 | ||
7458 | lb_rif->ul_vr_id = ul_vr->id; | |
25f844dd | 7459 | lb_rif->ul_rif_id = 0; |
6ddb7426 PM |
7460 | ++ul_vr->rif_count; |
7461 | return 0; | |
7462 | ||
7463 | err_loopback_op: | |
2b52ce02 | 7464 | mlxsw_sp_vr_put(mlxsw_sp, ul_vr); |
6ddb7426 PM |
7465 | return err; |
7466 | } | |
7467 | ||
99974468 | 7468 | static void mlxsw_sp1_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif) |
6ddb7426 PM |
7469 | { |
7470 | struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); | |
7471 | struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; | |
7472 | struct mlxsw_sp_vr *ul_vr; | |
7473 | ||
7474 | ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id]; | |
3c747500 | 7475 | mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, false); |
6ddb7426 PM |
7476 | |
7477 | --ul_vr->rif_count; | |
2b52ce02 | 7478 | mlxsw_sp_vr_put(mlxsw_sp, ul_vr); |
6ddb7426 PM |
7479 | } |
7480 | ||
99974468 | 7481 | static const struct mlxsw_sp_rif_ops mlxsw_sp1_rif_ipip_lb_ops = { |
6ddb7426 PM |
7482 | .type = MLXSW_SP_RIF_TYPE_IPIP_LB, |
7483 | .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb), | |
7484 | .setup = mlxsw_sp_rif_ipip_lb_setup, | |
99974468 ND |
7485 | .configure = mlxsw_sp1_rif_ipip_lb_configure, |
7486 | .deconfigure = mlxsw_sp1_rif_ipip_lb_deconfigure, | |
6ddb7426 PM |
7487 | }; |
7488 | ||
99974468 | 7489 | const struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[] = { |
e4f3c1c1 | 7490 | [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops, |
c2e7490c | 7491 | [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_emu_ops, |
e4f3c1c1 | 7492 | [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops, |
99974468 ND |
7493 | [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp1_rif_ipip_lb_ops, |
7494 | }; | |
7495 | ||
7496 | static int | |
7497 | mlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) | |
7498 | { | |
7499 | return 0; | |
7500 | } | |
7501 | ||
7502 | static void mlxsw_sp2_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif) | |
7503 | { | |
7504 | } | |
7505 | ||
7506 | static const struct mlxsw_sp_rif_ops mlxsw_sp2_rif_ipip_lb_ops = { | |
7507 | .type = MLXSW_SP_RIF_TYPE_IPIP_LB, | |
7508 | .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb), | |
7509 | .setup = mlxsw_sp_rif_ipip_lb_setup, | |
7510 | .configure = mlxsw_sp2_rif_ipip_lb_configure, | |
7511 | .deconfigure = mlxsw_sp2_rif_ipip_lb_deconfigure, | |
7512 | }; | |
7513 | ||
7514 | const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = { | |
7515 | [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops, | |
7516 | [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_emu_ops, | |
7517 | [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops, | |
7518 | [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp2_rif_ipip_lb_ops, | |
e4f3c1c1 IS |
7519 | }; |
7520 | ||
348b8fc3 IS |
7521 | static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) |
7522 | { | |
7523 | u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); | |
7524 | ||
7525 | mlxsw_sp->router->rifs = kcalloc(max_rifs, | |
7526 | sizeof(struct mlxsw_sp_rif *), | |
7527 | GFP_KERNEL); | |
7528 | if (!mlxsw_sp->router->rifs) | |
7529 | return -ENOMEM; | |
e4f3c1c1 | 7530 | |
348b8fc3 IS |
7531 | return 0; |
7532 | } | |
7533 | ||
7534 | static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) | |
7535 | { | |
7536 | int i; | |
7537 | ||
7538 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) | |
7539 | WARN_ON_ONCE(mlxsw_sp->router->rifs[i]); | |
7540 | ||
7541 | kfree(mlxsw_sp->router->rifs); | |
7542 | } | |
7543 | ||
dcbda282 PM |
7544 | static int |
7545 | mlxsw_sp_ipip_config_tigcr(struct mlxsw_sp *mlxsw_sp) | |
7546 | { | |
7547 | char tigcr_pl[MLXSW_REG_TIGCR_LEN]; | |
7548 | ||
7549 | mlxsw_reg_tigcr_pack(tigcr_pl, true, 0); | |
7550 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tigcr), tigcr_pl); | |
7551 | } | |
7552 | ||
38ebc0f4 PM |
7553 | static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) |
7554 | { | |
7555 | mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr; | |
1012b9ac | 7556 | INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list); |
dcbda282 | 7557 | return mlxsw_sp_ipip_config_tigcr(mlxsw_sp); |
38ebc0f4 PM |
7558 | } |
7559 | ||
7560 | static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp) | |
7561 | { | |
1012b9ac | 7562 | WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list)); |
38ebc0f4 PM |
7563 | } |
7564 | ||
c3852ef7 IS |
7565 | static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) |
7566 | { | |
7e39d115 | 7567 | struct mlxsw_sp_router *router; |
c3852ef7 IS |
7568 | |
7569 | /* Flush pending FIB notifications and then flush the device's | |
7570 | * table before requesting another dump. The FIB notification | |
7571 | * block is unregistered, so no need to take RTNL. | |
7572 | */ | |
7573 | mlxsw_core_flush_owq(); | |
7e39d115 IS |
7574 | router = container_of(nb, struct mlxsw_sp_router, fib_nb); |
7575 | mlxsw_sp_router_fib_flush(router->mlxsw_sp); | |
c3852ef7 IS |
7576 | } |
7577 | ||
af658b6a IS |
7578 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
7579 | static void mlxsw_sp_mp_hash_header_set(char *recr2_pl, int header) | |
7580 | { | |
7581 | mlxsw_reg_recr2_outer_header_enables_set(recr2_pl, header, true); | |
7582 | } | |
7583 | ||
7584 | static void mlxsw_sp_mp_hash_field_set(char *recr2_pl, int field) | |
7585 | { | |
7586 | mlxsw_reg_recr2_outer_header_fields_enable_set(recr2_pl, field, true); | |
7587 | } | |
7588 | ||
7589 | static void mlxsw_sp_mp4_hash_init(char *recr2_pl) | |
7590 | { | |
7591 | bool only_l3 = !init_net.ipv4.sysctl_fib_multipath_hash_policy; | |
7592 | ||
7593 | mlxsw_sp_mp_hash_header_set(recr2_pl, | |
7594 | MLXSW_REG_RECR2_IPV4_EN_NOT_TCP_NOT_UDP); | |
7595 | mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_IPV4_EN_TCP_UDP); | |
7596 | mlxsw_reg_recr2_ipv4_sip_enable(recr2_pl); | |
7597 | mlxsw_reg_recr2_ipv4_dip_enable(recr2_pl); | |
7598 | if (only_l3) | |
7599 | return; | |
7600 | mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_EN_IPV4); | |
7601 | mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV4_PROTOCOL); | |
7602 | mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_SPORT); | |
7603 | mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_TCP_UDP_DPORT); | |
7604 | } | |
7605 | ||
7606 | static void mlxsw_sp_mp6_hash_init(char *recr2_pl) | |
7607 | { | |
918ee507 | 7608 | bool only_l3 = !ip6_multipath_hash_policy(&init_net); |
5e18b9c5 | 7609 | |
af658b6a IS |
7610 | mlxsw_sp_mp_hash_header_set(recr2_pl, |
7611 | MLXSW_REG_RECR2_IPV6_EN_NOT_TCP_NOT_UDP); | |
7612 | mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_IPV6_EN_TCP_UDP); | |
7613 | mlxsw_reg_recr2_ipv6_sip_enable(recr2_pl); | |
7614 | mlxsw_reg_recr2_ipv6_dip_enable(recr2_pl); | |
af658b6a | 7615 | mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV6_NEXT_HEADER); |
5e18b9c5 DA |
7616 | if (only_l3) { |
7617 | mlxsw_sp_mp_hash_field_set(recr2_pl, | |
7618 | MLXSW_REG_RECR2_IPV6_FLOW_LABEL); | |
7619 | } else { | |
7620 | mlxsw_sp_mp_hash_header_set(recr2_pl, | |
7621 | MLXSW_REG_RECR2_TCP_UDP_EN_IPV6); | |
7622 | mlxsw_sp_mp_hash_field_set(recr2_pl, | |
7623 | MLXSW_REG_RECR2_TCP_UDP_SPORT); | |
7624 | mlxsw_sp_mp_hash_field_set(recr2_pl, | |
7625 | MLXSW_REG_RECR2_TCP_UDP_DPORT); | |
7626 | } | |
af658b6a IS |
7627 | } |
7628 | ||
7629 | static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp) | |
7630 | { | |
7631 | char recr2_pl[MLXSW_REG_RECR2_LEN]; | |
7632 | u32 seed; | |
7633 | ||
7634 | get_random_bytes(&seed, sizeof(seed)); | |
7635 | mlxsw_reg_recr2_pack(recr2_pl, seed); | |
7636 | mlxsw_sp_mp4_hash_init(recr2_pl); | |
7637 | mlxsw_sp_mp6_hash_init(recr2_pl); | |
7638 | ||
7639 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(recr2), recr2_pl); | |
7640 | } | |
7641 | #else | |
7642 | static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp) | |
7643 | { | |
7644 | return 0; | |
7645 | } | |
7646 | #endif | |
7647 | ||
48276a29 YM |
7648 | static int mlxsw_sp_dscp_init(struct mlxsw_sp *mlxsw_sp) |
7649 | { | |
7650 | char rdpm_pl[MLXSW_REG_RDPM_LEN]; | |
7651 | unsigned int i; | |
7652 | ||
7653 | MLXSW_REG_ZERO(rdpm, rdpm_pl); | |
7654 | ||
7655 | /* HW is determining switch priority based on DSCP-bits, but the | |
7656 | * kernel is still doing that based on the ToS. Since there's a | |
7657 | * mismatch in bits we need to make sure to translate the right | |
7658 | * value ToS would observe, skipping the 2 least-significant ECN bits. | |
7659 | */ | |
7660 | for (i = 0; i < MLXSW_REG_RDPM_DSCP_ENTRY_REC_MAX_COUNT; i++) | |
7661 | mlxsw_reg_rdpm_pack(rdpm_pl, i, rt_tos2priority(i << 2)); | |
7662 | ||
7663 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rdpm), rdpm_pl); | |
7664 | } | |
7665 | ||
4724ba56 IS |
7666 | static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) |
7667 | { | |
64953423 | 7668 | bool usp = init_net.ipv4.sysctl_ip_fwd_update_priority; |
4724ba56 IS |
7669 | char rgcr_pl[MLXSW_REG_RGCR_LEN]; |
7670 | u64 max_rifs; | |
7671 | int err; | |
7672 | ||
7673 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS)) | |
7674 | return -EIO; | |
4724ba56 | 7675 | max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); |
4724ba56 | 7676 | |
e29237e7 | 7677 | mlxsw_reg_rgcr_pack(rgcr_pl, true, true); |
4724ba56 | 7678 | mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); |
64953423 | 7679 | mlxsw_reg_rgcr_usp_set(rgcr_pl, usp); |
4724ba56 IS |
7680 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); |
7681 | if (err) | |
348b8fc3 | 7682 | return err; |
4724ba56 | 7683 | return 0; |
4724ba56 IS |
7684 | } |
7685 | ||
7686 | static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) | |
7687 | { | |
7688 | char rgcr_pl[MLXSW_REG_RGCR_LEN]; | |
4724ba56 | 7689 | |
e29237e7 | 7690 | mlxsw_reg_rgcr_pack(rgcr_pl, false, false); |
4724ba56 | 7691 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); |
4724ba56 IS |
7692 | } |
7693 | ||
b45f64d1 JP |
7694 | int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) |
7695 | { | |
9011b677 | 7696 | struct mlxsw_sp_router *router; |
b45f64d1 JP |
7697 | int err; |
7698 | ||
9011b677 IS |
7699 | router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL); |
7700 | if (!router) | |
7701 | return -ENOMEM; | |
7702 | mlxsw_sp->router = router; | |
7703 | router->mlxsw_sp = mlxsw_sp; | |
7704 | ||
965fa8e6 IS |
7705 | router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event; |
7706 | err = register_inetaddr_notifier(&router->inetaddr_nb); | |
7707 | if (err) | |
7708 | goto err_register_inetaddr_notifier; | |
7709 | ||
7710 | router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event; | |
7711 | err = register_inet6addr_notifier(&router->inet6addr_nb); | |
7712 | if (err) | |
7713 | goto err_register_inet6addr_notifier; | |
7714 | ||
9011b677 | 7715 | INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list); |
b45f64d1 JP |
7716 | err = __mlxsw_sp_router_init(mlxsw_sp); |
7717 | if (err) | |
9011b677 | 7718 | goto err_router_init; |
b45f64d1 | 7719 | |
348b8fc3 IS |
7720 | err = mlxsw_sp_rifs_init(mlxsw_sp); |
7721 | if (err) | |
7722 | goto err_rifs_init; | |
7723 | ||
38ebc0f4 PM |
7724 | err = mlxsw_sp_ipips_init(mlxsw_sp); |
7725 | if (err) | |
7726 | goto err_ipips_init; | |
7727 | ||
9011b677 | 7728 | err = rhashtable_init(&mlxsw_sp->router->nexthop_ht, |
c53b8e1b IS |
7729 | &mlxsw_sp_nexthop_ht_params); |
7730 | if (err) | |
7731 | goto err_nexthop_ht_init; | |
7732 | ||
9011b677 | 7733 | err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht, |
e9ad5e7d IS |
7734 | &mlxsw_sp_nexthop_group_ht_params); |
7735 | if (err) | |
7736 | goto err_nexthop_group_ht_init; | |
7737 | ||
dbe4598c | 7738 | INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list); |
8494ab06 IS |
7739 | err = mlxsw_sp_lpm_init(mlxsw_sp); |
7740 | if (err) | |
7741 | goto err_lpm_init; | |
7742 | ||
d42b0965 YG |
7743 | err = mlxsw_sp_mr_init(mlxsw_sp, &mlxsw_sp_mr_tcam_ops); |
7744 | if (err) | |
7745 | goto err_mr_init; | |
7746 | ||
b45f64d1 JP |
7747 | err = mlxsw_sp_vrs_init(mlxsw_sp); |
7748 | if (err) | |
7749 | goto err_vrs_init; | |
7750 | ||
8c9583a8 | 7751 | err = mlxsw_sp_neigh_init(mlxsw_sp); |
b45f64d1 JP |
7752 | if (err) |
7753 | goto err_neigh_init; | |
7754 | ||
48fac885 IS |
7755 | mlxsw_sp->router->netevent_nb.notifier_call = |
7756 | mlxsw_sp_router_netevent_event; | |
7757 | err = register_netevent_notifier(&mlxsw_sp->router->netevent_nb); | |
7758 | if (err) | |
7759 | goto err_register_netevent_notifier; | |
7760 | ||
af658b6a IS |
7761 | err = mlxsw_sp_mp_hash_init(mlxsw_sp); |
7762 | if (err) | |
7763 | goto err_mp_hash_init; | |
7764 | ||
48276a29 YM |
7765 | err = mlxsw_sp_dscp_init(mlxsw_sp); |
7766 | if (err) | |
7767 | goto err_dscp_init; | |
7768 | ||
7e39d115 IS |
7769 | mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event; |
7770 | err = register_fib_notifier(&mlxsw_sp->router->fib_nb, | |
c3852ef7 IS |
7771 | mlxsw_sp_router_fib_dump_flush); |
7772 | if (err) | |
7773 | goto err_register_fib_notifier; | |
7774 | ||
b45f64d1 JP |
7775 | return 0; |
7776 | ||
c3852ef7 | 7777 | err_register_fib_notifier: |
48276a29 | 7778 | err_dscp_init: |
af658b6a | 7779 | err_mp_hash_init: |
48fac885 IS |
7780 | unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb); |
7781 | err_register_netevent_notifier: | |
c3852ef7 | 7782 | mlxsw_sp_neigh_fini(mlxsw_sp); |
b45f64d1 JP |
7783 | err_neigh_init: |
7784 | mlxsw_sp_vrs_fini(mlxsw_sp); | |
7785 | err_vrs_init: | |
d42b0965 YG |
7786 | mlxsw_sp_mr_fini(mlxsw_sp); |
7787 | err_mr_init: | |
8494ab06 IS |
7788 | mlxsw_sp_lpm_fini(mlxsw_sp); |
7789 | err_lpm_init: | |
9011b677 | 7790 | rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht); |
e9ad5e7d | 7791 | err_nexthop_group_ht_init: |
9011b677 | 7792 | rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); |
c53b8e1b | 7793 | err_nexthop_ht_init: |
38ebc0f4 PM |
7794 | mlxsw_sp_ipips_fini(mlxsw_sp); |
7795 | err_ipips_init: | |
348b8fc3 IS |
7796 | mlxsw_sp_rifs_fini(mlxsw_sp); |
7797 | err_rifs_init: | |
b45f64d1 | 7798 | __mlxsw_sp_router_fini(mlxsw_sp); |
9011b677 | 7799 | err_router_init: |
965fa8e6 IS |
7800 | unregister_inet6addr_notifier(&router->inet6addr_nb); |
7801 | err_register_inet6addr_notifier: | |
7802 | unregister_inetaddr_notifier(&router->inetaddr_nb); | |
7803 | err_register_inetaddr_notifier: | |
9011b677 | 7804 | kfree(mlxsw_sp->router); |
b45f64d1 JP |
7805 | return err; |
7806 | } | |
7807 | ||
7808 | void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) | |
7809 | { | |
7e39d115 | 7810 | unregister_fib_notifier(&mlxsw_sp->router->fib_nb); |
48fac885 | 7811 | unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb); |
b45f64d1 JP |
7812 | mlxsw_sp_neigh_fini(mlxsw_sp); |
7813 | mlxsw_sp_vrs_fini(mlxsw_sp); | |
d42b0965 | 7814 | mlxsw_sp_mr_fini(mlxsw_sp); |
8494ab06 | 7815 | mlxsw_sp_lpm_fini(mlxsw_sp); |
9011b677 IS |
7816 | rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht); |
7817 | rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); | |
38ebc0f4 | 7818 | mlxsw_sp_ipips_fini(mlxsw_sp); |
348b8fc3 | 7819 | mlxsw_sp_rifs_fini(mlxsw_sp); |
b45f64d1 | 7820 | __mlxsw_sp_router_fini(mlxsw_sp); |
965fa8e6 IS |
7821 | unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb); |
7822 | unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb); | |
9011b677 | 7823 | kfree(mlxsw_sp->router); |
b45f64d1 | 7824 | } |