blob: db834220a2fe2cffeaa2673cfb45347f0f7e79e3 [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Petr Machata6ddb7422017-09-02 23:49:19 +02003 * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
Ido Schimmel464dce12016-07-02 11:00:15 +02004 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
Yotam Gigic723c7352016-07-05 11:27:43 +02006 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
Petr Machata6ddb7422017-09-02 23:49:19 +02007 * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02008 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * Alternatively, this software may be distributed under the terms of the
22 * GNU General Public License ("GPL") version 2 as published by the Free
23 * Software Foundation.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/kernel.h>
39#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020040#include <linux/rhashtable.h>
41#include <linux/bitops.h>
42#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020043#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010044#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010045#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020046#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020047#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020048#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020049#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020050#include <net/neighbour.h>
51#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020052#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020053#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010054#include <net/fib_rules.h>
Petr Machata6ddb7422017-09-02 23:49:19 +020055#include <net/ip_tunnels.h>
Ido Schimmel57837882017-03-16 09:08:16 +010056#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020057#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020058#include <net/ndisc.h>
59#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020060#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020061
62#include "spectrum.h"
63#include "core.h"
64#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020065#include "spectrum_cnt.h"
66#include "spectrum_dpipe.h"
Petr Machata38ebc0f2017-09-02 23:49:17 +020067#include "spectrum_ipip.h"
Yotam Gigid42b0962017-09-27 08:23:20 +020068#include "spectrum_mr.h"
69#include "spectrum_mr_tcam.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020070#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020071
Ido Schimmel9011b672017-05-16 19:38:25 +020072struct mlxsw_sp_vr;
73struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020074struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020075
76struct mlxsw_sp_router {
77 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020078 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020079 struct mlxsw_sp_vr *vrs;
80 struct rhashtable neigh_ht;
81 struct rhashtable nexthop_group_ht;
82 struct rhashtable nexthop_ht;
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +020083 struct list_head nexthop_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020084 struct {
85 struct mlxsw_sp_lpm_tree *trees;
86 unsigned int tree_count;
87 } lpm;
88 struct {
89 struct delayed_work dw;
90 unsigned long interval; /* ms */
91 } neighs_update;
92 struct delayed_work nexthop_probe_dw;
93#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
94 struct list_head nexthop_neighs_list;
Petr Machata1012b9a2017-09-02 23:49:23 +020095 struct list_head ipip_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020096 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020097 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020098 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020099 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +0200100};
101
Ido Schimmel4724ba562017-03-10 08:53:39 +0100102struct mlxsw_sp_rif {
103 struct list_head nexthop_list;
104 struct list_head neigh_list;
105 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200106 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100107 unsigned char addr[ETH_ALEN];
108 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100109 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100110 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200111 const struct mlxsw_sp_rif_ops *ops;
112 struct mlxsw_sp *mlxsw_sp;
113
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200114 unsigned int counter_ingress;
115 bool counter_ingress_valid;
116 unsigned int counter_egress;
117 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100118};
119
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200120struct mlxsw_sp_rif_params {
121 struct net_device *dev;
122 union {
123 u16 system_port;
124 u16 lag_id;
125 };
126 u16 vid;
127 bool lag;
128};
129
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200130struct mlxsw_sp_rif_subport {
131 struct mlxsw_sp_rif common;
132 union {
133 u16 system_port;
134 u16 lag_id;
135 };
136 u16 vid;
137 bool lag;
138};
139
Petr Machata6ddb7422017-09-02 23:49:19 +0200140struct mlxsw_sp_rif_ipip_lb {
141 struct mlxsw_sp_rif common;
142 struct mlxsw_sp_rif_ipip_lb_config lb_config;
143 u16 ul_vr_id; /* Reserved for Spectrum-2. */
144};
145
146struct mlxsw_sp_rif_params_ipip_lb {
147 struct mlxsw_sp_rif_params common;
148 struct mlxsw_sp_rif_ipip_lb_config lb_config;
149};
150
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200151struct mlxsw_sp_rif_ops {
152 enum mlxsw_sp_rif_type type;
153 size_t rif_size;
154
155 void (*setup)(struct mlxsw_sp_rif *rif,
156 const struct mlxsw_sp_rif_params *params);
157 int (*configure)(struct mlxsw_sp_rif *rif);
158 void (*deconfigure)(struct mlxsw_sp_rif *rif);
159 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
160};
161
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200162static unsigned int *
163mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
164 enum mlxsw_sp_rif_counter_dir dir)
165{
166 switch (dir) {
167 case MLXSW_SP_RIF_COUNTER_EGRESS:
168 return &rif->counter_egress;
169 case MLXSW_SP_RIF_COUNTER_INGRESS:
170 return &rif->counter_ingress;
171 }
172 return NULL;
173}
174
175static bool
176mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
177 enum mlxsw_sp_rif_counter_dir dir)
178{
179 switch (dir) {
180 case MLXSW_SP_RIF_COUNTER_EGRESS:
181 return rif->counter_egress_valid;
182 case MLXSW_SP_RIF_COUNTER_INGRESS:
183 return rif->counter_ingress_valid;
184 }
185 return false;
186}
187
188static void
189mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
190 enum mlxsw_sp_rif_counter_dir dir,
191 bool valid)
192{
193 switch (dir) {
194 case MLXSW_SP_RIF_COUNTER_EGRESS:
195 rif->counter_egress_valid = valid;
196 break;
197 case MLXSW_SP_RIF_COUNTER_INGRESS:
198 rif->counter_ingress_valid = valid;
199 break;
200 }
201}
202
203static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
204 unsigned int counter_index, bool enable,
205 enum mlxsw_sp_rif_counter_dir dir)
206{
207 char ritr_pl[MLXSW_REG_RITR_LEN];
208 bool is_egress = false;
209 int err;
210
211 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
212 is_egress = true;
213 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
214 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
215 if (err)
216 return err;
217
218 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
219 is_egress);
220 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
221}
222
223int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
224 struct mlxsw_sp_rif *rif,
225 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
226{
227 char ricnt_pl[MLXSW_REG_RICNT_LEN];
228 unsigned int *p_counter_index;
229 bool valid;
230 int err;
231
232 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
233 if (!valid)
234 return -EINVAL;
235
236 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
237 if (!p_counter_index)
238 return -EINVAL;
239 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
240 MLXSW_REG_RICNT_OPCODE_NOP);
241 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
242 if (err)
243 return err;
244 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
245 return 0;
246}
247
248static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
249 unsigned int counter_index)
250{
251 char ricnt_pl[MLXSW_REG_RICNT_LEN];
252
253 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
254 MLXSW_REG_RICNT_OPCODE_CLEAR);
255 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
256}
257
258int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
259 struct mlxsw_sp_rif *rif,
260 enum mlxsw_sp_rif_counter_dir dir)
261{
262 unsigned int *p_counter_index;
263 int err;
264
265 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
266 if (!p_counter_index)
267 return -EINVAL;
268 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
269 p_counter_index);
270 if (err)
271 return err;
272
273 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
274 if (err)
275 goto err_counter_clear;
276
277 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
278 *p_counter_index, true, dir);
279 if (err)
280 goto err_counter_edit;
281 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
282 return 0;
283
284err_counter_edit:
285err_counter_clear:
286 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
287 *p_counter_index);
288 return err;
289}
290
291void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
292 struct mlxsw_sp_rif *rif,
293 enum mlxsw_sp_rif_counter_dir dir)
294{
295 unsigned int *p_counter_index;
296
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200297 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
298 return;
299
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200300 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
301 if (WARN_ON(!p_counter_index))
302 return;
303 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
304 *p_counter_index, false, dir);
305 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
306 *p_counter_index);
307 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
308}
309
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200310static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
311{
312 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
313 struct devlink *devlink;
314
315 devlink = priv_to_devlink(mlxsw_sp->core);
316 if (!devlink_dpipe_table_counter_enabled(devlink,
317 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
318 return;
319 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
320}
321
322static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
323{
324 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
325
326 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
327}
328
Ido Schimmel4724ba562017-03-10 08:53:39 +0100329static struct mlxsw_sp_rif *
330mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
331 const struct net_device *dev);
332
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200333#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200334
335struct mlxsw_sp_prefix_usage {
336 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
337};
338
Jiri Pirko53342022016-07-04 08:23:08 +0200339#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
340 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
341
342static bool
343mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
344 struct mlxsw_sp_prefix_usage *prefix_usage2)
345{
346 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
347}
348
Jiri Pirko6b75c482016-07-04 08:23:09 +0200349static bool
350mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
351{
352 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
353
354 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
355}
356
357static void
358mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
359 struct mlxsw_sp_prefix_usage *prefix_usage2)
360{
361 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
362}
363
364static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200365mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
366 unsigned char prefix_len)
367{
368 set_bit(prefix_len, prefix_usage->b);
369}
370
371static void
372mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
373 unsigned char prefix_len)
374{
375 clear_bit(prefix_len, prefix_usage->b);
376}
377
378struct mlxsw_sp_fib_key {
379 unsigned char addr[sizeof(struct in6_addr)];
380 unsigned char prefix_len;
381};
382
Jiri Pirko61c503f2016-07-04 08:23:11 +0200383enum mlxsw_sp_fib_entry_type {
384 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
385 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
386 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
Petr Machata4607f6d2017-09-02 23:49:25 +0200387
388 /* This is a special case of local delivery, where a packet should be
389 * decapsulated on reception. Note that there is no corresponding ENCAP,
390 * because that's a type of next hop, not of FIB entry. (There can be
391 * several next hops in a REMOTE entry, and some of them may be
392 * encapsulating entries.)
393 */
394 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
Jiri Pirko61c503f2016-07-04 08:23:11 +0200395};
396
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200397struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200398struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200399
Ido Schimmel9aecce12017-02-09 10:28:42 +0100400struct mlxsw_sp_fib_node {
401 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200402 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100403 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100404 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100405 struct mlxsw_sp_fib_key key;
406};
407
Petr Machata4607f6d2017-09-02 23:49:25 +0200408struct mlxsw_sp_fib_entry_decap {
409 struct mlxsw_sp_ipip_entry *ipip_entry;
410 u32 tunnel_index;
411};
412
Ido Schimmel9aecce12017-02-09 10:28:42 +0100413struct mlxsw_sp_fib_entry {
414 struct list_head list;
415 struct mlxsw_sp_fib_node *fib_node;
416 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200417 struct list_head nexthop_group_node;
418 struct mlxsw_sp_nexthop_group *nh_group;
Petr Machata4607f6d2017-09-02 23:49:25 +0200419 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200420};
421
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200422struct mlxsw_sp_fib4_entry {
423 struct mlxsw_sp_fib_entry common;
424 u32 tb_id;
425 u32 prio;
426 u8 tos;
427 u8 type;
428};
429
Ido Schimmel428b8512017-08-03 13:28:28 +0200430struct mlxsw_sp_fib6_entry {
431 struct mlxsw_sp_fib_entry common;
432 struct list_head rt6_list;
433 unsigned int nrt6;
434};
435
436struct mlxsw_sp_rt6 {
437 struct list_head list;
438 struct rt6_info *rt;
439};
440
Ido Schimmel9011b672017-05-16 19:38:25 +0200441struct mlxsw_sp_lpm_tree {
442 u8 id; /* tree ID */
443 unsigned int ref_count;
444 enum mlxsw_sp_l3proto proto;
445 struct mlxsw_sp_prefix_usage prefix_usage;
446};
447
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200448struct mlxsw_sp_fib {
449 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100450 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100451 struct mlxsw_sp_vr *vr;
452 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200453 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
454 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100455 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200456};
457
Ido Schimmel9011b672017-05-16 19:38:25 +0200458struct mlxsw_sp_vr {
459 u16 id; /* virtual router ID */
460 u32 tb_id; /* kernel fib table id */
461 unsigned int rif_count;
462 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200463 struct mlxsw_sp_fib *fib6;
Yotam Gigid42b0962017-09-27 08:23:20 +0200464 struct mlxsw_sp_mr_table *mr4_table;
Ido Schimmel9011b672017-05-16 19:38:25 +0200465};
466
Ido Schimmel9aecce12017-02-09 10:28:42 +0100467static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200468
Ido Schimmel76610eb2017-03-10 08:53:41 +0100469static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
470 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200471{
472 struct mlxsw_sp_fib *fib;
473 int err;
474
475 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
476 if (!fib)
477 return ERR_PTR(-ENOMEM);
478 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
479 if (err)
480 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100481 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100482 fib->proto = proto;
483 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200484 return fib;
485
486err_rhashtable_init:
487 kfree(fib);
488 return ERR_PTR(err);
489}
490
491static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
492{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100493 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100494 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200495 rhashtable_destroy(&fib->ht);
496 kfree(fib);
497}
498
Jiri Pirko53342022016-07-04 08:23:08 +0200499static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100500mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200501{
502 static struct mlxsw_sp_lpm_tree *lpm_tree;
503 int i;
504
Ido Schimmel9011b672017-05-16 19:38:25 +0200505 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
506 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100507 if (lpm_tree->ref_count == 0)
508 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200509 }
510 return NULL;
511}
512
513static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
514 struct mlxsw_sp_lpm_tree *lpm_tree)
515{
516 char ralta_pl[MLXSW_REG_RALTA_LEN];
517
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200518 mlxsw_reg_ralta_pack(ralta_pl, true,
519 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
520 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200521 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
522}
523
Ido Schimmelcc702672017-08-14 10:54:03 +0200524static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
525 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200526{
527 char ralta_pl[MLXSW_REG_RALTA_LEN];
528
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200529 mlxsw_reg_ralta_pack(ralta_pl, false,
530 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
531 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200532 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200533}
534
535static int
536mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
537 struct mlxsw_sp_prefix_usage *prefix_usage,
538 struct mlxsw_sp_lpm_tree *lpm_tree)
539{
540 char ralst_pl[MLXSW_REG_RALST_LEN];
541 u8 root_bin = 0;
542 u8 prefix;
543 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
544
545 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
546 root_bin = prefix;
547
548 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
549 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
550 if (prefix == 0)
551 continue;
552 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
553 MLXSW_REG_RALST_BIN_NO_CHILD);
554 last_prefix = prefix;
555 }
556 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
557}
558
559static struct mlxsw_sp_lpm_tree *
560mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
561 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100562 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200563{
564 struct mlxsw_sp_lpm_tree *lpm_tree;
565 int err;
566
Ido Schimmel382dbb42017-03-10 08:53:40 +0100567 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200568 if (!lpm_tree)
569 return ERR_PTR(-EBUSY);
570 lpm_tree->proto = proto;
571 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
572 if (err)
573 return ERR_PTR(err);
574
575 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
576 lpm_tree);
577 if (err)
578 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200579 memcpy(&lpm_tree->prefix_usage, prefix_usage,
580 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200581 return lpm_tree;
582
583err_left_struct_set:
584 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
585 return ERR_PTR(err);
586}
587
Ido Schimmelcc702672017-08-14 10:54:03 +0200588static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
589 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200590{
Ido Schimmelcc702672017-08-14 10:54:03 +0200591 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200592}
593
594static struct mlxsw_sp_lpm_tree *
595mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
596 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100597 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200598{
599 struct mlxsw_sp_lpm_tree *lpm_tree;
600 int i;
601
Ido Schimmel9011b672017-05-16 19:38:25 +0200602 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
603 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200604 if (lpm_tree->ref_count != 0 &&
605 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200606 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
607 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200608 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200609 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200610 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
611}
Jiri Pirko53342022016-07-04 08:23:08 +0200612
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200613static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
614{
Jiri Pirko53342022016-07-04 08:23:08 +0200615 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200616}
617
Ido Schimmelcc702672017-08-14 10:54:03 +0200618static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
619 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200620{
621 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200622 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200623}
624
Ido Schimmeld7a60302017-06-08 08:47:43 +0200625#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100626
627static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200628{
629 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100630 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200631 int i;
632
Ido Schimmel8494ab02017-03-24 08:02:47 +0100633 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
634 return -EIO;
635
636 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200637 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
638 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100639 sizeof(struct mlxsw_sp_lpm_tree),
640 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200641 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100642 return -ENOMEM;
643
Ido Schimmel9011b672017-05-16 19:38:25 +0200644 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
645 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200646 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
647 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100648
649 return 0;
650}
651
652static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
653{
Ido Schimmel9011b672017-05-16 19:38:25 +0200654 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200655}
656
Ido Schimmel76610eb2017-03-10 08:53:41 +0100657static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
658{
Yotam Gigid42b0962017-09-27 08:23:20 +0200659 return !!vr->fib4 || !!vr->fib6 || !!vr->mr4_table;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100660}
661
Jiri Pirko6b75c482016-07-04 08:23:09 +0200662static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
663{
664 struct mlxsw_sp_vr *vr;
665 int i;
666
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200667 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200668 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100669 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200670 return vr;
671 }
672 return NULL;
673}
674
675static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200676 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200677{
678 char raltb_pl[MLXSW_REG_RALTB_LEN];
679
Ido Schimmel76610eb2017-03-10 08:53:41 +0100680 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
681 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200682 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200683 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
684}
685
686static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100687 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200688{
689 char raltb_pl[MLXSW_REG_RALTB_LEN];
690
691 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100692 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
693 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200694 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
695}
696
697static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
698{
Yotam Gigi7e50d432017-09-27 08:23:19 +0200699 /* For our purpose, squash main, default and local tables into one */
700 if (tb_id == RT_TABLE_LOCAL || tb_id == RT_TABLE_DEFAULT)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200701 tb_id = RT_TABLE_MAIN;
702 return tb_id;
703}
704
705static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100706 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200707{
708 struct mlxsw_sp_vr *vr;
709 int i;
710
711 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200712
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200713 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200714 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100715 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200716 return vr;
717 }
718 return NULL;
719}
720
Ido Schimmel76610eb2017-03-10 08:53:41 +0100721static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
722 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200723{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100724 switch (proto) {
725 case MLXSW_SP_L3_PROTO_IPV4:
726 return vr->fib4;
727 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200728 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100729 }
730 return NULL;
731}
732
733static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
734 u32 tb_id)
735{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200736 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200737 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200738
739 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
740 if (!vr)
741 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100742 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
743 if (IS_ERR(vr->fib4))
744 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200745 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
746 if (IS_ERR(vr->fib6)) {
747 err = PTR_ERR(vr->fib6);
748 goto err_fib6_create;
749 }
Yotam Gigid42b0962017-09-27 08:23:20 +0200750 vr->mr4_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,
751 MLXSW_SP_L3_PROTO_IPV4);
752 if (IS_ERR(vr->mr4_table)) {
753 err = PTR_ERR(vr->mr4_table);
754 goto err_mr_table_create;
755 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200756 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200757 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200758
Yotam Gigid42b0962017-09-27 08:23:20 +0200759err_mr_table_create:
760 mlxsw_sp_fib_destroy(vr->fib6);
761 vr->fib6 = NULL;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200762err_fib6_create:
763 mlxsw_sp_fib_destroy(vr->fib4);
764 vr->fib4 = NULL;
765 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200766}
767
Ido Schimmel76610eb2017-03-10 08:53:41 +0100768static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200769{
Yotam Gigid42b0962017-09-27 08:23:20 +0200770 mlxsw_sp_mr_table_destroy(vr->mr4_table);
771 vr->mr4_table = NULL;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200772 mlxsw_sp_fib_destroy(vr->fib6);
773 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100774 mlxsw_sp_fib_destroy(vr->fib4);
775 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200776}
777
Ido Schimmel76610eb2017-03-10 08:53:41 +0100778static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200779{
780 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200781
782 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100783 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
784 if (!vr)
785 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200786 return vr;
787}
788
Ido Schimmel76610eb2017-03-10 08:53:41 +0100789static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200790{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200791 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
Yotam Gigid42b0962017-09-27 08:23:20 +0200792 list_empty(&vr->fib6->node_list) &&
793 mlxsw_sp_mr_table_empty(vr->mr4_table))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100794 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200795}
796
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200797static bool
798mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
799 enum mlxsw_sp_l3proto proto, u8 tree_id)
800{
801 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
802
803 if (!mlxsw_sp_vr_is_used(vr))
804 return false;
805 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
806 return true;
807 return false;
808}
809
810static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
811 struct mlxsw_sp_fib *fib,
812 struct mlxsw_sp_lpm_tree *new_tree)
813{
814 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
815 int err;
816
817 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
818 if (err)
819 return err;
820 fib->lpm_tree = new_tree;
821 mlxsw_sp_lpm_tree_hold(new_tree);
822 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
823 return 0;
824}
825
826static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
827 struct mlxsw_sp_fib *fib,
828 struct mlxsw_sp_lpm_tree *new_tree)
829{
830 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
831 enum mlxsw_sp_l3proto proto = fib->proto;
832 u8 old_id, new_id = new_tree->id;
833 struct mlxsw_sp_vr *vr;
834 int i, err;
835
836 if (!old_tree)
837 goto no_replace;
838 old_id = old_tree->id;
839
840 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
841 vr = &mlxsw_sp->router->vrs[i];
842 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
843 continue;
844 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
845 mlxsw_sp_vr_fib(vr, proto),
846 new_tree);
847 if (err)
848 goto err_tree_replace;
849 }
850
851 return 0;
852
853err_tree_replace:
854 for (i--; i >= 0; i--) {
855 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
856 continue;
857 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
858 mlxsw_sp_vr_fib(vr, proto),
859 old_tree);
860 }
861 return err;
862
863no_replace:
864 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
865 if (err)
866 return err;
867 fib->lpm_tree = new_tree;
868 mlxsw_sp_lpm_tree_hold(new_tree);
869 return 0;
870}
871
872static void
873mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
874 enum mlxsw_sp_l3proto proto,
875 struct mlxsw_sp_prefix_usage *req_prefix_usage)
876{
877 int i;
878
879 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
880 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
881 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
882 unsigned char prefix;
883
884 if (!mlxsw_sp_vr_is_used(vr))
885 continue;
886 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
887 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
888 }
889}
890
Nogah Frankel9497c042016-09-20 11:16:54 +0200891static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200892{
893 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200894 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200895 int i;
896
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200897 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200898 return -EIO;
899
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200900 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200901 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
902 GFP_KERNEL);
903 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200904 return -ENOMEM;
905
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200906 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200907 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200908 vr->id = i;
909 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200910
911 return 0;
912}
913
Ido Schimmelac571de2016-11-14 11:26:32 +0100914static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
915
Nogah Frankel9497c042016-09-20 11:16:54 +0200916static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
917{
Ido Schimmel30572242016-12-03 16:45:01 +0100918 /* At this stage we're guaranteed not to have new incoming
919 * FIB notifications and the work queue is free from FIBs
920 * sitting on top of mlxsw netdevs. However, we can still
921 * have other FIBs queued. Flush the queue before flushing
922 * the device's tables. No need for locks, as we're the only
923 * writer.
924 */
925 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100926 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200927 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200928}
929
Petr Machata6ddb7422017-09-02 23:49:19 +0200930static struct net_device *
931__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
932{
933 struct ip_tunnel *tun = netdev_priv(ol_dev);
934 struct net *net = dev_net(ol_dev);
935
936 return __dev_get_by_index(net, tun->parms.link);
937}
938
939static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
940{
941 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
942
943 if (d)
944 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
945 else
946 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
947}
948
Petr Machata1012b9a2017-09-02 23:49:23 +0200949static struct mlxsw_sp_rif *
950mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
951 const struct mlxsw_sp_rif_params *params);
952
953static struct mlxsw_sp_rif_ipip_lb *
954mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
955 enum mlxsw_sp_ipip_type ipipt,
956 struct net_device *ol_dev)
957{
958 struct mlxsw_sp_rif_params_ipip_lb lb_params;
959 const struct mlxsw_sp_ipip_ops *ipip_ops;
960 struct mlxsw_sp_rif *rif;
961
962 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
963 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
964 .common.dev = ol_dev,
965 .common.lag = false,
966 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
967 };
968
969 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common);
970 if (IS_ERR(rif))
971 return ERR_CAST(rif);
972 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
973}
974
975static struct mlxsw_sp_ipip_entry *
976mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
977 enum mlxsw_sp_ipip_type ipipt,
978 struct net_device *ol_dev)
979{
980 struct mlxsw_sp_ipip_entry *ipip_entry;
981 struct mlxsw_sp_ipip_entry *ret = NULL;
982
983 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
984 if (!ipip_entry)
985 return ERR_PTR(-ENOMEM);
986
987 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
988 ol_dev);
989 if (IS_ERR(ipip_entry->ol_lb)) {
990 ret = ERR_CAST(ipip_entry->ol_lb);
991 goto err_ol_ipip_lb_create;
992 }
993
994 ipip_entry->ipipt = ipipt;
995 ipip_entry->ol_dev = ol_dev;
996
997 return ipip_entry;
998
999err_ol_ipip_lb_create:
1000 kfree(ipip_entry);
1001 return ret;
1002}
1003
1004static void
1005mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry)
1006{
1007 WARN_ON(ipip_entry->ref_count > 0);
1008 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
1009 kfree(ipip_entry);
1010}
1011
1012static __be32
1013mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev)
1014{
1015 struct ip_tunnel *tun = netdev_priv(ol_dev);
1016
1017 return tun->parms.iph.saddr;
1018}
1019
1020union mlxsw_sp_l3addr
1021mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
1022 const struct net_device *ol_dev)
1023{
1024 switch (proto) {
1025 case MLXSW_SP_L3_PROTO_IPV4:
1026 return (union mlxsw_sp_l3addr) {
1027 .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev),
1028 };
1029 case MLXSW_SP_L3_PROTO_IPV6:
1030 break;
1031 };
1032
1033 WARN_ON(1);
1034 return (union mlxsw_sp_l3addr) {
1035 .addr4 = 0,
1036 };
1037}
1038
Petr Machataee954d1a2017-09-02 23:49:29 +02001039__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
1040{
1041 struct ip_tunnel *tun = netdev_priv(ol_dev);
1042
1043 return tun->parms.iph.daddr;
1044}
1045
1046union mlxsw_sp_l3addr
1047mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1048 const struct net_device *ol_dev)
1049{
1050 switch (proto) {
1051 case MLXSW_SP_L3_PROTO_IPV4:
1052 return (union mlxsw_sp_l3addr) {
1053 .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev),
1054 };
1055 case MLXSW_SP_L3_PROTO_IPV6:
1056 break;
1057 };
1058
1059 WARN_ON(1);
1060 return (union mlxsw_sp_l3addr) {
1061 .addr4 = 0,
1062 };
1063}
1064
Petr Machata1012b9a2017-09-02 23:49:23 +02001065static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1066 const union mlxsw_sp_l3addr *addr2)
1067{
1068 return !memcmp(addr1, addr2, sizeof(*addr1));
1069}
1070
1071static bool
1072mlxsw_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
Petr Machata4607f6d2017-09-02 23:49:25 +02001090static int
1091mlxsw_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
1098 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1099 if (err)
1100 return err;
1101
1102 ipip_entry->decap_fib_entry = fib_entry;
1103 fib_entry->decap.ipip_entry = ipip_entry;
1104 fib_entry->decap.tunnel_index = tunnel_index;
1105 return 0;
1106}
1107
1108static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1109 struct mlxsw_sp_fib_entry *fib_entry)
1110{
1111 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1112 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1113 fib_entry->decap.ipip_entry = NULL;
1114 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1115}
1116
Petr Machata1cc38fb2017-09-02 23:49:26 +02001117static struct mlxsw_sp_fib_node *
1118mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1119 size_t addr_len, unsigned char prefix_len);
Petr Machata4607f6d2017-09-02 23:49:25 +02001120static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1121 struct mlxsw_sp_fib_entry *fib_entry);
1122
1123static void
1124mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1125 struct mlxsw_sp_ipip_entry *ipip_entry)
1126{
1127 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1128
1129 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1130 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1131
1132 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1133}
1134
Petr Machata1cc38fb2017-09-02 23:49:26 +02001135static void
1136mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1137 struct mlxsw_sp_ipip_entry *ipip_entry,
1138 struct mlxsw_sp_fib_entry *decap_fib_entry)
1139{
1140 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1141 ipip_entry))
1142 return;
1143 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1144
1145 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1146 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1147}
1148
1149/* Given an IPIP entry, find the corresponding decap route. */
1150static struct mlxsw_sp_fib_entry *
1151mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1152 struct mlxsw_sp_ipip_entry *ipip_entry)
1153{
1154 static struct mlxsw_sp_fib_node *fib_node;
1155 const struct mlxsw_sp_ipip_ops *ipip_ops;
1156 struct mlxsw_sp_fib_entry *fib_entry;
1157 unsigned char saddr_prefix_len;
1158 union mlxsw_sp_l3addr saddr;
1159 struct mlxsw_sp_fib *ul_fib;
1160 struct mlxsw_sp_vr *ul_vr;
1161 const void *saddrp;
1162 size_t saddr_len;
1163 u32 ul_tb_id;
1164 u32 saddr4;
1165
1166 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1167
1168 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1169 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1170 if (!ul_vr)
1171 return NULL;
1172
1173 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1174 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1175 ipip_entry->ol_dev);
1176
1177 switch (ipip_ops->ul_proto) {
1178 case MLXSW_SP_L3_PROTO_IPV4:
1179 saddr4 = be32_to_cpu(saddr.addr4);
1180 saddrp = &saddr4;
1181 saddr_len = 4;
1182 saddr_prefix_len = 32;
1183 break;
1184 case MLXSW_SP_L3_PROTO_IPV6:
1185 WARN_ON(1);
1186 return NULL;
1187 }
1188
1189 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1190 saddr_prefix_len);
1191 if (!fib_node || list_empty(&fib_node->entry_list))
1192 return NULL;
1193
1194 fib_entry = list_first_entry(&fib_node->entry_list,
1195 struct mlxsw_sp_fib_entry, list);
1196 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1197 return NULL;
1198
1199 return fib_entry;
1200}
1201
Petr Machata1012b9a2017-09-02 23:49:23 +02001202static struct mlxsw_sp_ipip_entry *
1203mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1204 enum mlxsw_sp_ipip_type ipipt,
1205 struct net_device *ol_dev)
1206{
1207 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1208 struct mlxsw_sp_router *router = mlxsw_sp->router;
1209 struct mlxsw_sp_ipip_entry *ipip_entry;
1210 enum mlxsw_sp_l3proto ul_proto;
1211 union mlxsw_sp_l3addr saddr;
1212
1213 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1214 ipip_list_node) {
1215 if (ipip_entry->ol_dev == ol_dev)
1216 goto inc_ref_count;
1217
1218 /* The configuration where several tunnels have the same local
1219 * address in the same underlay table needs special treatment in
1220 * the HW. That is currently not implemented in the driver.
1221 */
1222 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1223 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1224 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1225 ul_tb_id, ipip_entry))
1226 return ERR_PTR(-EEXIST);
1227 }
1228
1229 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1230 if (IS_ERR(ipip_entry))
1231 return ipip_entry;
1232
1233 list_add_tail(&ipip_entry->ipip_list_node,
1234 &mlxsw_sp->router->ipip_list);
1235
1236inc_ref_count:
1237 ++ipip_entry->ref_count;
1238 return ipip_entry;
1239}
1240
1241static void
1242mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1243 struct mlxsw_sp_ipip_entry *ipip_entry)
1244{
1245 if (--ipip_entry->ref_count == 0) {
1246 list_del(&ipip_entry->ipip_list_node);
1247 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1248 }
1249}
1250
Petr Machata4607f6d2017-09-02 23:49:25 +02001251static bool
1252mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1253 const struct net_device *ul_dev,
1254 enum mlxsw_sp_l3proto ul_proto,
1255 union mlxsw_sp_l3addr ul_dip,
1256 struct mlxsw_sp_ipip_entry *ipip_entry)
1257{
1258 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1259 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1260 struct net_device *ipip_ul_dev;
1261
1262 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1263 return false;
1264
1265 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1266 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1267 ul_tb_id, ipip_entry) &&
1268 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1269}
1270
1271/* Given decap parameters, find the corresponding IPIP entry. */
1272static struct mlxsw_sp_ipip_entry *
1273mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1274 const struct net_device *ul_dev,
1275 enum mlxsw_sp_l3proto ul_proto,
1276 union mlxsw_sp_l3addr ul_dip)
1277{
1278 struct mlxsw_sp_ipip_entry *ipip_entry;
1279
1280 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1281 ipip_list_node)
1282 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1283 ul_proto, ul_dip,
1284 ipip_entry))
1285 return ipip_entry;
1286
1287 return NULL;
1288}
1289
Petr Machata6698c162017-10-16 16:26:36 +02001290static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
1291 const struct net_device *dev,
1292 enum mlxsw_sp_ipip_type *p_type)
1293{
1294 struct mlxsw_sp_router *router = mlxsw_sp->router;
1295 const struct mlxsw_sp_ipip_ops *ipip_ops;
1296 enum mlxsw_sp_ipip_type ipipt;
1297
1298 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
1299 ipip_ops = router->ipip_ops_arr[ipipt];
1300 if (dev->type == ipip_ops->dev_type) {
1301 if (p_type)
1302 *p_type = ipipt;
1303 return true;
1304 }
1305 }
1306 return false;
1307}
1308
Petr Machata00635872017-10-16 16:26:37 +02001309bool mlxsw_sp_netdev_is_ipip(const struct mlxsw_sp *mlxsw_sp,
1310 const struct net_device *dev)
1311{
1312 return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
1313}
1314
1315static struct mlxsw_sp_ipip_entry *
1316mlxsw_sp_ipip_entry_find_by_ol_dev(struct mlxsw_sp *mlxsw_sp,
1317 const struct net_device *ol_dev)
1318{
1319 struct mlxsw_sp_ipip_entry *ipip_entry;
1320
1321 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1322 ipip_list_node)
1323 if (ipip_entry->ol_dev == ol_dev)
1324 return ipip_entry;
1325
1326 return NULL;
1327}
1328
1329static int mlxsw_sp_netdevice_ipip_reg_event(struct mlxsw_sp *mlxsw_sp,
1330 struct net_device *ol_dev)
1331{
1332 struct mlxsw_sp_router *router = mlxsw_sp->router;
1333 struct mlxsw_sp_ipip_entry *ipip_entry;
1334 enum mlxsw_sp_ipip_type ipipt;
1335
1336 mlxsw_sp_netdev_ipip_type(mlxsw_sp, ol_dev, &ipipt);
1337 if (router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, ol_dev,
1338 MLXSW_SP_L3_PROTO_IPV4) ||
1339 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, ol_dev,
1340 MLXSW_SP_L3_PROTO_IPV6)) {
1341 ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
1342 if (IS_ERR(ipip_entry))
1343 return PTR_ERR(ipip_entry);
1344 }
1345
1346 return 0;
1347}
1348
1349static void mlxsw_sp_netdevice_ipip_unreg_event(struct mlxsw_sp *mlxsw_sp,
1350 struct net_device *ol_dev)
1351{
1352 struct mlxsw_sp_ipip_entry *ipip_entry;
1353
1354 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
1355 if (ipip_entry)
1356 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
1357}
1358
1359static int mlxsw_sp_netdevice_ipip_up_event(struct mlxsw_sp *mlxsw_sp,
1360 struct net_device *ol_dev)
1361{
1362 struct mlxsw_sp_fib_entry *decap_fib_entry;
1363 struct mlxsw_sp_ipip_entry *ipip_entry;
1364
1365 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
1366 if (ipip_entry) {
1367 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp,
1368 ipip_entry);
1369 if (decap_fib_entry)
1370 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1371 decap_fib_entry);
1372 }
1373
1374 return 0;
1375}
1376
1377static void mlxsw_sp_netdevice_ipip_down_event(struct mlxsw_sp *mlxsw_sp,
1378 struct net_device *ol_dev)
1379{
1380 struct mlxsw_sp_ipip_entry *ipip_entry;
1381
1382 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
1383 if (ipip_entry && ipip_entry->decap_fib_entry)
1384 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1385}
1386
1387int mlxsw_sp_netdevice_ipip_event(struct mlxsw_sp *mlxsw_sp,
1388 struct net_device *ol_dev,
1389 unsigned long event)
1390{
1391 switch (event) {
1392 case NETDEV_REGISTER:
1393 return mlxsw_sp_netdevice_ipip_reg_event(mlxsw_sp, ol_dev);
1394 case NETDEV_UNREGISTER:
1395 mlxsw_sp_netdevice_ipip_unreg_event(mlxsw_sp, ol_dev);
1396 return 0;
1397 case NETDEV_UP:
1398 return mlxsw_sp_netdevice_ipip_up_event(mlxsw_sp, ol_dev);
1399 case NETDEV_DOWN:
1400 mlxsw_sp_netdevice_ipip_down_event(mlxsw_sp, ol_dev);
1401 return 0;
1402 }
1403 return 0;
1404}
1405
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001406struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +01001407 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001408};
1409
1410struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +01001411 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001412 struct rhash_head ht_node;
1413 struct mlxsw_sp_neigh_key key;
1414 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001415 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001416 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001417 struct list_head nexthop_list; /* list of nexthops using
1418 * this neigh entry
1419 */
Yotam Gigib2157142016-07-05 11:27:51 +02001420 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001421 unsigned int counter_index;
1422 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001423};
1424
1425static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1426 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1427 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1428 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1429};
1430
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001431struct mlxsw_sp_neigh_entry *
1432mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1433 struct mlxsw_sp_neigh_entry *neigh_entry)
1434{
1435 if (!neigh_entry) {
1436 if (list_empty(&rif->neigh_list))
1437 return NULL;
1438 else
1439 return list_first_entry(&rif->neigh_list,
1440 typeof(*neigh_entry),
1441 rif_list_node);
1442 }
Arkadi Sharshevskyec2437f2017-09-25 10:32:24 +02001443 if (list_is_last(&neigh_entry->rif_list_node, &rif->neigh_list))
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001444 return NULL;
1445 return list_next_entry(neigh_entry, rif_list_node);
1446}
1447
1448int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1449{
1450 return neigh_entry->key.n->tbl->family;
1451}
1452
1453unsigned char *
1454mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1455{
1456 return neigh_entry->ha;
1457}
1458
1459u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1460{
1461 struct neighbour *n;
1462
1463 n = neigh_entry->key.n;
1464 return ntohl(*((__be32 *) n->primary_key));
1465}
1466
Arkadi Sharshevsky02507682017-08-31 17:59:15 +02001467struct in6_addr *
1468mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1469{
1470 struct neighbour *n;
1471
1472 n = neigh_entry->key.n;
1473 return (struct in6_addr *) &n->primary_key;
1474}
1475
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001476int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1477 struct mlxsw_sp_neigh_entry *neigh_entry,
1478 u64 *p_counter)
1479{
1480 if (!neigh_entry->counter_valid)
1481 return -EINVAL;
1482
1483 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1484 p_counter, NULL);
1485}
1486
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001487static struct mlxsw_sp_neigh_entry *
1488mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1489 u16 rif)
1490{
1491 struct mlxsw_sp_neigh_entry *neigh_entry;
1492
1493 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1494 if (!neigh_entry)
1495 return NULL;
1496
1497 neigh_entry->key.n = n;
1498 neigh_entry->rif = rif;
1499 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1500
1501 return neigh_entry;
1502}
1503
1504static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1505{
1506 kfree(neigh_entry);
1507}
1508
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001509static int
1510mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1511 struct mlxsw_sp_neigh_entry *neigh_entry)
1512{
Ido Schimmel9011b672017-05-16 19:38:25 +02001513 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001514 &neigh_entry->ht_node,
1515 mlxsw_sp_neigh_ht_params);
1516}
1517
1518static void
1519mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1520 struct mlxsw_sp_neigh_entry *neigh_entry)
1521{
Ido Schimmel9011b672017-05-16 19:38:25 +02001522 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001523 &neigh_entry->ht_node,
1524 mlxsw_sp_neigh_ht_params);
1525}
1526
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001527static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001528mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1529 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001530{
1531 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001532 const char *table_name;
1533
1534 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1535 case AF_INET:
1536 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1537 break;
1538 case AF_INET6:
1539 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1540 break;
1541 default:
1542 WARN_ON(1);
1543 return false;
1544 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001545
1546 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001547 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001548}
1549
1550static void
1551mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1552 struct mlxsw_sp_neigh_entry *neigh_entry)
1553{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001554 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001555 return;
1556
1557 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1558 return;
1559
1560 neigh_entry->counter_valid = true;
1561}
1562
1563static void
1564mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1565 struct mlxsw_sp_neigh_entry *neigh_entry)
1566{
1567 if (!neigh_entry->counter_valid)
1568 return;
1569 mlxsw_sp_flow_counter_free(mlxsw_sp,
1570 neigh_entry->counter_index);
1571 neigh_entry->counter_valid = false;
1572}
1573
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001574static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001575mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001576{
1577 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001578 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001579 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001580
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001581 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1582 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001583 return ERR_PTR(-EINVAL);
1584
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001585 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001586 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001587 return ERR_PTR(-ENOMEM);
1588
1589 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1590 if (err)
1591 goto err_neigh_entry_insert;
1592
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001593 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001594 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001595
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001596 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001597
1598err_neigh_entry_insert:
1599 mlxsw_sp_neigh_entry_free(neigh_entry);
1600 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001601}
1602
1603static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001604mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1605 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001606{
Ido Schimmel9665b742017-02-08 11:16:42 +01001607 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001608 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001609 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1610 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001611}
1612
1613static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001614mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001615{
Jiri Pirko33b13412016-11-10 12:31:04 +01001616 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001617
Jiri Pirko33b13412016-11-10 12:31:04 +01001618 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001619 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001620 &key, mlxsw_sp_neigh_ht_params);
1621}
1622
Yotam Gigic723c7352016-07-05 11:27:43 +02001623static void
1624mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1625{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001626 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001627
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001628#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001629 interval = min_t(unsigned long,
1630 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1631 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001632#else
1633 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1634#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001635 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001636}
1637
1638static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1639 char *rauhtd_pl,
1640 int ent_index)
1641{
1642 struct net_device *dev;
1643 struct neighbour *n;
1644 __be32 dipn;
1645 u32 dip;
1646 u16 rif;
1647
1648 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1649
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001650 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001651 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1652 return;
1653 }
1654
1655 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001656 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001657 n = neigh_lookup(&arp_tbl, &dipn, dev);
1658 if (!n) {
1659 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1660 &dip);
1661 return;
1662 }
1663
1664 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1665 neigh_event_send(n, NULL);
1666 neigh_release(n);
1667}
1668
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001669#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001670static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1671 char *rauhtd_pl,
1672 int rec_index)
1673{
1674 struct net_device *dev;
1675 struct neighbour *n;
1676 struct in6_addr dip;
1677 u16 rif;
1678
1679 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1680 (char *) &dip);
1681
1682 if (!mlxsw_sp->router->rifs[rif]) {
1683 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1684 return;
1685 }
1686
1687 dev = mlxsw_sp->router->rifs[rif]->dev;
1688 n = neigh_lookup(&nd_tbl, &dip, dev);
1689 if (!n) {
1690 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1691 &dip);
1692 return;
1693 }
1694
1695 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1696 neigh_event_send(n, NULL);
1697 neigh_release(n);
1698}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001699#else
1700static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1701 char *rauhtd_pl,
1702 int rec_index)
1703{
1704}
1705#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001706
Yotam Gigic723c7352016-07-05 11:27:43 +02001707static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1708 char *rauhtd_pl,
1709 int rec_index)
1710{
1711 u8 num_entries;
1712 int i;
1713
1714 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1715 rec_index);
1716 /* Hardware starts counting at 0, so add 1. */
1717 num_entries++;
1718
1719 /* Each record consists of several neighbour entries. */
1720 for (i = 0; i < num_entries; i++) {
1721 int ent_index;
1722
1723 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1724 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1725 ent_index);
1726 }
1727
1728}
1729
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001730static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1731 char *rauhtd_pl,
1732 int rec_index)
1733{
1734 /* One record contains one entry. */
1735 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1736 rec_index);
1737}
1738
Yotam Gigic723c7352016-07-05 11:27:43 +02001739static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1740 char *rauhtd_pl, int rec_index)
1741{
1742 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1743 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1744 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1745 rec_index);
1746 break;
1747 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001748 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1749 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001750 break;
1751 }
1752}
1753
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001754static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1755{
1756 u8 num_rec, last_rec_index, num_entries;
1757
1758 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1759 last_rec_index = num_rec - 1;
1760
1761 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1762 return false;
1763 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1764 MLXSW_REG_RAUHTD_TYPE_IPV6)
1765 return true;
1766
1767 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1768 last_rec_index);
1769 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1770 return true;
1771 return false;
1772}
1773
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001774static int
1775__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1776 char *rauhtd_pl,
1777 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001778{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001779 int i, num_rec;
1780 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001781
1782 /* Make sure the neighbour's netdev isn't removed in the
1783 * process.
1784 */
1785 rtnl_lock();
1786 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001787 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001788 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1789 rauhtd_pl);
1790 if (err) {
Petr Machata7ff176f2017-10-02 12:21:57 +02001791 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour table\n");
Yotam Gigic723c7352016-07-05 11:27:43 +02001792 break;
1793 }
1794 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1795 for (i = 0; i < num_rec; i++)
1796 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1797 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001798 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001799 rtnl_unlock();
1800
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001801 return err;
1802}
1803
1804static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1805{
1806 enum mlxsw_reg_rauhtd_type type;
1807 char *rauhtd_pl;
1808 int err;
1809
1810 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1811 if (!rauhtd_pl)
1812 return -ENOMEM;
1813
1814 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1815 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1816 if (err)
1817 goto out;
1818
1819 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1820 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1821out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001822 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001823 return err;
1824}
1825
1826static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1827{
1828 struct mlxsw_sp_neigh_entry *neigh_entry;
1829
1830 /* Take RTNL mutex here to prevent lists from changes */
1831 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001832 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001833 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001834 /* If this neigh have nexthops, make the kernel think this neigh
1835 * is active regardless of the traffic.
1836 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001837 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001838 rtnl_unlock();
1839}
1840
1841static void
1842mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1843{
Ido Schimmel9011b672017-05-16 19:38:25 +02001844 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001845
Ido Schimmel9011b672017-05-16 19:38:25 +02001846 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001847 msecs_to_jiffies(interval));
1848}
1849
1850static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1851{
Ido Schimmel9011b672017-05-16 19:38:25 +02001852 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001853 int err;
1854
Ido Schimmel9011b672017-05-16 19:38:25 +02001855 router = container_of(work, struct mlxsw_sp_router,
1856 neighs_update.dw.work);
1857 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001858 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001859 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001860
Ido Schimmel9011b672017-05-16 19:38:25 +02001861 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001862
Ido Schimmel9011b672017-05-16 19:38:25 +02001863 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001864}
1865
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001866static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1867{
1868 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001869 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001870
Ido Schimmel9011b672017-05-16 19:38:25 +02001871 router = container_of(work, struct mlxsw_sp_router,
1872 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001873 /* Iterate over nexthop neighbours, find those who are unresolved and
1874 * send arp on them. This solves the chicken-egg problem when
1875 * the nexthop wouldn't get offloaded until the neighbor is resolved
1876 * but it wouldn't get resolved ever in case traffic is flowing in HW
1877 * using different nexthop.
1878 *
1879 * Take RTNL mutex here to prevent lists from changes.
1880 */
1881 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001882 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001883 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001884 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001885 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001886 rtnl_unlock();
1887
Ido Schimmel9011b672017-05-16 19:38:25 +02001888 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001889 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1890}
1891
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001892static void
1893mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1894 struct mlxsw_sp_neigh_entry *neigh_entry,
1895 bool removing);
1896
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001897static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001898{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001899 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1900 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1901}
1902
1903static void
1904mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1905 struct mlxsw_sp_neigh_entry *neigh_entry,
1906 enum mlxsw_reg_rauht_op op)
1907{
Jiri Pirko33b13412016-11-10 12:31:04 +01001908 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001909 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001910 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001911
1912 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1913 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001914 if (neigh_entry->counter_valid)
1915 mlxsw_reg_rauht_pack_counter(rauht_pl,
1916 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001917 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1918}
1919
1920static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001921mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1922 struct mlxsw_sp_neigh_entry *neigh_entry,
1923 enum mlxsw_reg_rauht_op op)
1924{
1925 struct neighbour *n = neigh_entry->key.n;
1926 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1927 const char *dip = n->primary_key;
1928
1929 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1930 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001931 if (neigh_entry->counter_valid)
1932 mlxsw_reg_rauht_pack_counter(rauht_pl,
1933 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001934 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1935}
1936
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001937bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001938{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001939 struct neighbour *n = neigh_entry->key.n;
1940
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001941 /* Packets with a link-local destination address are trapped
1942 * after LPM lookup and never reach the neighbour table, so
1943 * there is no need to program such neighbours to the device.
1944 */
1945 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1946 IPV6_ADDR_LINKLOCAL)
1947 return true;
1948 return false;
1949}
1950
1951static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001952mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1953 struct mlxsw_sp_neigh_entry *neigh_entry,
1954 bool adding)
1955{
1956 if (!adding && !neigh_entry->connected)
1957 return;
1958 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001959 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001960 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1961 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001962 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001963 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001964 return;
1965 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1966 mlxsw_sp_rauht_op(adding));
1967 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001968 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001969 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001970}
1971
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001972void
1973mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1974 struct mlxsw_sp_neigh_entry *neigh_entry,
1975 bool adding)
1976{
1977 if (adding)
1978 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1979 else
1980 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1981 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1982}
1983
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001984struct mlxsw_sp_neigh_event_work {
1985 struct work_struct work;
1986 struct mlxsw_sp *mlxsw_sp;
1987 struct neighbour *n;
1988};
1989
1990static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1991{
1992 struct mlxsw_sp_neigh_event_work *neigh_work =
1993 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1994 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1995 struct mlxsw_sp_neigh_entry *neigh_entry;
1996 struct neighbour *n = neigh_work->n;
1997 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001998 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001999 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002000
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002001 /* If these parameters are changed after we release the lock,
2002 * then we are guaranteed to receive another event letting us
2003 * know about it.
2004 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002005 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002006 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002007 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002008 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002009 read_unlock_bh(&n->lock);
2010
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002011 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01002012 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002013 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2014 if (!entry_connected && !neigh_entry)
2015 goto out;
2016 if (!neigh_entry) {
2017 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2018 if (IS_ERR(neigh_entry))
2019 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002020 }
2021
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002022 memcpy(neigh_entry->ha, ha, ETH_ALEN);
2023 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
2024 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
2025
2026 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2027 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2028
2029out:
2030 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002031 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002032 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002033}
2034
Jiri Pirkoe7322632016-09-01 10:37:43 +02002035int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
2036 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02002037{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002038 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02002039 struct mlxsw_sp_port *mlxsw_sp_port;
2040 struct mlxsw_sp *mlxsw_sp;
2041 unsigned long interval;
2042 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002043 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02002044
2045 switch (event) {
2046 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
2047 p = ptr;
2048
2049 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02002050 if (!p->dev || (p->tbl->family != AF_INET &&
2051 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02002052 return NOTIFY_DONE;
2053
2054 /* We are in atomic context and can't take RTNL mutex,
2055 * so use RCU variant to walk the device chain.
2056 */
2057 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
2058 if (!mlxsw_sp_port)
2059 return NOTIFY_DONE;
2060
2061 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2062 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02002063 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02002064
2065 mlxsw_sp_port_dev_put(mlxsw_sp_port);
2066 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002067 case NETEVENT_NEIGH_UPDATE:
2068 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002069
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02002070 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002071 return NOTIFY_DONE;
2072
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002073 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002074 if (!mlxsw_sp_port)
2075 return NOTIFY_DONE;
2076
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002077 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
2078 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002079 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002080 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002081 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002082
2083 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
2084 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2085 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002086
2087 /* Take a reference to ensure the neighbour won't be
2088 * destructed until we drop the reference in delayed
2089 * work.
2090 */
2091 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002092 mlxsw_core_schedule_work(&neigh_work->work);
2093 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02002094 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02002095 }
2096
2097 return NOTIFY_DONE;
2098}
2099
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002100static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
2101{
Yotam Gigic723c7352016-07-05 11:27:43 +02002102 int err;
2103
Ido Schimmel9011b672017-05-16 19:38:25 +02002104 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02002105 &mlxsw_sp_neigh_ht_params);
2106 if (err)
2107 return err;
2108
2109 /* Initialize the polling interval according to the default
2110 * table.
2111 */
2112 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
2113
Yotam Gigi0b2361d2016-07-05 11:27:52 +02002114 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02002115 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02002116 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02002117 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02002118 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02002119 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
2120 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02002121 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002122}
2123
2124static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
2125{
Ido Schimmel9011b672017-05-16 19:38:25 +02002126 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
2127 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
2128 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002129}
2130
Ido Schimmel9665b742017-02-08 11:16:42 +01002131static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002132 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002133{
2134 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
2135
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002136 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002137 rif_list_node) {
2138 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01002139 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002140 }
Ido Schimmel9665b742017-02-08 11:16:42 +01002141}
2142
Petr Machata35225e42017-09-02 23:49:22 +02002143enum mlxsw_sp_nexthop_type {
2144 MLXSW_SP_NEXTHOP_TYPE_ETH,
Petr Machata1012b9a2017-09-02 23:49:23 +02002145 MLXSW_SP_NEXTHOP_TYPE_IPIP,
Petr Machata35225e42017-09-02 23:49:22 +02002146};
2147
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002148struct mlxsw_sp_nexthop_key {
2149 struct fib_nh *fib_nh;
2150};
2151
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002152struct mlxsw_sp_nexthop {
2153 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01002154 struct list_head rif_list_node;
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02002155 struct list_head router_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002156 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2157 * this belongs to
2158 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002159 struct rhash_head ht_node;
2160 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002161 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002162 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002163 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002164 u8 should_offload:1, /* set indicates this neigh is connected and
2165 * should be put to KVD linear area of this group.
2166 */
2167 offloaded:1, /* set in case the neigh is actually put into
2168 * KVD linear area of this group.
2169 */
2170 update:1; /* set indicates that MAC of this neigh should be
2171 * updated in HW
2172 */
Petr Machata35225e42017-09-02 23:49:22 +02002173 enum mlxsw_sp_nexthop_type type;
2174 union {
2175 struct mlxsw_sp_neigh_entry *neigh_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02002176 struct mlxsw_sp_ipip_entry *ipip_entry;
Petr Machata35225e42017-09-02 23:49:22 +02002177 };
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02002178 unsigned int counter_index;
2179 bool counter_valid;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002180};
2181
2182struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002183 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002184 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002185 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002186 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002187 u8 adj_index_valid:1,
2188 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002189 u32 adj_index;
2190 u16 ecmp_size;
2191 u16 count;
2192 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002193#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002194};
2195
Arkadi Sharshevsky427e6522017-09-25 10:32:30 +02002196void mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp,
2197 struct mlxsw_sp_nexthop *nh)
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02002198{
2199 struct devlink *devlink;
2200
2201 devlink = priv_to_devlink(mlxsw_sp->core);
2202 if (!devlink_dpipe_table_counter_enabled(devlink,
2203 MLXSW_SP_DPIPE_TABLE_NAME_ADJ))
2204 return;
2205
2206 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nh->counter_index))
2207 return;
2208
2209 nh->counter_valid = true;
2210}
2211
Arkadi Sharshevsky427e6522017-09-25 10:32:30 +02002212void mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp,
2213 struct mlxsw_sp_nexthop *nh)
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02002214{
2215 if (!nh->counter_valid)
2216 return;
2217 mlxsw_sp_flow_counter_free(mlxsw_sp, nh->counter_index);
2218 nh->counter_valid = false;
2219}
2220
2221int mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp,
2222 struct mlxsw_sp_nexthop *nh, u64 *p_counter)
2223{
2224 if (!nh->counter_valid)
2225 return -EINVAL;
2226
2227 return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter_index,
2228 p_counter, NULL);
2229}
2230
Arkadi Sharshevskyc556cd22017-09-25 10:32:25 +02002231struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,
2232 struct mlxsw_sp_nexthop *nh)
2233{
2234 if (!nh) {
2235 if (list_empty(&router->nexthop_list))
2236 return NULL;
2237 else
2238 return list_first_entry(&router->nexthop_list,
2239 typeof(*nh), router_list_node);
2240 }
2241 if (list_is_last(&nh->router_list_node, &router->nexthop_list))
2242 return NULL;
2243 return list_next_entry(nh, router_list_node);
2244}
2245
2246bool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh)
2247{
2248 return nh->offloaded;
2249}
2250
2251unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)
2252{
2253 if (!nh->offloaded)
2254 return NULL;
2255 return nh->neigh_entry->ha;
2256}
2257
2258int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
2259 u32 *p_adj_hash_index)
2260{
2261 struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
2262 u32 adj_hash_index = 0;
2263 int i;
2264
2265 if (!nh->offloaded || !nh_grp->adj_index_valid)
2266 return -EINVAL;
2267
2268 *p_adj_index = nh_grp->adj_index;
2269
2270 for (i = 0; i < nh_grp->count; i++) {
2271 struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
2272
2273 if (nh_iter == nh)
2274 break;
2275 if (nh_iter->offloaded)
2276 adj_hash_index++;
2277 }
2278
2279 *p_adj_hash_index = adj_hash_index;
2280 return 0;
2281}
2282
2283struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)
2284{
2285 return nh->rif;
2286}
2287
2288bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
2289{
2290 struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
2291 int i;
2292
2293 for (i = 0; i < nh_grp->count; i++) {
2294 struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
2295
2296 if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP)
2297 return true;
2298 }
2299 return false;
2300}
2301
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002302static struct fib_info *
2303mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2304{
2305 return nh_grp->priv;
2306}
2307
2308struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002309 enum mlxsw_sp_l3proto proto;
2310 union {
2311 struct fib_info *fi;
2312 struct mlxsw_sp_fib6_entry *fib6_entry;
2313 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002314};
2315
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002316static bool
2317mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2318 const struct in6_addr *gw, int ifindex)
2319{
2320 int i;
2321
2322 for (i = 0; i < nh_grp->count; i++) {
2323 const struct mlxsw_sp_nexthop *nh;
2324
2325 nh = &nh_grp->nexthops[i];
2326 if (nh->ifindex == ifindex &&
2327 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2328 return true;
2329 }
2330
2331 return false;
2332}
2333
2334static bool
2335mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2336 const struct mlxsw_sp_fib6_entry *fib6_entry)
2337{
2338 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2339
2340 if (nh_grp->count != fib6_entry->nrt6)
2341 return false;
2342
2343 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2344 struct in6_addr *gw;
2345 int ifindex;
2346
2347 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2348 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2349 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2350 return false;
2351 }
2352
2353 return true;
2354}
2355
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002356static int
2357mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2358{
2359 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2360 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2361
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002362 switch (cmp_arg->proto) {
2363 case MLXSW_SP_L3_PROTO_IPV4:
2364 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2365 case MLXSW_SP_L3_PROTO_IPV6:
2366 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2367 cmp_arg->fib6_entry);
2368 default:
2369 WARN_ON(1);
2370 return 1;
2371 }
2372}
2373
2374static int
2375mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2376{
2377 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002378}
2379
2380static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2381{
2382 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002383 const struct mlxsw_sp_nexthop *nh;
2384 struct fib_info *fi;
2385 unsigned int val;
2386 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002387
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002388 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2389 case AF_INET:
2390 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2391 return jhash(&fi, sizeof(fi), seed);
2392 case AF_INET6:
2393 val = nh_grp->count;
2394 for (i = 0; i < nh_grp->count; i++) {
2395 nh = &nh_grp->nexthops[i];
2396 val ^= nh->ifindex;
2397 }
2398 return jhash(&val, sizeof(val), seed);
2399 default:
2400 WARN_ON(1);
2401 return 0;
2402 }
2403}
2404
2405static u32
2406mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2407{
2408 unsigned int val = fib6_entry->nrt6;
2409 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2410 struct net_device *dev;
2411
2412 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2413 dev = mlxsw_sp_rt6->rt->dst.dev;
2414 val ^= dev->ifindex;
2415 }
2416
2417 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002418}
2419
2420static u32
2421mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2422{
2423 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2424
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002425 switch (cmp_arg->proto) {
2426 case MLXSW_SP_L3_PROTO_IPV4:
2427 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2428 case MLXSW_SP_L3_PROTO_IPV6:
2429 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2430 default:
2431 WARN_ON(1);
2432 return 0;
2433 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002434}
2435
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002436static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002437 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002438 .hashfn = mlxsw_sp_nexthop_group_hash,
2439 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2440 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002441};
2442
2443static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2444 struct mlxsw_sp_nexthop_group *nh_grp)
2445{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002446 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2447 !nh_grp->gateway)
2448 return 0;
2449
Ido Schimmel9011b672017-05-16 19:38:25 +02002450 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002451 &nh_grp->ht_node,
2452 mlxsw_sp_nexthop_group_ht_params);
2453}
2454
2455static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2456 struct mlxsw_sp_nexthop_group *nh_grp)
2457{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002458 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2459 !nh_grp->gateway)
2460 return;
2461
Ido Schimmel9011b672017-05-16 19:38:25 +02002462 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002463 &nh_grp->ht_node,
2464 mlxsw_sp_nexthop_group_ht_params);
2465}
2466
2467static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002468mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2469 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002470{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002471 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2472
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002473 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002474 cmp_arg.fi = fi;
2475 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2476 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002477 mlxsw_sp_nexthop_group_ht_params);
2478}
2479
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002480static struct mlxsw_sp_nexthop_group *
2481mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2482 struct mlxsw_sp_fib6_entry *fib6_entry)
2483{
2484 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2485
2486 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2487 cmp_arg.fib6_entry = fib6_entry;
2488 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2489 &cmp_arg,
2490 mlxsw_sp_nexthop_group_ht_params);
2491}
2492
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002493static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2494 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2495 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2496 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2497};
2498
2499static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2500 struct mlxsw_sp_nexthop *nh)
2501{
Ido Schimmel9011b672017-05-16 19:38:25 +02002502 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002503 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2504}
2505
2506static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2507 struct mlxsw_sp_nexthop *nh)
2508{
Ido Schimmel9011b672017-05-16 19:38:25 +02002509 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002510 mlxsw_sp_nexthop_ht_params);
2511}
2512
Ido Schimmelad178c82017-02-08 11:16:40 +01002513static struct mlxsw_sp_nexthop *
2514mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2515 struct mlxsw_sp_nexthop_key key)
2516{
Ido Schimmel9011b672017-05-16 19:38:25 +02002517 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002518 mlxsw_sp_nexthop_ht_params);
2519}
2520
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002521static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002522 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002523 u32 adj_index, u16 ecmp_size,
2524 u32 new_adj_index,
2525 u16 new_ecmp_size)
2526{
2527 char raleu_pl[MLXSW_REG_RALEU_LEN];
2528
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002529 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002530 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2531 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002532 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002533 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2534}
2535
2536static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2537 struct mlxsw_sp_nexthop_group *nh_grp,
2538 u32 old_adj_index, u16 old_ecmp_size)
2539{
2540 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002541 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002542 int err;
2543
2544 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002545 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002546 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002547 fib = fib_entry->fib_node->fib;
2548 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002549 old_adj_index,
2550 old_ecmp_size,
2551 nh_grp->adj_index,
2552 nh_grp->ecmp_size);
2553 if (err)
2554 return err;
2555 }
2556 return 0;
2557}
2558
Arkadi Sharshevsky427e6522017-09-25 10:32:30 +02002559int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2560 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002561{
2562 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2563 char ratr_pl[MLXSW_REG_RATR_LEN];
2564
2565 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002566 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2567 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002568 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02002569 if (nh->counter_valid)
2570 mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter_index, true);
2571 else
2572 mlxsw_reg_ratr_counter_pack(ratr_pl, 0, false);
2573
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002574 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2575}
2576
Petr Machata1012b9a2017-09-02 23:49:23 +02002577static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2578 u32 adj_index,
2579 struct mlxsw_sp_nexthop *nh)
2580{
2581 const struct mlxsw_sp_ipip_ops *ipip_ops;
2582
2583 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2584 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2585}
2586
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002587static int
Petr Machata35225e42017-09-02 23:49:22 +02002588mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2589 struct mlxsw_sp_nexthop_group *nh_grp,
2590 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002591{
2592 u32 adj_index = nh_grp->adj_index; /* base */
2593 struct mlxsw_sp_nexthop *nh;
2594 int i;
2595 int err;
2596
2597 for (i = 0; i < nh_grp->count; i++) {
2598 nh = &nh_grp->nexthops[i];
2599
2600 if (!nh->should_offload) {
2601 nh->offloaded = 0;
2602 continue;
2603 }
2604
Ido Schimmela59b7e02017-01-23 11:11:42 +01002605 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002606 switch (nh->type) {
2607 case MLXSW_SP_NEXTHOP_TYPE_ETH:
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02002608 err = mlxsw_sp_nexthop_update
Petr Machata35225e42017-09-02 23:49:22 +02002609 (mlxsw_sp, adj_index, nh);
2610 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002611 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2612 err = mlxsw_sp_nexthop_ipip_update
2613 (mlxsw_sp, adj_index, nh);
2614 break;
Petr Machata35225e42017-09-02 23:49:22 +02002615 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002616 if (err)
2617 return err;
2618 nh->update = 0;
2619 nh->offloaded = 1;
2620 }
2621 adj_index++;
2622 }
2623 return 0;
2624}
2625
Ido Schimmel1819ae32017-07-21 18:04:28 +02002626static bool
2627mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2628 const struct mlxsw_sp_fib_entry *fib_entry);
2629
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002630static int
2631mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2632 struct mlxsw_sp_nexthop_group *nh_grp)
2633{
2634 struct mlxsw_sp_fib_entry *fib_entry;
2635 int err;
2636
2637 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002638 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2639 fib_entry))
2640 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002641 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2642 if (err)
2643 return err;
2644 }
2645 return 0;
2646}
2647
2648static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002649mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2650 enum mlxsw_reg_ralue_op op, int err);
2651
2652static void
2653mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2654{
2655 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2656 struct mlxsw_sp_fib_entry *fib_entry;
2657
2658 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2659 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2660 fib_entry))
2661 continue;
2662 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2663 }
2664}
2665
2666static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002667mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2668 struct mlxsw_sp_nexthop_group *nh_grp)
2669{
2670 struct mlxsw_sp_nexthop *nh;
2671 bool offload_change = false;
2672 u32 adj_index;
2673 u16 ecmp_size = 0;
2674 bool old_adj_index_valid;
2675 u32 old_adj_index;
2676 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002677 int i;
2678 int err;
2679
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002680 if (!nh_grp->gateway) {
2681 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2682 return;
2683 }
2684
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002685 for (i = 0; i < nh_grp->count; i++) {
2686 nh = &nh_grp->nexthops[i];
2687
Petr Machata56b8a9e2017-07-31 09:27:29 +02002688 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002689 offload_change = true;
2690 if (nh->should_offload)
2691 nh->update = 1;
2692 }
2693 if (nh->should_offload)
2694 ecmp_size++;
2695 }
2696 if (!offload_change) {
2697 /* Nothing was added or removed, so no need to reallocate. Just
2698 * update MAC on existing adjacency indexes.
2699 */
Petr Machata35225e42017-09-02 23:49:22 +02002700 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002701 if (err) {
2702 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2703 goto set_trap;
2704 }
2705 return;
2706 }
2707 if (!ecmp_size)
2708 /* No neigh of this group is connected so we just set
2709 * the trap and let everthing flow through kernel.
2710 */
2711 goto set_trap;
2712
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002713 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2714 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002715 /* We ran out of KVD linear space, just set the
2716 * trap and let everything flow through kernel.
2717 */
2718 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2719 goto set_trap;
2720 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002721 old_adj_index_valid = nh_grp->adj_index_valid;
2722 old_adj_index = nh_grp->adj_index;
2723 old_ecmp_size = nh_grp->ecmp_size;
2724 nh_grp->adj_index_valid = 1;
2725 nh_grp->adj_index = adj_index;
2726 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002727 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002728 if (err) {
2729 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2730 goto set_trap;
2731 }
2732
2733 if (!old_adj_index_valid) {
2734 /* The trap was set for fib entries, so we have to call
2735 * fib entry update to unset it and use adjacency index.
2736 */
2737 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2738 if (err) {
2739 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2740 goto set_trap;
2741 }
2742 return;
2743 }
2744
2745 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2746 old_adj_index, old_ecmp_size);
2747 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2748 if (err) {
2749 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2750 goto set_trap;
2751 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002752
2753 /* Offload state within the group changed, so update the flags. */
2754 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2755
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002756 return;
2757
2758set_trap:
2759 old_adj_index_valid = nh_grp->adj_index_valid;
2760 nh_grp->adj_index_valid = 0;
2761 for (i = 0; i < nh_grp->count; i++) {
2762 nh = &nh_grp->nexthops[i];
2763 nh->offloaded = 0;
2764 }
2765 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2766 if (err)
2767 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2768 if (old_adj_index_valid)
2769 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2770}
2771
2772static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2773 bool removing)
2774{
Petr Machata213666a2017-07-31 09:27:30 +02002775 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002776 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002777 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002778 nh->should_offload = 0;
2779 nh->update = 1;
2780}
2781
2782static void
2783mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2784 struct mlxsw_sp_neigh_entry *neigh_entry,
2785 bool removing)
2786{
2787 struct mlxsw_sp_nexthop *nh;
2788
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002789 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2790 neigh_list_node) {
2791 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2792 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2793 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002794}
2795
Ido Schimmel9665b742017-02-08 11:16:42 +01002796static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002797 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002798{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002799 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002800 return;
2801
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002802 nh->rif = rif;
2803 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002804}
2805
2806static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2807{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002808 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002809 return;
2810
2811 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002812 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002813}
2814
Ido Schimmela8c97012017-02-08 11:16:35 +01002815static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2816 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002817{
2818 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002819 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002820 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002821 int err;
2822
Ido Schimmelad178c82017-02-08 11:16:40 +01002823 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002824 return 0;
2825
Jiri Pirko33b13412016-11-10 12:31:04 +01002826 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002827 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002828 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002829 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002830 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002831 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002832 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002833 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2834 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002835 if (IS_ERR(n))
2836 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002837 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002838 }
2839 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2840 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002841 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2842 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002843 err = -EINVAL;
2844 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002845 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002846 }
Yotam Gigib2157142016-07-05 11:27:51 +02002847
2848 /* If that is the first nexthop connected to that neigh, add to
2849 * nexthop_neighs_list
2850 */
2851 if (list_empty(&neigh_entry->nexthop_list))
2852 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002853 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002854
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002855 nh->neigh_entry = neigh_entry;
2856 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2857 read_lock_bh(&n->lock);
2858 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002859 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002860 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002861 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002862
2863 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002864
2865err_neigh_entry_create:
2866 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002867 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002868}
2869
Ido Schimmela8c97012017-02-08 11:16:35 +01002870static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2871 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002872{
2873 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002874 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002875
Ido Schimmelb8399a12017-02-08 11:16:33 +01002876 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002877 return;
2878 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002879
Ido Schimmel58312122016-12-23 09:32:50 +01002880 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002881 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002882 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002883
2884 /* If that is the last nexthop connected to that neigh, remove from
2885 * nexthop_neighs_list
2886 */
Ido Schimmele58be792017-02-08 11:16:28 +01002887 if (list_empty(&neigh_entry->nexthop_list))
2888 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002889
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002890 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2891 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2892
2893 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002894}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002895
Petr Machata1012b9a2017-09-02 23:49:23 +02002896static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2897 enum mlxsw_sp_ipip_type ipipt,
2898 struct mlxsw_sp_nexthop *nh,
2899 struct net_device *ol_dev)
2900{
2901 if (!nh->nh_grp->gateway || nh->ipip_entry)
2902 return 0;
2903
2904 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2905 if (IS_ERR(nh->ipip_entry))
2906 return PTR_ERR(nh->ipip_entry);
2907
2908 __mlxsw_sp_nexthop_neigh_update(nh, false);
2909 return 0;
2910}
2911
2912static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2913 struct mlxsw_sp_nexthop *nh)
2914{
2915 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2916
2917 if (!ipip_entry)
2918 return;
2919
2920 __mlxsw_sp_nexthop_neigh_update(nh, true);
2921 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2922 nh->ipip_entry = NULL;
2923}
2924
2925static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2926 const struct fib_nh *fib_nh,
2927 enum mlxsw_sp_ipip_type *p_ipipt)
2928{
2929 struct net_device *dev = fib_nh->nh_dev;
2930
2931 return dev &&
2932 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2933 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2934}
2935
Petr Machata35225e42017-09-02 23:49:22 +02002936static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2937 struct mlxsw_sp_nexthop *nh)
2938{
2939 switch (nh->type) {
2940 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2941 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2942 mlxsw_sp_nexthop_rif_fini(nh);
2943 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002944 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
Petr Machatade0f43c2017-10-02 12:14:57 +02002945 mlxsw_sp_nexthop_rif_fini(nh);
Petr Machata1012b9a2017-09-02 23:49:23 +02002946 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2947 break;
Petr Machata35225e42017-09-02 23:49:22 +02002948 }
2949}
2950
2951static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2952 struct mlxsw_sp_nexthop *nh,
2953 struct fib_nh *fib_nh)
2954{
Petr Machata1012b9a2017-09-02 23:49:23 +02002955 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002956 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002957 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002958 struct mlxsw_sp_rif *rif;
2959 int err;
2960
Petr Machata1012b9a2017-09-02 23:49:23 +02002961 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2962 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2963 MLXSW_SP_L3_PROTO_IPV4)) {
2964 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
Petr Machatade0f43c2017-10-02 12:14:57 +02002965 err = mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2966 if (err)
2967 return err;
2968 mlxsw_sp_nexthop_rif_init(nh, &nh->ipip_entry->ol_lb->common);
2969 return 0;
Petr Machata1012b9a2017-09-02 23:49:23 +02002970 }
2971
Petr Machata35225e42017-09-02 23:49:22 +02002972 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2973 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2974 if (!rif)
2975 return 0;
2976
2977 mlxsw_sp_nexthop_rif_init(nh, rif);
2978 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2979 if (err)
2980 goto err_neigh_init;
2981
2982 return 0;
2983
2984err_neigh_init:
2985 mlxsw_sp_nexthop_rif_fini(nh);
2986 return err;
2987}
2988
2989static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2990 struct mlxsw_sp_nexthop *nh)
2991{
2992 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2993}
2994
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002995static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2996 struct mlxsw_sp_nexthop_group *nh_grp,
2997 struct mlxsw_sp_nexthop *nh,
2998 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002999{
3000 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01003001 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01003002 int err;
3003
3004 nh->nh_grp = nh_grp;
3005 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02003006 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01003007 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
3008 if (err)
3009 return err;
3010
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02003011 mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02003012 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
3013
Ido Schimmel97989ee2017-03-10 08:53:38 +01003014 if (!dev)
3015 return 0;
3016
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01003017 in_dev = __in_dev_get_rtnl(dev);
3018 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
3019 fib_nh->nh_flags & RTNH_F_LINKDOWN)
3020 return 0;
3021
Petr Machata35225e42017-09-02 23:49:22 +02003022 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01003023 if (err)
3024 goto err_nexthop_neigh_init;
3025
3026 return 0;
3027
3028err_nexthop_neigh_init:
3029 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
3030 return err;
3031}
3032
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003033static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
3034 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01003035{
Petr Machata35225e42017-09-02 23:49:22 +02003036 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02003037 list_del(&nh->router_list_node);
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02003038 mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01003039 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003040}
3041
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003042static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
3043 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01003044{
3045 struct mlxsw_sp_nexthop_key key;
3046 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01003047
Ido Schimmel9011b672017-05-16 19:38:25 +02003048 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01003049 return;
3050
3051 key.fib_nh = fib_nh;
3052 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
3053 if (WARN_ON_ONCE(!nh))
3054 return;
3055
Ido Schimmelad178c82017-02-08 11:16:40 +01003056 switch (event) {
3057 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02003058 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003059 break;
3060 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02003061 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003062 break;
3063 }
3064
3065 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
3066}
3067
Ido Schimmel9665b742017-02-08 11:16:42 +01003068static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003069 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01003070{
3071 struct mlxsw_sp_nexthop *nh, *tmp;
3072
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003073 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02003074 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01003075 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
3076 }
3077}
3078
Petr Machata9b014512017-09-02 23:49:20 +02003079static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
3080 const struct fib_info *fi)
3081{
Petr Machata1012b9a2017-09-02 23:49:23 +02003082 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
3083 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02003084}
3085
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003086static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003087mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003088{
3089 struct mlxsw_sp_nexthop_group *nh_grp;
3090 struct mlxsw_sp_nexthop *nh;
3091 struct fib_nh *fib_nh;
3092 size_t alloc_size;
3093 int i;
3094 int err;
3095
3096 alloc_size = sizeof(*nh_grp) +
3097 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
3098 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3099 if (!nh_grp)
3100 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003101 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003102 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02003103 nh_grp->neigh_tbl = &arp_tbl;
3104
Petr Machata9b014512017-09-02 23:49:20 +02003105 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003106 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02003107 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003108 for (i = 0; i < nh_grp->count; i++) {
3109 nh = &nh_grp->nexthops[i];
3110 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003111 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003112 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003113 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003114 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01003115 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3116 if (err)
3117 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003118 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3119 return nh_grp;
3120
Ido Schimmele9ad5e72017-02-08 11:16:29 +01003121err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003122err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01003123 for (i--; i >= 0; i--) {
3124 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003125 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01003126 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003127 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003128 kfree(nh_grp);
3129 return ERR_PTR(err);
3130}
3131
3132static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003133mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
3134 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003135{
3136 struct mlxsw_sp_nexthop *nh;
3137 int i;
3138
Ido Schimmele9ad5e72017-02-08 11:16:29 +01003139 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003140 for (i = 0; i < nh_grp->count; i++) {
3141 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003142 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003143 }
Ido Schimmel58312122016-12-23 09:32:50 +01003144 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3145 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003146 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003147 kfree(nh_grp);
3148}
3149
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003150static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
3151 struct mlxsw_sp_fib_entry *fib_entry,
3152 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003153{
3154 struct mlxsw_sp_nexthop_group *nh_grp;
3155
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003156 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003157 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003158 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003159 if (IS_ERR(nh_grp))
3160 return PTR_ERR(nh_grp);
3161 }
3162 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
3163 fib_entry->nh_group = nh_grp;
3164 return 0;
3165}
3166
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003167static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
3168 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003169{
3170 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3171
3172 list_del(&fib_entry->nexthop_group_node);
3173 if (!list_empty(&nh_grp->fib_list))
3174 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003175 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003176}
3177
Ido Schimmel013b20f2017-02-08 11:16:36 +01003178static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003179mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
3180{
3181 struct mlxsw_sp_fib4_entry *fib4_entry;
3182
3183 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
3184 common);
3185 return !fib4_entry->tos;
3186}
3187
3188static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01003189mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
3190{
3191 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
3192
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003193 switch (fib_entry->fib_node->fib->proto) {
3194 case MLXSW_SP_L3_PROTO_IPV4:
3195 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
3196 return false;
3197 break;
3198 case MLXSW_SP_L3_PROTO_IPV6:
3199 break;
3200 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003201
Ido Schimmel013b20f2017-02-08 11:16:36 +01003202 switch (fib_entry->type) {
3203 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
3204 return !!nh_group->adj_index_valid;
3205 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01003206 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02003207 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3208 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003209 default:
3210 return false;
3211 }
3212}
3213
Ido Schimmel428b8512017-08-03 13:28:28 +02003214static struct mlxsw_sp_nexthop *
3215mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
3216 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3217{
3218 int i;
3219
3220 for (i = 0; i < nh_grp->count; i++) {
3221 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3222 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3223
3224 if (nh->rif && nh->rif->dev == rt->dst.dev &&
3225 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
3226 &rt->rt6i_gateway))
3227 return nh;
3228 continue;
3229 }
3230
3231 return NULL;
3232}
3233
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003234static void
3235mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3236{
3237 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3238 int i;
3239
Petr Machata4607f6d2017-09-02 23:49:25 +02003240 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3241 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003242 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3243 return;
3244 }
3245
3246 for (i = 0; i < nh_grp->count; i++) {
3247 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3248
3249 if (nh->offloaded)
3250 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3251 else
3252 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3253 }
3254}
3255
3256static void
3257mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3258{
3259 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3260 int i;
3261
3262 for (i = 0; i < nh_grp->count; i++) {
3263 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3264
3265 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3266 }
3267}
3268
Ido Schimmel428b8512017-08-03 13:28:28 +02003269static void
3270mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3271{
3272 struct mlxsw_sp_fib6_entry *fib6_entry;
3273 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3274
3275 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3276 common);
3277
3278 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3279 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003280 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003281 return;
3282 }
3283
3284 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3285 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3286 struct mlxsw_sp_nexthop *nh;
3287
3288 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3289 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003290 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003291 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003292 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003293 }
3294}
3295
3296static void
3297mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3298{
3299 struct mlxsw_sp_fib6_entry *fib6_entry;
3300 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3301
3302 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3303 common);
3304 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3305 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3306
Ido Schimmelfe400792017-08-15 09:09:49 +02003307 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003308 }
3309}
3310
Ido Schimmel013b20f2017-02-08 11:16:36 +01003311static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3312{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003313 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003314 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003315 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003316 break;
3317 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003318 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3319 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003320 }
3321}
3322
3323static void
3324mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3325{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003326 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003327 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003328 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003329 break;
3330 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003331 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3332 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003333 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003334}
3335
3336static void
3337mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3338 enum mlxsw_reg_ralue_op op, int err)
3339{
3340 switch (op) {
3341 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003342 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3343 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3344 if (err)
3345 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003346 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003347 mlxsw_sp_fib_entry_offload_set(fib_entry);
Petr Machata85f44a12017-10-02 12:21:58 +02003348 else
Ido Schimmel013b20f2017-02-08 11:16:36 +01003349 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3350 return;
3351 default:
3352 return;
3353 }
3354}
3355
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003356static void
3357mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3358 const struct mlxsw_sp_fib_entry *fib_entry,
3359 enum mlxsw_reg_ralue_op op)
3360{
3361 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3362 enum mlxsw_reg_ralxx_protocol proto;
3363 u32 *p_dip;
3364
3365 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3366
3367 switch (fib->proto) {
3368 case MLXSW_SP_L3_PROTO_IPV4:
3369 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3370 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3371 fib_entry->fib_node->key.prefix_len,
3372 *p_dip);
3373 break;
3374 case MLXSW_SP_L3_PROTO_IPV6:
3375 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3376 fib_entry->fib_node->key.prefix_len,
3377 fib_entry->fib_node->key.addr);
3378 break;
3379 }
3380}
3381
3382static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3383 struct mlxsw_sp_fib_entry *fib_entry,
3384 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003385{
3386 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003387 enum mlxsw_reg_ralue_trap_action trap_action;
3388 u16 trap_id = 0;
3389 u32 adjacency_index = 0;
3390 u16 ecmp_size = 0;
3391
3392 /* In case the nexthop group adjacency index is valid, use it
3393 * with provided ECMP size. Otherwise, setup trap and pass
3394 * traffic to kernel.
3395 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003396 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003397 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3398 adjacency_index = fib_entry->nh_group->adj_index;
3399 ecmp_size = fib_entry->nh_group->ecmp_size;
3400 } else {
3401 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3402 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3403 }
3404
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003405 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003406 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3407 adjacency_index, ecmp_size);
3408 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3409}
3410
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003411static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3412 struct mlxsw_sp_fib_entry *fib_entry,
3413 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003414{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003415 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003416 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003417 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003418 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003419 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003420
3421 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3422 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003423 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003424 } else {
3425 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3426 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3427 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003428
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003429 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003430 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3431 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003432 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3433}
3434
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003435static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3436 struct mlxsw_sp_fib_entry *fib_entry,
3437 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003438{
3439 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003440
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003441 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003442 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3443 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3444}
3445
Petr Machata4607f6d2017-09-02 23:49:25 +02003446static int
3447mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3448 struct mlxsw_sp_fib_entry *fib_entry,
3449 enum mlxsw_reg_ralue_op op)
3450{
3451 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3452 const struct mlxsw_sp_ipip_ops *ipip_ops;
3453
3454 if (WARN_ON(!ipip_entry))
3455 return -EINVAL;
3456
3457 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3458 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3459 fib_entry->decap.tunnel_index);
3460}
3461
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003462static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3463 struct mlxsw_sp_fib_entry *fib_entry,
3464 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003465{
3466 switch (fib_entry->type) {
3467 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003468 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003469 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003470 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003471 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003472 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003473 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3474 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3475 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003476 }
3477 return -EINVAL;
3478}
3479
3480static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3481 struct mlxsw_sp_fib_entry *fib_entry,
3482 enum mlxsw_reg_ralue_op op)
3483{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003484 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003485
Ido Schimmel013b20f2017-02-08 11:16:36 +01003486 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003487
Ido Schimmel013b20f2017-02-08 11:16:36 +01003488 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003489}
3490
3491static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3492 struct mlxsw_sp_fib_entry *fib_entry)
3493{
Jiri Pirko7146da32016-09-01 10:37:41 +02003494 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3495 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003496}
3497
3498static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3499 struct mlxsw_sp_fib_entry *fib_entry)
3500{
3501 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3502 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3503}
3504
Jiri Pirko61c503f2016-07-04 08:23:11 +02003505static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003506mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3507 const struct fib_entry_notifier_info *fen_info,
3508 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003509{
Petr Machata4607f6d2017-09-02 23:49:25 +02003510 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3511 struct net_device *dev = fen_info->fi->fib_dev;
3512 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003513 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003514
Ido Schimmel97989ee2017-03-10 08:53:38 +01003515 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003516 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003517 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3518 MLXSW_SP_L3_PROTO_IPV4, dip);
3519 if (ipip_entry) {
3520 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3521 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3522 fib_entry,
3523 ipip_entry);
3524 }
3525 /* fall through */
3526 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003527 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3528 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003529 case RTN_UNREACHABLE: /* fall through */
3530 case RTN_BLACKHOLE: /* fall through */
3531 case RTN_PROHIBIT:
3532 /* Packets hitting these routes need to be trapped, but
3533 * can do so with a lower priority than packets directed
3534 * at the host, so use action type local instead of trap.
3535 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003536 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003537 return 0;
3538 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003539 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003540 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003541 else
3542 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003543 return 0;
3544 default:
3545 return -EINVAL;
3546 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003547}
3548
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003549static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003550mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3551 struct mlxsw_sp_fib_node *fib_node,
3552 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003553{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003554 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003555 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003556 int err;
3557
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003558 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3559 if (!fib4_entry)
3560 return ERR_PTR(-ENOMEM);
3561 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003562
3563 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3564 if (err)
3565 goto err_fib4_entry_type_set;
3566
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003567 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003568 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003569 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003570
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003571 fib4_entry->prio = fen_info->fi->fib_priority;
3572 fib4_entry->tb_id = fen_info->tb_id;
3573 fib4_entry->type = fen_info->type;
3574 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003575
3576 fib_entry->fib_node = fib_node;
3577
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003578 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003579
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003580err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003581err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003582 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003583 return ERR_PTR(err);
3584}
3585
3586static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003587 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003588{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003589 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003590 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003591}
3592
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003593static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003594mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3595 const struct fib_entry_notifier_info *fen_info)
3596{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003597 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003598 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003599 struct mlxsw_sp_fib *fib;
3600 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003601
Ido Schimmel160e22a2017-07-18 10:10:20 +02003602 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3603 if (!vr)
3604 return NULL;
3605 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3606
3607 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3608 sizeof(fen_info->dst),
3609 fen_info->dst_len);
3610 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003611 return NULL;
3612
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003613 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3614 if (fib4_entry->tb_id == fen_info->tb_id &&
3615 fib4_entry->tos == fen_info->tos &&
3616 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003617 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3618 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003619 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003620 }
3621 }
3622
3623 return NULL;
3624}
3625
3626static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3627 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3628 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3629 .key_len = sizeof(struct mlxsw_sp_fib_key),
3630 .automatic_shrinking = true,
3631};
3632
3633static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3634 struct mlxsw_sp_fib_node *fib_node)
3635{
3636 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3637 mlxsw_sp_fib_ht_params);
3638}
3639
3640static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3641 struct mlxsw_sp_fib_node *fib_node)
3642{
3643 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3644 mlxsw_sp_fib_ht_params);
3645}
3646
3647static struct mlxsw_sp_fib_node *
3648mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3649 size_t addr_len, unsigned char prefix_len)
3650{
3651 struct mlxsw_sp_fib_key key;
3652
3653 memset(&key, 0, sizeof(key));
3654 memcpy(key.addr, addr, addr_len);
3655 key.prefix_len = prefix_len;
3656 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3657}
3658
3659static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003660mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003661 size_t addr_len, unsigned char prefix_len)
3662{
3663 struct mlxsw_sp_fib_node *fib_node;
3664
3665 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3666 if (!fib_node)
3667 return NULL;
3668
3669 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003670 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003671 memcpy(fib_node->key.addr, addr, addr_len);
3672 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003673
3674 return fib_node;
3675}
3676
3677static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3678{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003679 list_del(&fib_node->list);
3680 WARN_ON(!list_empty(&fib_node->entry_list));
3681 kfree(fib_node);
3682}
3683
3684static bool
3685mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3686 const struct mlxsw_sp_fib_entry *fib_entry)
3687{
3688 return list_first_entry(&fib_node->entry_list,
3689 struct mlxsw_sp_fib_entry, list) == fib_entry;
3690}
3691
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003692static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3693 struct mlxsw_sp_fib *fib,
3694 struct mlxsw_sp_fib_node *fib_node)
3695{
3696 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3697 struct mlxsw_sp_lpm_tree *lpm_tree;
3698 int err;
3699
3700 /* Since the tree is shared between all virtual routers we must
3701 * make sure it contains all the required prefix lengths. This
3702 * can be computed by either adding the new prefix length to the
3703 * existing prefix usage of a bound tree, or by aggregating the
3704 * prefix lengths across all virtual routers and adding the new
3705 * one as well.
3706 */
3707 if (fib->lpm_tree)
3708 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3709 &fib->lpm_tree->prefix_usage);
3710 else
3711 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3712 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3713
3714 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3715 fib->proto);
3716 if (IS_ERR(lpm_tree))
3717 return PTR_ERR(lpm_tree);
3718
3719 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3720 return 0;
3721
3722 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3723 if (err)
3724 return err;
3725
3726 return 0;
3727}
3728
3729static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3730 struct mlxsw_sp_fib *fib)
3731{
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003732 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3733 return;
3734 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3735 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3736 fib->lpm_tree = NULL;
3737}
3738
Ido Schimmel9aecce12017-02-09 10:28:42 +01003739static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3740{
3741 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003742 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003743
3744 if (fib->prefix_ref_count[prefix_len]++ == 0)
3745 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3746}
3747
3748static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3749{
3750 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003751 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003752
3753 if (--fib->prefix_ref_count[prefix_len] == 0)
3754 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3755}
3756
Ido Schimmel76610eb2017-03-10 08:53:41 +01003757static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3758 struct mlxsw_sp_fib_node *fib_node,
3759 struct mlxsw_sp_fib *fib)
3760{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003761 int err;
3762
3763 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3764 if (err)
3765 return err;
3766 fib_node->fib = fib;
3767
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003768 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3769 if (err)
3770 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003771
3772 mlxsw_sp_fib_node_prefix_inc(fib_node);
3773
3774 return 0;
3775
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003776err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003777 fib_node->fib = NULL;
3778 mlxsw_sp_fib_node_remove(fib, fib_node);
3779 return err;
3780}
3781
3782static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3783 struct mlxsw_sp_fib_node *fib_node)
3784{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003785 struct mlxsw_sp_fib *fib = fib_node->fib;
3786
3787 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003788 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003789 fib_node->fib = NULL;
3790 mlxsw_sp_fib_node_remove(fib, fib_node);
3791}
3792
Ido Schimmel9aecce12017-02-09 10:28:42 +01003793static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003794mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3795 size_t addr_len, unsigned char prefix_len,
3796 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003797{
3798 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003799 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003800 struct mlxsw_sp_vr *vr;
3801 int err;
3802
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003803 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003804 if (IS_ERR(vr))
3805 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003806 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003807
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003808 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003809 if (fib_node)
3810 return fib_node;
3811
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003812 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003813 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003814 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003815 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003816 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003817
Ido Schimmel76610eb2017-03-10 08:53:41 +01003818 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3819 if (err)
3820 goto err_fib_node_init;
3821
Ido Schimmel9aecce12017-02-09 10:28:42 +01003822 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003823
Ido Schimmel76610eb2017-03-10 08:53:41 +01003824err_fib_node_init:
3825 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003826err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003827 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003828 return ERR_PTR(err);
3829}
3830
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003831static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3832 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003833{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003834 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003835
Ido Schimmel9aecce12017-02-09 10:28:42 +01003836 if (!list_empty(&fib_node->entry_list))
3837 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003838 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003839 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003840 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003841}
3842
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003843static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003844mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003845 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003846{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003847 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003848
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003849 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3850 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003851 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003852 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003853 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003854 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003855 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003856 if (fib4_entry->prio >= new4_entry->prio ||
3857 fib4_entry->tos < new4_entry->tos)
3858 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003859 }
3860
3861 return NULL;
3862}
3863
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003864static int
3865mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3866 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003867{
3868 struct mlxsw_sp_fib_node *fib_node;
3869
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003870 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003871 return -EINVAL;
3872
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003873 fib_node = fib4_entry->common.fib_node;
3874 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3875 common.list) {
3876 if (fib4_entry->tb_id != new4_entry->tb_id ||
3877 fib4_entry->tos != new4_entry->tos ||
3878 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003879 break;
3880 }
3881
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003882 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003883 return 0;
3884}
3885
Ido Schimmel9aecce12017-02-09 10:28:42 +01003886static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003887mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003888 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003889{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003890 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003891 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003892
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003893 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003894
Ido Schimmel4283bce2017-02-09 10:28:43 +01003895 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003896 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3897 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003898 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003899
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003900 /* Insert new entry before replaced one, so that we can later
3901 * remove the second.
3902 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003903 if (fib4_entry) {
3904 list_add_tail(&new4_entry->common.list,
3905 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003906 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003907 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003908
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003909 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3910 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003911 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003912 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003913 }
3914
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003915 if (fib4_entry)
3916 list_add(&new4_entry->common.list,
3917 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003918 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003919 list_add(&new4_entry->common.list,
3920 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003921 }
3922
3923 return 0;
3924}
3925
3926static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003927mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003928{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003929 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003930}
3931
Ido Schimmel80c238f2017-07-18 10:10:29 +02003932static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3933 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003934{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003935 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3936
Ido Schimmel9aecce12017-02-09 10:28:42 +01003937 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3938 return 0;
3939
3940 /* To prevent packet loss, overwrite the previously offloaded
3941 * entry.
3942 */
3943 if (!list_is_singular(&fib_node->entry_list)) {
3944 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3945 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3946
3947 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3948 }
3949
3950 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3951}
3952
Ido Schimmel80c238f2017-07-18 10:10:29 +02003953static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3954 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003955{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003956 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3957
Ido Schimmel9aecce12017-02-09 10:28:42 +01003958 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3959 return;
3960
3961 /* Promote the next entry by overwriting the deleted entry */
3962 if (!list_is_singular(&fib_node->entry_list)) {
3963 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3964 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3965
3966 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3967 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3968 return;
3969 }
3970
3971 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3972}
3973
3974static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003975 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003976 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003977{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003978 int err;
3979
Ido Schimmel9efbee62017-07-18 10:10:28 +02003980 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003981 if (err)
3982 return err;
3983
Ido Schimmel80c238f2017-07-18 10:10:29 +02003984 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003985 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003986 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003987
Ido Schimmel9aecce12017-02-09 10:28:42 +01003988 return 0;
3989
Ido Schimmel80c238f2017-07-18 10:10:29 +02003990err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003991 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003992 return err;
3993}
3994
3995static void
3996mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003997 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003998{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003999 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004000 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02004001
4002 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
4003 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004004}
4005
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004006static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004007 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004008 bool replace)
4009{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004010 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
4011 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004012
4013 if (!replace)
4014 return;
4015
4016 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004017 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004018
4019 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
4020 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004021 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004022}
4023
Ido Schimmel9aecce12017-02-09 10:28:42 +01004024static int
4025mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01004026 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004027 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01004028{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004029 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004030 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02004031 int err;
4032
Ido Schimmel9011b672017-05-16 19:38:25 +02004033 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004034 return 0;
4035
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004036 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
4037 &fen_info->dst, sizeof(fen_info->dst),
4038 fen_info->dst_len,
4039 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004040 if (IS_ERR(fib_node)) {
4041 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
4042 return PTR_ERR(fib_node);
4043 }
4044
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004045 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
4046 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004047 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004048 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004049 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004050 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02004051
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004052 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004053 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004054 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004055 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
4056 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004057 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01004058
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004059 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004060
Jiri Pirko61c503f2016-07-04 08:23:11 +02004061 return 0;
4062
Ido Schimmel9aecce12017-02-09 10:28:42 +01004063err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004064 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004065err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004066 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02004067 return err;
4068}
4069
Jiri Pirko37956d72016-10-20 16:05:43 +02004070static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
4071 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02004072{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004073 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004074 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02004075
Ido Schimmel9011b672017-05-16 19:38:25 +02004076 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02004077 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004078
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004079 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
4080 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02004081 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004082 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02004083
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004084 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4085 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004086 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02004087}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004088
Ido Schimmel428b8512017-08-03 13:28:28 +02004089static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
4090{
4091 /* Packets with link-local destination IP arriving to the router
4092 * are trapped to the CPU, so no need to program specific routes
4093 * for them.
4094 */
4095 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
4096 return true;
4097
4098 /* Multicast routes aren't supported, so ignore them. Neighbour
4099 * Discovery packets are specifically trapped.
4100 */
4101 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
4102 return true;
4103
4104 /* Cloned routes are irrelevant in the forwarding path. */
4105 if (rt->rt6i_flags & RTF_CACHE)
4106 return true;
4107
4108 return false;
4109}
4110
4111static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
4112{
4113 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4114
4115 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
4116 if (!mlxsw_sp_rt6)
4117 return ERR_PTR(-ENOMEM);
4118
4119 /* In case of route replace, replaced route is deleted with
4120 * no notification. Take reference to prevent accessing freed
4121 * memory.
4122 */
4123 mlxsw_sp_rt6->rt = rt;
4124 rt6_hold(rt);
4125
4126 return mlxsw_sp_rt6;
4127}
4128
4129#if IS_ENABLED(CONFIG_IPV6)
4130static void mlxsw_sp_rt6_release(struct rt6_info *rt)
4131{
4132 rt6_release(rt);
4133}
4134#else
4135static void mlxsw_sp_rt6_release(struct rt6_info *rt)
4136{
4137}
4138#endif
4139
4140static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
4141{
4142 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
4143 kfree(mlxsw_sp_rt6);
4144}
4145
4146static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
4147{
4148 /* RTF_CACHE routes are ignored */
4149 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
4150}
4151
4152static struct rt6_info *
4153mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
4154{
4155 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
4156 list)->rt;
4157}
4158
4159static struct mlxsw_sp_fib6_entry *
4160mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004161 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004162{
4163 struct mlxsw_sp_fib6_entry *fib6_entry;
4164
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004165 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004166 return NULL;
4167
4168 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4169 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4170
4171 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
4172 * virtual router.
4173 */
4174 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4175 continue;
4176 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4177 break;
4178 if (rt->rt6i_metric < nrt->rt6i_metric)
4179 continue;
4180 if (rt->rt6i_metric == nrt->rt6i_metric &&
4181 mlxsw_sp_fib6_rt_can_mp(rt))
4182 return fib6_entry;
4183 if (rt->rt6i_metric > nrt->rt6i_metric)
4184 break;
4185 }
4186
4187 return NULL;
4188}
4189
4190static struct mlxsw_sp_rt6 *
4191mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
4192 const struct rt6_info *rt)
4193{
4194 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4195
4196 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
4197 if (mlxsw_sp_rt6->rt == rt)
4198 return mlxsw_sp_rt6;
4199 }
4200
4201 return NULL;
4202}
4203
Petr Machata8f28a302017-09-02 23:49:24 +02004204static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
4205 const struct rt6_info *rt,
4206 enum mlxsw_sp_ipip_type *ret)
4207{
4208 return rt->dst.dev &&
4209 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
4210}
4211
Petr Machata35225e42017-09-02 23:49:22 +02004212static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
4213 struct mlxsw_sp_nexthop_group *nh_grp,
4214 struct mlxsw_sp_nexthop *nh,
4215 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02004216{
Petr Machata8f28a302017-09-02 23:49:24 +02004217 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02004218 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02004219 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02004220 struct mlxsw_sp_rif *rif;
4221 int err;
4222
Petr Machata8f28a302017-09-02 23:49:24 +02004223 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4224 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4225 MLXSW_SP_L3_PROTO_IPV6)) {
4226 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
Petr Machatade0f43c2017-10-02 12:14:57 +02004227 err = mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4228 if (err)
4229 return err;
4230 mlxsw_sp_nexthop_rif_init(nh, &nh->ipip_entry->ol_lb->common);
4231 return 0;
Petr Machata8f28a302017-09-02 23:49:24 +02004232 }
4233
Petr Machata35225e42017-09-02 23:49:22 +02004234 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02004235 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4236 if (!rif)
4237 return 0;
4238 mlxsw_sp_nexthop_rif_init(nh, rif);
4239
4240 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4241 if (err)
4242 goto err_nexthop_neigh_init;
4243
4244 return 0;
4245
4246err_nexthop_neigh_init:
4247 mlxsw_sp_nexthop_rif_fini(nh);
4248 return err;
4249}
4250
Petr Machata35225e42017-09-02 23:49:22 +02004251static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4252 struct mlxsw_sp_nexthop *nh)
4253{
4254 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4255}
4256
4257static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4258 struct mlxsw_sp_nexthop_group *nh_grp,
4259 struct mlxsw_sp_nexthop *nh,
4260 const struct rt6_info *rt)
4261{
4262 struct net_device *dev = rt->dst.dev;
4263
4264 nh->nh_grp = nh_grp;
4265 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02004266 mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
Petr Machata35225e42017-09-02 23:49:22 +02004267
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004268 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
4269
Petr Machata35225e42017-09-02 23:49:22 +02004270 if (!dev)
4271 return 0;
4272 nh->ifindex = dev->ifindex;
4273
4274 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4275}
4276
Ido Schimmel428b8512017-08-03 13:28:28 +02004277static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4278 struct mlxsw_sp_nexthop *nh)
4279{
Petr Machata35225e42017-09-02 23:49:22 +02004280 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02004281 list_del(&nh->router_list_node);
Arkadi Sharshevskya5390272017-09-25 10:32:28 +02004282 mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
Ido Schimmel428b8512017-08-03 13:28:28 +02004283}
4284
Petr Machataf6050ee2017-09-02 23:49:21 +02004285static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4286 const struct rt6_info *rt)
4287{
Petr Machata8f28a302017-09-02 23:49:24 +02004288 return rt->rt6i_flags & RTF_GATEWAY ||
4289 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004290}
4291
Ido Schimmel428b8512017-08-03 13:28:28 +02004292static struct mlxsw_sp_nexthop_group *
4293mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4294 struct mlxsw_sp_fib6_entry *fib6_entry)
4295{
4296 struct mlxsw_sp_nexthop_group *nh_grp;
4297 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4298 struct mlxsw_sp_nexthop *nh;
4299 size_t alloc_size;
4300 int i = 0;
4301 int err;
4302
4303 alloc_size = sizeof(*nh_grp) +
4304 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4305 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4306 if (!nh_grp)
4307 return ERR_PTR(-ENOMEM);
4308 INIT_LIST_HEAD(&nh_grp->fib_list);
4309#if IS_ENABLED(CONFIG_IPV6)
4310 nh_grp->neigh_tbl = &nd_tbl;
4311#endif
4312 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4313 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004314 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004315 nh_grp->count = fib6_entry->nrt6;
4316 for (i = 0; i < nh_grp->count; i++) {
4317 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4318
4319 nh = &nh_grp->nexthops[i];
4320 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4321 if (err)
4322 goto err_nexthop6_init;
4323 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4324 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004325
4326 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4327 if (err)
4328 goto err_nexthop_group_insert;
4329
Ido Schimmel428b8512017-08-03 13:28:28 +02004330 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4331 return nh_grp;
4332
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004333err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004334err_nexthop6_init:
4335 for (i--; i >= 0; i--) {
4336 nh = &nh_grp->nexthops[i];
4337 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4338 }
4339 kfree(nh_grp);
4340 return ERR_PTR(err);
4341}
4342
4343static void
4344mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4345 struct mlxsw_sp_nexthop_group *nh_grp)
4346{
4347 struct mlxsw_sp_nexthop *nh;
4348 int i = nh_grp->count;
4349
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004350 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004351 for (i--; i >= 0; i--) {
4352 nh = &nh_grp->nexthops[i];
4353 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4354 }
4355 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4356 WARN_ON(nh_grp->adj_index_valid);
4357 kfree(nh_grp);
4358}
4359
4360static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4361 struct mlxsw_sp_fib6_entry *fib6_entry)
4362{
4363 struct mlxsw_sp_nexthop_group *nh_grp;
4364
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004365 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4366 if (!nh_grp) {
4367 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4368 if (IS_ERR(nh_grp))
4369 return PTR_ERR(nh_grp);
4370 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004371
4372 list_add_tail(&fib6_entry->common.nexthop_group_node,
4373 &nh_grp->fib_list);
4374 fib6_entry->common.nh_group = nh_grp;
4375
4376 return 0;
4377}
4378
4379static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4380 struct mlxsw_sp_fib_entry *fib_entry)
4381{
4382 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4383
4384 list_del(&fib_entry->nexthop_group_node);
4385 if (!list_empty(&nh_grp->fib_list))
4386 return;
4387 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4388}
4389
4390static int
4391mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4392 struct mlxsw_sp_fib6_entry *fib6_entry)
4393{
4394 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4395 int err;
4396
4397 fib6_entry->common.nh_group = NULL;
4398 list_del(&fib6_entry->common.nexthop_group_node);
4399
4400 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4401 if (err)
4402 goto err_nexthop6_group_get;
4403
4404 /* In case this entry is offloaded, then the adjacency index
4405 * currently associated with it in the device's table is that
4406 * of the old group. Start using the new one instead.
4407 */
4408 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4409 if (err)
4410 goto err_fib_node_entry_add;
4411
4412 if (list_empty(&old_nh_grp->fib_list))
4413 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4414
4415 return 0;
4416
4417err_fib_node_entry_add:
4418 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4419err_nexthop6_group_get:
4420 list_add_tail(&fib6_entry->common.nexthop_group_node,
4421 &old_nh_grp->fib_list);
4422 fib6_entry->common.nh_group = old_nh_grp;
4423 return err;
4424}
4425
4426static int
4427mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4428 struct mlxsw_sp_fib6_entry *fib6_entry,
4429 struct rt6_info *rt)
4430{
4431 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4432 int err;
4433
4434 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4435 if (IS_ERR(mlxsw_sp_rt6))
4436 return PTR_ERR(mlxsw_sp_rt6);
4437
4438 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4439 fib6_entry->nrt6++;
4440
4441 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4442 if (err)
4443 goto err_nexthop6_group_update;
4444
4445 return 0;
4446
4447err_nexthop6_group_update:
4448 fib6_entry->nrt6--;
4449 list_del(&mlxsw_sp_rt6->list);
4450 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4451 return err;
4452}
4453
4454static void
4455mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4456 struct mlxsw_sp_fib6_entry *fib6_entry,
4457 struct rt6_info *rt)
4458{
4459 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4460
4461 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4462 if (WARN_ON(!mlxsw_sp_rt6))
4463 return;
4464
4465 fib6_entry->nrt6--;
4466 list_del(&mlxsw_sp_rt6->list);
4467 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4468 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4469}
4470
Petr Machataf6050ee2017-09-02 23:49:21 +02004471static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4472 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004473 const struct rt6_info *rt)
4474{
4475 /* Packets hitting RTF_REJECT routes need to be discarded by the
4476 * stack. We can rely on their destination device not having a
4477 * RIF (it's the loopback device) and can thus use action type
4478 * local, which will cause them to be trapped with a lower
4479 * priority than packets that need to be locally received.
4480 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004481 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004482 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4483 else if (rt->rt6i_flags & RTF_REJECT)
4484 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004485 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004486 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4487 else
4488 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4489}
4490
4491static void
4492mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4493{
4494 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4495
4496 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4497 list) {
4498 fib6_entry->nrt6--;
4499 list_del(&mlxsw_sp_rt6->list);
4500 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4501 }
4502}
4503
4504static struct mlxsw_sp_fib6_entry *
4505mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4506 struct mlxsw_sp_fib_node *fib_node,
4507 struct rt6_info *rt)
4508{
4509 struct mlxsw_sp_fib6_entry *fib6_entry;
4510 struct mlxsw_sp_fib_entry *fib_entry;
4511 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4512 int err;
4513
4514 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4515 if (!fib6_entry)
4516 return ERR_PTR(-ENOMEM);
4517 fib_entry = &fib6_entry->common;
4518
4519 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4520 if (IS_ERR(mlxsw_sp_rt6)) {
4521 err = PTR_ERR(mlxsw_sp_rt6);
4522 goto err_rt6_create;
4523 }
4524
Petr Machataf6050ee2017-09-02 23:49:21 +02004525 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004526
4527 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4528 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4529 fib6_entry->nrt6 = 1;
4530 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4531 if (err)
4532 goto err_nexthop6_group_get;
4533
4534 fib_entry->fib_node = fib_node;
4535
4536 return fib6_entry;
4537
4538err_nexthop6_group_get:
4539 list_del(&mlxsw_sp_rt6->list);
4540 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4541err_rt6_create:
4542 kfree(fib6_entry);
4543 return ERR_PTR(err);
4544}
4545
4546static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4547 struct mlxsw_sp_fib6_entry *fib6_entry)
4548{
4549 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4550 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4551 WARN_ON(fib6_entry->nrt6);
4552 kfree(fib6_entry);
4553}
4554
4555static struct mlxsw_sp_fib6_entry *
4556mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004557 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004558{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004559 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004560
4561 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4562 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4563
4564 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4565 continue;
4566 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4567 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004568 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4569 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4570 mlxsw_sp_fib6_rt_can_mp(nrt))
4571 return fib6_entry;
4572 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4573 fallback = fallback ?: fib6_entry;
4574 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004575 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004576 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004577 }
4578
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004579 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004580}
4581
4582static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004583mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4584 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004585{
4586 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4587 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4588 struct mlxsw_sp_fib6_entry *fib6_entry;
4589
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004590 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4591
4592 if (replace && WARN_ON(!fib6_entry))
4593 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004594
4595 if (fib6_entry) {
4596 list_add_tail(&new6_entry->common.list,
4597 &fib6_entry->common.list);
4598 } else {
4599 struct mlxsw_sp_fib6_entry *last;
4600
4601 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4602 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4603
4604 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4605 break;
4606 fib6_entry = last;
4607 }
4608
4609 if (fib6_entry)
4610 list_add(&new6_entry->common.list,
4611 &fib6_entry->common.list);
4612 else
4613 list_add(&new6_entry->common.list,
4614 &fib_node->entry_list);
4615 }
4616
4617 return 0;
4618}
4619
4620static void
4621mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4622{
4623 list_del(&fib6_entry->common.list);
4624}
4625
4626static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004627 struct mlxsw_sp_fib6_entry *fib6_entry,
4628 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004629{
4630 int err;
4631
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004632 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004633 if (err)
4634 return err;
4635
4636 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4637 if (err)
4638 goto err_fib_node_entry_add;
4639
4640 return 0;
4641
4642err_fib_node_entry_add:
4643 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4644 return err;
4645}
4646
4647static void
4648mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4649 struct mlxsw_sp_fib6_entry *fib6_entry)
4650{
4651 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4652 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4653}
4654
4655static struct mlxsw_sp_fib6_entry *
4656mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4657 const struct rt6_info *rt)
4658{
4659 struct mlxsw_sp_fib6_entry *fib6_entry;
4660 struct mlxsw_sp_fib_node *fib_node;
4661 struct mlxsw_sp_fib *fib;
4662 struct mlxsw_sp_vr *vr;
4663
4664 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4665 if (!vr)
4666 return NULL;
4667 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4668
4669 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4670 sizeof(rt->rt6i_dst.addr),
4671 rt->rt6i_dst.plen);
4672 if (!fib_node)
4673 return NULL;
4674
4675 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4676 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4677
4678 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4679 rt->rt6i_metric == iter_rt->rt6i_metric &&
4680 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4681 return fib6_entry;
4682 }
4683
4684 return NULL;
4685}
4686
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004687static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4688 struct mlxsw_sp_fib6_entry *fib6_entry,
4689 bool replace)
4690{
4691 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4692 struct mlxsw_sp_fib6_entry *replaced;
4693
4694 if (!replace)
4695 return;
4696
4697 replaced = list_next_entry(fib6_entry, common.list);
4698
4699 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4700 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4701 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4702}
4703
Ido Schimmel428b8512017-08-03 13:28:28 +02004704static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004705 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004706{
4707 struct mlxsw_sp_fib6_entry *fib6_entry;
4708 struct mlxsw_sp_fib_node *fib_node;
4709 int err;
4710
4711 if (mlxsw_sp->router->aborted)
4712 return 0;
4713
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004714 if (rt->rt6i_src.plen)
4715 return -EINVAL;
4716
Ido Schimmel428b8512017-08-03 13:28:28 +02004717 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4718 return 0;
4719
4720 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4721 &rt->rt6i_dst.addr,
4722 sizeof(rt->rt6i_dst.addr),
4723 rt->rt6i_dst.plen,
4724 MLXSW_SP_L3_PROTO_IPV6);
4725 if (IS_ERR(fib_node))
4726 return PTR_ERR(fib_node);
4727
4728 /* Before creating a new entry, try to append route to an existing
4729 * multipath entry.
4730 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004731 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004732 if (fib6_entry) {
4733 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4734 if (err)
4735 goto err_fib6_entry_nexthop_add;
4736 return 0;
4737 }
4738
4739 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4740 if (IS_ERR(fib6_entry)) {
4741 err = PTR_ERR(fib6_entry);
4742 goto err_fib6_entry_create;
4743 }
4744
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004745 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004746 if (err)
4747 goto err_fib6_node_entry_link;
4748
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004749 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4750
Ido Schimmel428b8512017-08-03 13:28:28 +02004751 return 0;
4752
4753err_fib6_node_entry_link:
4754 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4755err_fib6_entry_create:
4756err_fib6_entry_nexthop_add:
4757 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4758 return err;
4759}
4760
4761static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4762 struct rt6_info *rt)
4763{
4764 struct mlxsw_sp_fib6_entry *fib6_entry;
4765 struct mlxsw_sp_fib_node *fib_node;
4766
4767 if (mlxsw_sp->router->aborted)
4768 return;
4769
4770 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4771 return;
4772
4773 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4774 if (WARN_ON(!fib6_entry))
4775 return;
4776
4777 /* If route is part of a multipath entry, but not the last one
4778 * removed, then only reduce its nexthop group.
4779 */
4780 if (!list_is_singular(&fib6_entry->rt6_list)) {
4781 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4782 return;
4783 }
4784
4785 fib_node = fib6_entry->common.fib_node;
4786
4787 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4788 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4789 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4790}
4791
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004792static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4793 enum mlxsw_reg_ralxx_protocol proto,
4794 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004795{
4796 char ralta_pl[MLXSW_REG_RALTA_LEN];
4797 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004798 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004799
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004800 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004801 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4802 if (err)
4803 return err;
4804
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004805 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004806 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4807 if (err)
4808 return err;
4809
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004810 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004811 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004812 char raltb_pl[MLXSW_REG_RALTB_LEN];
4813 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004814
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004815 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004816 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4817 raltb_pl);
4818 if (err)
4819 return err;
4820
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004821 mlxsw_reg_ralue_pack(ralue_pl, proto,
4822 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004823 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4824 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4825 ralue_pl);
4826 if (err)
4827 return err;
4828 }
4829
4830 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004831}
4832
Yotam Gigid42b0962017-09-27 08:23:20 +02004833static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
4834 struct mfc_entry_notifier_info *men_info,
4835 bool replace)
4836{
4837 struct mlxsw_sp_vr *vr;
4838
4839 if (mlxsw_sp->router->aborted)
4840 return 0;
4841
4842 vr = mlxsw_sp_vr_get(mlxsw_sp, men_info->tb_id);
4843 if (IS_ERR(vr))
4844 return PTR_ERR(vr);
4845
4846 return mlxsw_sp_mr_route4_add(vr->mr4_table, men_info->mfc, replace);
4847}
4848
4849static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
4850 struct mfc_entry_notifier_info *men_info)
4851{
4852 struct mlxsw_sp_vr *vr;
4853
4854 if (mlxsw_sp->router->aborted)
4855 return;
4856
4857 vr = mlxsw_sp_vr_find(mlxsw_sp, men_info->tb_id);
4858 if (WARN_ON(!vr))
4859 return;
4860
4861 mlxsw_sp_mr_route4_del(vr->mr4_table, men_info->mfc);
4862 mlxsw_sp_vr_put(vr);
4863}
4864
4865static int
4866mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
4867 struct vif_entry_notifier_info *ven_info)
4868{
4869 struct mlxsw_sp_rif *rif;
4870 struct mlxsw_sp_vr *vr;
4871
4872 if (mlxsw_sp->router->aborted)
4873 return 0;
4874
4875 vr = mlxsw_sp_vr_get(mlxsw_sp, ven_info->tb_id);
4876 if (IS_ERR(vr))
4877 return PTR_ERR(vr);
4878
4879 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev);
4880 return mlxsw_sp_mr_vif_add(vr->mr4_table, ven_info->dev,
4881 ven_info->vif_index,
4882 ven_info->vif_flags, rif);
4883}
4884
4885static void
4886mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
4887 struct vif_entry_notifier_info *ven_info)
4888{
4889 struct mlxsw_sp_vr *vr;
4890
4891 if (mlxsw_sp->router->aborted)
4892 return;
4893
4894 vr = mlxsw_sp_vr_find(mlxsw_sp, ven_info->tb_id);
4895 if (WARN_ON(!vr))
4896 return;
4897
4898 mlxsw_sp_mr_vif_del(vr->mr4_table, ven_info->vif_index);
4899 mlxsw_sp_vr_put(vr);
4900}
4901
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004902static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4903{
4904 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4905 int err;
4906
4907 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4908 MLXSW_SP_LPM_TREE_MIN);
4909 if (err)
4910 return err;
4911
Yotam Gigid42b0962017-09-27 08:23:20 +02004912 /* The multicast router code does not need an abort trap as by default,
4913 * packets that don't match any routes are trapped to the CPU.
4914 */
4915
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004916 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4917 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4918 MLXSW_SP_LPM_TREE_MIN + 1);
4919}
4920
Ido Schimmel9aecce12017-02-09 10:28:42 +01004921static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4922 struct mlxsw_sp_fib_node *fib_node)
4923{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004924 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004925
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004926 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4927 common.list) {
4928 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004929
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004930 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4931 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004932 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004933 /* Break when entry list is empty and node was freed.
4934 * Otherwise, we'll access freed memory in the next
4935 * iteration.
4936 */
4937 if (do_break)
4938 break;
4939 }
4940}
4941
Ido Schimmel428b8512017-08-03 13:28:28 +02004942static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4943 struct mlxsw_sp_fib_node *fib_node)
4944{
4945 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4946
4947 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4948 common.list) {
4949 bool do_break = &tmp->common.list == &fib_node->entry_list;
4950
4951 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4952 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4953 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4954 if (do_break)
4955 break;
4956 }
4957}
4958
Ido Schimmel9aecce12017-02-09 10:28:42 +01004959static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4960 struct mlxsw_sp_fib_node *fib_node)
4961{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004962 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004963 case MLXSW_SP_L3_PROTO_IPV4:
4964 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4965 break;
4966 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004967 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004968 break;
4969 }
4970}
4971
Ido Schimmel76610eb2017-03-10 08:53:41 +01004972static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4973 struct mlxsw_sp_vr *vr,
4974 enum mlxsw_sp_l3proto proto)
4975{
4976 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4977 struct mlxsw_sp_fib_node *fib_node, *tmp;
4978
4979 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4980 bool do_break = &tmp->list == &fib->node_list;
4981
4982 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4983 if (do_break)
4984 break;
4985 }
4986}
4987
Ido Schimmelac571de2016-11-14 11:26:32 +01004988static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004989{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004990 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004991
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004992 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004993 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004994
Ido Schimmel76610eb2017-03-10 08:53:41 +01004995 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004996 continue;
Yotam Gigid42b0962017-09-27 08:23:20 +02004997
4998 mlxsw_sp_mr_table_flush(vr->mr4_table);
Ido Schimmel76610eb2017-03-10 08:53:41 +01004999 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02005000
5001 /* If virtual router was only used for IPv4, then it's no
5002 * longer used.
5003 */
5004 if (!mlxsw_sp_vr_is_used(vr))
5005 continue;
5006 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005007 }
Ido Schimmelac571de2016-11-14 11:26:32 +01005008}
5009
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02005010static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01005011{
5012 int err;
5013
Ido Schimmel9011b672017-05-16 19:38:25 +02005014 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01005015 return;
5016 dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
Ido Schimmelac571de2016-11-14 11:26:32 +01005017 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005018 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005019 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
5020 if (err)
5021 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
5022}
5023
Ido Schimmel30572242016-12-03 16:45:01 +01005024struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01005025 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01005026 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02005027 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01005028 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01005029 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01005030 struct fib_nh_notifier_info fnh_info;
Yotam Gigid42b0962017-09-27 08:23:20 +02005031 struct mfc_entry_notifier_info men_info;
5032 struct vif_entry_notifier_info ven_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01005033 };
Ido Schimmel30572242016-12-03 16:45:01 +01005034 struct mlxsw_sp *mlxsw_sp;
5035 unsigned long event;
5036};
5037
Ido Schimmel66a57632017-08-03 13:28:26 +02005038static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005039{
Ido Schimmel30572242016-12-03 16:45:01 +01005040 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01005041 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01005042 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01005043 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01005044 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005045 int err;
5046
Ido Schimmel30572242016-12-03 16:45:01 +01005047 /* Protect internal structures from changes */
5048 rtnl_lock();
5049 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01005050 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01005051 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005052 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01005053 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01005054 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
5055 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01005056 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005057 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02005058 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01005059 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005060 break;
5061 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01005062 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
5063 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005064 break;
5065 case FIB_EVENT_RULE_ADD: /* fall through */
5066 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01005067 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01005068 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02005069 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01005070 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005071 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01005072 case FIB_EVENT_NH_ADD: /* fall through */
5073 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02005074 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
5075 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01005076 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
5077 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005078 }
Ido Schimmel30572242016-12-03 16:45:01 +01005079 rtnl_unlock();
5080 kfree(fib_work);
5081}
5082
Ido Schimmel66a57632017-08-03 13:28:26 +02005083static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
5084{
Ido Schimmel583419f2017-08-03 13:28:27 +02005085 struct mlxsw_sp_fib_event_work *fib_work =
5086 container_of(work, struct mlxsw_sp_fib_event_work, work);
5087 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
5088 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02005089 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02005090 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02005091
5092 rtnl_lock();
5093 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02005094 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02005095 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02005096 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02005097 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02005098 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02005099 if (err)
5100 mlxsw_sp_router_fib_abort(mlxsw_sp);
5101 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
5102 break;
5103 case FIB_EVENT_ENTRY_DEL:
5104 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
5105 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
5106 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02005107 case FIB_EVENT_RULE_ADD: /* fall through */
5108 case FIB_EVENT_RULE_DEL:
5109 rule = fib_work->fr_info.rule;
5110 if (!fib6_rule_default(rule) && !rule->l3mdev)
5111 mlxsw_sp_router_fib_abort(mlxsw_sp);
5112 fib_rule_put(rule);
5113 break;
5114 }
5115 rtnl_unlock();
5116 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02005117}
5118
Yotam Gigid42b0962017-09-27 08:23:20 +02005119static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
5120{
5121 struct mlxsw_sp_fib_event_work *fib_work =
5122 container_of(work, struct mlxsw_sp_fib_event_work, work);
5123 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
5124 struct fib_rule *rule;
5125 bool replace;
5126 int err;
5127
5128 rtnl_lock();
5129 switch (fib_work->event) {
5130 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
5131 case FIB_EVENT_ENTRY_ADD:
5132 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
5133
5134 err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_work->men_info,
5135 replace);
5136 if (err)
5137 mlxsw_sp_router_fib_abort(mlxsw_sp);
5138 ipmr_cache_put(fib_work->men_info.mfc);
5139 break;
5140 case FIB_EVENT_ENTRY_DEL:
5141 mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);
5142 ipmr_cache_put(fib_work->men_info.mfc);
5143 break;
5144 case FIB_EVENT_VIF_ADD:
5145 err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,
5146 &fib_work->ven_info);
5147 if (err)
5148 mlxsw_sp_router_fib_abort(mlxsw_sp);
5149 dev_put(fib_work->ven_info.dev);
5150 break;
5151 case FIB_EVENT_VIF_DEL:
5152 mlxsw_sp_router_fibmr_vif_del(mlxsw_sp,
5153 &fib_work->ven_info);
5154 dev_put(fib_work->ven_info.dev);
5155 break;
5156 case FIB_EVENT_RULE_ADD: /* fall through */
5157 case FIB_EVENT_RULE_DEL:
5158 rule = fib_work->fr_info.rule;
5159 if (!ipmr_rule_default(rule) && !rule->l3mdev)
5160 mlxsw_sp_router_fib_abort(mlxsw_sp);
5161 fib_rule_put(rule);
5162 break;
5163 }
5164 rtnl_unlock();
5165 kfree(fib_work);
5166}
5167
Ido Schimmel66a57632017-08-03 13:28:26 +02005168static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
5169 struct fib_notifier_info *info)
5170{
5171 switch (fib_work->event) {
5172 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
5173 case FIB_EVENT_ENTRY_APPEND: /* fall through */
5174 case FIB_EVENT_ENTRY_ADD: /* fall through */
5175 case FIB_EVENT_ENTRY_DEL:
5176 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
5177 /* Take referece on fib_info to prevent it from being
5178 * freed while work is queued. Release it afterwards.
5179 */
5180 fib_info_hold(fib_work->fen_info.fi);
5181 break;
5182 case FIB_EVENT_RULE_ADD: /* fall through */
5183 case FIB_EVENT_RULE_DEL:
5184 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
5185 fib_rule_get(fib_work->fr_info.rule);
5186 break;
5187 case FIB_EVENT_NH_ADD: /* fall through */
5188 case FIB_EVENT_NH_DEL:
5189 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
5190 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
5191 break;
5192 }
5193}
5194
5195static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
5196 struct fib_notifier_info *info)
5197{
Ido Schimmel583419f2017-08-03 13:28:27 +02005198 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02005199 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02005200 case FIB_EVENT_ENTRY_ADD: /* fall through */
5201 case FIB_EVENT_ENTRY_DEL:
5202 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
5203 rt6_hold(fib_work->fen6_info.rt);
5204 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02005205 case FIB_EVENT_RULE_ADD: /* fall through */
5206 case FIB_EVENT_RULE_DEL:
5207 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
5208 fib_rule_get(fib_work->fr_info.rule);
5209 break;
5210 }
Ido Schimmel66a57632017-08-03 13:28:26 +02005211}
5212
Yotam Gigid42b0962017-09-27 08:23:20 +02005213static void
5214mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
5215 struct fib_notifier_info *info)
5216{
5217 switch (fib_work->event) {
5218 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
5219 case FIB_EVENT_ENTRY_ADD: /* fall through */
5220 case FIB_EVENT_ENTRY_DEL:
5221 memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));
5222 ipmr_cache_hold(fib_work->men_info.mfc);
5223 break;
5224 case FIB_EVENT_VIF_ADD: /* fall through */
5225 case FIB_EVENT_VIF_DEL:
5226 memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info));
5227 dev_hold(fib_work->ven_info.dev);
5228 break;
5229 case FIB_EVENT_RULE_ADD: /* fall through */
5230 case FIB_EVENT_RULE_DEL:
5231 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
5232 fib_rule_get(fib_work->fr_info.rule);
5233 break;
5234 }
5235}
5236
Ido Schimmel30572242016-12-03 16:45:01 +01005237/* Called with rcu_read_lock() */
5238static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
5239 unsigned long event, void *ptr)
5240{
Ido Schimmel30572242016-12-03 16:45:01 +01005241 struct mlxsw_sp_fib_event_work *fib_work;
5242 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02005243 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01005244
Ido Schimmel8e29f972017-09-15 15:31:07 +02005245 if (!net_eq(info->net, &init_net) ||
Yotam Gigi664375e2017-09-27 08:23:22 +02005246 (info->family != AF_INET && info->family != AF_INET6 &&
5247 info->family != RTNL_FAMILY_IPMR))
Ido Schimmel30572242016-12-03 16:45:01 +01005248 return NOTIFY_DONE;
5249
5250 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
5251 if (WARN_ON(!fib_work))
5252 return NOTIFY_BAD;
5253
Ido Schimmel7e39d112017-05-16 19:38:28 +02005254 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5255 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01005256 fib_work->event = event;
5257
Ido Schimmel66a57632017-08-03 13:28:26 +02005258 switch (info->family) {
5259 case AF_INET:
5260 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
5261 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01005262 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02005263 case AF_INET6:
5264 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
5265 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01005266 break;
Yotam Gigid42b0962017-09-27 08:23:20 +02005267 case RTNL_FAMILY_IPMR:
5268 INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);
5269 mlxsw_sp_router_fibmr_event(fib_work, info);
5270 break;
Ido Schimmel30572242016-12-03 16:45:01 +01005271 }
5272
Ido Schimmela0e47612017-02-06 16:20:10 +01005273 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01005274
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005275 return NOTIFY_DONE;
5276}
5277
Ido Schimmel4724ba562017-03-10 08:53:39 +01005278static struct mlxsw_sp_rif *
5279mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
5280 const struct net_device *dev)
5281{
5282 int i;
5283
5284 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005285 if (mlxsw_sp->router->rifs[i] &&
5286 mlxsw_sp->router->rifs[i]->dev == dev)
5287 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005288
5289 return NULL;
5290}
5291
5292static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
5293{
5294 char ritr_pl[MLXSW_REG_RITR_LEN];
5295 int err;
5296
5297 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
5298 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5299 if (WARN_ON_ONCE(err))
5300 return err;
5301
5302 mlxsw_reg_ritr_enable_set(ritr_pl, false);
5303 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5304}
5305
5306static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005307 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005308{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005309 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
5310 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
5311 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005312}
5313
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005314static bool
5315mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
5316 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005317{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005318 struct inet6_dev *inet6_dev;
5319 bool addr_list_empty = true;
5320 struct in_device *idev;
5321
Ido Schimmel4724ba562017-03-10 08:53:39 +01005322 switch (event) {
5323 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02005324 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005325 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005326 idev = __in_dev_get_rtnl(dev);
5327 if (idev && idev->ifa_list)
5328 addr_list_empty = false;
5329
5330 inet6_dev = __in6_dev_get(dev);
5331 if (addr_list_empty && inet6_dev &&
5332 !list_empty(&inet6_dev->addr_list))
5333 addr_list_empty = false;
5334
5335 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005336 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005337 return true;
5338 /* It is possible we already removed the RIF ourselves
5339 * if it was assigned to a netdev that is now a bridge
5340 * or LAG slave.
5341 */
5342 return false;
5343 }
5344
5345 return false;
5346}
5347
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005348static enum mlxsw_sp_rif_type
5349mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
5350 const struct net_device *dev)
5351{
5352 enum mlxsw_sp_fid_type type;
5353
Petr Machata6ddb7422017-09-02 23:49:19 +02005354 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
5355 return MLXSW_SP_RIF_TYPE_IPIP_LB;
5356
5357 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005358 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
5359 type = MLXSW_SP_FID_TYPE_8021Q;
5360 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
5361 type = MLXSW_SP_FID_TYPE_8021Q;
5362 else if (netif_is_bridge_master(dev))
5363 type = MLXSW_SP_FID_TYPE_8021D;
5364 else
5365 type = MLXSW_SP_FID_TYPE_RFID;
5366
5367 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
5368}
5369
Ido Schimmelde5ed992017-06-04 16:53:40 +02005370static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005371{
5372 int i;
5373
Ido Schimmelde5ed992017-06-04 16:53:40 +02005374 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
5375 if (!mlxsw_sp->router->rifs[i]) {
5376 *p_rif_index = i;
5377 return 0;
5378 }
5379 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01005380
Ido Schimmelde5ed992017-06-04 16:53:40 +02005381 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005382}
5383
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005384static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5385 u16 vr_id,
5386 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005387{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005388 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005389
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005390 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005391 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005392 return NULL;
5393
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005394 INIT_LIST_HEAD(&rif->nexthop_list);
5395 INIT_LIST_HEAD(&rif->neigh_list);
5396 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5397 rif->mtu = l3_dev->mtu;
5398 rif->vr_id = vr_id;
5399 rif->dev = l3_dev;
5400 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005401
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005402 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005403}
5404
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005405struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5406 u16 rif_index)
5407{
5408 return mlxsw_sp->router->rifs[rif_index];
5409}
5410
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005411u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5412{
5413 return rif->rif_index;
5414}
5415
Petr Machata92107cf2017-09-02 23:49:28 +02005416u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5417{
5418 return lb_rif->common.rif_index;
5419}
5420
5421u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5422{
5423 return lb_rif->ul_vr_id;
5424}
5425
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005426int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5427{
5428 return rif->dev->ifindex;
5429}
5430
Yotam Gigi91e4d592017-09-19 10:00:19 +02005431const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
5432{
5433 return rif->dev;
5434}
5435
Ido Schimmel4724ba562017-03-10 08:53:39 +01005436static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005437mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5438 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005439{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005440 u32 tb_id = l3mdev_fib_table(params->dev);
5441 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005442 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005443 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005444 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005445 struct mlxsw_sp_vr *vr;
5446 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005447 int err;
5448
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005449 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5450 ops = mlxsw_sp->router->rif_ops_arr[type];
5451
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005452 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5453 if (IS_ERR(vr))
5454 return ERR_CAST(vr);
Petr Machata28a04c72017-10-02 12:14:56 +02005455 vr->rif_count++;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005456
Ido Schimmelde5ed992017-06-04 16:53:40 +02005457 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5458 if (err)
5459 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005460
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005461 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005462 if (!rif) {
5463 err = -ENOMEM;
5464 goto err_rif_alloc;
5465 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005466 rif->mlxsw_sp = mlxsw_sp;
5467 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005468
Petr Machata010cadf2017-09-02 23:49:18 +02005469 if (ops->fid_get) {
5470 fid = ops->fid_get(rif);
5471 if (IS_ERR(fid)) {
5472 err = PTR_ERR(fid);
5473 goto err_fid_get;
5474 }
5475 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005476 }
5477
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005478 if (ops->setup)
5479 ops->setup(rif, params);
5480
5481 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005482 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005483 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005484
Yotam Gigid42b0962017-09-27 08:23:20 +02005485 err = mlxsw_sp_mr_rif_add(vr->mr4_table, rif);
5486 if (err)
5487 goto err_mr_rif_add;
5488
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005489 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005490 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005491
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005492 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005493
Yotam Gigid42b0962017-09-27 08:23:20 +02005494err_mr_rif_add:
5495 ops->deconfigure(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005496err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005497 if (fid)
5498 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005499err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005500 kfree(rif);
5501err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005502err_rif_index_alloc:
Petr Machata28a04c72017-10-02 12:14:56 +02005503 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005504 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005505 return ERR_PTR(err);
5506}
5507
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005508void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005509{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005510 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5511 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005512 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005513 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005514
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005515 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005516 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005517
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005518 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005519 mlxsw_sp_rif_counters_free(rif);
Yotam Gigid42b0962017-09-27 08:23:20 +02005520 mlxsw_sp_mr_rif_del(vr->mr4_table, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005521 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005522 if (fid)
5523 /* Loopback RIFs are not associated with a FID. */
5524 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005525 kfree(rif);
Petr Machata28a04c72017-10-02 12:14:56 +02005526 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005527 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005528}
5529
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005530static void
5531mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5532 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5533{
5534 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5535
5536 params->vid = mlxsw_sp_port_vlan->vid;
5537 params->lag = mlxsw_sp_port->lagged;
5538 if (params->lag)
5539 params->lag_id = mlxsw_sp_port->lag_id;
5540 else
5541 params->system_port = mlxsw_sp_port->local_port;
5542}
5543
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005544static int
Ido Schimmela1107482017-05-26 08:37:39 +02005545mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005546 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005547{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005548 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005549 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005550 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005551 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005552 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005553 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005554
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005555 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005556 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005557 struct mlxsw_sp_rif_params params = {
5558 .dev = l3_dev,
5559 };
5560
5561 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5562 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005563 if (IS_ERR(rif))
5564 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005565 }
5566
Ido Schimmela1107482017-05-26 08:37:39 +02005567 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005568 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005569 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5570 if (err)
5571 goto err_fid_port_vid_map;
5572
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005573 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005574 if (err)
5575 goto err_port_vid_learning_set;
5576
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005577 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005578 BR_STATE_FORWARDING);
5579 if (err)
5580 goto err_port_vid_stp_set;
5581
Ido Schimmela1107482017-05-26 08:37:39 +02005582 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005583
Ido Schimmel4724ba562017-03-10 08:53:39 +01005584 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005585
5586err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005587 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005588err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005589 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5590err_fid_port_vid_map:
5591 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005592 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005593}
5594
Ido Schimmela1107482017-05-26 08:37:39 +02005595void
5596mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005597{
Ido Schimmelce95e152017-05-26 08:37:27 +02005598 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005599 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005600 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005601
Ido Schimmela1107482017-05-26 08:37:39 +02005602 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5603 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005604
Ido Schimmela1107482017-05-26 08:37:39 +02005605 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005606 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5607 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005608 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5609 /* If router port holds the last reference on the rFID, then the
5610 * associated Sub-port RIF will be destroyed.
5611 */
5612 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005613}
5614
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005615static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5616 struct net_device *port_dev,
5617 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005618{
5619 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005620 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005621
Ido Schimmelce95e152017-05-26 08:37:27 +02005622 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005623 if (WARN_ON(!mlxsw_sp_port_vlan))
5624 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005625
5626 switch (event) {
5627 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005628 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005629 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005630 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005631 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005632 break;
5633 }
5634
5635 return 0;
5636}
5637
5638static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5639 unsigned long event)
5640{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005641 if (netif_is_bridge_port(port_dev) ||
5642 netif_is_lag_port(port_dev) ||
5643 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005644 return 0;
5645
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005646 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005647}
5648
5649static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5650 struct net_device *lag_dev,
5651 unsigned long event, u16 vid)
5652{
5653 struct net_device *port_dev;
5654 struct list_head *iter;
5655 int err;
5656
5657 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5658 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005659 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5660 port_dev,
5661 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005662 if (err)
5663 return err;
5664 }
5665 }
5666
5667 return 0;
5668}
5669
5670static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5671 unsigned long event)
5672{
5673 if (netif_is_bridge_port(lag_dev))
5674 return 0;
5675
5676 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5677}
5678
Ido Schimmel4724ba562017-03-10 08:53:39 +01005679static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005680 unsigned long event)
5681{
5682 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005683 struct mlxsw_sp_rif_params params = {
5684 .dev = l3_dev,
5685 };
Ido Schimmela1107482017-05-26 08:37:39 +02005686 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005687
5688 switch (event) {
5689 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005690 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5691 if (IS_ERR(rif))
5692 return PTR_ERR(rif);
5693 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005694 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005695 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005696 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005697 break;
5698 }
5699
5700 return 0;
5701}
5702
5703static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5704 unsigned long event)
5705{
5706 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005707 u16 vid = vlan_dev_vlan_id(vlan_dev);
5708
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005709 if (netif_is_bridge_port(vlan_dev))
5710 return 0;
5711
Ido Schimmel4724ba562017-03-10 08:53:39 +01005712 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005713 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5714 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005715 else if (netif_is_lag_master(real_dev))
5716 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5717 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005718 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005719 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005720
5721 return 0;
5722}
5723
Ido Schimmelb1e45522017-04-30 19:47:14 +03005724static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5725 unsigned long event)
5726{
5727 if (mlxsw_sp_port_dev_check(dev))
5728 return mlxsw_sp_inetaddr_port_event(dev, event);
5729 else if (netif_is_lag_master(dev))
5730 return mlxsw_sp_inetaddr_lag_event(dev, event);
5731 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005732 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005733 else if (is_vlan_dev(dev))
5734 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5735 else
5736 return 0;
5737}
5738
Ido Schimmel4724ba562017-03-10 08:53:39 +01005739int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5740 unsigned long event, void *ptr)
5741{
5742 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5743 struct net_device *dev = ifa->ifa_dev->dev;
5744 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005745 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005746 int err = 0;
5747
5748 mlxsw_sp = mlxsw_sp_lower_get(dev);
5749 if (!mlxsw_sp)
5750 goto out;
5751
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005752 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005753 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005754 goto out;
5755
Ido Schimmelb1e45522017-04-30 19:47:14 +03005756 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005757out:
5758 return notifier_from_errno(err);
5759}
5760
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005761struct mlxsw_sp_inet6addr_event_work {
5762 struct work_struct work;
5763 struct net_device *dev;
5764 unsigned long event;
5765};
5766
5767static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5768{
5769 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5770 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5771 struct net_device *dev = inet6addr_work->dev;
5772 unsigned long event = inet6addr_work->event;
5773 struct mlxsw_sp *mlxsw_sp;
5774 struct mlxsw_sp_rif *rif;
5775
5776 rtnl_lock();
5777 mlxsw_sp = mlxsw_sp_lower_get(dev);
5778 if (!mlxsw_sp)
5779 goto out;
5780
5781 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5782 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5783 goto out;
5784
5785 __mlxsw_sp_inetaddr_event(dev, event);
5786out:
5787 rtnl_unlock();
5788 dev_put(dev);
5789 kfree(inet6addr_work);
5790}
5791
5792/* Called with rcu_read_lock() */
5793int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5794 unsigned long event, void *ptr)
5795{
5796 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5797 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5798 struct net_device *dev = if6->idev->dev;
5799
5800 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5801 return NOTIFY_DONE;
5802
5803 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5804 if (!inet6addr_work)
5805 return NOTIFY_BAD;
5806
5807 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5808 inet6addr_work->dev = dev;
5809 inet6addr_work->event = event;
5810 dev_hold(dev);
5811 mlxsw_core_schedule_work(&inet6addr_work->work);
5812
5813 return NOTIFY_DONE;
5814}
5815
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005816static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005817 const char *mac, int mtu)
5818{
5819 char ritr_pl[MLXSW_REG_RITR_LEN];
5820 int err;
5821
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005822 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005823 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5824 if (err)
5825 return err;
5826
5827 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5828 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5829 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5830 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5831}
5832
5833int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5834{
5835 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005836 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005837 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005838 int err;
5839
5840 mlxsw_sp = mlxsw_sp_lower_get(dev);
5841 if (!mlxsw_sp)
5842 return 0;
5843
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005844 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5845 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005846 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005847 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005848
Ido Schimmela1107482017-05-26 08:37:39 +02005849 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005850 if (err)
5851 return err;
5852
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005853 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5854 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005855 if (err)
5856 goto err_rif_edit;
5857
Ido Schimmela1107482017-05-26 08:37:39 +02005858 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005859 if (err)
5860 goto err_rif_fdb_op;
5861
Yotam Gigifd890fe2017-09-27 08:23:21 +02005862 if (rif->mtu != dev->mtu) {
5863 struct mlxsw_sp_vr *vr;
5864
5865 /* The RIF is relevant only to its mr_table instance, as unlike
5866 * unicast routing, in multicast routing a RIF cannot be shared
5867 * between several multicast routing tables.
5868 */
5869 vr = &mlxsw_sp->router->vrs[rif->vr_id];
5870 mlxsw_sp_mr_rif_mtu_update(vr->mr4_table, rif, dev->mtu);
5871 }
5872
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005873 ether_addr_copy(rif->addr, dev->dev_addr);
5874 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005875
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005876 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005877
5878 return 0;
5879
5880err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005881 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005882err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005883 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005884 return err;
5885}
5886
Ido Schimmelb1e45522017-04-30 19:47:14 +03005887static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5888 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005889{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005890 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005891
Ido Schimmelb1e45522017-04-30 19:47:14 +03005892 /* If netdev is already associated with a RIF, then we need to
5893 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005894 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005895 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5896 if (rif)
5897 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005898
Ido Schimmelb1e45522017-04-30 19:47:14 +03005899 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005900}
5901
Ido Schimmelb1e45522017-04-30 19:47:14 +03005902static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5903 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005904{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005905 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005906
Ido Schimmelb1e45522017-04-30 19:47:14 +03005907 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5908 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005909 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005910 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005911}
5912
Ido Schimmelb1e45522017-04-30 19:47:14 +03005913int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5914 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005915{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005916 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5917 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005918
Ido Schimmelb1e45522017-04-30 19:47:14 +03005919 if (!mlxsw_sp)
5920 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005921
Ido Schimmelb1e45522017-04-30 19:47:14 +03005922 switch (event) {
5923 case NETDEV_PRECHANGEUPPER:
5924 return 0;
5925 case NETDEV_CHANGEUPPER:
5926 if (info->linking)
5927 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5928 else
5929 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5930 break;
5931 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005932
Ido Schimmelb1e45522017-04-30 19:47:14 +03005933 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005934}
5935
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005936static struct mlxsw_sp_rif_subport *
5937mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005938{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005939 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005940}
5941
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005942static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5943 const struct mlxsw_sp_rif_params *params)
5944{
5945 struct mlxsw_sp_rif_subport *rif_subport;
5946
5947 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5948 rif_subport->vid = params->vid;
5949 rif_subport->lag = params->lag;
5950 if (params->lag)
5951 rif_subport->lag_id = params->lag_id;
5952 else
5953 rif_subport->system_port = params->system_port;
5954}
5955
5956static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5957{
5958 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5959 struct mlxsw_sp_rif_subport *rif_subport;
5960 char ritr_pl[MLXSW_REG_RITR_LEN];
5961
5962 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5963 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005964 rif->rif_index, rif->vr_id, rif->dev->mtu);
5965 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005966 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5967 rif_subport->lag ? rif_subport->lag_id :
5968 rif_subport->system_port,
5969 rif_subport->vid);
5970
5971 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5972}
5973
5974static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5975{
Petr Machata010cadf2017-09-02 23:49:18 +02005976 int err;
5977
5978 err = mlxsw_sp_rif_subport_op(rif, true);
5979 if (err)
5980 return err;
5981
5982 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5983 mlxsw_sp_fid_index(rif->fid), true);
5984 if (err)
5985 goto err_rif_fdb_op;
5986
5987 mlxsw_sp_fid_rif_set(rif->fid, rif);
5988 return 0;
5989
5990err_rif_fdb_op:
5991 mlxsw_sp_rif_subport_op(rif, false);
5992 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005993}
5994
5995static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5996{
Petr Machata010cadf2017-09-02 23:49:18 +02005997 struct mlxsw_sp_fid *fid = rif->fid;
5998
5999 mlxsw_sp_fid_rif_set(fid, NULL);
6000 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
6001 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006002 mlxsw_sp_rif_subport_op(rif, false);
6003}
6004
6005static struct mlxsw_sp_fid *
6006mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
6007{
6008 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
6009}
6010
6011static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
6012 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
6013 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
6014 .setup = mlxsw_sp_rif_subport_setup,
6015 .configure = mlxsw_sp_rif_subport_configure,
6016 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
6017 .fid_get = mlxsw_sp_rif_subport_fid_get,
6018};
6019
6020static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
6021 enum mlxsw_reg_ritr_if_type type,
6022 u16 vid_fid, bool enable)
6023{
6024 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6025 char ritr_pl[MLXSW_REG_RITR_LEN];
6026
6027 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02006028 rif->dev->mtu);
6029 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006030 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
6031
6032 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
6033}
6034
Yotam Gigib35750f2017-10-09 11:15:33 +02006035u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006036{
6037 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
6038}
6039
6040static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
6041{
6042 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6043 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
6044 int err;
6045
6046 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
6047 if (err)
6048 return err;
6049
Ido Schimmel0d284812017-07-18 10:10:12 +02006050 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6051 mlxsw_sp_router_port(mlxsw_sp), true);
6052 if (err)
6053 goto err_fid_mc_flood_set;
6054
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006055 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6056 mlxsw_sp_router_port(mlxsw_sp), true);
6057 if (err)
6058 goto err_fid_bc_flood_set;
6059
Petr Machata010cadf2017-09-02 23:49:18 +02006060 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
6061 mlxsw_sp_fid_index(rif->fid), true);
6062 if (err)
6063 goto err_rif_fdb_op;
6064
6065 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006066 return 0;
6067
Petr Machata010cadf2017-09-02 23:49:18 +02006068err_rif_fdb_op:
6069 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6070 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006071err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02006072 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6073 mlxsw_sp_router_port(mlxsw_sp), false);
6074err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006075 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
6076 return err;
6077}
6078
6079static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
6080{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006081 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02006082 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6083 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006084
Petr Machata010cadf2017-09-02 23:49:18 +02006085 mlxsw_sp_fid_rif_set(fid, NULL);
6086 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
6087 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006088 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6089 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02006090 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6091 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006092 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
6093}
6094
6095static struct mlxsw_sp_fid *
6096mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
6097{
6098 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
6099
6100 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
6101}
6102
6103static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
6104 .type = MLXSW_SP_RIF_TYPE_VLAN,
6105 .rif_size = sizeof(struct mlxsw_sp_rif),
6106 .configure = mlxsw_sp_rif_vlan_configure,
6107 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
6108 .fid_get = mlxsw_sp_rif_vlan_fid_get,
6109};
6110
6111static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
6112{
6113 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6114 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
6115 int err;
6116
6117 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
6118 true);
6119 if (err)
6120 return err;
6121
Ido Schimmel0d284812017-07-18 10:10:12 +02006122 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6123 mlxsw_sp_router_port(mlxsw_sp), true);
6124 if (err)
6125 goto err_fid_mc_flood_set;
6126
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006127 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6128 mlxsw_sp_router_port(mlxsw_sp), true);
6129 if (err)
6130 goto err_fid_bc_flood_set;
6131
Petr Machata010cadf2017-09-02 23:49:18 +02006132 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
6133 mlxsw_sp_fid_index(rif->fid), true);
6134 if (err)
6135 goto err_rif_fdb_op;
6136
6137 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006138 return 0;
6139
Petr Machata010cadf2017-09-02 23:49:18 +02006140err_rif_fdb_op:
6141 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6142 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006143err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02006144 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6145 mlxsw_sp_router_port(mlxsw_sp), false);
6146err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006147 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
6148 return err;
6149}
6150
6151static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
6152{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006153 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02006154 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6155 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006156
Petr Machata010cadf2017-09-02 23:49:18 +02006157 mlxsw_sp_fid_rif_set(fid, NULL);
6158 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
6159 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006160 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
6161 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02006162 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
6163 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006164 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
6165}
6166
6167static struct mlxsw_sp_fid *
6168mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
6169{
6170 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
6171}
6172
6173static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
6174 .type = MLXSW_SP_RIF_TYPE_FID,
6175 .rif_size = sizeof(struct mlxsw_sp_rif),
6176 .configure = mlxsw_sp_rif_fid_configure,
6177 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
6178 .fid_get = mlxsw_sp_rif_fid_fid_get,
6179};
6180
Petr Machata6ddb7422017-09-02 23:49:19 +02006181static struct mlxsw_sp_rif_ipip_lb *
6182mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
6183{
6184 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
6185}
6186
6187static void
6188mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
6189 const struct mlxsw_sp_rif_params *params)
6190{
6191 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
6192 struct mlxsw_sp_rif_ipip_lb *rif_lb;
6193
6194 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
6195 common);
6196 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
6197 rif_lb->lb_config = params_lb->lb_config;
6198}
6199
6200static int
6201mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
6202 struct mlxsw_sp_vr *ul_vr, bool enable)
6203{
6204 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
6205 struct mlxsw_sp_rif *rif = &lb_rif->common;
6206 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6207 char ritr_pl[MLXSW_REG_RITR_LEN];
6208 u32 saddr4;
6209
6210 switch (lb_cf.ul_protocol) {
6211 case MLXSW_SP_L3_PROTO_IPV4:
6212 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
6213 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
6214 rif->rif_index, rif->vr_id, rif->dev->mtu);
6215 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
6216 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
6217 ul_vr->id, saddr4, lb_cf.okey);
6218 break;
6219
6220 case MLXSW_SP_L3_PROTO_IPV6:
6221 return -EAFNOSUPPORT;
6222 }
6223
6224 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
6225}
6226
6227static int
6228mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
6229{
6230 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
6231 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
6232 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6233 struct mlxsw_sp_vr *ul_vr;
6234 int err;
6235
6236 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
6237 if (IS_ERR(ul_vr))
6238 return PTR_ERR(ul_vr);
6239
6240 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
6241 if (err)
6242 goto err_loopback_op;
6243
6244 lb_rif->ul_vr_id = ul_vr->id;
6245 ++ul_vr->rif_count;
6246 return 0;
6247
6248err_loopback_op:
6249 mlxsw_sp_vr_put(ul_vr);
6250 return err;
6251}
6252
6253static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
6254{
6255 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
6256 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
6257 struct mlxsw_sp_vr *ul_vr;
6258
6259 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
6260 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
6261
6262 --ul_vr->rif_count;
6263 mlxsw_sp_vr_put(ul_vr);
6264}
6265
6266static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
6267 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
6268 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
6269 .setup = mlxsw_sp_rif_ipip_lb_setup,
6270 .configure = mlxsw_sp_rif_ipip_lb_configure,
6271 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
6272};
6273
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006274static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
6275 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
6276 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
6277 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02006278 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006279};
6280
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006281static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
6282{
6283 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
6284
6285 mlxsw_sp->router->rifs = kcalloc(max_rifs,
6286 sizeof(struct mlxsw_sp_rif *),
6287 GFP_KERNEL);
6288 if (!mlxsw_sp->router->rifs)
6289 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02006290
6291 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
6292
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006293 return 0;
6294}
6295
6296static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
6297{
6298 int i;
6299
6300 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
6301 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
6302
6303 kfree(mlxsw_sp->router->rifs);
6304}
6305
Petr Machata38ebc0f2017-09-02 23:49:17 +02006306static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
6307{
6308 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02006309 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006310 return 0;
6311}
6312
6313static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
6314{
Petr Machata1012b9a2017-09-02 23:49:23 +02006315 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02006316}
6317
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006318static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
6319{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006320 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006321
6322 /* Flush pending FIB notifications and then flush the device's
6323 * table before requesting another dump. The FIB notification
6324 * block is unregistered, so no need to take RTNL.
6325 */
6326 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02006327 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
6328 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006329}
6330
Ido Schimmel4724ba562017-03-10 08:53:39 +01006331static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
6332{
6333 char rgcr_pl[MLXSW_REG_RGCR_LEN];
6334 u64 max_rifs;
6335 int err;
6336
6337 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
6338 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006339 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006340
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02006341 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006342 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
6343 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
6344 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006345 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006346 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01006347}
6348
6349static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6350{
6351 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01006352
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02006353 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006354 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01006355}
6356
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006357int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
6358{
Ido Schimmel9011b672017-05-16 19:38:25 +02006359 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006360 int err;
6361
Ido Schimmel9011b672017-05-16 19:38:25 +02006362 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
6363 if (!router)
6364 return -ENOMEM;
6365 mlxsw_sp->router = router;
6366 router->mlxsw_sp = mlxsw_sp;
6367
6368 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006369 err = __mlxsw_sp_router_init(mlxsw_sp);
6370 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02006371 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006372
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006373 err = mlxsw_sp_rifs_init(mlxsw_sp);
6374 if (err)
6375 goto err_rifs_init;
6376
Petr Machata38ebc0f2017-09-02 23:49:17 +02006377 err = mlxsw_sp_ipips_init(mlxsw_sp);
6378 if (err)
6379 goto err_ipips_init;
6380
Ido Schimmel9011b672017-05-16 19:38:25 +02006381 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006382 &mlxsw_sp_nexthop_ht_params);
6383 if (err)
6384 goto err_nexthop_ht_init;
6385
Ido Schimmel9011b672017-05-16 19:38:25 +02006386 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006387 &mlxsw_sp_nexthop_group_ht_params);
6388 if (err)
6389 goto err_nexthop_group_ht_init;
6390
Arkadi Sharshevskydbe45982017-09-25 10:32:23 +02006391 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006392 err = mlxsw_sp_lpm_init(mlxsw_sp);
6393 if (err)
6394 goto err_lpm_init;
6395
Yotam Gigid42b0962017-09-27 08:23:20 +02006396 err = mlxsw_sp_mr_init(mlxsw_sp, &mlxsw_sp_mr_tcam_ops);
6397 if (err)
6398 goto err_mr_init;
6399
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006400 err = mlxsw_sp_vrs_init(mlxsw_sp);
6401 if (err)
6402 goto err_vrs_init;
6403
Ido Schimmel8c9583a2016-10-27 15:12:57 +02006404 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006405 if (err)
6406 goto err_neigh_init;
6407
Ido Schimmel7e39d112017-05-16 19:38:28 +02006408 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6409 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006410 mlxsw_sp_router_fib_dump_flush);
6411 if (err)
6412 goto err_register_fib_notifier;
6413
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006414 return 0;
6415
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006416err_register_fib_notifier:
6417 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006418err_neigh_init:
6419 mlxsw_sp_vrs_fini(mlxsw_sp);
6420err_vrs_init:
Yotam Gigid42b0962017-09-27 08:23:20 +02006421 mlxsw_sp_mr_fini(mlxsw_sp);
6422err_mr_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01006423 mlxsw_sp_lpm_fini(mlxsw_sp);
6424err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006425 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006426err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006427 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006428err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02006429 mlxsw_sp_ipips_fini(mlxsw_sp);
6430err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006431 mlxsw_sp_rifs_fini(mlxsw_sp);
6432err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006433 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006434err_router_init:
6435 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006436 return err;
6437}
6438
6439void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6440{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006441 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006442 mlxsw_sp_neigh_fini(mlxsw_sp);
6443 mlxsw_sp_vrs_fini(mlxsw_sp);
Yotam Gigid42b0962017-09-27 08:23:20 +02006444 mlxsw_sp_mr_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006445 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006446 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6447 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006448 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006449 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006450 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006451 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006452}