blob: 0b989e16a4243fde12718b8b92e577de407b8ef9 [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * 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>
Ido Schimmel464dce12016-07-02 11:00:15 +02007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020039#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020042#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010043#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010044#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020045#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020046#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020047#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020048#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020049#include <net/neighbour.h>
50#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020051#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020052#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010053#include <net/fib_rules.h>
Ido Schimmel57837882017-03-16 09:08:16 +010054#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020055#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020056#include <net/ndisc.h>
57#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020058#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020059
60#include "spectrum.h"
61#include "core.h"
62#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020063#include "spectrum_cnt.h"
64#include "spectrum_dpipe.h"
65#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020066
Ido Schimmel9011b672017-05-16 19:38:25 +020067struct mlxsw_sp_vr;
68struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020069struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020070
71struct mlxsw_sp_router {
72 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020073 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020074 struct mlxsw_sp_vr *vrs;
75 struct rhashtable neigh_ht;
76 struct rhashtable nexthop_group_ht;
77 struct rhashtable nexthop_ht;
78 struct {
79 struct mlxsw_sp_lpm_tree *trees;
80 unsigned int tree_count;
81 } lpm;
82 struct {
83 struct delayed_work dw;
84 unsigned long interval; /* ms */
85 } neighs_update;
86 struct delayed_work nexthop_probe_dw;
87#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
88 struct list_head nexthop_neighs_list;
89 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020090 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020091 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020092};
93
Ido Schimmel4724ba562017-03-10 08:53:39 +010094struct mlxsw_sp_rif {
95 struct list_head nexthop_list;
96 struct list_head neigh_list;
97 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +020098 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +010099 unsigned char addr[ETH_ALEN];
100 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100101 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100102 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200103 const struct mlxsw_sp_rif_ops *ops;
104 struct mlxsw_sp *mlxsw_sp;
105
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200106 unsigned int counter_ingress;
107 bool counter_ingress_valid;
108 unsigned int counter_egress;
109 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100110};
111
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200112struct mlxsw_sp_rif_params {
113 struct net_device *dev;
114 union {
115 u16 system_port;
116 u16 lag_id;
117 };
118 u16 vid;
119 bool lag;
120};
121
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200122struct mlxsw_sp_rif_subport {
123 struct mlxsw_sp_rif common;
124 union {
125 u16 system_port;
126 u16 lag_id;
127 };
128 u16 vid;
129 bool lag;
130};
131
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200132struct mlxsw_sp_rif_ops {
133 enum mlxsw_sp_rif_type type;
134 size_t rif_size;
135
136 void (*setup)(struct mlxsw_sp_rif *rif,
137 const struct mlxsw_sp_rif_params *params);
138 int (*configure)(struct mlxsw_sp_rif *rif);
139 void (*deconfigure)(struct mlxsw_sp_rif *rif);
140 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
141};
142
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200143static unsigned int *
144mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
145 enum mlxsw_sp_rif_counter_dir dir)
146{
147 switch (dir) {
148 case MLXSW_SP_RIF_COUNTER_EGRESS:
149 return &rif->counter_egress;
150 case MLXSW_SP_RIF_COUNTER_INGRESS:
151 return &rif->counter_ingress;
152 }
153 return NULL;
154}
155
156static bool
157mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
158 enum mlxsw_sp_rif_counter_dir dir)
159{
160 switch (dir) {
161 case MLXSW_SP_RIF_COUNTER_EGRESS:
162 return rif->counter_egress_valid;
163 case MLXSW_SP_RIF_COUNTER_INGRESS:
164 return rif->counter_ingress_valid;
165 }
166 return false;
167}
168
169static void
170mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
171 enum mlxsw_sp_rif_counter_dir dir,
172 bool valid)
173{
174 switch (dir) {
175 case MLXSW_SP_RIF_COUNTER_EGRESS:
176 rif->counter_egress_valid = valid;
177 break;
178 case MLXSW_SP_RIF_COUNTER_INGRESS:
179 rif->counter_ingress_valid = valid;
180 break;
181 }
182}
183
184static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
185 unsigned int counter_index, bool enable,
186 enum mlxsw_sp_rif_counter_dir dir)
187{
188 char ritr_pl[MLXSW_REG_RITR_LEN];
189 bool is_egress = false;
190 int err;
191
192 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
193 is_egress = true;
194 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
195 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
196 if (err)
197 return err;
198
199 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
200 is_egress);
201 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
202}
203
204int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
205 struct mlxsw_sp_rif *rif,
206 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
207{
208 char ricnt_pl[MLXSW_REG_RICNT_LEN];
209 unsigned int *p_counter_index;
210 bool valid;
211 int err;
212
213 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
214 if (!valid)
215 return -EINVAL;
216
217 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
218 if (!p_counter_index)
219 return -EINVAL;
220 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
221 MLXSW_REG_RICNT_OPCODE_NOP);
222 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
223 if (err)
224 return err;
225 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
226 return 0;
227}
228
229static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
230 unsigned int counter_index)
231{
232 char ricnt_pl[MLXSW_REG_RICNT_LEN];
233
234 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
235 MLXSW_REG_RICNT_OPCODE_CLEAR);
236 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
237}
238
239int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
240 struct mlxsw_sp_rif *rif,
241 enum mlxsw_sp_rif_counter_dir dir)
242{
243 unsigned int *p_counter_index;
244 int err;
245
246 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
247 if (!p_counter_index)
248 return -EINVAL;
249 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
250 p_counter_index);
251 if (err)
252 return err;
253
254 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
255 if (err)
256 goto err_counter_clear;
257
258 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
259 *p_counter_index, true, dir);
260 if (err)
261 goto err_counter_edit;
262 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
263 return 0;
264
265err_counter_edit:
266err_counter_clear:
267 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
268 *p_counter_index);
269 return err;
270}
271
272void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
273 struct mlxsw_sp_rif *rif,
274 enum mlxsw_sp_rif_counter_dir dir)
275{
276 unsigned int *p_counter_index;
277
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200278 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
279 return;
280
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200281 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
282 if (WARN_ON(!p_counter_index))
283 return;
284 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
285 *p_counter_index, false, dir);
286 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
287 *p_counter_index);
288 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
289}
290
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200291static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
292{
293 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
294 struct devlink *devlink;
295
296 devlink = priv_to_devlink(mlxsw_sp->core);
297 if (!devlink_dpipe_table_counter_enabled(devlink,
298 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
299 return;
300 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
301}
302
303static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
304{
305 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
306
307 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
308}
309
Ido Schimmel4724ba562017-03-10 08:53:39 +0100310static struct mlxsw_sp_rif *
311mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
312 const struct net_device *dev);
313
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200314#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200315
316struct mlxsw_sp_prefix_usage {
317 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
318};
319
Jiri Pirko53342022016-07-04 08:23:08 +0200320#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
321 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
322
323static bool
324mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
325 struct mlxsw_sp_prefix_usage *prefix_usage2)
326{
327 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
328}
329
Jiri Pirko6b75c482016-07-04 08:23:09 +0200330static bool
331mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
332{
333 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
334
335 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
336}
337
338static void
339mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
340 struct mlxsw_sp_prefix_usage *prefix_usage2)
341{
342 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
343}
344
345static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200346mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
347 unsigned char prefix_len)
348{
349 set_bit(prefix_len, prefix_usage->b);
350}
351
352static void
353mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
354 unsigned char prefix_len)
355{
356 clear_bit(prefix_len, prefix_usage->b);
357}
358
359struct mlxsw_sp_fib_key {
360 unsigned char addr[sizeof(struct in6_addr)];
361 unsigned char prefix_len;
362};
363
Jiri Pirko61c503f2016-07-04 08:23:11 +0200364enum mlxsw_sp_fib_entry_type {
365 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
366 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
367 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
368};
369
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200370struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200371struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200372
Ido Schimmel9aecce12017-02-09 10:28:42 +0100373struct mlxsw_sp_fib_node {
374 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200375 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100376 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100377 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100378 struct mlxsw_sp_fib_key key;
379};
380
Ido Schimmel9aecce12017-02-09 10:28:42 +0100381struct mlxsw_sp_fib_entry {
382 struct list_head list;
383 struct mlxsw_sp_fib_node *fib_node;
384 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200385 struct list_head nexthop_group_node;
386 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200387};
388
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200389struct mlxsw_sp_fib4_entry {
390 struct mlxsw_sp_fib_entry common;
391 u32 tb_id;
392 u32 prio;
393 u8 tos;
394 u8 type;
395};
396
Ido Schimmel428b8512017-08-03 13:28:28 +0200397struct mlxsw_sp_fib6_entry {
398 struct mlxsw_sp_fib_entry common;
399 struct list_head rt6_list;
400 unsigned int nrt6;
401};
402
403struct mlxsw_sp_rt6 {
404 struct list_head list;
405 struct rt6_info *rt;
406};
407
Ido Schimmel9011b672017-05-16 19:38:25 +0200408enum mlxsw_sp_l3proto {
409 MLXSW_SP_L3_PROTO_IPV4,
410 MLXSW_SP_L3_PROTO_IPV6,
411};
412
413struct mlxsw_sp_lpm_tree {
414 u8 id; /* tree ID */
415 unsigned int ref_count;
416 enum mlxsw_sp_l3proto proto;
417 struct mlxsw_sp_prefix_usage prefix_usage;
418};
419
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200420struct mlxsw_sp_fib {
421 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100422 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100423 struct mlxsw_sp_vr *vr;
424 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200425 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
426 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100427 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200428};
429
Ido Schimmel9011b672017-05-16 19:38:25 +0200430struct mlxsw_sp_vr {
431 u16 id; /* virtual router ID */
432 u32 tb_id; /* kernel fib table id */
433 unsigned int rif_count;
434 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200435 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200436};
437
Ido Schimmel9aecce12017-02-09 10:28:42 +0100438static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200439
Ido Schimmel76610eb2017-03-10 08:53:41 +0100440static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
441 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200442{
443 struct mlxsw_sp_fib *fib;
444 int err;
445
446 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
447 if (!fib)
448 return ERR_PTR(-ENOMEM);
449 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
450 if (err)
451 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100452 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100453 fib->proto = proto;
454 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200455 return fib;
456
457err_rhashtable_init:
458 kfree(fib);
459 return ERR_PTR(err);
460}
461
462static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
463{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100464 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100465 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200466 rhashtable_destroy(&fib->ht);
467 kfree(fib);
468}
469
Jiri Pirko53342022016-07-04 08:23:08 +0200470static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100471mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200472{
473 static struct mlxsw_sp_lpm_tree *lpm_tree;
474 int i;
475
Ido Schimmel9011b672017-05-16 19:38:25 +0200476 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
477 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100478 if (lpm_tree->ref_count == 0)
479 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200480 }
481 return NULL;
482}
483
484static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
485 struct mlxsw_sp_lpm_tree *lpm_tree)
486{
487 char ralta_pl[MLXSW_REG_RALTA_LEN];
488
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200489 mlxsw_reg_ralta_pack(ralta_pl, true,
490 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
491 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200492 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
493}
494
Ido Schimmelcc702672017-08-14 10:54:03 +0200495static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
496 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200497{
498 char ralta_pl[MLXSW_REG_RALTA_LEN];
499
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200500 mlxsw_reg_ralta_pack(ralta_pl, false,
501 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
502 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200503 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200504}
505
506static int
507mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
508 struct mlxsw_sp_prefix_usage *prefix_usage,
509 struct mlxsw_sp_lpm_tree *lpm_tree)
510{
511 char ralst_pl[MLXSW_REG_RALST_LEN];
512 u8 root_bin = 0;
513 u8 prefix;
514 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
515
516 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
517 root_bin = prefix;
518
519 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
520 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
521 if (prefix == 0)
522 continue;
523 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
524 MLXSW_REG_RALST_BIN_NO_CHILD);
525 last_prefix = prefix;
526 }
527 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
528}
529
530static struct mlxsw_sp_lpm_tree *
531mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
532 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100533 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200534{
535 struct mlxsw_sp_lpm_tree *lpm_tree;
536 int err;
537
Ido Schimmel382dbb42017-03-10 08:53:40 +0100538 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200539 if (!lpm_tree)
540 return ERR_PTR(-EBUSY);
541 lpm_tree->proto = proto;
542 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
543 if (err)
544 return ERR_PTR(err);
545
546 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
547 lpm_tree);
548 if (err)
549 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200550 memcpy(&lpm_tree->prefix_usage, prefix_usage,
551 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200552 return lpm_tree;
553
554err_left_struct_set:
555 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
556 return ERR_PTR(err);
557}
558
Ido Schimmelcc702672017-08-14 10:54:03 +0200559static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
560 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200561{
Ido Schimmelcc702672017-08-14 10:54:03 +0200562 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200563}
564
565static struct mlxsw_sp_lpm_tree *
566mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
567 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100568 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200569{
570 struct mlxsw_sp_lpm_tree *lpm_tree;
571 int i;
572
Ido Schimmel9011b672017-05-16 19:38:25 +0200573 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
574 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200575 if (lpm_tree->ref_count != 0 &&
576 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200577 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
578 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200579 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200580 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200581 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
582}
Jiri Pirko53342022016-07-04 08:23:08 +0200583
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200584static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
585{
Jiri Pirko53342022016-07-04 08:23:08 +0200586 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200587}
588
Ido Schimmelcc702672017-08-14 10:54:03 +0200589static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
590 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200591{
592 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200593 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200594}
595
Ido Schimmeld7a60302017-06-08 08:47:43 +0200596#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100597
598static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200599{
600 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100601 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200602 int i;
603
Ido Schimmel8494ab02017-03-24 08:02:47 +0100604 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
605 return -EIO;
606
607 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200608 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
609 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100610 sizeof(struct mlxsw_sp_lpm_tree),
611 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200612 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100613 return -ENOMEM;
614
Ido Schimmel9011b672017-05-16 19:38:25 +0200615 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
616 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200617 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
618 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100619
620 return 0;
621}
622
623static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
624{
Ido Schimmel9011b672017-05-16 19:38:25 +0200625 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200626}
627
Ido Schimmel76610eb2017-03-10 08:53:41 +0100628static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
629{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200630 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100631}
632
Jiri Pirko6b75c482016-07-04 08:23:09 +0200633static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
634{
635 struct mlxsw_sp_vr *vr;
636 int i;
637
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200638 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200639 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100640 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200641 return vr;
642 }
643 return NULL;
644}
645
646static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200647 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200648{
649 char raltb_pl[MLXSW_REG_RALTB_LEN];
650
Ido Schimmel76610eb2017-03-10 08:53:41 +0100651 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
652 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200653 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200654 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
655}
656
657static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100658 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200659{
660 char raltb_pl[MLXSW_REG_RALTB_LEN];
661
662 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100663 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
664 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200665 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
666}
667
668static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
669{
670 /* For our purpose, squash main and local table into one */
671 if (tb_id == RT_TABLE_LOCAL)
672 tb_id = RT_TABLE_MAIN;
673 return tb_id;
674}
675
676static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100677 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200678{
679 struct mlxsw_sp_vr *vr;
680 int i;
681
682 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200683
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200684 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200685 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100686 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200687 return vr;
688 }
689 return NULL;
690}
691
Ido Schimmel76610eb2017-03-10 08:53:41 +0100692static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
693 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200694{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100695 switch (proto) {
696 case MLXSW_SP_L3_PROTO_IPV4:
697 return vr->fib4;
698 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200699 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100700 }
701 return NULL;
702}
703
704static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
705 u32 tb_id)
706{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200707 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200708 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200709
710 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
711 if (!vr)
712 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100713 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
714 if (IS_ERR(vr->fib4))
715 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200716 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
717 if (IS_ERR(vr->fib6)) {
718 err = PTR_ERR(vr->fib6);
719 goto err_fib6_create;
720 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200721 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200722 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200723
724err_fib6_create:
725 mlxsw_sp_fib_destroy(vr->fib4);
726 vr->fib4 = NULL;
727 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200728}
729
Ido Schimmel76610eb2017-03-10 08:53:41 +0100730static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200731{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200732 mlxsw_sp_fib_destroy(vr->fib6);
733 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100734 mlxsw_sp_fib_destroy(vr->fib4);
735 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200736}
737
Ido Schimmel76610eb2017-03-10 08:53:41 +0100738static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200739{
740 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200741
742 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100743 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
744 if (!vr)
745 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746 return vr;
747}
748
Ido Schimmel76610eb2017-03-10 08:53:41 +0100749static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200750{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200751 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
752 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100753 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200754}
755
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200756static bool
757mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
758 enum mlxsw_sp_l3proto proto, u8 tree_id)
759{
760 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
761
762 if (!mlxsw_sp_vr_is_used(vr))
763 return false;
764 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
765 return true;
766 return false;
767}
768
769static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
770 struct mlxsw_sp_fib *fib,
771 struct mlxsw_sp_lpm_tree *new_tree)
772{
773 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
774 int err;
775
776 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
777 if (err)
778 return err;
779 fib->lpm_tree = new_tree;
780 mlxsw_sp_lpm_tree_hold(new_tree);
781 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
782 return 0;
783}
784
785static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
786 struct mlxsw_sp_fib *fib,
787 struct mlxsw_sp_lpm_tree *new_tree)
788{
789 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
790 enum mlxsw_sp_l3proto proto = fib->proto;
791 u8 old_id, new_id = new_tree->id;
792 struct mlxsw_sp_vr *vr;
793 int i, err;
794
795 if (!old_tree)
796 goto no_replace;
797 old_id = old_tree->id;
798
799 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
800 vr = &mlxsw_sp->router->vrs[i];
801 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
802 continue;
803 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
804 mlxsw_sp_vr_fib(vr, proto),
805 new_tree);
806 if (err)
807 goto err_tree_replace;
808 }
809
810 return 0;
811
812err_tree_replace:
813 for (i--; i >= 0; i--) {
814 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
815 continue;
816 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
817 mlxsw_sp_vr_fib(vr, proto),
818 old_tree);
819 }
820 return err;
821
822no_replace:
823 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
824 if (err)
825 return err;
826 fib->lpm_tree = new_tree;
827 mlxsw_sp_lpm_tree_hold(new_tree);
828 return 0;
829}
830
831static void
832mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
833 enum mlxsw_sp_l3proto proto,
834 struct mlxsw_sp_prefix_usage *req_prefix_usage)
835{
836 int i;
837
838 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
839 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
840 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
841 unsigned char prefix;
842
843 if (!mlxsw_sp_vr_is_used(vr))
844 continue;
845 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
846 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
847 }
848}
849
Nogah Frankel9497c042016-09-20 11:16:54 +0200850static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200851{
852 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200853 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200854 int i;
855
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200856 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200857 return -EIO;
858
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200859 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200860 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
861 GFP_KERNEL);
862 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200863 return -ENOMEM;
864
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200865 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200866 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200867 vr->id = i;
868 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200869
870 return 0;
871}
872
Ido Schimmelac571de2016-11-14 11:26:32 +0100873static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
874
Nogah Frankel9497c042016-09-20 11:16:54 +0200875static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
876{
Ido Schimmel30572242016-12-03 16:45:01 +0100877 /* At this stage we're guaranteed not to have new incoming
878 * FIB notifications and the work queue is free from FIBs
879 * sitting on top of mlxsw netdevs. However, we can still
880 * have other FIBs queued. Flush the queue before flushing
881 * the device's tables. No need for locks, as we're the only
882 * writer.
883 */
884 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100885 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200886 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200887}
888
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200889struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100890 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200891};
892
893struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100894 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200895 struct rhash_head ht_node;
896 struct mlxsw_sp_neigh_key key;
897 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100898 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200899 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200900 struct list_head nexthop_list; /* list of nexthops using
901 * this neigh entry
902 */
Yotam Gigib2157142016-07-05 11:27:51 +0200903 struct list_head nexthop_neighs_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200904};
905
906static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
907 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
908 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
909 .key_len = sizeof(struct mlxsw_sp_neigh_key),
910};
911
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +0200912struct mlxsw_sp_neigh_entry *
913mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
914 struct mlxsw_sp_neigh_entry *neigh_entry)
915{
916 if (!neigh_entry) {
917 if (list_empty(&rif->neigh_list))
918 return NULL;
919 else
920 return list_first_entry(&rif->neigh_list,
921 typeof(*neigh_entry),
922 rif_list_node);
923 }
924 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
925 return NULL;
926 return list_next_entry(neigh_entry, rif_list_node);
927}
928
929int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
930{
931 return neigh_entry->key.n->tbl->family;
932}
933
934unsigned char *
935mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
936{
937 return neigh_entry->ha;
938}
939
940u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
941{
942 struct neighbour *n;
943
944 n = neigh_entry->key.n;
945 return ntohl(*((__be32 *) n->primary_key));
946}
947
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100948static struct mlxsw_sp_neigh_entry *
949mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
950 u16 rif)
951{
952 struct mlxsw_sp_neigh_entry *neigh_entry;
953
954 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
955 if (!neigh_entry)
956 return NULL;
957
958 neigh_entry->key.n = n;
959 neigh_entry->rif = rif;
960 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
961
962 return neigh_entry;
963}
964
965static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
966{
967 kfree(neigh_entry);
968}
969
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200970static int
971mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
972 struct mlxsw_sp_neigh_entry *neigh_entry)
973{
Ido Schimmel9011b672017-05-16 19:38:25 +0200974 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200975 &neigh_entry->ht_node,
976 mlxsw_sp_neigh_ht_params);
977}
978
979static void
980mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
981 struct mlxsw_sp_neigh_entry *neigh_entry)
982{
Ido Schimmel9011b672017-05-16 19:38:25 +0200983 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200984 &neigh_entry->ht_node,
985 mlxsw_sp_neigh_ht_params);
986}
987
988static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100989mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200990{
991 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100992 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100993 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200994
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100995 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
996 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100997 return ERR_PTR(-EINVAL);
998
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100999 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001000 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001001 return ERR_PTR(-ENOMEM);
1002
1003 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1004 if (err)
1005 goto err_neigh_entry_insert;
1006
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001007 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001008
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001009 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001010
1011err_neigh_entry_insert:
1012 mlxsw_sp_neigh_entry_free(neigh_entry);
1013 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001014}
1015
1016static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001017mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1018 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001019{
Ido Schimmel9665b742017-02-08 11:16:42 +01001020 list_del(&neigh_entry->rif_list_node);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001021 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1022 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001023}
1024
1025static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001026mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001027{
Jiri Pirko33b13412016-11-10 12:31:04 +01001028 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001029
Jiri Pirko33b13412016-11-10 12:31:04 +01001030 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001031 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001032 &key, mlxsw_sp_neigh_ht_params);
1033}
1034
Yotam Gigic723c7352016-07-05 11:27:43 +02001035static void
1036mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1037{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001038 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001039
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001040#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001041 interval = min_t(unsigned long,
1042 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1043 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001044#else
1045 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1046#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001047 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001048}
1049
1050static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1051 char *rauhtd_pl,
1052 int ent_index)
1053{
1054 struct net_device *dev;
1055 struct neighbour *n;
1056 __be32 dipn;
1057 u32 dip;
1058 u16 rif;
1059
1060 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1061
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001062 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001063 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1064 return;
1065 }
1066
1067 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001068 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001069 n = neigh_lookup(&arp_tbl, &dipn, dev);
1070 if (!n) {
1071 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1072 &dip);
1073 return;
1074 }
1075
1076 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1077 neigh_event_send(n, NULL);
1078 neigh_release(n);
1079}
1080
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001081#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001082static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1083 char *rauhtd_pl,
1084 int rec_index)
1085{
1086 struct net_device *dev;
1087 struct neighbour *n;
1088 struct in6_addr dip;
1089 u16 rif;
1090
1091 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1092 (char *) &dip);
1093
1094 if (!mlxsw_sp->router->rifs[rif]) {
1095 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1096 return;
1097 }
1098
1099 dev = mlxsw_sp->router->rifs[rif]->dev;
1100 n = neigh_lookup(&nd_tbl, &dip, dev);
1101 if (!n) {
1102 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1103 &dip);
1104 return;
1105 }
1106
1107 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1108 neigh_event_send(n, NULL);
1109 neigh_release(n);
1110}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001111#else
1112static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1113 char *rauhtd_pl,
1114 int rec_index)
1115{
1116}
1117#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001118
Yotam Gigic723c7352016-07-05 11:27:43 +02001119static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1120 char *rauhtd_pl,
1121 int rec_index)
1122{
1123 u8 num_entries;
1124 int i;
1125
1126 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1127 rec_index);
1128 /* Hardware starts counting at 0, so add 1. */
1129 num_entries++;
1130
1131 /* Each record consists of several neighbour entries. */
1132 for (i = 0; i < num_entries; i++) {
1133 int ent_index;
1134
1135 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1136 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1137 ent_index);
1138 }
1139
1140}
1141
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001142static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1143 char *rauhtd_pl,
1144 int rec_index)
1145{
1146 /* One record contains one entry. */
1147 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1148 rec_index);
1149}
1150
Yotam Gigic723c7352016-07-05 11:27:43 +02001151static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1152 char *rauhtd_pl, int rec_index)
1153{
1154 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1155 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1156 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1157 rec_index);
1158 break;
1159 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001160 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1161 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001162 break;
1163 }
1164}
1165
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001166static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1167{
1168 u8 num_rec, last_rec_index, num_entries;
1169
1170 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1171 last_rec_index = num_rec - 1;
1172
1173 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1174 return false;
1175 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1176 MLXSW_REG_RAUHTD_TYPE_IPV6)
1177 return true;
1178
1179 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1180 last_rec_index);
1181 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1182 return true;
1183 return false;
1184}
1185
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001186static int
1187__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1188 char *rauhtd_pl,
1189 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001190{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001191 int i, num_rec;
1192 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001193
1194 /* Make sure the neighbour's netdev isn't removed in the
1195 * process.
1196 */
1197 rtnl_lock();
1198 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001199 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001200 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1201 rauhtd_pl);
1202 if (err) {
1203 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1204 break;
1205 }
1206 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1207 for (i = 0; i < num_rec; i++)
1208 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1209 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001210 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001211 rtnl_unlock();
1212
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001213 return err;
1214}
1215
1216static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1217{
1218 enum mlxsw_reg_rauhtd_type type;
1219 char *rauhtd_pl;
1220 int err;
1221
1222 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1223 if (!rauhtd_pl)
1224 return -ENOMEM;
1225
1226 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1227 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1228 if (err)
1229 goto out;
1230
1231 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1232 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1233out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001234 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001235 return err;
1236}
1237
1238static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1239{
1240 struct mlxsw_sp_neigh_entry *neigh_entry;
1241
1242 /* Take RTNL mutex here to prevent lists from changes */
1243 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001244 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001245 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001246 /* If this neigh have nexthops, make the kernel think this neigh
1247 * is active regardless of the traffic.
1248 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001249 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001250 rtnl_unlock();
1251}
1252
1253static void
1254mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1255{
Ido Schimmel9011b672017-05-16 19:38:25 +02001256 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001257
Ido Schimmel9011b672017-05-16 19:38:25 +02001258 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001259 msecs_to_jiffies(interval));
1260}
1261
1262static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1263{
Ido Schimmel9011b672017-05-16 19:38:25 +02001264 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001265 int err;
1266
Ido Schimmel9011b672017-05-16 19:38:25 +02001267 router = container_of(work, struct mlxsw_sp_router,
1268 neighs_update.dw.work);
1269 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001270 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001271 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001272
Ido Schimmel9011b672017-05-16 19:38:25 +02001273 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001274
Ido Schimmel9011b672017-05-16 19:38:25 +02001275 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001276}
1277
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001278static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1279{
1280 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001281 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001282
Ido Schimmel9011b672017-05-16 19:38:25 +02001283 router = container_of(work, struct mlxsw_sp_router,
1284 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001285 /* Iterate over nexthop neighbours, find those who are unresolved and
1286 * send arp on them. This solves the chicken-egg problem when
1287 * the nexthop wouldn't get offloaded until the neighbor is resolved
1288 * but it wouldn't get resolved ever in case traffic is flowing in HW
1289 * using different nexthop.
1290 *
1291 * Take RTNL mutex here to prevent lists from changes.
1292 */
1293 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001294 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001295 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001296 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001297 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001298 rtnl_unlock();
1299
Ido Schimmel9011b672017-05-16 19:38:25 +02001300 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001301 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1302}
1303
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001304static void
1305mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1306 struct mlxsw_sp_neigh_entry *neigh_entry,
1307 bool removing);
1308
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001309static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001310{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001311 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1312 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1313}
1314
1315static void
1316mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1317 struct mlxsw_sp_neigh_entry *neigh_entry,
1318 enum mlxsw_reg_rauht_op op)
1319{
Jiri Pirko33b13412016-11-10 12:31:04 +01001320 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001321 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001322 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001323
1324 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1325 dip);
1326 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1327}
1328
1329static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001330mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1331 struct mlxsw_sp_neigh_entry *neigh_entry,
1332 enum mlxsw_reg_rauht_op op)
1333{
1334 struct neighbour *n = neigh_entry->key.n;
1335 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1336 const char *dip = n->primary_key;
1337
1338 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1339 dip);
1340 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1341}
1342
1343static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1344{
1345 /* Packets with a link-local destination address are trapped
1346 * after LPM lookup and never reach the neighbour table, so
1347 * there is no need to program such neighbours to the device.
1348 */
1349 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1350 IPV6_ADDR_LINKLOCAL)
1351 return true;
1352 return false;
1353}
1354
1355static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001356mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1357 struct mlxsw_sp_neigh_entry *neigh_entry,
1358 bool adding)
1359{
1360 if (!adding && !neigh_entry->connected)
1361 return;
1362 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001363 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001364 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1365 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001366 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001367 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1368 return;
1369 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1370 mlxsw_sp_rauht_op(adding));
1371 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001372 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001373 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001374}
1375
1376struct mlxsw_sp_neigh_event_work {
1377 struct work_struct work;
1378 struct mlxsw_sp *mlxsw_sp;
1379 struct neighbour *n;
1380};
1381
1382static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1383{
1384 struct mlxsw_sp_neigh_event_work *neigh_work =
1385 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1386 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1387 struct mlxsw_sp_neigh_entry *neigh_entry;
1388 struct neighbour *n = neigh_work->n;
1389 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001390 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001391 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001392
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001393 /* If these parameters are changed after we release the lock,
1394 * then we are guaranteed to receive another event letting us
1395 * know about it.
1396 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001397 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001398 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001399 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001400 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001401 read_unlock_bh(&n->lock);
1402
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001403 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001404 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001405 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1406 if (!entry_connected && !neigh_entry)
1407 goto out;
1408 if (!neigh_entry) {
1409 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1410 if (IS_ERR(neigh_entry))
1411 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001412 }
1413
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001414 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1415 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1416 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1417
1418 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1419 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1420
1421out:
1422 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001423 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001424 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001425}
1426
Jiri Pirkoe7322632016-09-01 10:37:43 +02001427int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1428 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001429{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001430 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001431 struct mlxsw_sp_port *mlxsw_sp_port;
1432 struct mlxsw_sp *mlxsw_sp;
1433 unsigned long interval;
1434 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001435 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001436
1437 switch (event) {
1438 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1439 p = ptr;
1440
1441 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001442 if (!p->dev || (p->tbl->family != AF_INET &&
1443 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001444 return NOTIFY_DONE;
1445
1446 /* We are in atomic context and can't take RTNL mutex,
1447 * so use RCU variant to walk the device chain.
1448 */
1449 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1450 if (!mlxsw_sp_port)
1451 return NOTIFY_DONE;
1452
1453 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1454 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001455 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001456
1457 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1458 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001459 case NETEVENT_NEIGH_UPDATE:
1460 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001461
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001462 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001463 return NOTIFY_DONE;
1464
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001465 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001466 if (!mlxsw_sp_port)
1467 return NOTIFY_DONE;
1468
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001469 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1470 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001471 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001472 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001473 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001474
1475 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1476 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1477 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001478
1479 /* Take a reference to ensure the neighbour won't be
1480 * destructed until we drop the reference in delayed
1481 * work.
1482 */
1483 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001484 mlxsw_core_schedule_work(&neigh_work->work);
1485 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001486 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001487 }
1488
1489 return NOTIFY_DONE;
1490}
1491
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001492static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1493{
Yotam Gigic723c7352016-07-05 11:27:43 +02001494 int err;
1495
Ido Schimmel9011b672017-05-16 19:38:25 +02001496 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001497 &mlxsw_sp_neigh_ht_params);
1498 if (err)
1499 return err;
1500
1501 /* Initialize the polling interval according to the default
1502 * table.
1503 */
1504 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1505
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001506 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001507 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001508 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001509 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001510 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001511 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1512 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001513 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001514}
1515
1516static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1517{
Ido Schimmel9011b672017-05-16 19:38:25 +02001518 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1519 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1520 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001521}
1522
Ido Schimmel9665b742017-02-08 11:16:42 +01001523static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001524 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001525{
1526 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1527
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001528 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001529 rif_list_node) {
1530 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001531 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001532 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001533}
1534
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001535struct mlxsw_sp_nexthop_key {
1536 struct fib_nh *fib_nh;
1537};
1538
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001539struct mlxsw_sp_nexthop {
1540 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001541 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001542 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1543 * this belongs to
1544 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001545 struct rhash_head ht_node;
1546 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001547 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001548 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001549 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001550 u8 should_offload:1, /* set indicates this neigh is connected and
1551 * should be put to KVD linear area of this group.
1552 */
1553 offloaded:1, /* set in case the neigh is actually put into
1554 * KVD linear area of this group.
1555 */
1556 update:1; /* set indicates that MAC of this neigh should be
1557 * updated in HW
1558 */
1559 struct mlxsw_sp_neigh_entry *neigh_entry;
1560};
1561
1562struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001563 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001564 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001565 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001566 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001567 u8 adj_index_valid:1,
1568 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001569 u32 adj_index;
1570 u16 ecmp_size;
1571 u16 count;
1572 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001573#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001574};
1575
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001576static struct fib_info *
1577mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1578{
1579 return nh_grp->priv;
1580}
1581
1582struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001583 enum mlxsw_sp_l3proto proto;
1584 union {
1585 struct fib_info *fi;
1586 struct mlxsw_sp_fib6_entry *fib6_entry;
1587 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001588};
1589
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001590static bool
1591mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
1592 const struct in6_addr *gw, int ifindex)
1593{
1594 int i;
1595
1596 for (i = 0; i < nh_grp->count; i++) {
1597 const struct mlxsw_sp_nexthop *nh;
1598
1599 nh = &nh_grp->nexthops[i];
1600 if (nh->ifindex == ifindex &&
1601 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
1602 return true;
1603 }
1604
1605 return false;
1606}
1607
1608static bool
1609mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
1610 const struct mlxsw_sp_fib6_entry *fib6_entry)
1611{
1612 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1613
1614 if (nh_grp->count != fib6_entry->nrt6)
1615 return false;
1616
1617 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1618 struct in6_addr *gw;
1619 int ifindex;
1620
1621 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
1622 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
1623 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
1624 return false;
1625 }
1626
1627 return true;
1628}
1629
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001630static int
1631mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1632{
1633 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1634 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1635
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001636 switch (cmp_arg->proto) {
1637 case MLXSW_SP_L3_PROTO_IPV4:
1638 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1639 case MLXSW_SP_L3_PROTO_IPV6:
1640 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
1641 cmp_arg->fib6_entry);
1642 default:
1643 WARN_ON(1);
1644 return 1;
1645 }
1646}
1647
1648static int
1649mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
1650{
1651 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001652}
1653
1654static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1655{
1656 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001657 const struct mlxsw_sp_nexthop *nh;
1658 struct fib_info *fi;
1659 unsigned int val;
1660 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001661
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001662 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
1663 case AF_INET:
1664 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1665 return jhash(&fi, sizeof(fi), seed);
1666 case AF_INET6:
1667 val = nh_grp->count;
1668 for (i = 0; i < nh_grp->count; i++) {
1669 nh = &nh_grp->nexthops[i];
1670 val ^= nh->ifindex;
1671 }
1672 return jhash(&val, sizeof(val), seed);
1673 default:
1674 WARN_ON(1);
1675 return 0;
1676 }
1677}
1678
1679static u32
1680mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
1681{
1682 unsigned int val = fib6_entry->nrt6;
1683 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1684 struct net_device *dev;
1685
1686 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1687 dev = mlxsw_sp_rt6->rt->dst.dev;
1688 val ^= dev->ifindex;
1689 }
1690
1691 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001692}
1693
1694static u32
1695mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1696{
1697 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1698
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001699 switch (cmp_arg->proto) {
1700 case MLXSW_SP_L3_PROTO_IPV4:
1701 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1702 case MLXSW_SP_L3_PROTO_IPV6:
1703 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
1704 default:
1705 WARN_ON(1);
1706 return 0;
1707 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001708}
1709
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001710static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001711 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001712 .hashfn = mlxsw_sp_nexthop_group_hash,
1713 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1714 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001715};
1716
1717static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1718 struct mlxsw_sp_nexthop_group *nh_grp)
1719{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001720 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1721 !nh_grp->gateway)
1722 return 0;
1723
Ido Schimmel9011b672017-05-16 19:38:25 +02001724 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001725 &nh_grp->ht_node,
1726 mlxsw_sp_nexthop_group_ht_params);
1727}
1728
1729static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1730 struct mlxsw_sp_nexthop_group *nh_grp)
1731{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001732 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1733 !nh_grp->gateway)
1734 return;
1735
Ido Schimmel9011b672017-05-16 19:38:25 +02001736 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001737 &nh_grp->ht_node,
1738 mlxsw_sp_nexthop_group_ht_params);
1739}
1740
1741static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001742mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1743 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001744{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001745 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1746
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001747 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001748 cmp_arg.fi = fi;
1749 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1750 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001751 mlxsw_sp_nexthop_group_ht_params);
1752}
1753
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001754static struct mlxsw_sp_nexthop_group *
1755mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
1756 struct mlxsw_sp_fib6_entry *fib6_entry)
1757{
1758 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1759
1760 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
1761 cmp_arg.fib6_entry = fib6_entry;
1762 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1763 &cmp_arg,
1764 mlxsw_sp_nexthop_group_ht_params);
1765}
1766
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001767static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1768 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1769 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1770 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1771};
1772
1773static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1774 struct mlxsw_sp_nexthop *nh)
1775{
Ido Schimmel9011b672017-05-16 19:38:25 +02001776 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001777 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1778}
1779
1780static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1781 struct mlxsw_sp_nexthop *nh)
1782{
Ido Schimmel9011b672017-05-16 19:38:25 +02001783 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001784 mlxsw_sp_nexthop_ht_params);
1785}
1786
Ido Schimmelad178c82017-02-08 11:16:40 +01001787static struct mlxsw_sp_nexthop *
1788mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1789 struct mlxsw_sp_nexthop_key key)
1790{
Ido Schimmel9011b672017-05-16 19:38:25 +02001791 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001792 mlxsw_sp_nexthop_ht_params);
1793}
1794
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001795static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001796 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001797 u32 adj_index, u16 ecmp_size,
1798 u32 new_adj_index,
1799 u16 new_ecmp_size)
1800{
1801 char raleu_pl[MLXSW_REG_RALEU_LEN];
1802
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001803 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001804 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1805 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001806 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001807 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1808}
1809
1810static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1811 struct mlxsw_sp_nexthop_group *nh_grp,
1812 u32 old_adj_index, u16 old_ecmp_size)
1813{
1814 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001815 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001816 int err;
1817
1818 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001819 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001820 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001821 fib = fib_entry->fib_node->fib;
1822 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001823 old_adj_index,
1824 old_ecmp_size,
1825 nh_grp->adj_index,
1826 nh_grp->ecmp_size);
1827 if (err)
1828 return err;
1829 }
1830 return 0;
1831}
1832
1833static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1834 struct mlxsw_sp_nexthop *nh)
1835{
1836 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1837 char ratr_pl[MLXSW_REG_RATR_LEN];
1838
1839 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1840 true, adj_index, neigh_entry->rif);
1841 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1842 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1843}
1844
1845static int
1846mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001847 struct mlxsw_sp_nexthop_group *nh_grp,
1848 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001849{
1850 u32 adj_index = nh_grp->adj_index; /* base */
1851 struct mlxsw_sp_nexthop *nh;
1852 int i;
1853 int err;
1854
1855 for (i = 0; i < nh_grp->count; i++) {
1856 nh = &nh_grp->nexthops[i];
1857
1858 if (!nh->should_offload) {
1859 nh->offloaded = 0;
1860 continue;
1861 }
1862
Ido Schimmela59b7e02017-01-23 11:11:42 +01001863 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001864 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1865 adj_index, nh);
1866 if (err)
1867 return err;
1868 nh->update = 0;
1869 nh->offloaded = 1;
1870 }
1871 adj_index++;
1872 }
1873 return 0;
1874}
1875
1876static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1877 struct mlxsw_sp_fib_entry *fib_entry);
1878
Ido Schimmel1819ae32017-07-21 18:04:28 +02001879static bool
1880mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1881 const struct mlxsw_sp_fib_entry *fib_entry);
1882
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001883static int
1884mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1885 struct mlxsw_sp_nexthop_group *nh_grp)
1886{
1887 struct mlxsw_sp_fib_entry *fib_entry;
1888 int err;
1889
1890 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001891 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1892 fib_entry))
1893 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001894 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1895 if (err)
1896 return err;
1897 }
1898 return 0;
1899}
1900
1901static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001902mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1903 enum mlxsw_reg_ralue_op op, int err);
1904
1905static void
1906mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1907{
1908 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1909 struct mlxsw_sp_fib_entry *fib_entry;
1910
1911 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1912 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1913 fib_entry))
1914 continue;
1915 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1916 }
1917}
1918
1919static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001920mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1921 struct mlxsw_sp_nexthop_group *nh_grp)
1922{
1923 struct mlxsw_sp_nexthop *nh;
1924 bool offload_change = false;
1925 u32 adj_index;
1926 u16 ecmp_size = 0;
1927 bool old_adj_index_valid;
1928 u32 old_adj_index;
1929 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001930 int i;
1931 int err;
1932
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001933 if (!nh_grp->gateway) {
1934 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1935 return;
1936 }
1937
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001938 for (i = 0; i < nh_grp->count; i++) {
1939 nh = &nh_grp->nexthops[i];
1940
Petr Machata56b8a9e2017-07-31 09:27:29 +02001941 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001942 offload_change = true;
1943 if (nh->should_offload)
1944 nh->update = 1;
1945 }
1946 if (nh->should_offload)
1947 ecmp_size++;
1948 }
1949 if (!offload_change) {
1950 /* Nothing was added or removed, so no need to reallocate. Just
1951 * update MAC on existing adjacency indexes.
1952 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001953 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1954 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001955 if (err) {
1956 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1957 goto set_trap;
1958 }
1959 return;
1960 }
1961 if (!ecmp_size)
1962 /* No neigh of this group is connected so we just set
1963 * the trap and let everthing flow through kernel.
1964 */
1965 goto set_trap;
1966
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001967 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1968 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001969 /* We ran out of KVD linear space, just set the
1970 * trap and let everything flow through kernel.
1971 */
1972 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1973 goto set_trap;
1974 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001975 old_adj_index_valid = nh_grp->adj_index_valid;
1976 old_adj_index = nh_grp->adj_index;
1977 old_ecmp_size = nh_grp->ecmp_size;
1978 nh_grp->adj_index_valid = 1;
1979 nh_grp->adj_index = adj_index;
1980 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001981 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001982 if (err) {
1983 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1984 goto set_trap;
1985 }
1986
1987 if (!old_adj_index_valid) {
1988 /* The trap was set for fib entries, so we have to call
1989 * fib entry update to unset it and use adjacency index.
1990 */
1991 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1992 if (err) {
1993 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1994 goto set_trap;
1995 }
1996 return;
1997 }
1998
1999 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2000 old_adj_index, old_ecmp_size);
2001 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2002 if (err) {
2003 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2004 goto set_trap;
2005 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002006
2007 /* Offload state within the group changed, so update the flags. */
2008 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2009
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002010 return;
2011
2012set_trap:
2013 old_adj_index_valid = nh_grp->adj_index_valid;
2014 nh_grp->adj_index_valid = 0;
2015 for (i = 0; i < nh_grp->count; i++) {
2016 nh = &nh_grp->nexthops[i];
2017 nh->offloaded = 0;
2018 }
2019 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2020 if (err)
2021 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2022 if (old_adj_index_valid)
2023 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2024}
2025
2026static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2027 bool removing)
2028{
Petr Machata213666a2017-07-31 09:27:30 +02002029 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002030 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002031 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002032 nh->should_offload = 0;
2033 nh->update = 1;
2034}
2035
2036static void
2037mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2038 struct mlxsw_sp_neigh_entry *neigh_entry,
2039 bool removing)
2040{
2041 struct mlxsw_sp_nexthop *nh;
2042
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002043 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2044 neigh_list_node) {
2045 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2046 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2047 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002048}
2049
Ido Schimmel9665b742017-02-08 11:16:42 +01002050static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002051 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002052{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002053 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002054 return;
2055
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002056 nh->rif = rif;
2057 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002058}
2059
2060static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2061{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002062 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002063 return;
2064
2065 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002066 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002067}
2068
Ido Schimmela8c97012017-02-08 11:16:35 +01002069static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2070 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002071{
2072 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002073 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002074 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002075 int err;
2076
Ido Schimmelad178c82017-02-08 11:16:40 +01002077 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002078 return 0;
2079
Jiri Pirko33b13412016-11-10 12:31:04 +01002080 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002081 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002082 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002083 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002084 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002085 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002086 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002087 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2088 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002089 if (IS_ERR(n))
2090 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002091 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002092 }
2093 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2094 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002095 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2096 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002097 err = -EINVAL;
2098 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002099 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002100 }
Yotam Gigib2157142016-07-05 11:27:51 +02002101
2102 /* If that is the first nexthop connected to that neigh, add to
2103 * nexthop_neighs_list
2104 */
2105 if (list_empty(&neigh_entry->nexthop_list))
2106 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002107 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002108
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002109 nh->neigh_entry = neigh_entry;
2110 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2111 read_lock_bh(&n->lock);
2112 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002113 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002114 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002115 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002116
2117 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002118
2119err_neigh_entry_create:
2120 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002121 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002122}
2123
Ido Schimmela8c97012017-02-08 11:16:35 +01002124static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2125 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002126{
2127 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002128 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002129
Ido Schimmelb8399a12017-02-08 11:16:33 +01002130 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002131 return;
2132 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002133
Ido Schimmel58312122016-12-23 09:32:50 +01002134 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002135 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002136 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002137
2138 /* If that is the last nexthop connected to that neigh, remove from
2139 * nexthop_neighs_list
2140 */
Ido Schimmele58be792017-02-08 11:16:28 +01002141 if (list_empty(&neigh_entry->nexthop_list))
2142 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002143
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002144 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2145 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2146
2147 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002148}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002149
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002150static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2151 struct mlxsw_sp_nexthop_group *nh_grp,
2152 struct mlxsw_sp_nexthop *nh,
2153 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002154{
2155 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002156 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002157 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002158 int err;
2159
2160 nh->nh_grp = nh_grp;
2161 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002162 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002163 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2164 if (err)
2165 return err;
2166
Ido Schimmel97989ee2017-03-10 08:53:38 +01002167 if (!dev)
2168 return 0;
2169
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002170 in_dev = __in_dev_get_rtnl(dev);
2171 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2172 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2173 return 0;
2174
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002175 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2176 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002177 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002178 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002179
2180 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2181 if (err)
2182 goto err_nexthop_neigh_init;
2183
2184 return 0;
2185
2186err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002187 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002188 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2189 return err;
2190}
2191
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002192static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2193 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002194{
2195 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002196 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002197 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002198}
2199
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002200static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2201 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002202{
2203 struct mlxsw_sp_nexthop_key key;
2204 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002205 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002206
Ido Schimmel9011b672017-05-16 19:38:25 +02002207 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002208 return;
2209
2210 key.fib_nh = fib_nh;
2211 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2212 if (WARN_ON_ONCE(!nh))
2213 return;
2214
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002215 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2216 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002217 return;
2218
2219 switch (event) {
2220 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002221 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002222 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2223 break;
2224 case FIB_EVENT_NH_DEL:
2225 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002226 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002227 break;
2228 }
2229
2230 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2231}
2232
Ido Schimmel9665b742017-02-08 11:16:42 +01002233static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002234 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002235{
2236 struct mlxsw_sp_nexthop *nh, *tmp;
2237
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002238 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002239 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2240 mlxsw_sp_nexthop_rif_fini(nh);
2241 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2242 }
2243}
2244
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002245static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002246mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002247{
2248 struct mlxsw_sp_nexthop_group *nh_grp;
2249 struct mlxsw_sp_nexthop *nh;
2250 struct fib_nh *fib_nh;
2251 size_t alloc_size;
2252 int i;
2253 int err;
2254
2255 alloc_size = sizeof(*nh_grp) +
2256 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2257 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2258 if (!nh_grp)
2259 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002260 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002261 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002262 nh_grp->neigh_tbl = &arp_tbl;
2263
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002264 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002265 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002266 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002267 for (i = 0; i < nh_grp->count; i++) {
2268 nh = &nh_grp->nexthops[i];
2269 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002270 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002271 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002272 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002273 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002274 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2275 if (err)
2276 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002277 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2278 return nh_grp;
2279
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002280err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002281err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002282 for (i--; i >= 0; i--) {
2283 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002284 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002285 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002286 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002287 kfree(nh_grp);
2288 return ERR_PTR(err);
2289}
2290
2291static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002292mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2293 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002294{
2295 struct mlxsw_sp_nexthop *nh;
2296 int i;
2297
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002298 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002299 for (i = 0; i < nh_grp->count; i++) {
2300 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002301 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002302 }
Ido Schimmel58312122016-12-23 09:32:50 +01002303 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2304 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002305 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002306 kfree(nh_grp);
2307}
2308
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002309static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2310 struct mlxsw_sp_fib_entry *fib_entry,
2311 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002312{
2313 struct mlxsw_sp_nexthop_group *nh_grp;
2314
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002315 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002316 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002317 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002318 if (IS_ERR(nh_grp))
2319 return PTR_ERR(nh_grp);
2320 }
2321 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2322 fib_entry->nh_group = nh_grp;
2323 return 0;
2324}
2325
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002326static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2327 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002328{
2329 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2330
2331 list_del(&fib_entry->nexthop_group_node);
2332 if (!list_empty(&nh_grp->fib_list))
2333 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002334 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002335}
2336
Ido Schimmel013b20f2017-02-08 11:16:36 +01002337static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002338mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2339{
2340 struct mlxsw_sp_fib4_entry *fib4_entry;
2341
2342 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2343 common);
2344 return !fib4_entry->tos;
2345}
2346
2347static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002348mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2349{
2350 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2351
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002352 switch (fib_entry->fib_node->fib->proto) {
2353 case MLXSW_SP_L3_PROTO_IPV4:
2354 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2355 return false;
2356 break;
2357 case MLXSW_SP_L3_PROTO_IPV6:
2358 break;
2359 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002360
Ido Schimmel013b20f2017-02-08 11:16:36 +01002361 switch (fib_entry->type) {
2362 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2363 return !!nh_group->adj_index_valid;
2364 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002365 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002366 default:
2367 return false;
2368 }
2369}
2370
Ido Schimmel428b8512017-08-03 13:28:28 +02002371static struct mlxsw_sp_nexthop *
2372mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2373 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2374{
2375 int i;
2376
2377 for (i = 0; i < nh_grp->count; i++) {
2378 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2379 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2380
2381 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2382 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2383 &rt->rt6i_gateway))
2384 return nh;
2385 continue;
2386 }
2387
2388 return NULL;
2389}
2390
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002391static void
2392mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2393{
2394 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2395 int i;
2396
2397 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2398 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2399 return;
2400 }
2401
2402 for (i = 0; i < nh_grp->count; i++) {
2403 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2404
2405 if (nh->offloaded)
2406 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2407 else
2408 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2409 }
2410}
2411
2412static void
2413mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2414{
2415 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2416 int i;
2417
2418 for (i = 0; i < nh_grp->count; i++) {
2419 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2420
2421 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2422 }
2423}
2424
Ido Schimmel428b8512017-08-03 13:28:28 +02002425static void
2426mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2427{
2428 struct mlxsw_sp_fib6_entry *fib6_entry;
2429 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2430
2431 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2432 common);
2433
2434 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2435 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02002436 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002437 return;
2438 }
2439
2440 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2441 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2442 struct mlxsw_sp_nexthop *nh;
2443
2444 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2445 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02002446 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002447 else
Ido Schimmelfe400792017-08-15 09:09:49 +02002448 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002449 }
2450}
2451
2452static void
2453mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2454{
2455 struct mlxsw_sp_fib6_entry *fib6_entry;
2456 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2457
2458 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2459 common);
2460 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2461 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2462
Ido Schimmelfe400792017-08-15 09:09:49 +02002463 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002464 }
2465}
2466
Ido Schimmel013b20f2017-02-08 11:16:36 +01002467static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2468{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002469 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002470 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002471 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002472 break;
2473 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002474 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2475 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002476 }
2477}
2478
2479static void
2480mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2481{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002482 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002483 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002484 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002485 break;
2486 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002487 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2488 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002489 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002490}
2491
2492static void
2493mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2494 enum mlxsw_reg_ralue_op op, int err)
2495{
2496 switch (op) {
2497 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002498 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2499 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2500 if (err)
2501 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002502 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002503 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002504 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002505 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2506 return;
2507 default:
2508 return;
2509 }
2510}
2511
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002512static void
2513mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2514 const struct mlxsw_sp_fib_entry *fib_entry,
2515 enum mlxsw_reg_ralue_op op)
2516{
2517 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2518 enum mlxsw_reg_ralxx_protocol proto;
2519 u32 *p_dip;
2520
2521 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2522
2523 switch (fib->proto) {
2524 case MLXSW_SP_L3_PROTO_IPV4:
2525 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2526 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2527 fib_entry->fib_node->key.prefix_len,
2528 *p_dip);
2529 break;
2530 case MLXSW_SP_L3_PROTO_IPV6:
2531 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2532 fib_entry->fib_node->key.prefix_len,
2533 fib_entry->fib_node->key.addr);
2534 break;
2535 }
2536}
2537
2538static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2539 struct mlxsw_sp_fib_entry *fib_entry,
2540 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002541{
2542 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002543 enum mlxsw_reg_ralue_trap_action trap_action;
2544 u16 trap_id = 0;
2545 u32 adjacency_index = 0;
2546 u16 ecmp_size = 0;
2547
2548 /* In case the nexthop group adjacency index is valid, use it
2549 * with provided ECMP size. Otherwise, setup trap and pass
2550 * traffic to kernel.
2551 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002552 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002553 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2554 adjacency_index = fib_entry->nh_group->adj_index;
2555 ecmp_size = fib_entry->nh_group->ecmp_size;
2556 } else {
2557 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2558 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2559 }
2560
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002561 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002562 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2563 adjacency_index, ecmp_size);
2564 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2565}
2566
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002567static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2568 struct mlxsw_sp_fib_entry *fib_entry,
2569 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002570{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002571 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002572 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002573 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002574 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002575 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002576
2577 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2578 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002579 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002580 } else {
2581 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2582 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2583 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002584
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002585 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002586 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2587 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002588 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2589}
2590
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002591static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2592 struct mlxsw_sp_fib_entry *fib_entry,
2593 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002594{
2595 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002596
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002597 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002598 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2599 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2600}
2601
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002602static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2603 struct mlxsw_sp_fib_entry *fib_entry,
2604 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002605{
2606 switch (fib_entry->type) {
2607 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002608 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002609 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002610 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002611 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002612 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002613 }
2614 return -EINVAL;
2615}
2616
2617static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2618 struct mlxsw_sp_fib_entry *fib_entry,
2619 enum mlxsw_reg_ralue_op op)
2620{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002621 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002622
Ido Schimmel013b20f2017-02-08 11:16:36 +01002623 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002624
Ido Schimmel013b20f2017-02-08 11:16:36 +01002625 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002626}
2627
2628static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2629 struct mlxsw_sp_fib_entry *fib_entry)
2630{
Jiri Pirko7146da32016-09-01 10:37:41 +02002631 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2632 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002633}
2634
2635static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2636 struct mlxsw_sp_fib_entry *fib_entry)
2637{
2638 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2639 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2640}
2641
Jiri Pirko61c503f2016-07-04 08:23:11 +02002642static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002643mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2644 const struct fib_entry_notifier_info *fen_info,
2645 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002646{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002647 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002648
Ido Schimmel97989ee2017-03-10 08:53:38 +01002649 switch (fen_info->type) {
2650 case RTN_BROADCAST: /* fall through */
2651 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002652 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2653 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002654 case RTN_UNREACHABLE: /* fall through */
2655 case RTN_BLACKHOLE: /* fall through */
2656 case RTN_PROHIBIT:
2657 /* Packets hitting these routes need to be trapped, but
2658 * can do so with a lower priority than packets directed
2659 * at the host, so use action type local instead of trap.
2660 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002661 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002662 return 0;
2663 case RTN_UNICAST:
2664 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2665 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2666 else
2667 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2668 return 0;
2669 default:
2670 return -EINVAL;
2671 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002672}
2673
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002674static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002675mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2676 struct mlxsw_sp_fib_node *fib_node,
2677 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002678{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002679 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002680 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002681 int err;
2682
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002683 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2684 if (!fib4_entry)
2685 return ERR_PTR(-ENOMEM);
2686 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002687
2688 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2689 if (err)
2690 goto err_fib4_entry_type_set;
2691
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002692 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002693 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002694 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002695
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002696 fib4_entry->prio = fen_info->fi->fib_priority;
2697 fib4_entry->tb_id = fen_info->tb_id;
2698 fib4_entry->type = fen_info->type;
2699 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002700
2701 fib_entry->fib_node = fib_node;
2702
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002703 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002704
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002705err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002706err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002707 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002708 return ERR_PTR(err);
2709}
2710
2711static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002712 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002713{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002714 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002715 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002716}
2717
2718static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002719mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2720 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002721
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002722static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002723mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2724 const struct fib_entry_notifier_info *fen_info)
2725{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002726 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002727 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002728 struct mlxsw_sp_fib *fib;
2729 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002730
Ido Schimmel160e22a2017-07-18 10:10:20 +02002731 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2732 if (!vr)
2733 return NULL;
2734 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2735
2736 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2737 sizeof(fen_info->dst),
2738 fen_info->dst_len);
2739 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002740 return NULL;
2741
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002742 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2743 if (fib4_entry->tb_id == fen_info->tb_id &&
2744 fib4_entry->tos == fen_info->tos &&
2745 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002746 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2747 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002748 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002749 }
2750 }
2751
2752 return NULL;
2753}
2754
2755static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2756 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2757 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2758 .key_len = sizeof(struct mlxsw_sp_fib_key),
2759 .automatic_shrinking = true,
2760};
2761
2762static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2763 struct mlxsw_sp_fib_node *fib_node)
2764{
2765 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2766 mlxsw_sp_fib_ht_params);
2767}
2768
2769static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2770 struct mlxsw_sp_fib_node *fib_node)
2771{
2772 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2773 mlxsw_sp_fib_ht_params);
2774}
2775
2776static struct mlxsw_sp_fib_node *
2777mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2778 size_t addr_len, unsigned char prefix_len)
2779{
2780 struct mlxsw_sp_fib_key key;
2781
2782 memset(&key, 0, sizeof(key));
2783 memcpy(key.addr, addr, addr_len);
2784 key.prefix_len = prefix_len;
2785 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2786}
2787
2788static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002789mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002790 size_t addr_len, unsigned char prefix_len)
2791{
2792 struct mlxsw_sp_fib_node *fib_node;
2793
2794 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2795 if (!fib_node)
2796 return NULL;
2797
2798 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002799 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002800 memcpy(fib_node->key.addr, addr, addr_len);
2801 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002802
2803 return fib_node;
2804}
2805
2806static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2807{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002808 list_del(&fib_node->list);
2809 WARN_ON(!list_empty(&fib_node->entry_list));
2810 kfree(fib_node);
2811}
2812
2813static bool
2814mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2815 const struct mlxsw_sp_fib_entry *fib_entry)
2816{
2817 return list_first_entry(&fib_node->entry_list,
2818 struct mlxsw_sp_fib_entry, list) == fib_entry;
2819}
2820
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002821static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2822 struct mlxsw_sp_fib *fib,
2823 struct mlxsw_sp_fib_node *fib_node)
2824{
2825 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2826 struct mlxsw_sp_lpm_tree *lpm_tree;
2827 int err;
2828
2829 /* Since the tree is shared between all virtual routers we must
2830 * make sure it contains all the required prefix lengths. This
2831 * can be computed by either adding the new prefix length to the
2832 * existing prefix usage of a bound tree, or by aggregating the
2833 * prefix lengths across all virtual routers and adding the new
2834 * one as well.
2835 */
2836 if (fib->lpm_tree)
2837 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2838 &fib->lpm_tree->prefix_usage);
2839 else
2840 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2841 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2842
2843 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2844 fib->proto);
2845 if (IS_ERR(lpm_tree))
2846 return PTR_ERR(lpm_tree);
2847
2848 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2849 return 0;
2850
2851 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2852 if (err)
2853 return err;
2854
2855 return 0;
2856}
2857
2858static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2859 struct mlxsw_sp_fib *fib)
2860{
2861 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2862 struct mlxsw_sp_lpm_tree *lpm_tree;
2863
2864 /* Aggregate prefix lengths across all virtual routers to make
2865 * sure we only have used prefix lengths in the LPM tree.
2866 */
2867 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2868 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2869 fib->proto);
2870 if (IS_ERR(lpm_tree))
2871 goto err_tree_get;
2872 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2873
2874err_tree_get:
2875 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2876 return;
2877 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2878 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2879 fib->lpm_tree = NULL;
2880}
2881
Ido Schimmel9aecce12017-02-09 10:28:42 +01002882static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2883{
2884 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002885 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002886
2887 if (fib->prefix_ref_count[prefix_len]++ == 0)
2888 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2889}
2890
2891static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2892{
2893 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002894 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002895
2896 if (--fib->prefix_ref_count[prefix_len] == 0)
2897 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2898}
2899
Ido Schimmel76610eb2017-03-10 08:53:41 +01002900static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2901 struct mlxsw_sp_fib_node *fib_node,
2902 struct mlxsw_sp_fib *fib)
2903{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002904 int err;
2905
2906 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2907 if (err)
2908 return err;
2909 fib_node->fib = fib;
2910
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002911 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
2912 if (err)
2913 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002914
2915 mlxsw_sp_fib_node_prefix_inc(fib_node);
2916
2917 return 0;
2918
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002919err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002920 fib_node->fib = NULL;
2921 mlxsw_sp_fib_node_remove(fib, fib_node);
2922 return err;
2923}
2924
2925static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2926 struct mlxsw_sp_fib_node *fib_node)
2927{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002928 struct mlxsw_sp_fib *fib = fib_node->fib;
2929
2930 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002931 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002932 fib_node->fib = NULL;
2933 mlxsw_sp_fib_node_remove(fib, fib_node);
2934}
2935
Ido Schimmel9aecce12017-02-09 10:28:42 +01002936static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002937mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2938 size_t addr_len, unsigned char prefix_len,
2939 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002940{
2941 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002942 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002943 struct mlxsw_sp_vr *vr;
2944 int err;
2945
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002946 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002947 if (IS_ERR(vr))
2948 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002949 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002950
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002951 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002952 if (fib_node)
2953 return fib_node;
2954
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002955 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002956 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002957 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002958 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002959 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002960
Ido Schimmel76610eb2017-03-10 08:53:41 +01002961 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2962 if (err)
2963 goto err_fib_node_init;
2964
Ido Schimmel9aecce12017-02-09 10:28:42 +01002965 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002966
Ido Schimmel76610eb2017-03-10 08:53:41 +01002967err_fib_node_init:
2968 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002969err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002970 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002971 return ERR_PTR(err);
2972}
2973
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002974static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2975 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002976{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002977 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002978
Ido Schimmel9aecce12017-02-09 10:28:42 +01002979 if (!list_empty(&fib_node->entry_list))
2980 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002981 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002982 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002983 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002984}
2985
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002986static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002987mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002988 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002989{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002990 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002991
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002992 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2993 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002994 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002995 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002996 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002997 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002998 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002999 if (fib4_entry->prio >= new4_entry->prio ||
3000 fib4_entry->tos < new4_entry->tos)
3001 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003002 }
3003
3004 return NULL;
3005}
3006
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003007static int
3008mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3009 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003010{
3011 struct mlxsw_sp_fib_node *fib_node;
3012
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003013 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003014 return -EINVAL;
3015
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003016 fib_node = fib4_entry->common.fib_node;
3017 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3018 common.list) {
3019 if (fib4_entry->tb_id != new4_entry->tb_id ||
3020 fib4_entry->tos != new4_entry->tos ||
3021 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003022 break;
3023 }
3024
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003025 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003026 return 0;
3027}
3028
Ido Schimmel9aecce12017-02-09 10:28:42 +01003029static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003030mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003031 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003032{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003033 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003034 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003035
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003036 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003037
Ido Schimmel4283bce2017-02-09 10:28:43 +01003038 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003039 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3040 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003041 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003042
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003043 /* Insert new entry before replaced one, so that we can later
3044 * remove the second.
3045 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003046 if (fib4_entry) {
3047 list_add_tail(&new4_entry->common.list,
3048 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003049 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003050 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003051
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003052 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3053 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003054 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003055 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003056 }
3057
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003058 if (fib4_entry)
3059 list_add(&new4_entry->common.list,
3060 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003061 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003062 list_add(&new4_entry->common.list,
3063 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003064 }
3065
3066 return 0;
3067}
3068
3069static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003070mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003071{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003072 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003073}
3074
Ido Schimmel80c238f2017-07-18 10:10:29 +02003075static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3076 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003077{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003078 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3079
Ido Schimmel9aecce12017-02-09 10:28:42 +01003080 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3081 return 0;
3082
3083 /* To prevent packet loss, overwrite the previously offloaded
3084 * entry.
3085 */
3086 if (!list_is_singular(&fib_node->entry_list)) {
3087 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3088 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3089
3090 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3091 }
3092
3093 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3094}
3095
Ido Schimmel80c238f2017-07-18 10:10:29 +02003096static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3097 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003098{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003099 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3100
Ido Schimmel9aecce12017-02-09 10:28:42 +01003101 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3102 return;
3103
3104 /* Promote the next entry by overwriting the deleted entry */
3105 if (!list_is_singular(&fib_node->entry_list)) {
3106 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3107 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3108
3109 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3110 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3111 return;
3112 }
3113
3114 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3115}
3116
3117static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003118 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003119 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003120{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003121 int err;
3122
Ido Schimmel9efbee62017-07-18 10:10:28 +02003123 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003124 if (err)
3125 return err;
3126
Ido Schimmel80c238f2017-07-18 10:10:29 +02003127 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003128 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003129 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003130
Ido Schimmel9aecce12017-02-09 10:28:42 +01003131 return 0;
3132
Ido Schimmel80c238f2017-07-18 10:10:29 +02003133err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003134 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003135 return err;
3136}
3137
3138static void
3139mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003140 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003141{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003142 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003143 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003144}
3145
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003146static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003147 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003148 bool replace)
3149{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003150 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3151 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003152
3153 if (!replace)
3154 return;
3155
3156 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003157 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003158
3159 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3160 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003161 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003162}
3163
Ido Schimmel9aecce12017-02-09 10:28:42 +01003164static int
3165mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003166 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003167 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003168{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003169 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003170 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003171 int err;
3172
Ido Schimmel9011b672017-05-16 19:38:25 +02003173 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003174 return 0;
3175
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003176 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3177 &fen_info->dst, sizeof(fen_info->dst),
3178 fen_info->dst_len,
3179 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003180 if (IS_ERR(fib_node)) {
3181 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3182 return PTR_ERR(fib_node);
3183 }
3184
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003185 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3186 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003187 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003188 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003189 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003190 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003191
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003192 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003193 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003194 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003195 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3196 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003197 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003198
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003199 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003200
Jiri Pirko61c503f2016-07-04 08:23:11 +02003201 return 0;
3202
Ido Schimmel9aecce12017-02-09 10:28:42 +01003203err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003204 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003205err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003206 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003207 return err;
3208}
3209
Jiri Pirko37956d72016-10-20 16:05:43 +02003210static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3211 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003212{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003213 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003214 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003215
Ido Schimmel9011b672017-05-16 19:38:25 +02003216 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003217 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003218
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003219 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3220 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003221 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003222 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003223
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003224 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3225 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003226 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003227}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003228
Ido Schimmel428b8512017-08-03 13:28:28 +02003229static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3230{
3231 /* Packets with link-local destination IP arriving to the router
3232 * are trapped to the CPU, so no need to program specific routes
3233 * for them.
3234 */
3235 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3236 return true;
3237
3238 /* Multicast routes aren't supported, so ignore them. Neighbour
3239 * Discovery packets are specifically trapped.
3240 */
3241 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3242 return true;
3243
3244 /* Cloned routes are irrelevant in the forwarding path. */
3245 if (rt->rt6i_flags & RTF_CACHE)
3246 return true;
3247
3248 return false;
3249}
3250
3251static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3252{
3253 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3254
3255 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3256 if (!mlxsw_sp_rt6)
3257 return ERR_PTR(-ENOMEM);
3258
3259 /* In case of route replace, replaced route is deleted with
3260 * no notification. Take reference to prevent accessing freed
3261 * memory.
3262 */
3263 mlxsw_sp_rt6->rt = rt;
3264 rt6_hold(rt);
3265
3266 return mlxsw_sp_rt6;
3267}
3268
3269#if IS_ENABLED(CONFIG_IPV6)
3270static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3271{
3272 rt6_release(rt);
3273}
3274#else
3275static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3276{
3277}
3278#endif
3279
3280static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3281{
3282 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3283 kfree(mlxsw_sp_rt6);
3284}
3285
3286static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3287{
3288 /* RTF_CACHE routes are ignored */
3289 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3290}
3291
3292static struct rt6_info *
3293mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3294{
3295 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3296 list)->rt;
3297}
3298
3299static struct mlxsw_sp_fib6_entry *
3300mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003301 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003302{
3303 struct mlxsw_sp_fib6_entry *fib6_entry;
3304
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003305 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003306 return NULL;
3307
3308 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3309 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3310
3311 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3312 * virtual router.
3313 */
3314 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3315 continue;
3316 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3317 break;
3318 if (rt->rt6i_metric < nrt->rt6i_metric)
3319 continue;
3320 if (rt->rt6i_metric == nrt->rt6i_metric &&
3321 mlxsw_sp_fib6_rt_can_mp(rt))
3322 return fib6_entry;
3323 if (rt->rt6i_metric > nrt->rt6i_metric)
3324 break;
3325 }
3326
3327 return NULL;
3328}
3329
3330static struct mlxsw_sp_rt6 *
3331mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3332 const struct rt6_info *rt)
3333{
3334 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3335
3336 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3337 if (mlxsw_sp_rt6->rt == rt)
3338 return mlxsw_sp_rt6;
3339 }
3340
3341 return NULL;
3342}
3343
3344static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3345 struct mlxsw_sp_nexthop_group *nh_grp,
3346 struct mlxsw_sp_nexthop *nh,
3347 const struct rt6_info *rt)
3348{
3349 struct net_device *dev = rt->dst.dev;
3350 struct mlxsw_sp_rif *rif;
3351 int err;
3352
3353 nh->nh_grp = nh_grp;
3354 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3355
3356 if (!dev)
3357 return 0;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003358 nh->ifindex = dev->ifindex;
Ido Schimmel428b8512017-08-03 13:28:28 +02003359
3360 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3361 if (!rif)
3362 return 0;
3363 mlxsw_sp_nexthop_rif_init(nh, rif);
3364
3365 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3366 if (err)
3367 goto err_nexthop_neigh_init;
3368
3369 return 0;
3370
3371err_nexthop_neigh_init:
3372 mlxsw_sp_nexthop_rif_fini(nh);
3373 return err;
3374}
3375
3376static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3377 struct mlxsw_sp_nexthop *nh)
3378{
3379 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3380 mlxsw_sp_nexthop_rif_fini(nh);
3381}
3382
3383static struct mlxsw_sp_nexthop_group *
3384mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3385 struct mlxsw_sp_fib6_entry *fib6_entry)
3386{
3387 struct mlxsw_sp_nexthop_group *nh_grp;
3388 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3389 struct mlxsw_sp_nexthop *nh;
3390 size_t alloc_size;
3391 int i = 0;
3392 int err;
3393
3394 alloc_size = sizeof(*nh_grp) +
3395 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3396 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3397 if (!nh_grp)
3398 return ERR_PTR(-ENOMEM);
3399 INIT_LIST_HEAD(&nh_grp->fib_list);
3400#if IS_ENABLED(CONFIG_IPV6)
3401 nh_grp->neigh_tbl = &nd_tbl;
3402#endif
3403 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3404 struct mlxsw_sp_rt6, list);
3405 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3406 nh_grp->count = fib6_entry->nrt6;
3407 for (i = 0; i < nh_grp->count; i++) {
3408 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3409
3410 nh = &nh_grp->nexthops[i];
3411 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3412 if (err)
3413 goto err_nexthop6_init;
3414 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3415 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003416
3417 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3418 if (err)
3419 goto err_nexthop_group_insert;
3420
Ido Schimmel428b8512017-08-03 13:28:28 +02003421 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3422 return nh_grp;
3423
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003424err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02003425err_nexthop6_init:
3426 for (i--; i >= 0; i--) {
3427 nh = &nh_grp->nexthops[i];
3428 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3429 }
3430 kfree(nh_grp);
3431 return ERR_PTR(err);
3432}
3433
3434static void
3435mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3436 struct mlxsw_sp_nexthop_group *nh_grp)
3437{
3438 struct mlxsw_sp_nexthop *nh;
3439 int i = nh_grp->count;
3440
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003441 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02003442 for (i--; i >= 0; i--) {
3443 nh = &nh_grp->nexthops[i];
3444 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3445 }
3446 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3447 WARN_ON(nh_grp->adj_index_valid);
3448 kfree(nh_grp);
3449}
3450
3451static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3452 struct mlxsw_sp_fib6_entry *fib6_entry)
3453{
3454 struct mlxsw_sp_nexthop_group *nh_grp;
3455
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003456 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
3457 if (!nh_grp) {
3458 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3459 if (IS_ERR(nh_grp))
3460 return PTR_ERR(nh_grp);
3461 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003462
3463 list_add_tail(&fib6_entry->common.nexthop_group_node,
3464 &nh_grp->fib_list);
3465 fib6_entry->common.nh_group = nh_grp;
3466
3467 return 0;
3468}
3469
3470static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3471 struct mlxsw_sp_fib_entry *fib_entry)
3472{
3473 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3474
3475 list_del(&fib_entry->nexthop_group_node);
3476 if (!list_empty(&nh_grp->fib_list))
3477 return;
3478 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3479}
3480
3481static int
3482mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3483 struct mlxsw_sp_fib6_entry *fib6_entry)
3484{
3485 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3486 int err;
3487
3488 fib6_entry->common.nh_group = NULL;
3489 list_del(&fib6_entry->common.nexthop_group_node);
3490
3491 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3492 if (err)
3493 goto err_nexthop6_group_get;
3494
3495 /* In case this entry is offloaded, then the adjacency index
3496 * currently associated with it in the device's table is that
3497 * of the old group. Start using the new one instead.
3498 */
3499 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3500 if (err)
3501 goto err_fib_node_entry_add;
3502
3503 if (list_empty(&old_nh_grp->fib_list))
3504 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3505
3506 return 0;
3507
3508err_fib_node_entry_add:
3509 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3510err_nexthop6_group_get:
3511 list_add_tail(&fib6_entry->common.nexthop_group_node,
3512 &old_nh_grp->fib_list);
3513 fib6_entry->common.nh_group = old_nh_grp;
3514 return err;
3515}
3516
3517static int
3518mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3519 struct mlxsw_sp_fib6_entry *fib6_entry,
3520 struct rt6_info *rt)
3521{
3522 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3523 int err;
3524
3525 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3526 if (IS_ERR(mlxsw_sp_rt6))
3527 return PTR_ERR(mlxsw_sp_rt6);
3528
3529 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3530 fib6_entry->nrt6++;
3531
3532 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3533 if (err)
3534 goto err_nexthop6_group_update;
3535
3536 return 0;
3537
3538err_nexthop6_group_update:
3539 fib6_entry->nrt6--;
3540 list_del(&mlxsw_sp_rt6->list);
3541 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3542 return err;
3543}
3544
3545static void
3546mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3547 struct mlxsw_sp_fib6_entry *fib6_entry,
3548 struct rt6_info *rt)
3549{
3550 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3551
3552 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3553 if (WARN_ON(!mlxsw_sp_rt6))
3554 return;
3555
3556 fib6_entry->nrt6--;
3557 list_del(&mlxsw_sp_rt6->list);
3558 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3559 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3560}
3561
3562static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3563 const struct rt6_info *rt)
3564{
3565 /* Packets hitting RTF_REJECT routes need to be discarded by the
3566 * stack. We can rely on their destination device not having a
3567 * RIF (it's the loopback device) and can thus use action type
3568 * local, which will cause them to be trapped with a lower
3569 * priority than packets that need to be locally received.
3570 */
3571 if (rt->rt6i_flags & RTF_LOCAL)
3572 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3573 else if (rt->rt6i_flags & RTF_REJECT)
3574 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3575 else if (rt->rt6i_flags & RTF_GATEWAY)
3576 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3577 else
3578 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3579}
3580
3581static void
3582mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3583{
3584 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3585
3586 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3587 list) {
3588 fib6_entry->nrt6--;
3589 list_del(&mlxsw_sp_rt6->list);
3590 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3591 }
3592}
3593
3594static struct mlxsw_sp_fib6_entry *
3595mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3596 struct mlxsw_sp_fib_node *fib_node,
3597 struct rt6_info *rt)
3598{
3599 struct mlxsw_sp_fib6_entry *fib6_entry;
3600 struct mlxsw_sp_fib_entry *fib_entry;
3601 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3602 int err;
3603
3604 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3605 if (!fib6_entry)
3606 return ERR_PTR(-ENOMEM);
3607 fib_entry = &fib6_entry->common;
3608
3609 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3610 if (IS_ERR(mlxsw_sp_rt6)) {
3611 err = PTR_ERR(mlxsw_sp_rt6);
3612 goto err_rt6_create;
3613 }
3614
3615 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3616
3617 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3618 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3619 fib6_entry->nrt6 = 1;
3620 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3621 if (err)
3622 goto err_nexthop6_group_get;
3623
3624 fib_entry->fib_node = fib_node;
3625
3626 return fib6_entry;
3627
3628err_nexthop6_group_get:
3629 list_del(&mlxsw_sp_rt6->list);
3630 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3631err_rt6_create:
3632 kfree(fib6_entry);
3633 return ERR_PTR(err);
3634}
3635
3636static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3637 struct mlxsw_sp_fib6_entry *fib6_entry)
3638{
3639 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3640 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3641 WARN_ON(fib6_entry->nrt6);
3642 kfree(fib6_entry);
3643}
3644
3645static struct mlxsw_sp_fib6_entry *
3646mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003647 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003648{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003649 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003650
3651 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3652 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3653
3654 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3655 continue;
3656 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3657 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003658 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3659 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3660 mlxsw_sp_fib6_rt_can_mp(nrt))
3661 return fib6_entry;
3662 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3663 fallback = fallback ?: fib6_entry;
3664 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003665 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003666 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003667 }
3668
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003669 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003670}
3671
3672static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003673mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3674 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003675{
3676 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3677 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3678 struct mlxsw_sp_fib6_entry *fib6_entry;
3679
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003680 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3681
3682 if (replace && WARN_ON(!fib6_entry))
3683 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003684
3685 if (fib6_entry) {
3686 list_add_tail(&new6_entry->common.list,
3687 &fib6_entry->common.list);
3688 } else {
3689 struct mlxsw_sp_fib6_entry *last;
3690
3691 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3692 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3693
3694 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3695 break;
3696 fib6_entry = last;
3697 }
3698
3699 if (fib6_entry)
3700 list_add(&new6_entry->common.list,
3701 &fib6_entry->common.list);
3702 else
3703 list_add(&new6_entry->common.list,
3704 &fib_node->entry_list);
3705 }
3706
3707 return 0;
3708}
3709
3710static void
3711mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3712{
3713 list_del(&fib6_entry->common.list);
3714}
3715
3716static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003717 struct mlxsw_sp_fib6_entry *fib6_entry,
3718 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003719{
3720 int err;
3721
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003722 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003723 if (err)
3724 return err;
3725
3726 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3727 if (err)
3728 goto err_fib_node_entry_add;
3729
3730 return 0;
3731
3732err_fib_node_entry_add:
3733 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3734 return err;
3735}
3736
3737static void
3738mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3739 struct mlxsw_sp_fib6_entry *fib6_entry)
3740{
3741 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3742 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3743}
3744
3745static struct mlxsw_sp_fib6_entry *
3746mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3747 const struct rt6_info *rt)
3748{
3749 struct mlxsw_sp_fib6_entry *fib6_entry;
3750 struct mlxsw_sp_fib_node *fib_node;
3751 struct mlxsw_sp_fib *fib;
3752 struct mlxsw_sp_vr *vr;
3753
3754 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3755 if (!vr)
3756 return NULL;
3757 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3758
3759 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3760 sizeof(rt->rt6i_dst.addr),
3761 rt->rt6i_dst.plen);
3762 if (!fib_node)
3763 return NULL;
3764
3765 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3766 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3767
3768 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3769 rt->rt6i_metric == iter_rt->rt6i_metric &&
3770 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3771 return fib6_entry;
3772 }
3773
3774 return NULL;
3775}
3776
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003777static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3778 struct mlxsw_sp_fib6_entry *fib6_entry,
3779 bool replace)
3780{
3781 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3782 struct mlxsw_sp_fib6_entry *replaced;
3783
3784 if (!replace)
3785 return;
3786
3787 replaced = list_next_entry(fib6_entry, common.list);
3788
3789 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3790 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3791 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3792}
3793
Ido Schimmel428b8512017-08-03 13:28:28 +02003794static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003795 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003796{
3797 struct mlxsw_sp_fib6_entry *fib6_entry;
3798 struct mlxsw_sp_fib_node *fib_node;
3799 int err;
3800
3801 if (mlxsw_sp->router->aborted)
3802 return 0;
3803
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003804 if (rt->rt6i_src.plen)
3805 return -EINVAL;
3806
Ido Schimmel428b8512017-08-03 13:28:28 +02003807 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3808 return 0;
3809
3810 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3811 &rt->rt6i_dst.addr,
3812 sizeof(rt->rt6i_dst.addr),
3813 rt->rt6i_dst.plen,
3814 MLXSW_SP_L3_PROTO_IPV6);
3815 if (IS_ERR(fib_node))
3816 return PTR_ERR(fib_node);
3817
3818 /* Before creating a new entry, try to append route to an existing
3819 * multipath entry.
3820 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003821 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003822 if (fib6_entry) {
3823 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3824 if (err)
3825 goto err_fib6_entry_nexthop_add;
3826 return 0;
3827 }
3828
3829 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3830 if (IS_ERR(fib6_entry)) {
3831 err = PTR_ERR(fib6_entry);
3832 goto err_fib6_entry_create;
3833 }
3834
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003835 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003836 if (err)
3837 goto err_fib6_node_entry_link;
3838
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003839 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3840
Ido Schimmel428b8512017-08-03 13:28:28 +02003841 return 0;
3842
3843err_fib6_node_entry_link:
3844 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3845err_fib6_entry_create:
3846err_fib6_entry_nexthop_add:
3847 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3848 return err;
3849}
3850
3851static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3852 struct rt6_info *rt)
3853{
3854 struct mlxsw_sp_fib6_entry *fib6_entry;
3855 struct mlxsw_sp_fib_node *fib_node;
3856
3857 if (mlxsw_sp->router->aborted)
3858 return;
3859
3860 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3861 return;
3862
3863 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3864 if (WARN_ON(!fib6_entry))
3865 return;
3866
3867 /* If route is part of a multipath entry, but not the last one
3868 * removed, then only reduce its nexthop group.
3869 */
3870 if (!list_is_singular(&fib6_entry->rt6_list)) {
3871 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3872 return;
3873 }
3874
3875 fib_node = fib6_entry->common.fib_node;
3876
3877 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3878 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3879 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3880}
3881
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003882static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3883 enum mlxsw_reg_ralxx_protocol proto,
3884 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003885{
3886 char ralta_pl[MLXSW_REG_RALTA_LEN];
3887 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003888 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003889
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003890 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003891 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3892 if (err)
3893 return err;
3894
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003895 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003896 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3897 if (err)
3898 return err;
3899
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003900 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003901 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003902 char raltb_pl[MLXSW_REG_RALTB_LEN];
3903 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003904
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003905 if (!mlxsw_sp_vr_is_used(vr))
3906 continue;
3907
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003908 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003909 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3910 raltb_pl);
3911 if (err)
3912 return err;
3913
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003914 mlxsw_reg_ralue_pack(ralue_pl, proto,
3915 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003916 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3917 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3918 ralue_pl);
3919 if (err)
3920 return err;
3921 }
3922
3923 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003924}
3925
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003926static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3927{
3928 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3929 int err;
3930
3931 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3932 MLXSW_SP_LPM_TREE_MIN);
3933 if (err)
3934 return err;
3935
3936 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3937 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3938 MLXSW_SP_LPM_TREE_MIN + 1);
3939}
3940
Ido Schimmel9aecce12017-02-09 10:28:42 +01003941static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3942 struct mlxsw_sp_fib_node *fib_node)
3943{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003944 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003945
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003946 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
3947 common.list) {
3948 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003949
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003950 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3951 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003952 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003953 /* Break when entry list is empty and node was freed.
3954 * Otherwise, we'll access freed memory in the next
3955 * iteration.
3956 */
3957 if (do_break)
3958 break;
3959 }
3960}
3961
Ido Schimmel428b8512017-08-03 13:28:28 +02003962static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
3963 struct mlxsw_sp_fib_node *fib_node)
3964{
3965 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
3966
3967 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
3968 common.list) {
3969 bool do_break = &tmp->common.list == &fib_node->entry_list;
3970
3971 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3972 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3973 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3974 if (do_break)
3975 break;
3976 }
3977}
3978
Ido Schimmel9aecce12017-02-09 10:28:42 +01003979static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
3980 struct mlxsw_sp_fib_node *fib_node)
3981{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003982 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003983 case MLXSW_SP_L3_PROTO_IPV4:
3984 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
3985 break;
3986 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003987 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003988 break;
3989 }
3990}
3991
Ido Schimmel76610eb2017-03-10 08:53:41 +01003992static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
3993 struct mlxsw_sp_vr *vr,
3994 enum mlxsw_sp_l3proto proto)
3995{
3996 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
3997 struct mlxsw_sp_fib_node *fib_node, *tmp;
3998
3999 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4000 bool do_break = &tmp->list == &fib->node_list;
4001
4002 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4003 if (do_break)
4004 break;
4005 }
4006}
4007
Ido Schimmelac571de2016-11-14 11:26:32 +01004008static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004009{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004010 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004011
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004012 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004013 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004014
Ido Schimmel76610eb2017-03-10 08:53:41 +01004015 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004016 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004017 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004018
4019 /* If virtual router was only used for IPv4, then it's no
4020 * longer used.
4021 */
4022 if (!mlxsw_sp_vr_is_used(vr))
4023 continue;
4024 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004025 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004026}
4027
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004028static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004029{
4030 int err;
4031
Ido Schimmel9011b672017-05-16 19:38:25 +02004032 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004033 return;
4034 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 +01004035 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004036 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004037 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4038 if (err)
4039 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4040}
4041
Ido Schimmel30572242016-12-03 16:45:01 +01004042struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004043 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004044 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004045 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004046 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004047 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004048 struct fib_nh_notifier_info fnh_info;
4049 };
Ido Schimmel30572242016-12-03 16:45:01 +01004050 struct mlxsw_sp *mlxsw_sp;
4051 unsigned long event;
4052};
4053
Ido Schimmel66a57632017-08-03 13:28:26 +02004054static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004055{
Ido Schimmel30572242016-12-03 16:45:01 +01004056 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004057 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004058 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004059 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004060 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004061 int err;
4062
Ido Schimmel30572242016-12-03 16:45:01 +01004063 /* Protect internal structures from changes */
4064 rtnl_lock();
4065 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004066 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004067 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004068 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004069 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004070 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4071 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004072 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004073 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004074 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004075 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004076 break;
4077 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004078 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4079 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004080 break;
4081 case FIB_EVENT_RULE_ADD: /* fall through */
4082 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004083 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004084 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004085 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004086 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004087 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004088 case FIB_EVENT_NH_ADD: /* fall through */
4089 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004090 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4091 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004092 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4093 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004094 }
Ido Schimmel30572242016-12-03 16:45:01 +01004095 rtnl_unlock();
4096 kfree(fib_work);
4097}
4098
Ido Schimmel66a57632017-08-03 13:28:26 +02004099static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4100{
Ido Schimmel583419f2017-08-03 13:28:27 +02004101 struct mlxsw_sp_fib_event_work *fib_work =
4102 container_of(work, struct mlxsw_sp_fib_event_work, work);
4103 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4104 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004105 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004106 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004107
4108 rtnl_lock();
4109 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004110 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004111 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004112 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004113 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004114 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004115 if (err)
4116 mlxsw_sp_router_fib_abort(mlxsw_sp);
4117 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4118 break;
4119 case FIB_EVENT_ENTRY_DEL:
4120 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4121 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4122 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004123 case FIB_EVENT_RULE_ADD: /* fall through */
4124 case FIB_EVENT_RULE_DEL:
4125 rule = fib_work->fr_info.rule;
4126 if (!fib6_rule_default(rule) && !rule->l3mdev)
4127 mlxsw_sp_router_fib_abort(mlxsw_sp);
4128 fib_rule_put(rule);
4129 break;
4130 }
4131 rtnl_unlock();
4132 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004133}
4134
4135static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4136 struct fib_notifier_info *info)
4137{
4138 switch (fib_work->event) {
4139 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4140 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4141 case FIB_EVENT_ENTRY_ADD: /* fall through */
4142 case FIB_EVENT_ENTRY_DEL:
4143 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4144 /* Take referece on fib_info to prevent it from being
4145 * freed while work is queued. Release it afterwards.
4146 */
4147 fib_info_hold(fib_work->fen_info.fi);
4148 break;
4149 case FIB_EVENT_RULE_ADD: /* fall through */
4150 case FIB_EVENT_RULE_DEL:
4151 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4152 fib_rule_get(fib_work->fr_info.rule);
4153 break;
4154 case FIB_EVENT_NH_ADD: /* fall through */
4155 case FIB_EVENT_NH_DEL:
4156 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4157 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4158 break;
4159 }
4160}
4161
4162static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4163 struct fib_notifier_info *info)
4164{
Ido Schimmel583419f2017-08-03 13:28:27 +02004165 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004166 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004167 case FIB_EVENT_ENTRY_ADD: /* fall through */
4168 case FIB_EVENT_ENTRY_DEL:
4169 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4170 rt6_hold(fib_work->fen6_info.rt);
4171 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004172 case FIB_EVENT_RULE_ADD: /* fall through */
4173 case FIB_EVENT_RULE_DEL:
4174 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4175 fib_rule_get(fib_work->fr_info.rule);
4176 break;
4177 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004178}
4179
Ido Schimmel30572242016-12-03 16:45:01 +01004180/* Called with rcu_read_lock() */
4181static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4182 unsigned long event, void *ptr)
4183{
Ido Schimmel30572242016-12-03 16:45:01 +01004184 struct mlxsw_sp_fib_event_work *fib_work;
4185 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004186 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004187
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004188 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004189 return NOTIFY_DONE;
4190
4191 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4192 if (WARN_ON(!fib_work))
4193 return NOTIFY_BAD;
4194
Ido Schimmel7e39d112017-05-16 19:38:28 +02004195 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4196 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004197 fib_work->event = event;
4198
Ido Schimmel66a57632017-08-03 13:28:26 +02004199 switch (info->family) {
4200 case AF_INET:
4201 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4202 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004203 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004204 case AF_INET6:
4205 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4206 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004207 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004208 }
4209
Ido Schimmela0e47612017-02-06 16:20:10 +01004210 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004211
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004212 return NOTIFY_DONE;
4213}
4214
Ido Schimmel4724ba562017-03-10 08:53:39 +01004215static struct mlxsw_sp_rif *
4216mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4217 const struct net_device *dev)
4218{
4219 int i;
4220
4221 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004222 if (mlxsw_sp->router->rifs[i] &&
4223 mlxsw_sp->router->rifs[i]->dev == dev)
4224 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004225
4226 return NULL;
4227}
4228
4229static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4230{
4231 char ritr_pl[MLXSW_REG_RITR_LEN];
4232 int err;
4233
4234 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4235 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4236 if (WARN_ON_ONCE(err))
4237 return err;
4238
4239 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4240 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4241}
4242
4243static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004244 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004245{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004246 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4247 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4248 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004249}
4250
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004251static bool
4252mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4253 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004254{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004255 struct inet6_dev *inet6_dev;
4256 bool addr_list_empty = true;
4257 struct in_device *idev;
4258
Ido Schimmel4724ba562017-03-10 08:53:39 +01004259 switch (event) {
4260 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004261 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004262 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004263 idev = __in_dev_get_rtnl(dev);
4264 if (idev && idev->ifa_list)
4265 addr_list_empty = false;
4266
4267 inet6_dev = __in6_dev_get(dev);
4268 if (addr_list_empty && inet6_dev &&
4269 !list_empty(&inet6_dev->addr_list))
4270 addr_list_empty = false;
4271
4272 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004273 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004274 return true;
4275 /* It is possible we already removed the RIF ourselves
4276 * if it was assigned to a netdev that is now a bridge
4277 * or LAG slave.
4278 */
4279 return false;
4280 }
4281
4282 return false;
4283}
4284
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004285static enum mlxsw_sp_rif_type
4286mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4287 const struct net_device *dev)
4288{
4289 enum mlxsw_sp_fid_type type;
4290
4291 /* RIF type is derived from the type of the underlying FID */
4292 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4293 type = MLXSW_SP_FID_TYPE_8021Q;
4294 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4295 type = MLXSW_SP_FID_TYPE_8021Q;
4296 else if (netif_is_bridge_master(dev))
4297 type = MLXSW_SP_FID_TYPE_8021D;
4298 else
4299 type = MLXSW_SP_FID_TYPE_RFID;
4300
4301 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4302}
4303
Ido Schimmelde5ed992017-06-04 16:53:40 +02004304static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004305{
4306 int i;
4307
Ido Schimmelde5ed992017-06-04 16:53:40 +02004308 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4309 if (!mlxsw_sp->router->rifs[i]) {
4310 *p_rif_index = i;
4311 return 0;
4312 }
4313 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004314
Ido Schimmelde5ed992017-06-04 16:53:40 +02004315 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004316}
4317
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004318static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4319 u16 vr_id,
4320 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004321{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004322 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004323
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004324 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004325 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004326 return NULL;
4327
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004328 INIT_LIST_HEAD(&rif->nexthop_list);
4329 INIT_LIST_HEAD(&rif->neigh_list);
4330 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4331 rif->mtu = l3_dev->mtu;
4332 rif->vr_id = vr_id;
4333 rif->dev = l3_dev;
4334 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004335
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004336 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004337}
4338
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004339struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4340 u16 rif_index)
4341{
4342 return mlxsw_sp->router->rifs[rif_index];
4343}
4344
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004345u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4346{
4347 return rif->rif_index;
4348}
4349
4350int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4351{
4352 return rif->dev->ifindex;
4353}
4354
Ido Schimmel4724ba562017-03-10 08:53:39 +01004355static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004356mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4357 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004358{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004359 u32 tb_id = l3mdev_fib_table(params->dev);
4360 const struct mlxsw_sp_rif_ops *ops;
4361 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004362 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004363 struct mlxsw_sp_fid *fid;
4364 struct mlxsw_sp_vr *vr;
4365 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004366 int err;
4367
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004368 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4369 ops = mlxsw_sp->router->rif_ops_arr[type];
4370
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004371 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4372 if (IS_ERR(vr))
4373 return ERR_CAST(vr);
4374
Ido Schimmelde5ed992017-06-04 16:53:40 +02004375 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4376 if (err)
4377 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004378
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004379 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004380 if (!rif) {
4381 err = -ENOMEM;
4382 goto err_rif_alloc;
4383 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004384 rif->mlxsw_sp = mlxsw_sp;
4385 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004386
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004387 fid = ops->fid_get(rif);
4388 if (IS_ERR(fid)) {
4389 err = PTR_ERR(fid);
4390 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004391 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004392 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004393
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004394 if (ops->setup)
4395 ops->setup(rif, params);
4396
4397 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004398 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004399 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004400
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004401 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004402 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004403 if (err)
4404 goto err_rif_fdb_op;
4405
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004406 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004407 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004408 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004409 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004410
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004411 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004412
Ido Schimmel4724ba562017-03-10 08:53:39 +01004413err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004414 ops->deconfigure(rif);
4415err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004416 mlxsw_sp_fid_put(fid);
4417err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004418 kfree(rif);
4419err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004420err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004421 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004422 return ERR_PTR(err);
4423}
4424
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004425void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004426{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004427 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4428 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004429 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004430 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004431
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004432 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004433 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004434
Ido Schimmel69132292017-03-10 08:53:42 +01004435 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004436 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004437 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004438 mlxsw_sp_rif_counters_free(rif);
4439 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4440 mlxsw_sp_fid_index(fid), false);
4441 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004442 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004443 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004444 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004445}
4446
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004447static void
4448mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4449 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4450{
4451 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4452
4453 params->vid = mlxsw_sp_port_vlan->vid;
4454 params->lag = mlxsw_sp_port->lagged;
4455 if (params->lag)
4456 params->lag_id = mlxsw_sp_port->lag_id;
4457 else
4458 params->system_port = mlxsw_sp_port->local_port;
4459}
4460
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004461static int
Ido Schimmela1107482017-05-26 08:37:39 +02004462mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004463 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004464{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004465 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004466 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004467 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004468 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004469 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004470 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004471
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004472 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004473 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004474 struct mlxsw_sp_rif_params params = {
4475 .dev = l3_dev,
4476 };
4477
4478 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4479 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004480 if (IS_ERR(rif))
4481 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004482 }
4483
Ido Schimmela1107482017-05-26 08:37:39 +02004484 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004485 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004486 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4487 if (err)
4488 goto err_fid_port_vid_map;
4489
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004490 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004491 if (err)
4492 goto err_port_vid_learning_set;
4493
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004494 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004495 BR_STATE_FORWARDING);
4496 if (err)
4497 goto err_port_vid_stp_set;
4498
Ido Schimmela1107482017-05-26 08:37:39 +02004499 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004500
Ido Schimmel4724ba562017-03-10 08:53:39 +01004501 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004502
4503err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004504 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004505err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004506 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4507err_fid_port_vid_map:
4508 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004509 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004510}
4511
Ido Schimmela1107482017-05-26 08:37:39 +02004512void
4513mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004514{
Ido Schimmelce95e152017-05-26 08:37:27 +02004515 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004516 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004517 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004518
Ido Schimmela1107482017-05-26 08:37:39 +02004519 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4520 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004521
Ido Schimmela1107482017-05-26 08:37:39 +02004522 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004523 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4524 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004525 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4526 /* If router port holds the last reference on the rFID, then the
4527 * associated Sub-port RIF will be destroyed.
4528 */
4529 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004530}
4531
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004532static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4533 struct net_device *port_dev,
4534 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004535{
4536 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004537 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004538
Ido Schimmelce95e152017-05-26 08:37:27 +02004539 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004540 if (WARN_ON(!mlxsw_sp_port_vlan))
4541 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004542
4543 switch (event) {
4544 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004545 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004546 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004547 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004548 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004549 break;
4550 }
4551
4552 return 0;
4553}
4554
4555static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4556 unsigned long event)
4557{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004558 if (netif_is_bridge_port(port_dev) ||
4559 netif_is_lag_port(port_dev) ||
4560 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004561 return 0;
4562
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004563 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004564}
4565
4566static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4567 struct net_device *lag_dev,
4568 unsigned long event, u16 vid)
4569{
4570 struct net_device *port_dev;
4571 struct list_head *iter;
4572 int err;
4573
4574 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4575 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004576 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4577 port_dev,
4578 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004579 if (err)
4580 return err;
4581 }
4582 }
4583
4584 return 0;
4585}
4586
4587static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4588 unsigned long event)
4589{
4590 if (netif_is_bridge_port(lag_dev))
4591 return 0;
4592
4593 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4594}
4595
Ido Schimmel4724ba562017-03-10 08:53:39 +01004596static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004597 unsigned long event)
4598{
4599 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004600 struct mlxsw_sp_rif_params params = {
4601 .dev = l3_dev,
4602 };
Ido Schimmela1107482017-05-26 08:37:39 +02004603 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004604
4605 switch (event) {
4606 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004607 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4608 if (IS_ERR(rif))
4609 return PTR_ERR(rif);
4610 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004611 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004612 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004613 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004614 break;
4615 }
4616
4617 return 0;
4618}
4619
4620static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4621 unsigned long event)
4622{
4623 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004624 u16 vid = vlan_dev_vlan_id(vlan_dev);
4625
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004626 if (netif_is_bridge_port(vlan_dev))
4627 return 0;
4628
Ido Schimmel4724ba562017-03-10 08:53:39 +01004629 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004630 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4631 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004632 else if (netif_is_lag_master(real_dev))
4633 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4634 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004635 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004636 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004637
4638 return 0;
4639}
4640
Ido Schimmelb1e45522017-04-30 19:47:14 +03004641static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4642 unsigned long event)
4643{
4644 if (mlxsw_sp_port_dev_check(dev))
4645 return mlxsw_sp_inetaddr_port_event(dev, event);
4646 else if (netif_is_lag_master(dev))
4647 return mlxsw_sp_inetaddr_lag_event(dev, event);
4648 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004649 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004650 else if (is_vlan_dev(dev))
4651 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4652 else
4653 return 0;
4654}
4655
Ido Schimmel4724ba562017-03-10 08:53:39 +01004656int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4657 unsigned long event, void *ptr)
4658{
4659 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4660 struct net_device *dev = ifa->ifa_dev->dev;
4661 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004662 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004663 int err = 0;
4664
4665 mlxsw_sp = mlxsw_sp_lower_get(dev);
4666 if (!mlxsw_sp)
4667 goto out;
4668
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004669 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004670 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004671 goto out;
4672
Ido Schimmelb1e45522017-04-30 19:47:14 +03004673 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004674out:
4675 return notifier_from_errno(err);
4676}
4677
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004678struct mlxsw_sp_inet6addr_event_work {
4679 struct work_struct work;
4680 struct net_device *dev;
4681 unsigned long event;
4682};
4683
4684static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4685{
4686 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4687 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4688 struct net_device *dev = inet6addr_work->dev;
4689 unsigned long event = inet6addr_work->event;
4690 struct mlxsw_sp *mlxsw_sp;
4691 struct mlxsw_sp_rif *rif;
4692
4693 rtnl_lock();
4694 mlxsw_sp = mlxsw_sp_lower_get(dev);
4695 if (!mlxsw_sp)
4696 goto out;
4697
4698 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4699 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4700 goto out;
4701
4702 __mlxsw_sp_inetaddr_event(dev, event);
4703out:
4704 rtnl_unlock();
4705 dev_put(dev);
4706 kfree(inet6addr_work);
4707}
4708
4709/* Called with rcu_read_lock() */
4710int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4711 unsigned long event, void *ptr)
4712{
4713 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4714 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4715 struct net_device *dev = if6->idev->dev;
4716
4717 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4718 return NOTIFY_DONE;
4719
4720 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4721 if (!inet6addr_work)
4722 return NOTIFY_BAD;
4723
4724 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4725 inet6addr_work->dev = dev;
4726 inet6addr_work->event = event;
4727 dev_hold(dev);
4728 mlxsw_core_schedule_work(&inet6addr_work->work);
4729
4730 return NOTIFY_DONE;
4731}
4732
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004733static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004734 const char *mac, int mtu)
4735{
4736 char ritr_pl[MLXSW_REG_RITR_LEN];
4737 int err;
4738
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004739 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004740 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4741 if (err)
4742 return err;
4743
4744 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4745 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4746 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4747 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4748}
4749
4750int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4751{
4752 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004753 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004754 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004755 int err;
4756
4757 mlxsw_sp = mlxsw_sp_lower_get(dev);
4758 if (!mlxsw_sp)
4759 return 0;
4760
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004761 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4762 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004763 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004764 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004765
Ido Schimmela1107482017-05-26 08:37:39 +02004766 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004767 if (err)
4768 return err;
4769
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004770 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4771 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004772 if (err)
4773 goto err_rif_edit;
4774
Ido Schimmela1107482017-05-26 08:37:39 +02004775 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004776 if (err)
4777 goto err_rif_fdb_op;
4778
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004779 ether_addr_copy(rif->addr, dev->dev_addr);
4780 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004781
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004782 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004783
4784 return 0;
4785
4786err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004787 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004788err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004789 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004790 return err;
4791}
4792
Ido Schimmelb1e45522017-04-30 19:47:14 +03004793static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4794 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004795{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004796 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004797
Ido Schimmelb1e45522017-04-30 19:47:14 +03004798 /* If netdev is already associated with a RIF, then we need to
4799 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004800 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004801 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4802 if (rif)
4803 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004804
Ido Schimmelb1e45522017-04-30 19:47:14 +03004805 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004806}
4807
Ido Schimmelb1e45522017-04-30 19:47:14 +03004808static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4809 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004810{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004811 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004812
Ido Schimmelb1e45522017-04-30 19:47:14 +03004813 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4814 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004815 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004816 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004817}
4818
Ido Schimmelb1e45522017-04-30 19:47:14 +03004819int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4820 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004821{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004822 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4823 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004824
Ido Schimmelb1e45522017-04-30 19:47:14 +03004825 if (!mlxsw_sp)
4826 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004827
Ido Schimmelb1e45522017-04-30 19:47:14 +03004828 switch (event) {
4829 case NETDEV_PRECHANGEUPPER:
4830 return 0;
4831 case NETDEV_CHANGEUPPER:
4832 if (info->linking)
4833 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4834 else
4835 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4836 break;
4837 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004838
Ido Schimmelb1e45522017-04-30 19:47:14 +03004839 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004840}
4841
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004842static struct mlxsw_sp_rif_subport *
4843mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004844{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004845 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004846}
4847
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004848static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4849 const struct mlxsw_sp_rif_params *params)
4850{
4851 struct mlxsw_sp_rif_subport *rif_subport;
4852
4853 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4854 rif_subport->vid = params->vid;
4855 rif_subport->lag = params->lag;
4856 if (params->lag)
4857 rif_subport->lag_id = params->lag_id;
4858 else
4859 rif_subport->system_port = params->system_port;
4860}
4861
4862static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4863{
4864 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4865 struct mlxsw_sp_rif_subport *rif_subport;
4866 char ritr_pl[MLXSW_REG_RITR_LEN];
4867
4868 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4869 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4870 rif->rif_index, rif->vr_id, rif->dev->mtu,
4871 rif->dev->dev_addr);
4872 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4873 rif_subport->lag ? rif_subport->lag_id :
4874 rif_subport->system_port,
4875 rif_subport->vid);
4876
4877 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4878}
4879
4880static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4881{
4882 return mlxsw_sp_rif_subport_op(rif, true);
4883}
4884
4885static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4886{
4887 mlxsw_sp_rif_subport_op(rif, false);
4888}
4889
4890static struct mlxsw_sp_fid *
4891mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4892{
4893 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4894}
4895
4896static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4897 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4898 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4899 .setup = mlxsw_sp_rif_subport_setup,
4900 .configure = mlxsw_sp_rif_subport_configure,
4901 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4902 .fid_get = mlxsw_sp_rif_subport_fid_get,
4903};
4904
4905static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4906 enum mlxsw_reg_ritr_if_type type,
4907 u16 vid_fid, bool enable)
4908{
4909 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4910 char ritr_pl[MLXSW_REG_RITR_LEN];
4911
4912 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4913 rif->dev->mtu, rif->dev->dev_addr);
4914 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4915
4916 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4917}
4918
4919static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4920{
4921 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4922}
4923
4924static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4925{
4926 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4927 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4928 int err;
4929
4930 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4931 if (err)
4932 return err;
4933
Ido Schimmel0d284812017-07-18 10:10:12 +02004934 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4935 mlxsw_sp_router_port(mlxsw_sp), true);
4936 if (err)
4937 goto err_fid_mc_flood_set;
4938
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004939 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4940 mlxsw_sp_router_port(mlxsw_sp), true);
4941 if (err)
4942 goto err_fid_bc_flood_set;
4943
4944 return 0;
4945
4946err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004947 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4948 mlxsw_sp_router_port(mlxsw_sp), false);
4949err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004950 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4951 return err;
4952}
4953
4954static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
4955{
4956 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4957 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4958
4959 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4960 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004961 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4962 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004963 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4964}
4965
4966static struct mlxsw_sp_fid *
4967mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
4968{
4969 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
4970
4971 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
4972}
4973
4974static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
4975 .type = MLXSW_SP_RIF_TYPE_VLAN,
4976 .rif_size = sizeof(struct mlxsw_sp_rif),
4977 .configure = mlxsw_sp_rif_vlan_configure,
4978 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
4979 .fid_get = mlxsw_sp_rif_vlan_fid_get,
4980};
4981
4982static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
4983{
4984 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4985 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4986 int err;
4987
4988 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
4989 true);
4990 if (err)
4991 return err;
4992
Ido Schimmel0d284812017-07-18 10:10:12 +02004993 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4994 mlxsw_sp_router_port(mlxsw_sp), true);
4995 if (err)
4996 goto err_fid_mc_flood_set;
4997
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004998 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4999 mlxsw_sp_router_port(mlxsw_sp), true);
5000 if (err)
5001 goto err_fid_bc_flood_set;
5002
5003 return 0;
5004
5005err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005006 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5007 mlxsw_sp_router_port(mlxsw_sp), false);
5008err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005009 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5010 return err;
5011}
5012
5013static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5014{
5015 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5016 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5017
5018 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5019 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005020 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5021 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005022 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5023}
5024
5025static struct mlxsw_sp_fid *
5026mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5027{
5028 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5029}
5030
5031static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5032 .type = MLXSW_SP_RIF_TYPE_FID,
5033 .rif_size = sizeof(struct mlxsw_sp_rif),
5034 .configure = mlxsw_sp_rif_fid_configure,
5035 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5036 .fid_get = mlxsw_sp_rif_fid_fid_get,
5037};
5038
5039static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5040 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5041 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5042 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
5043};
5044
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005045static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5046{
5047 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5048
5049 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5050 sizeof(struct mlxsw_sp_rif *),
5051 GFP_KERNEL);
5052 if (!mlxsw_sp->router->rifs)
5053 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005054
5055 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5056
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005057 return 0;
5058}
5059
5060static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5061{
5062 int i;
5063
5064 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5065 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5066
5067 kfree(mlxsw_sp->router->rifs);
5068}
5069
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005070static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5071{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005072 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005073
5074 /* Flush pending FIB notifications and then flush the device's
5075 * table before requesting another dump. The FIB notification
5076 * block is unregistered, so no need to take RTNL.
5077 */
5078 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005079 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5080 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005081}
5082
Ido Schimmel4724ba562017-03-10 08:53:39 +01005083static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5084{
5085 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5086 u64 max_rifs;
5087 int err;
5088
5089 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5090 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005091 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005092
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005093 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005094 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5095 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5096 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005097 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005098 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005099}
5100
5101static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5102{
5103 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005104
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005105 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005106 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005107}
5108
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005109int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5110{
Ido Schimmel9011b672017-05-16 19:38:25 +02005111 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005112 int err;
5113
Ido Schimmel9011b672017-05-16 19:38:25 +02005114 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5115 if (!router)
5116 return -ENOMEM;
5117 mlxsw_sp->router = router;
5118 router->mlxsw_sp = mlxsw_sp;
5119
5120 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005121 err = __mlxsw_sp_router_init(mlxsw_sp);
5122 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005123 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005124
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005125 err = mlxsw_sp_rifs_init(mlxsw_sp);
5126 if (err)
5127 goto err_rifs_init;
5128
Ido Schimmel9011b672017-05-16 19:38:25 +02005129 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005130 &mlxsw_sp_nexthop_ht_params);
5131 if (err)
5132 goto err_nexthop_ht_init;
5133
Ido Schimmel9011b672017-05-16 19:38:25 +02005134 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005135 &mlxsw_sp_nexthop_group_ht_params);
5136 if (err)
5137 goto err_nexthop_group_ht_init;
5138
Ido Schimmel8494ab02017-03-24 08:02:47 +01005139 err = mlxsw_sp_lpm_init(mlxsw_sp);
5140 if (err)
5141 goto err_lpm_init;
5142
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005143 err = mlxsw_sp_vrs_init(mlxsw_sp);
5144 if (err)
5145 goto err_vrs_init;
5146
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005147 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005148 if (err)
5149 goto err_neigh_init;
5150
Ido Schimmel7e39d112017-05-16 19:38:28 +02005151 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5152 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005153 mlxsw_sp_router_fib_dump_flush);
5154 if (err)
5155 goto err_register_fib_notifier;
5156
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005157 return 0;
5158
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005159err_register_fib_notifier:
5160 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005161err_neigh_init:
5162 mlxsw_sp_vrs_fini(mlxsw_sp);
5163err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005164 mlxsw_sp_lpm_fini(mlxsw_sp);
5165err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005166 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005167err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005168 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005169err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005170 mlxsw_sp_rifs_fini(mlxsw_sp);
5171err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005172 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005173err_router_init:
5174 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005175 return err;
5176}
5177
5178void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5179{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005180 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005181 mlxsw_sp_neigh_fini(mlxsw_sp);
5182 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005183 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005184 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5185 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005186 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005187 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005188 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005189}