blob: abccd84235ebd7786803f95c349039ea84e1c727 [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;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200904 unsigned int counter_index;
905 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200906};
907
908static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
909 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
910 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
911 .key_len = sizeof(struct mlxsw_sp_neigh_key),
912};
913
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +0200914struct mlxsw_sp_neigh_entry *
915mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
916 struct mlxsw_sp_neigh_entry *neigh_entry)
917{
918 if (!neigh_entry) {
919 if (list_empty(&rif->neigh_list))
920 return NULL;
921 else
922 return list_first_entry(&rif->neigh_list,
923 typeof(*neigh_entry),
924 rif_list_node);
925 }
926 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
927 return NULL;
928 return list_next_entry(neigh_entry, rif_list_node);
929}
930
931int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
932{
933 return neigh_entry->key.n->tbl->family;
934}
935
936unsigned char *
937mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
938{
939 return neigh_entry->ha;
940}
941
942u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
943{
944 struct neighbour *n;
945
946 n = neigh_entry->key.n;
947 return ntohl(*((__be32 *) n->primary_key));
948}
949
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200950int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
951 struct mlxsw_sp_neigh_entry *neigh_entry,
952 u64 *p_counter)
953{
954 if (!neigh_entry->counter_valid)
955 return -EINVAL;
956
957 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
958 p_counter, NULL);
959}
960
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100961static struct mlxsw_sp_neigh_entry *
962mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
963 u16 rif)
964{
965 struct mlxsw_sp_neigh_entry *neigh_entry;
966
967 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
968 if (!neigh_entry)
969 return NULL;
970
971 neigh_entry->key.n = n;
972 neigh_entry->rif = rif;
973 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
974
975 return neigh_entry;
976}
977
978static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
979{
980 kfree(neigh_entry);
981}
982
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200983static int
984mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
985 struct mlxsw_sp_neigh_entry *neigh_entry)
986{
Ido Schimmel9011b672017-05-16 19:38:25 +0200987 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200988 &neigh_entry->ht_node,
989 mlxsw_sp_neigh_ht_params);
990}
991
992static void
993mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
994 struct mlxsw_sp_neigh_entry *neigh_entry)
995{
Ido Schimmel9011b672017-05-16 19:38:25 +0200996 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200997 &neigh_entry->ht_node,
998 mlxsw_sp_neigh_ht_params);
999}
1000
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001001static bool
1002mlxsw_sp_neigh4_counter_should_alloc(struct mlxsw_sp *mlxsw_sp)
1003{
1004 struct devlink *devlink;
1005
1006 devlink = priv_to_devlink(mlxsw_sp->core);
1007 return devlink_dpipe_table_counter_enabled(devlink,
1008 MLXSW_SP_DPIPE_TABLE_NAME_HOST4);
1009}
1010
1011static void
1012mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1013 struct mlxsw_sp_neigh_entry *neigh_entry)
1014{
1015 if (mlxsw_sp_neigh_entry_type(neigh_entry) != AF_INET ||
1016 !mlxsw_sp_neigh4_counter_should_alloc(mlxsw_sp))
1017 return;
1018
1019 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1020 return;
1021
1022 neigh_entry->counter_valid = true;
1023}
1024
1025static void
1026mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1027 struct mlxsw_sp_neigh_entry *neigh_entry)
1028{
1029 if (!neigh_entry->counter_valid)
1030 return;
1031 mlxsw_sp_flow_counter_free(mlxsw_sp,
1032 neigh_entry->counter_index);
1033 neigh_entry->counter_valid = false;
1034}
1035
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001036static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001037mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001038{
1039 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001040 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001041 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001042
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001043 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1044 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001045 return ERR_PTR(-EINVAL);
1046
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001047 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001048 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001049 return ERR_PTR(-ENOMEM);
1050
1051 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1052 if (err)
1053 goto err_neigh_entry_insert;
1054
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001055 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001056 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001057
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001058 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001059
1060err_neigh_entry_insert:
1061 mlxsw_sp_neigh_entry_free(neigh_entry);
1062 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001063}
1064
1065static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001066mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1067 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001068{
Ido Schimmel9665b742017-02-08 11:16:42 +01001069 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001070 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001071 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1072 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001073}
1074
1075static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001076mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001077{
Jiri Pirko33b13412016-11-10 12:31:04 +01001078 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001079
Jiri Pirko33b13412016-11-10 12:31:04 +01001080 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001081 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001082 &key, mlxsw_sp_neigh_ht_params);
1083}
1084
Yotam Gigic723c7352016-07-05 11:27:43 +02001085static void
1086mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1087{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001088 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001089
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001090#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001091 interval = min_t(unsigned long,
1092 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1093 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001094#else
1095 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1096#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001097 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001098}
1099
1100static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1101 char *rauhtd_pl,
1102 int ent_index)
1103{
1104 struct net_device *dev;
1105 struct neighbour *n;
1106 __be32 dipn;
1107 u32 dip;
1108 u16 rif;
1109
1110 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1111
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001112 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001113 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1114 return;
1115 }
1116
1117 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001118 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001119 n = neigh_lookup(&arp_tbl, &dipn, dev);
1120 if (!n) {
1121 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1122 &dip);
1123 return;
1124 }
1125
1126 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1127 neigh_event_send(n, NULL);
1128 neigh_release(n);
1129}
1130
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001131#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001132static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1133 char *rauhtd_pl,
1134 int rec_index)
1135{
1136 struct net_device *dev;
1137 struct neighbour *n;
1138 struct in6_addr dip;
1139 u16 rif;
1140
1141 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1142 (char *) &dip);
1143
1144 if (!mlxsw_sp->router->rifs[rif]) {
1145 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1146 return;
1147 }
1148
1149 dev = mlxsw_sp->router->rifs[rif]->dev;
1150 n = neigh_lookup(&nd_tbl, &dip, dev);
1151 if (!n) {
1152 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1153 &dip);
1154 return;
1155 }
1156
1157 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1158 neigh_event_send(n, NULL);
1159 neigh_release(n);
1160}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001161#else
1162static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1163 char *rauhtd_pl,
1164 int rec_index)
1165{
1166}
1167#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001168
Yotam Gigic723c7352016-07-05 11:27:43 +02001169static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1170 char *rauhtd_pl,
1171 int rec_index)
1172{
1173 u8 num_entries;
1174 int i;
1175
1176 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1177 rec_index);
1178 /* Hardware starts counting at 0, so add 1. */
1179 num_entries++;
1180
1181 /* Each record consists of several neighbour entries. */
1182 for (i = 0; i < num_entries; i++) {
1183 int ent_index;
1184
1185 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1186 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1187 ent_index);
1188 }
1189
1190}
1191
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001192static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1193 char *rauhtd_pl,
1194 int rec_index)
1195{
1196 /* One record contains one entry. */
1197 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1198 rec_index);
1199}
1200
Yotam Gigic723c7352016-07-05 11:27:43 +02001201static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1202 char *rauhtd_pl, int rec_index)
1203{
1204 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1205 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1206 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1207 rec_index);
1208 break;
1209 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001210 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1211 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001212 break;
1213 }
1214}
1215
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001216static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1217{
1218 u8 num_rec, last_rec_index, num_entries;
1219
1220 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1221 last_rec_index = num_rec - 1;
1222
1223 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1224 return false;
1225 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1226 MLXSW_REG_RAUHTD_TYPE_IPV6)
1227 return true;
1228
1229 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1230 last_rec_index);
1231 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1232 return true;
1233 return false;
1234}
1235
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001236static int
1237__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1238 char *rauhtd_pl,
1239 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001240{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001241 int i, num_rec;
1242 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001243
1244 /* Make sure the neighbour's netdev isn't removed in the
1245 * process.
1246 */
1247 rtnl_lock();
1248 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001249 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001250 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1251 rauhtd_pl);
1252 if (err) {
1253 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1254 break;
1255 }
1256 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1257 for (i = 0; i < num_rec; i++)
1258 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1259 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001260 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001261 rtnl_unlock();
1262
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001263 return err;
1264}
1265
1266static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1267{
1268 enum mlxsw_reg_rauhtd_type type;
1269 char *rauhtd_pl;
1270 int err;
1271
1272 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1273 if (!rauhtd_pl)
1274 return -ENOMEM;
1275
1276 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1277 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1278 if (err)
1279 goto out;
1280
1281 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1282 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1283out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001284 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001285 return err;
1286}
1287
1288static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1289{
1290 struct mlxsw_sp_neigh_entry *neigh_entry;
1291
1292 /* Take RTNL mutex here to prevent lists from changes */
1293 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001294 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001295 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001296 /* If this neigh have nexthops, make the kernel think this neigh
1297 * is active regardless of the traffic.
1298 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001299 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001300 rtnl_unlock();
1301}
1302
1303static void
1304mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1305{
Ido Schimmel9011b672017-05-16 19:38:25 +02001306 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001307
Ido Schimmel9011b672017-05-16 19:38:25 +02001308 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001309 msecs_to_jiffies(interval));
1310}
1311
1312static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1313{
Ido Schimmel9011b672017-05-16 19:38:25 +02001314 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001315 int err;
1316
Ido Schimmel9011b672017-05-16 19:38:25 +02001317 router = container_of(work, struct mlxsw_sp_router,
1318 neighs_update.dw.work);
1319 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001320 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001321 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001322
Ido Schimmel9011b672017-05-16 19:38:25 +02001323 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001324
Ido Schimmel9011b672017-05-16 19:38:25 +02001325 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001326}
1327
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001328static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1329{
1330 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001331 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001332
Ido Schimmel9011b672017-05-16 19:38:25 +02001333 router = container_of(work, struct mlxsw_sp_router,
1334 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001335 /* Iterate over nexthop neighbours, find those who are unresolved and
1336 * send arp on them. This solves the chicken-egg problem when
1337 * the nexthop wouldn't get offloaded until the neighbor is resolved
1338 * but it wouldn't get resolved ever in case traffic is flowing in HW
1339 * using different nexthop.
1340 *
1341 * Take RTNL mutex here to prevent lists from changes.
1342 */
1343 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001344 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001345 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001346 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001347 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001348 rtnl_unlock();
1349
Ido Schimmel9011b672017-05-16 19:38:25 +02001350 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001351 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1352}
1353
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001354static void
1355mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1356 struct mlxsw_sp_neigh_entry *neigh_entry,
1357 bool removing);
1358
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001359static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001360{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001361 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1362 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1363}
1364
1365static void
1366mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1367 struct mlxsw_sp_neigh_entry *neigh_entry,
1368 enum mlxsw_reg_rauht_op op)
1369{
Jiri Pirko33b13412016-11-10 12:31:04 +01001370 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001371 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001372 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001373
1374 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1375 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001376 if (neigh_entry->counter_valid)
1377 mlxsw_reg_rauht_pack_counter(rauht_pl,
1378 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001379 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1380}
1381
1382static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001383mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1384 struct mlxsw_sp_neigh_entry *neigh_entry,
1385 enum mlxsw_reg_rauht_op op)
1386{
1387 struct neighbour *n = neigh_entry->key.n;
1388 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1389 const char *dip = n->primary_key;
1390
1391 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1392 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001393 if (neigh_entry->counter_valid)
1394 mlxsw_reg_rauht_pack_counter(rauht_pl,
1395 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001396 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1397}
1398
1399static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1400{
1401 /* Packets with a link-local destination address are trapped
1402 * after LPM lookup and never reach the neighbour table, so
1403 * there is no need to program such neighbours to the device.
1404 */
1405 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1406 IPV6_ADDR_LINKLOCAL)
1407 return true;
1408 return false;
1409}
1410
1411static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001412mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1413 struct mlxsw_sp_neigh_entry *neigh_entry,
1414 bool adding)
1415{
1416 if (!adding && !neigh_entry->connected)
1417 return;
1418 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001419 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001420 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1421 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001422 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001423 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1424 return;
1425 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1426 mlxsw_sp_rauht_op(adding));
1427 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001428 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001429 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001430}
1431
1432struct mlxsw_sp_neigh_event_work {
1433 struct work_struct work;
1434 struct mlxsw_sp *mlxsw_sp;
1435 struct neighbour *n;
1436};
1437
1438static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1439{
1440 struct mlxsw_sp_neigh_event_work *neigh_work =
1441 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1442 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1443 struct mlxsw_sp_neigh_entry *neigh_entry;
1444 struct neighbour *n = neigh_work->n;
1445 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001446 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001447 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001448
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001449 /* If these parameters are changed after we release the lock,
1450 * then we are guaranteed to receive another event letting us
1451 * know about it.
1452 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001453 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001454 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001455 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001456 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001457 read_unlock_bh(&n->lock);
1458
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001459 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001460 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001461 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1462 if (!entry_connected && !neigh_entry)
1463 goto out;
1464 if (!neigh_entry) {
1465 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1466 if (IS_ERR(neigh_entry))
1467 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001468 }
1469
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001470 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1471 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1472 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1473
1474 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1475 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1476
1477out:
1478 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001479 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001480 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001481}
1482
Jiri Pirkoe7322632016-09-01 10:37:43 +02001483int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1484 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001485{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001486 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001487 struct mlxsw_sp_port *mlxsw_sp_port;
1488 struct mlxsw_sp *mlxsw_sp;
1489 unsigned long interval;
1490 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001491 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001492
1493 switch (event) {
1494 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1495 p = ptr;
1496
1497 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001498 if (!p->dev || (p->tbl->family != AF_INET &&
1499 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001500 return NOTIFY_DONE;
1501
1502 /* We are in atomic context and can't take RTNL mutex,
1503 * so use RCU variant to walk the device chain.
1504 */
1505 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1506 if (!mlxsw_sp_port)
1507 return NOTIFY_DONE;
1508
1509 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1510 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001511 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001512
1513 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1514 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001515 case NETEVENT_NEIGH_UPDATE:
1516 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001517
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001518 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001519 return NOTIFY_DONE;
1520
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001521 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001522 if (!mlxsw_sp_port)
1523 return NOTIFY_DONE;
1524
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001525 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1526 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001527 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001528 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001529 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001530
1531 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1532 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1533 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001534
1535 /* Take a reference to ensure the neighbour won't be
1536 * destructed until we drop the reference in delayed
1537 * work.
1538 */
1539 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001540 mlxsw_core_schedule_work(&neigh_work->work);
1541 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001542 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001543 }
1544
1545 return NOTIFY_DONE;
1546}
1547
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001548static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1549{
Yotam Gigic723c7352016-07-05 11:27:43 +02001550 int err;
1551
Ido Schimmel9011b672017-05-16 19:38:25 +02001552 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001553 &mlxsw_sp_neigh_ht_params);
1554 if (err)
1555 return err;
1556
1557 /* Initialize the polling interval according to the default
1558 * table.
1559 */
1560 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1561
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001562 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001563 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001564 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001565 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001566 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001567 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1568 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001569 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001570}
1571
1572static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1573{
Ido Schimmel9011b672017-05-16 19:38:25 +02001574 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1575 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1576 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001577}
1578
Ido Schimmel9665b742017-02-08 11:16:42 +01001579static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001580 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001581{
1582 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1583
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001584 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001585 rif_list_node) {
1586 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001587 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001588 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001589}
1590
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001591struct mlxsw_sp_nexthop_key {
1592 struct fib_nh *fib_nh;
1593};
1594
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001595struct mlxsw_sp_nexthop {
1596 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001597 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001598 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1599 * this belongs to
1600 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001601 struct rhash_head ht_node;
1602 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001603 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001604 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001605 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001606 u8 should_offload:1, /* set indicates this neigh is connected and
1607 * should be put to KVD linear area of this group.
1608 */
1609 offloaded:1, /* set in case the neigh is actually put into
1610 * KVD linear area of this group.
1611 */
1612 update:1; /* set indicates that MAC of this neigh should be
1613 * updated in HW
1614 */
1615 struct mlxsw_sp_neigh_entry *neigh_entry;
1616};
1617
1618struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001619 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001620 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001621 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001622 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001623 u8 adj_index_valid:1,
1624 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001625 u32 adj_index;
1626 u16 ecmp_size;
1627 u16 count;
1628 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001629#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001630};
1631
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001632static struct fib_info *
1633mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1634{
1635 return nh_grp->priv;
1636}
1637
1638struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001639 enum mlxsw_sp_l3proto proto;
1640 union {
1641 struct fib_info *fi;
1642 struct mlxsw_sp_fib6_entry *fib6_entry;
1643 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001644};
1645
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001646static bool
1647mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
1648 const struct in6_addr *gw, int ifindex)
1649{
1650 int i;
1651
1652 for (i = 0; i < nh_grp->count; i++) {
1653 const struct mlxsw_sp_nexthop *nh;
1654
1655 nh = &nh_grp->nexthops[i];
1656 if (nh->ifindex == ifindex &&
1657 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
1658 return true;
1659 }
1660
1661 return false;
1662}
1663
1664static bool
1665mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
1666 const struct mlxsw_sp_fib6_entry *fib6_entry)
1667{
1668 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1669
1670 if (nh_grp->count != fib6_entry->nrt6)
1671 return false;
1672
1673 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1674 struct in6_addr *gw;
1675 int ifindex;
1676
1677 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
1678 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
1679 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
1680 return false;
1681 }
1682
1683 return true;
1684}
1685
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001686static int
1687mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1688{
1689 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1690 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1691
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001692 switch (cmp_arg->proto) {
1693 case MLXSW_SP_L3_PROTO_IPV4:
1694 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1695 case MLXSW_SP_L3_PROTO_IPV6:
1696 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
1697 cmp_arg->fib6_entry);
1698 default:
1699 WARN_ON(1);
1700 return 1;
1701 }
1702}
1703
1704static int
1705mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
1706{
1707 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001708}
1709
1710static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1711{
1712 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001713 const struct mlxsw_sp_nexthop *nh;
1714 struct fib_info *fi;
1715 unsigned int val;
1716 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001717
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001718 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
1719 case AF_INET:
1720 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1721 return jhash(&fi, sizeof(fi), seed);
1722 case AF_INET6:
1723 val = nh_grp->count;
1724 for (i = 0; i < nh_grp->count; i++) {
1725 nh = &nh_grp->nexthops[i];
1726 val ^= nh->ifindex;
1727 }
1728 return jhash(&val, sizeof(val), seed);
1729 default:
1730 WARN_ON(1);
1731 return 0;
1732 }
1733}
1734
1735static u32
1736mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
1737{
1738 unsigned int val = fib6_entry->nrt6;
1739 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1740 struct net_device *dev;
1741
1742 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1743 dev = mlxsw_sp_rt6->rt->dst.dev;
1744 val ^= dev->ifindex;
1745 }
1746
1747 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001748}
1749
1750static u32
1751mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1752{
1753 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1754
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001755 switch (cmp_arg->proto) {
1756 case MLXSW_SP_L3_PROTO_IPV4:
1757 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1758 case MLXSW_SP_L3_PROTO_IPV6:
1759 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
1760 default:
1761 WARN_ON(1);
1762 return 0;
1763 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001764}
1765
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001766static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001767 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001768 .hashfn = mlxsw_sp_nexthop_group_hash,
1769 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1770 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001771};
1772
1773static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1774 struct mlxsw_sp_nexthop_group *nh_grp)
1775{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001776 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1777 !nh_grp->gateway)
1778 return 0;
1779
Ido Schimmel9011b672017-05-16 19:38:25 +02001780 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001781 &nh_grp->ht_node,
1782 mlxsw_sp_nexthop_group_ht_params);
1783}
1784
1785static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1786 struct mlxsw_sp_nexthop_group *nh_grp)
1787{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001788 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1789 !nh_grp->gateway)
1790 return;
1791
Ido Schimmel9011b672017-05-16 19:38:25 +02001792 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001793 &nh_grp->ht_node,
1794 mlxsw_sp_nexthop_group_ht_params);
1795}
1796
1797static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001798mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1799 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001800{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001801 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1802
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001803 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001804 cmp_arg.fi = fi;
1805 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1806 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001807 mlxsw_sp_nexthop_group_ht_params);
1808}
1809
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001810static struct mlxsw_sp_nexthop_group *
1811mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
1812 struct mlxsw_sp_fib6_entry *fib6_entry)
1813{
1814 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1815
1816 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
1817 cmp_arg.fib6_entry = fib6_entry;
1818 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1819 &cmp_arg,
1820 mlxsw_sp_nexthop_group_ht_params);
1821}
1822
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001823static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1824 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1825 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1826 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1827};
1828
1829static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1830 struct mlxsw_sp_nexthop *nh)
1831{
Ido Schimmel9011b672017-05-16 19:38:25 +02001832 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001833 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1834}
1835
1836static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1837 struct mlxsw_sp_nexthop *nh)
1838{
Ido Schimmel9011b672017-05-16 19:38:25 +02001839 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001840 mlxsw_sp_nexthop_ht_params);
1841}
1842
Ido Schimmelad178c82017-02-08 11:16:40 +01001843static struct mlxsw_sp_nexthop *
1844mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1845 struct mlxsw_sp_nexthop_key key)
1846{
Ido Schimmel9011b672017-05-16 19:38:25 +02001847 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001848 mlxsw_sp_nexthop_ht_params);
1849}
1850
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001851static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001852 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001853 u32 adj_index, u16 ecmp_size,
1854 u32 new_adj_index,
1855 u16 new_ecmp_size)
1856{
1857 char raleu_pl[MLXSW_REG_RALEU_LEN];
1858
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001859 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001860 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1861 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001862 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001863 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1864}
1865
1866static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1867 struct mlxsw_sp_nexthop_group *nh_grp,
1868 u32 old_adj_index, u16 old_ecmp_size)
1869{
1870 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001871 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001872 int err;
1873
1874 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001875 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001876 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001877 fib = fib_entry->fib_node->fib;
1878 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001879 old_adj_index,
1880 old_ecmp_size,
1881 nh_grp->adj_index,
1882 nh_grp->ecmp_size);
1883 if (err)
1884 return err;
1885 }
1886 return 0;
1887}
1888
1889static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1890 struct mlxsw_sp_nexthop *nh)
1891{
1892 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1893 char ratr_pl[MLXSW_REG_RATR_LEN];
1894
1895 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1896 true, adj_index, neigh_entry->rif);
1897 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1898 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1899}
1900
1901static int
1902mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001903 struct mlxsw_sp_nexthop_group *nh_grp,
1904 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001905{
1906 u32 adj_index = nh_grp->adj_index; /* base */
1907 struct mlxsw_sp_nexthop *nh;
1908 int i;
1909 int err;
1910
1911 for (i = 0; i < nh_grp->count; i++) {
1912 nh = &nh_grp->nexthops[i];
1913
1914 if (!nh->should_offload) {
1915 nh->offloaded = 0;
1916 continue;
1917 }
1918
Ido Schimmela59b7e02017-01-23 11:11:42 +01001919 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001920 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1921 adj_index, nh);
1922 if (err)
1923 return err;
1924 nh->update = 0;
1925 nh->offloaded = 1;
1926 }
1927 adj_index++;
1928 }
1929 return 0;
1930}
1931
1932static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1933 struct mlxsw_sp_fib_entry *fib_entry);
1934
Ido Schimmel1819ae32017-07-21 18:04:28 +02001935static bool
1936mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1937 const struct mlxsw_sp_fib_entry *fib_entry);
1938
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001939static int
1940mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1941 struct mlxsw_sp_nexthop_group *nh_grp)
1942{
1943 struct mlxsw_sp_fib_entry *fib_entry;
1944 int err;
1945
1946 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001947 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1948 fib_entry))
1949 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001950 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1951 if (err)
1952 return err;
1953 }
1954 return 0;
1955}
1956
1957static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001958mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1959 enum mlxsw_reg_ralue_op op, int err);
1960
1961static void
1962mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1963{
1964 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1965 struct mlxsw_sp_fib_entry *fib_entry;
1966
1967 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1968 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1969 fib_entry))
1970 continue;
1971 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1972 }
1973}
1974
1975static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001976mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1977 struct mlxsw_sp_nexthop_group *nh_grp)
1978{
1979 struct mlxsw_sp_nexthop *nh;
1980 bool offload_change = false;
1981 u32 adj_index;
1982 u16 ecmp_size = 0;
1983 bool old_adj_index_valid;
1984 u32 old_adj_index;
1985 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001986 int i;
1987 int err;
1988
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001989 if (!nh_grp->gateway) {
1990 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1991 return;
1992 }
1993
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001994 for (i = 0; i < nh_grp->count; i++) {
1995 nh = &nh_grp->nexthops[i];
1996
Petr Machata56b8a9e2017-07-31 09:27:29 +02001997 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001998 offload_change = true;
1999 if (nh->should_offload)
2000 nh->update = 1;
2001 }
2002 if (nh->should_offload)
2003 ecmp_size++;
2004 }
2005 if (!offload_change) {
2006 /* Nothing was added or removed, so no need to reallocate. Just
2007 * update MAC on existing adjacency indexes.
2008 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01002009 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
2010 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002011 if (err) {
2012 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2013 goto set_trap;
2014 }
2015 return;
2016 }
2017 if (!ecmp_size)
2018 /* No neigh of this group is connected so we just set
2019 * the trap and let everthing flow through kernel.
2020 */
2021 goto set_trap;
2022
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002023 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2024 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002025 /* We ran out of KVD linear space, just set the
2026 * trap and let everything flow through kernel.
2027 */
2028 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2029 goto set_trap;
2030 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 old_adj_index_valid = nh_grp->adj_index_valid;
2032 old_adj_index = nh_grp->adj_index;
2033 old_ecmp_size = nh_grp->ecmp_size;
2034 nh_grp->adj_index_valid = 1;
2035 nh_grp->adj_index = adj_index;
2036 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01002037 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002038 if (err) {
2039 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2040 goto set_trap;
2041 }
2042
2043 if (!old_adj_index_valid) {
2044 /* The trap was set for fib entries, so we have to call
2045 * fib entry update to unset it and use adjacency index.
2046 */
2047 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2048 if (err) {
2049 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2050 goto set_trap;
2051 }
2052 return;
2053 }
2054
2055 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2056 old_adj_index, old_ecmp_size);
2057 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2058 if (err) {
2059 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2060 goto set_trap;
2061 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002062
2063 /* Offload state within the group changed, so update the flags. */
2064 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2065
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002066 return;
2067
2068set_trap:
2069 old_adj_index_valid = nh_grp->adj_index_valid;
2070 nh_grp->adj_index_valid = 0;
2071 for (i = 0; i < nh_grp->count; i++) {
2072 nh = &nh_grp->nexthops[i];
2073 nh->offloaded = 0;
2074 }
2075 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2076 if (err)
2077 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2078 if (old_adj_index_valid)
2079 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2080}
2081
2082static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2083 bool removing)
2084{
Petr Machata213666a2017-07-31 09:27:30 +02002085 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002086 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002087 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002088 nh->should_offload = 0;
2089 nh->update = 1;
2090}
2091
2092static void
2093mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2094 struct mlxsw_sp_neigh_entry *neigh_entry,
2095 bool removing)
2096{
2097 struct mlxsw_sp_nexthop *nh;
2098
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002099 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2100 neigh_list_node) {
2101 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2102 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2103 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002104}
2105
Ido Schimmel9665b742017-02-08 11:16:42 +01002106static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002107 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002108{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002109 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002110 return;
2111
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002112 nh->rif = rif;
2113 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002114}
2115
2116static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2117{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002118 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002119 return;
2120
2121 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002122 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002123}
2124
Ido Schimmela8c97012017-02-08 11:16:35 +01002125static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2126 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002127{
2128 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002129 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002130 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002131 int err;
2132
Ido Schimmelad178c82017-02-08 11:16:40 +01002133 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002134 return 0;
2135
Jiri Pirko33b13412016-11-10 12:31:04 +01002136 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002137 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002138 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002139 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002140 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002141 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002142 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002143 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2144 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002145 if (IS_ERR(n))
2146 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002147 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002148 }
2149 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2150 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002151 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2152 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002153 err = -EINVAL;
2154 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002155 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002156 }
Yotam Gigib2157142016-07-05 11:27:51 +02002157
2158 /* If that is the first nexthop connected to that neigh, add to
2159 * nexthop_neighs_list
2160 */
2161 if (list_empty(&neigh_entry->nexthop_list))
2162 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002163 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002164
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002165 nh->neigh_entry = neigh_entry;
2166 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2167 read_lock_bh(&n->lock);
2168 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002169 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002170 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002171 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002172
2173 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002174
2175err_neigh_entry_create:
2176 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002177 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002178}
2179
Ido Schimmela8c97012017-02-08 11:16:35 +01002180static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2181 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002182{
2183 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002184 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002185
Ido Schimmelb8399a12017-02-08 11:16:33 +01002186 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002187 return;
2188 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002189
Ido Schimmel58312122016-12-23 09:32:50 +01002190 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002191 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002192 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002193
2194 /* If that is the last nexthop connected to that neigh, remove from
2195 * nexthop_neighs_list
2196 */
Ido Schimmele58be792017-02-08 11:16:28 +01002197 if (list_empty(&neigh_entry->nexthop_list))
2198 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002199
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002200 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2201 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2202
2203 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002204}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002205
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002206static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2207 struct mlxsw_sp_nexthop_group *nh_grp,
2208 struct mlxsw_sp_nexthop *nh,
2209 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002210{
2211 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002212 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002213 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002214 int err;
2215
2216 nh->nh_grp = nh_grp;
2217 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002218 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002219 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2220 if (err)
2221 return err;
2222
Ido Schimmel97989ee2017-03-10 08:53:38 +01002223 if (!dev)
2224 return 0;
2225
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002226 in_dev = __in_dev_get_rtnl(dev);
2227 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2228 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2229 return 0;
2230
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002231 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2232 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002233 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002234 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002235
2236 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2237 if (err)
2238 goto err_nexthop_neigh_init;
2239
2240 return 0;
2241
2242err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002243 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002244 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2245 return err;
2246}
2247
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002248static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2249 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002250{
2251 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002252 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002253 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002254}
2255
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002256static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2257 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002258{
2259 struct mlxsw_sp_nexthop_key key;
2260 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002261 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002262
Ido Schimmel9011b672017-05-16 19:38:25 +02002263 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002264 return;
2265
2266 key.fib_nh = fib_nh;
2267 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2268 if (WARN_ON_ONCE(!nh))
2269 return;
2270
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002271 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2272 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002273 return;
2274
2275 switch (event) {
2276 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002277 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002278 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2279 break;
2280 case FIB_EVENT_NH_DEL:
2281 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002282 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002283 break;
2284 }
2285
2286 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2287}
2288
Ido Schimmel9665b742017-02-08 11:16:42 +01002289static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002290 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002291{
2292 struct mlxsw_sp_nexthop *nh, *tmp;
2293
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002294 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002295 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2296 mlxsw_sp_nexthop_rif_fini(nh);
2297 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2298 }
2299}
2300
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002301static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002302mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002303{
2304 struct mlxsw_sp_nexthop_group *nh_grp;
2305 struct mlxsw_sp_nexthop *nh;
2306 struct fib_nh *fib_nh;
2307 size_t alloc_size;
2308 int i;
2309 int err;
2310
2311 alloc_size = sizeof(*nh_grp) +
2312 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2313 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2314 if (!nh_grp)
2315 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002316 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002317 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002318 nh_grp->neigh_tbl = &arp_tbl;
2319
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002320 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002321 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002322 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002323 for (i = 0; i < nh_grp->count; i++) {
2324 nh = &nh_grp->nexthops[i];
2325 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002326 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002327 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002328 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002329 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002330 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2331 if (err)
2332 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002333 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2334 return nh_grp;
2335
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002336err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002337err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002338 for (i--; i >= 0; i--) {
2339 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002340 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002341 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002342 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002343 kfree(nh_grp);
2344 return ERR_PTR(err);
2345}
2346
2347static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002348mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2349 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002350{
2351 struct mlxsw_sp_nexthop *nh;
2352 int i;
2353
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002354 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002355 for (i = 0; i < nh_grp->count; i++) {
2356 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002357 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002358 }
Ido Schimmel58312122016-12-23 09:32:50 +01002359 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2360 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002361 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002362 kfree(nh_grp);
2363}
2364
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002365static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2366 struct mlxsw_sp_fib_entry *fib_entry,
2367 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002368{
2369 struct mlxsw_sp_nexthop_group *nh_grp;
2370
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002371 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002372 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002373 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002374 if (IS_ERR(nh_grp))
2375 return PTR_ERR(nh_grp);
2376 }
2377 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2378 fib_entry->nh_group = nh_grp;
2379 return 0;
2380}
2381
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002382static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2383 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002384{
2385 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2386
2387 list_del(&fib_entry->nexthop_group_node);
2388 if (!list_empty(&nh_grp->fib_list))
2389 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002390 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002391}
2392
Ido Schimmel013b20f2017-02-08 11:16:36 +01002393static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002394mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2395{
2396 struct mlxsw_sp_fib4_entry *fib4_entry;
2397
2398 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2399 common);
2400 return !fib4_entry->tos;
2401}
2402
2403static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002404mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2405{
2406 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2407
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002408 switch (fib_entry->fib_node->fib->proto) {
2409 case MLXSW_SP_L3_PROTO_IPV4:
2410 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2411 return false;
2412 break;
2413 case MLXSW_SP_L3_PROTO_IPV6:
2414 break;
2415 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002416
Ido Schimmel013b20f2017-02-08 11:16:36 +01002417 switch (fib_entry->type) {
2418 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2419 return !!nh_group->adj_index_valid;
2420 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002421 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002422 default:
2423 return false;
2424 }
2425}
2426
Ido Schimmel428b8512017-08-03 13:28:28 +02002427static struct mlxsw_sp_nexthop *
2428mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2429 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2430{
2431 int i;
2432
2433 for (i = 0; i < nh_grp->count; i++) {
2434 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2435 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2436
2437 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2438 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2439 &rt->rt6i_gateway))
2440 return nh;
2441 continue;
2442 }
2443
2444 return NULL;
2445}
2446
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002447static void
2448mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2449{
2450 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2451 int i;
2452
2453 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2454 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2455 return;
2456 }
2457
2458 for (i = 0; i < nh_grp->count; i++) {
2459 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2460
2461 if (nh->offloaded)
2462 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2463 else
2464 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2465 }
2466}
2467
2468static void
2469mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2470{
2471 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2472 int i;
2473
2474 for (i = 0; i < nh_grp->count; i++) {
2475 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2476
2477 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2478 }
2479}
2480
Ido Schimmel428b8512017-08-03 13:28:28 +02002481static void
2482mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2483{
2484 struct mlxsw_sp_fib6_entry *fib6_entry;
2485 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2486
2487 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2488 common);
2489
2490 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2491 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02002492 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002493 return;
2494 }
2495
2496 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2497 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2498 struct mlxsw_sp_nexthop *nh;
2499
2500 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2501 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02002502 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002503 else
Ido Schimmelfe400792017-08-15 09:09:49 +02002504 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002505 }
2506}
2507
2508static void
2509mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2510{
2511 struct mlxsw_sp_fib6_entry *fib6_entry;
2512 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2513
2514 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2515 common);
2516 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2517 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2518
Ido Schimmelfe400792017-08-15 09:09:49 +02002519 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002520 }
2521}
2522
Ido Schimmel013b20f2017-02-08 11:16:36 +01002523static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2524{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002525 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002526 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002527 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002528 break;
2529 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002530 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2531 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002532 }
2533}
2534
2535static void
2536mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2537{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002538 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002539 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002540 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002541 break;
2542 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002543 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2544 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002545 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002546}
2547
2548static void
2549mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2550 enum mlxsw_reg_ralue_op op, int err)
2551{
2552 switch (op) {
2553 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002554 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2555 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2556 if (err)
2557 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002558 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002559 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002560 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002561 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2562 return;
2563 default:
2564 return;
2565 }
2566}
2567
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002568static void
2569mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2570 const struct mlxsw_sp_fib_entry *fib_entry,
2571 enum mlxsw_reg_ralue_op op)
2572{
2573 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2574 enum mlxsw_reg_ralxx_protocol proto;
2575 u32 *p_dip;
2576
2577 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2578
2579 switch (fib->proto) {
2580 case MLXSW_SP_L3_PROTO_IPV4:
2581 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2582 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2583 fib_entry->fib_node->key.prefix_len,
2584 *p_dip);
2585 break;
2586 case MLXSW_SP_L3_PROTO_IPV6:
2587 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2588 fib_entry->fib_node->key.prefix_len,
2589 fib_entry->fib_node->key.addr);
2590 break;
2591 }
2592}
2593
2594static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2595 struct mlxsw_sp_fib_entry *fib_entry,
2596 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002597{
2598 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002599 enum mlxsw_reg_ralue_trap_action trap_action;
2600 u16 trap_id = 0;
2601 u32 adjacency_index = 0;
2602 u16 ecmp_size = 0;
2603
2604 /* In case the nexthop group adjacency index is valid, use it
2605 * with provided ECMP size. Otherwise, setup trap and pass
2606 * traffic to kernel.
2607 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002608 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002609 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2610 adjacency_index = fib_entry->nh_group->adj_index;
2611 ecmp_size = fib_entry->nh_group->ecmp_size;
2612 } else {
2613 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2614 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2615 }
2616
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002617 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002618 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2619 adjacency_index, ecmp_size);
2620 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2621}
2622
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002623static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2624 struct mlxsw_sp_fib_entry *fib_entry,
2625 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002626{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002627 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002628 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002629 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002630 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002631 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002632
2633 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2634 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002635 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002636 } else {
2637 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2638 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2639 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002640
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002641 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002642 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2643 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002644 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2645}
2646
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002647static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2648 struct mlxsw_sp_fib_entry *fib_entry,
2649 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002650{
2651 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002652
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002653 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002654 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2655 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2656}
2657
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002658static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2659 struct mlxsw_sp_fib_entry *fib_entry,
2660 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002661{
2662 switch (fib_entry->type) {
2663 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002664 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002665 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002666 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002667 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002668 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002669 }
2670 return -EINVAL;
2671}
2672
2673static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2674 struct mlxsw_sp_fib_entry *fib_entry,
2675 enum mlxsw_reg_ralue_op op)
2676{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002677 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002678
Ido Schimmel013b20f2017-02-08 11:16:36 +01002679 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002680
Ido Schimmel013b20f2017-02-08 11:16:36 +01002681 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002682}
2683
2684static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2685 struct mlxsw_sp_fib_entry *fib_entry)
2686{
Jiri Pirko7146da32016-09-01 10:37:41 +02002687 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2688 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002689}
2690
2691static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2692 struct mlxsw_sp_fib_entry *fib_entry)
2693{
2694 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2695 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2696}
2697
Jiri Pirko61c503f2016-07-04 08:23:11 +02002698static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002699mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2700 const struct fib_entry_notifier_info *fen_info,
2701 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002702{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002703 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002704
Ido Schimmel97989ee2017-03-10 08:53:38 +01002705 switch (fen_info->type) {
2706 case RTN_BROADCAST: /* fall through */
2707 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002708 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2709 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002710 case RTN_UNREACHABLE: /* fall through */
2711 case RTN_BLACKHOLE: /* fall through */
2712 case RTN_PROHIBIT:
2713 /* Packets hitting these routes need to be trapped, but
2714 * can do so with a lower priority than packets directed
2715 * at the host, so use action type local instead of trap.
2716 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002717 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002718 return 0;
2719 case RTN_UNICAST:
2720 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2721 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2722 else
2723 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2724 return 0;
2725 default:
2726 return -EINVAL;
2727 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002728}
2729
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002730static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002731mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2732 struct mlxsw_sp_fib_node *fib_node,
2733 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002734{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002735 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002736 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002737 int err;
2738
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002739 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2740 if (!fib4_entry)
2741 return ERR_PTR(-ENOMEM);
2742 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002743
2744 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2745 if (err)
2746 goto err_fib4_entry_type_set;
2747
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002748 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002749 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002750 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002751
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002752 fib4_entry->prio = fen_info->fi->fib_priority;
2753 fib4_entry->tb_id = fen_info->tb_id;
2754 fib4_entry->type = fen_info->type;
2755 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002756
2757 fib_entry->fib_node = fib_node;
2758
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002759 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002760
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002761err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002762err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002763 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002764 return ERR_PTR(err);
2765}
2766
2767static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002768 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002769{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002770 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002771 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002772}
2773
2774static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002775mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2776 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002777
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002778static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002779mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2780 const struct fib_entry_notifier_info *fen_info)
2781{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002782 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002783 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002784 struct mlxsw_sp_fib *fib;
2785 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002786
Ido Schimmel160e22a2017-07-18 10:10:20 +02002787 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2788 if (!vr)
2789 return NULL;
2790 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2791
2792 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2793 sizeof(fen_info->dst),
2794 fen_info->dst_len);
2795 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002796 return NULL;
2797
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002798 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2799 if (fib4_entry->tb_id == fen_info->tb_id &&
2800 fib4_entry->tos == fen_info->tos &&
2801 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002802 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2803 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002804 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002805 }
2806 }
2807
2808 return NULL;
2809}
2810
2811static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2812 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2813 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2814 .key_len = sizeof(struct mlxsw_sp_fib_key),
2815 .automatic_shrinking = true,
2816};
2817
2818static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2819 struct mlxsw_sp_fib_node *fib_node)
2820{
2821 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2822 mlxsw_sp_fib_ht_params);
2823}
2824
2825static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2826 struct mlxsw_sp_fib_node *fib_node)
2827{
2828 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2829 mlxsw_sp_fib_ht_params);
2830}
2831
2832static struct mlxsw_sp_fib_node *
2833mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2834 size_t addr_len, unsigned char prefix_len)
2835{
2836 struct mlxsw_sp_fib_key key;
2837
2838 memset(&key, 0, sizeof(key));
2839 memcpy(key.addr, addr, addr_len);
2840 key.prefix_len = prefix_len;
2841 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2842}
2843
2844static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002845mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002846 size_t addr_len, unsigned char prefix_len)
2847{
2848 struct mlxsw_sp_fib_node *fib_node;
2849
2850 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2851 if (!fib_node)
2852 return NULL;
2853
2854 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002855 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002856 memcpy(fib_node->key.addr, addr, addr_len);
2857 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002858
2859 return fib_node;
2860}
2861
2862static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2863{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002864 list_del(&fib_node->list);
2865 WARN_ON(!list_empty(&fib_node->entry_list));
2866 kfree(fib_node);
2867}
2868
2869static bool
2870mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2871 const struct mlxsw_sp_fib_entry *fib_entry)
2872{
2873 return list_first_entry(&fib_node->entry_list,
2874 struct mlxsw_sp_fib_entry, list) == fib_entry;
2875}
2876
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002877static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2878 struct mlxsw_sp_fib *fib,
2879 struct mlxsw_sp_fib_node *fib_node)
2880{
2881 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2882 struct mlxsw_sp_lpm_tree *lpm_tree;
2883 int err;
2884
2885 /* Since the tree is shared between all virtual routers we must
2886 * make sure it contains all the required prefix lengths. This
2887 * can be computed by either adding the new prefix length to the
2888 * existing prefix usage of a bound tree, or by aggregating the
2889 * prefix lengths across all virtual routers and adding the new
2890 * one as well.
2891 */
2892 if (fib->lpm_tree)
2893 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2894 &fib->lpm_tree->prefix_usage);
2895 else
2896 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2897 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2898
2899 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2900 fib->proto);
2901 if (IS_ERR(lpm_tree))
2902 return PTR_ERR(lpm_tree);
2903
2904 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2905 return 0;
2906
2907 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2908 if (err)
2909 return err;
2910
2911 return 0;
2912}
2913
2914static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2915 struct mlxsw_sp_fib *fib)
2916{
2917 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2918 struct mlxsw_sp_lpm_tree *lpm_tree;
2919
2920 /* Aggregate prefix lengths across all virtual routers to make
2921 * sure we only have used prefix lengths in the LPM tree.
2922 */
2923 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2924 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2925 fib->proto);
2926 if (IS_ERR(lpm_tree))
2927 goto err_tree_get;
2928 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2929
2930err_tree_get:
2931 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2932 return;
2933 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2934 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2935 fib->lpm_tree = NULL;
2936}
2937
Ido Schimmel9aecce12017-02-09 10:28:42 +01002938static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2939{
2940 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002941 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002942
2943 if (fib->prefix_ref_count[prefix_len]++ == 0)
2944 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2945}
2946
2947static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2948{
2949 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002950 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002951
2952 if (--fib->prefix_ref_count[prefix_len] == 0)
2953 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2954}
2955
Ido Schimmel76610eb2017-03-10 08:53:41 +01002956static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2957 struct mlxsw_sp_fib_node *fib_node,
2958 struct mlxsw_sp_fib *fib)
2959{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002960 int err;
2961
2962 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2963 if (err)
2964 return err;
2965 fib_node->fib = fib;
2966
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002967 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
2968 if (err)
2969 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002970
2971 mlxsw_sp_fib_node_prefix_inc(fib_node);
2972
2973 return 0;
2974
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002975err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002976 fib_node->fib = NULL;
2977 mlxsw_sp_fib_node_remove(fib, fib_node);
2978 return err;
2979}
2980
2981static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2982 struct mlxsw_sp_fib_node *fib_node)
2983{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002984 struct mlxsw_sp_fib *fib = fib_node->fib;
2985
2986 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002987 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002988 fib_node->fib = NULL;
2989 mlxsw_sp_fib_node_remove(fib, fib_node);
2990}
2991
Ido Schimmel9aecce12017-02-09 10:28:42 +01002992static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002993mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2994 size_t addr_len, unsigned char prefix_len,
2995 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002996{
2997 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002998 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002999 struct mlxsw_sp_vr *vr;
3000 int err;
3001
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003002 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003003 if (IS_ERR(vr))
3004 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003005 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003006
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003007 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003008 if (fib_node)
3009 return fib_node;
3010
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003011 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003012 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003013 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003014 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003015 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003016
Ido Schimmel76610eb2017-03-10 08:53:41 +01003017 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3018 if (err)
3019 goto err_fib_node_init;
3020
Ido Schimmel9aecce12017-02-09 10:28:42 +01003021 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003022
Ido Schimmel76610eb2017-03-10 08:53:41 +01003023err_fib_node_init:
3024 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003025err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003026 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003027 return ERR_PTR(err);
3028}
3029
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003030static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3031 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003032{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003033 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003034
Ido Schimmel9aecce12017-02-09 10:28:42 +01003035 if (!list_empty(&fib_node->entry_list))
3036 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003037 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003038 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003039 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003040}
3041
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003042static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003043mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003044 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003045{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003046 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003047
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003048 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3049 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003050 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003051 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003052 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003053 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003054 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003055 if (fib4_entry->prio >= new4_entry->prio ||
3056 fib4_entry->tos < new4_entry->tos)
3057 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003058 }
3059
3060 return NULL;
3061}
3062
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003063static int
3064mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3065 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003066{
3067 struct mlxsw_sp_fib_node *fib_node;
3068
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003069 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003070 return -EINVAL;
3071
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003072 fib_node = fib4_entry->common.fib_node;
3073 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3074 common.list) {
3075 if (fib4_entry->tb_id != new4_entry->tb_id ||
3076 fib4_entry->tos != new4_entry->tos ||
3077 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003078 break;
3079 }
3080
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003081 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003082 return 0;
3083}
3084
Ido Schimmel9aecce12017-02-09 10:28:42 +01003085static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003086mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003087 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003088{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003089 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003090 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003091
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003092 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003093
Ido Schimmel4283bce2017-02-09 10:28:43 +01003094 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003095 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3096 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003097 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003098
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003099 /* Insert new entry before replaced one, so that we can later
3100 * remove the second.
3101 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003102 if (fib4_entry) {
3103 list_add_tail(&new4_entry->common.list,
3104 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003105 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003106 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003107
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003108 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3109 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003110 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003111 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003112 }
3113
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003114 if (fib4_entry)
3115 list_add(&new4_entry->common.list,
3116 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003117 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003118 list_add(&new4_entry->common.list,
3119 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003120 }
3121
3122 return 0;
3123}
3124
3125static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003126mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003127{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003128 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003129}
3130
Ido Schimmel80c238f2017-07-18 10:10:29 +02003131static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3132 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003133{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003134 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3135
Ido Schimmel9aecce12017-02-09 10:28:42 +01003136 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3137 return 0;
3138
3139 /* To prevent packet loss, overwrite the previously offloaded
3140 * entry.
3141 */
3142 if (!list_is_singular(&fib_node->entry_list)) {
3143 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3144 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3145
3146 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3147 }
3148
3149 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3150}
3151
Ido Schimmel80c238f2017-07-18 10:10:29 +02003152static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3153 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003154{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003155 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3156
Ido Schimmel9aecce12017-02-09 10:28:42 +01003157 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3158 return;
3159
3160 /* Promote the next entry by overwriting the deleted entry */
3161 if (!list_is_singular(&fib_node->entry_list)) {
3162 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3163 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3164
3165 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3166 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3167 return;
3168 }
3169
3170 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3171}
3172
3173static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003174 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003175 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003176{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003177 int err;
3178
Ido Schimmel9efbee62017-07-18 10:10:28 +02003179 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003180 if (err)
3181 return err;
3182
Ido Schimmel80c238f2017-07-18 10:10:29 +02003183 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003184 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003185 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003186
Ido Schimmel9aecce12017-02-09 10:28:42 +01003187 return 0;
3188
Ido Schimmel80c238f2017-07-18 10:10:29 +02003189err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003190 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003191 return err;
3192}
3193
3194static void
3195mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003196 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003197{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003198 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003199 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003200}
3201
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003202static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003203 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003204 bool replace)
3205{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003206 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3207 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003208
3209 if (!replace)
3210 return;
3211
3212 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003213 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003214
3215 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3216 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003217 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003218}
3219
Ido Schimmel9aecce12017-02-09 10:28:42 +01003220static int
3221mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003222 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003223 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003224{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003225 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003226 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003227 int err;
3228
Ido Schimmel9011b672017-05-16 19:38:25 +02003229 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003230 return 0;
3231
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003232 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3233 &fen_info->dst, sizeof(fen_info->dst),
3234 fen_info->dst_len,
3235 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003236 if (IS_ERR(fib_node)) {
3237 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3238 return PTR_ERR(fib_node);
3239 }
3240
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003241 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3242 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003243 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003244 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003245 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003246 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003247
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003248 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003249 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003250 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003251 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3252 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003253 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003254
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003255 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003256
Jiri Pirko61c503f2016-07-04 08:23:11 +02003257 return 0;
3258
Ido Schimmel9aecce12017-02-09 10:28:42 +01003259err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003260 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003261err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003262 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003263 return err;
3264}
3265
Jiri Pirko37956d72016-10-20 16:05:43 +02003266static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3267 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003268{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003269 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003270 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003271
Ido Schimmel9011b672017-05-16 19:38:25 +02003272 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003273 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003274
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003275 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3276 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003277 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003278 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003279
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003280 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3281 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003282 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003283}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003284
Ido Schimmel428b8512017-08-03 13:28:28 +02003285static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3286{
3287 /* Packets with link-local destination IP arriving to the router
3288 * are trapped to the CPU, so no need to program specific routes
3289 * for them.
3290 */
3291 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3292 return true;
3293
3294 /* Multicast routes aren't supported, so ignore them. Neighbour
3295 * Discovery packets are specifically trapped.
3296 */
3297 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3298 return true;
3299
3300 /* Cloned routes are irrelevant in the forwarding path. */
3301 if (rt->rt6i_flags & RTF_CACHE)
3302 return true;
3303
3304 return false;
3305}
3306
3307static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3308{
3309 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3310
3311 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3312 if (!mlxsw_sp_rt6)
3313 return ERR_PTR(-ENOMEM);
3314
3315 /* In case of route replace, replaced route is deleted with
3316 * no notification. Take reference to prevent accessing freed
3317 * memory.
3318 */
3319 mlxsw_sp_rt6->rt = rt;
3320 rt6_hold(rt);
3321
3322 return mlxsw_sp_rt6;
3323}
3324
3325#if IS_ENABLED(CONFIG_IPV6)
3326static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3327{
3328 rt6_release(rt);
3329}
3330#else
3331static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3332{
3333}
3334#endif
3335
3336static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3337{
3338 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3339 kfree(mlxsw_sp_rt6);
3340}
3341
3342static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3343{
3344 /* RTF_CACHE routes are ignored */
3345 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3346}
3347
3348static struct rt6_info *
3349mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3350{
3351 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3352 list)->rt;
3353}
3354
3355static struct mlxsw_sp_fib6_entry *
3356mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003357 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003358{
3359 struct mlxsw_sp_fib6_entry *fib6_entry;
3360
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003361 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003362 return NULL;
3363
3364 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3365 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3366
3367 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3368 * virtual router.
3369 */
3370 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3371 continue;
3372 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3373 break;
3374 if (rt->rt6i_metric < nrt->rt6i_metric)
3375 continue;
3376 if (rt->rt6i_metric == nrt->rt6i_metric &&
3377 mlxsw_sp_fib6_rt_can_mp(rt))
3378 return fib6_entry;
3379 if (rt->rt6i_metric > nrt->rt6i_metric)
3380 break;
3381 }
3382
3383 return NULL;
3384}
3385
3386static struct mlxsw_sp_rt6 *
3387mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3388 const struct rt6_info *rt)
3389{
3390 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3391
3392 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3393 if (mlxsw_sp_rt6->rt == rt)
3394 return mlxsw_sp_rt6;
3395 }
3396
3397 return NULL;
3398}
3399
3400static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3401 struct mlxsw_sp_nexthop_group *nh_grp,
3402 struct mlxsw_sp_nexthop *nh,
3403 const struct rt6_info *rt)
3404{
3405 struct net_device *dev = rt->dst.dev;
3406 struct mlxsw_sp_rif *rif;
3407 int err;
3408
3409 nh->nh_grp = nh_grp;
3410 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3411
3412 if (!dev)
3413 return 0;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003414 nh->ifindex = dev->ifindex;
Ido Schimmel428b8512017-08-03 13:28:28 +02003415
3416 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3417 if (!rif)
3418 return 0;
3419 mlxsw_sp_nexthop_rif_init(nh, rif);
3420
3421 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3422 if (err)
3423 goto err_nexthop_neigh_init;
3424
3425 return 0;
3426
3427err_nexthop_neigh_init:
3428 mlxsw_sp_nexthop_rif_fini(nh);
3429 return err;
3430}
3431
3432static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3433 struct mlxsw_sp_nexthop *nh)
3434{
3435 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3436 mlxsw_sp_nexthop_rif_fini(nh);
3437}
3438
3439static struct mlxsw_sp_nexthop_group *
3440mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3441 struct mlxsw_sp_fib6_entry *fib6_entry)
3442{
3443 struct mlxsw_sp_nexthop_group *nh_grp;
3444 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3445 struct mlxsw_sp_nexthop *nh;
3446 size_t alloc_size;
3447 int i = 0;
3448 int err;
3449
3450 alloc_size = sizeof(*nh_grp) +
3451 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3452 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3453 if (!nh_grp)
3454 return ERR_PTR(-ENOMEM);
3455 INIT_LIST_HEAD(&nh_grp->fib_list);
3456#if IS_ENABLED(CONFIG_IPV6)
3457 nh_grp->neigh_tbl = &nd_tbl;
3458#endif
3459 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3460 struct mlxsw_sp_rt6, list);
3461 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3462 nh_grp->count = fib6_entry->nrt6;
3463 for (i = 0; i < nh_grp->count; i++) {
3464 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3465
3466 nh = &nh_grp->nexthops[i];
3467 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3468 if (err)
3469 goto err_nexthop6_init;
3470 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3471 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003472
3473 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3474 if (err)
3475 goto err_nexthop_group_insert;
3476
Ido Schimmel428b8512017-08-03 13:28:28 +02003477 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3478 return nh_grp;
3479
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003480err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02003481err_nexthop6_init:
3482 for (i--; i >= 0; i--) {
3483 nh = &nh_grp->nexthops[i];
3484 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3485 }
3486 kfree(nh_grp);
3487 return ERR_PTR(err);
3488}
3489
3490static void
3491mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3492 struct mlxsw_sp_nexthop_group *nh_grp)
3493{
3494 struct mlxsw_sp_nexthop *nh;
3495 int i = nh_grp->count;
3496
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003497 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02003498 for (i--; i >= 0; i--) {
3499 nh = &nh_grp->nexthops[i];
3500 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3501 }
3502 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3503 WARN_ON(nh_grp->adj_index_valid);
3504 kfree(nh_grp);
3505}
3506
3507static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3508 struct mlxsw_sp_fib6_entry *fib6_entry)
3509{
3510 struct mlxsw_sp_nexthop_group *nh_grp;
3511
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003512 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
3513 if (!nh_grp) {
3514 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3515 if (IS_ERR(nh_grp))
3516 return PTR_ERR(nh_grp);
3517 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003518
3519 list_add_tail(&fib6_entry->common.nexthop_group_node,
3520 &nh_grp->fib_list);
3521 fib6_entry->common.nh_group = nh_grp;
3522
3523 return 0;
3524}
3525
3526static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3527 struct mlxsw_sp_fib_entry *fib_entry)
3528{
3529 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3530
3531 list_del(&fib_entry->nexthop_group_node);
3532 if (!list_empty(&nh_grp->fib_list))
3533 return;
3534 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3535}
3536
3537static int
3538mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3539 struct mlxsw_sp_fib6_entry *fib6_entry)
3540{
3541 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3542 int err;
3543
3544 fib6_entry->common.nh_group = NULL;
3545 list_del(&fib6_entry->common.nexthop_group_node);
3546
3547 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3548 if (err)
3549 goto err_nexthop6_group_get;
3550
3551 /* In case this entry is offloaded, then the adjacency index
3552 * currently associated with it in the device's table is that
3553 * of the old group. Start using the new one instead.
3554 */
3555 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3556 if (err)
3557 goto err_fib_node_entry_add;
3558
3559 if (list_empty(&old_nh_grp->fib_list))
3560 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3561
3562 return 0;
3563
3564err_fib_node_entry_add:
3565 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3566err_nexthop6_group_get:
3567 list_add_tail(&fib6_entry->common.nexthop_group_node,
3568 &old_nh_grp->fib_list);
3569 fib6_entry->common.nh_group = old_nh_grp;
3570 return err;
3571}
3572
3573static int
3574mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3575 struct mlxsw_sp_fib6_entry *fib6_entry,
3576 struct rt6_info *rt)
3577{
3578 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3579 int err;
3580
3581 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3582 if (IS_ERR(mlxsw_sp_rt6))
3583 return PTR_ERR(mlxsw_sp_rt6);
3584
3585 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3586 fib6_entry->nrt6++;
3587
3588 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3589 if (err)
3590 goto err_nexthop6_group_update;
3591
3592 return 0;
3593
3594err_nexthop6_group_update:
3595 fib6_entry->nrt6--;
3596 list_del(&mlxsw_sp_rt6->list);
3597 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3598 return err;
3599}
3600
3601static void
3602mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3603 struct mlxsw_sp_fib6_entry *fib6_entry,
3604 struct rt6_info *rt)
3605{
3606 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3607
3608 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3609 if (WARN_ON(!mlxsw_sp_rt6))
3610 return;
3611
3612 fib6_entry->nrt6--;
3613 list_del(&mlxsw_sp_rt6->list);
3614 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3615 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3616}
3617
3618static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3619 const struct rt6_info *rt)
3620{
3621 /* Packets hitting RTF_REJECT routes need to be discarded by the
3622 * stack. We can rely on their destination device not having a
3623 * RIF (it's the loopback device) and can thus use action type
3624 * local, which will cause them to be trapped with a lower
3625 * priority than packets that need to be locally received.
3626 */
3627 if (rt->rt6i_flags & RTF_LOCAL)
3628 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3629 else if (rt->rt6i_flags & RTF_REJECT)
3630 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3631 else if (rt->rt6i_flags & RTF_GATEWAY)
3632 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3633 else
3634 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3635}
3636
3637static void
3638mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3639{
3640 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3641
3642 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3643 list) {
3644 fib6_entry->nrt6--;
3645 list_del(&mlxsw_sp_rt6->list);
3646 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3647 }
3648}
3649
3650static struct mlxsw_sp_fib6_entry *
3651mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3652 struct mlxsw_sp_fib_node *fib_node,
3653 struct rt6_info *rt)
3654{
3655 struct mlxsw_sp_fib6_entry *fib6_entry;
3656 struct mlxsw_sp_fib_entry *fib_entry;
3657 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3658 int err;
3659
3660 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3661 if (!fib6_entry)
3662 return ERR_PTR(-ENOMEM);
3663 fib_entry = &fib6_entry->common;
3664
3665 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3666 if (IS_ERR(mlxsw_sp_rt6)) {
3667 err = PTR_ERR(mlxsw_sp_rt6);
3668 goto err_rt6_create;
3669 }
3670
3671 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3672
3673 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3674 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3675 fib6_entry->nrt6 = 1;
3676 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3677 if (err)
3678 goto err_nexthop6_group_get;
3679
3680 fib_entry->fib_node = fib_node;
3681
3682 return fib6_entry;
3683
3684err_nexthop6_group_get:
3685 list_del(&mlxsw_sp_rt6->list);
3686 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3687err_rt6_create:
3688 kfree(fib6_entry);
3689 return ERR_PTR(err);
3690}
3691
3692static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3693 struct mlxsw_sp_fib6_entry *fib6_entry)
3694{
3695 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3696 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3697 WARN_ON(fib6_entry->nrt6);
3698 kfree(fib6_entry);
3699}
3700
3701static struct mlxsw_sp_fib6_entry *
3702mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003703 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003704{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003705 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003706
3707 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3708 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3709
3710 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3711 continue;
3712 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3713 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003714 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3715 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3716 mlxsw_sp_fib6_rt_can_mp(nrt))
3717 return fib6_entry;
3718 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3719 fallback = fallback ?: fib6_entry;
3720 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003721 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003722 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003723 }
3724
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003725 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003726}
3727
3728static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003729mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3730 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003731{
3732 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3733 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3734 struct mlxsw_sp_fib6_entry *fib6_entry;
3735
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003736 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3737
3738 if (replace && WARN_ON(!fib6_entry))
3739 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003740
3741 if (fib6_entry) {
3742 list_add_tail(&new6_entry->common.list,
3743 &fib6_entry->common.list);
3744 } else {
3745 struct mlxsw_sp_fib6_entry *last;
3746
3747 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3748 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3749
3750 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3751 break;
3752 fib6_entry = last;
3753 }
3754
3755 if (fib6_entry)
3756 list_add(&new6_entry->common.list,
3757 &fib6_entry->common.list);
3758 else
3759 list_add(&new6_entry->common.list,
3760 &fib_node->entry_list);
3761 }
3762
3763 return 0;
3764}
3765
3766static void
3767mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3768{
3769 list_del(&fib6_entry->common.list);
3770}
3771
3772static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003773 struct mlxsw_sp_fib6_entry *fib6_entry,
3774 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003775{
3776 int err;
3777
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003778 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003779 if (err)
3780 return err;
3781
3782 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3783 if (err)
3784 goto err_fib_node_entry_add;
3785
3786 return 0;
3787
3788err_fib_node_entry_add:
3789 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3790 return err;
3791}
3792
3793static void
3794mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3795 struct mlxsw_sp_fib6_entry *fib6_entry)
3796{
3797 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3798 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3799}
3800
3801static struct mlxsw_sp_fib6_entry *
3802mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3803 const struct rt6_info *rt)
3804{
3805 struct mlxsw_sp_fib6_entry *fib6_entry;
3806 struct mlxsw_sp_fib_node *fib_node;
3807 struct mlxsw_sp_fib *fib;
3808 struct mlxsw_sp_vr *vr;
3809
3810 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3811 if (!vr)
3812 return NULL;
3813 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3814
3815 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3816 sizeof(rt->rt6i_dst.addr),
3817 rt->rt6i_dst.plen);
3818 if (!fib_node)
3819 return NULL;
3820
3821 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3822 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3823
3824 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3825 rt->rt6i_metric == iter_rt->rt6i_metric &&
3826 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3827 return fib6_entry;
3828 }
3829
3830 return NULL;
3831}
3832
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003833static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3834 struct mlxsw_sp_fib6_entry *fib6_entry,
3835 bool replace)
3836{
3837 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3838 struct mlxsw_sp_fib6_entry *replaced;
3839
3840 if (!replace)
3841 return;
3842
3843 replaced = list_next_entry(fib6_entry, common.list);
3844
3845 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3846 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3847 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3848}
3849
Ido Schimmel428b8512017-08-03 13:28:28 +02003850static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003851 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003852{
3853 struct mlxsw_sp_fib6_entry *fib6_entry;
3854 struct mlxsw_sp_fib_node *fib_node;
3855 int err;
3856
3857 if (mlxsw_sp->router->aborted)
3858 return 0;
3859
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003860 if (rt->rt6i_src.plen)
3861 return -EINVAL;
3862
Ido Schimmel428b8512017-08-03 13:28:28 +02003863 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3864 return 0;
3865
3866 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3867 &rt->rt6i_dst.addr,
3868 sizeof(rt->rt6i_dst.addr),
3869 rt->rt6i_dst.plen,
3870 MLXSW_SP_L3_PROTO_IPV6);
3871 if (IS_ERR(fib_node))
3872 return PTR_ERR(fib_node);
3873
3874 /* Before creating a new entry, try to append route to an existing
3875 * multipath entry.
3876 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003877 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003878 if (fib6_entry) {
3879 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3880 if (err)
3881 goto err_fib6_entry_nexthop_add;
3882 return 0;
3883 }
3884
3885 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3886 if (IS_ERR(fib6_entry)) {
3887 err = PTR_ERR(fib6_entry);
3888 goto err_fib6_entry_create;
3889 }
3890
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003891 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003892 if (err)
3893 goto err_fib6_node_entry_link;
3894
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003895 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3896
Ido Schimmel428b8512017-08-03 13:28:28 +02003897 return 0;
3898
3899err_fib6_node_entry_link:
3900 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3901err_fib6_entry_create:
3902err_fib6_entry_nexthop_add:
3903 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3904 return err;
3905}
3906
3907static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3908 struct rt6_info *rt)
3909{
3910 struct mlxsw_sp_fib6_entry *fib6_entry;
3911 struct mlxsw_sp_fib_node *fib_node;
3912
3913 if (mlxsw_sp->router->aborted)
3914 return;
3915
3916 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3917 return;
3918
3919 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3920 if (WARN_ON(!fib6_entry))
3921 return;
3922
3923 /* If route is part of a multipath entry, but not the last one
3924 * removed, then only reduce its nexthop group.
3925 */
3926 if (!list_is_singular(&fib6_entry->rt6_list)) {
3927 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3928 return;
3929 }
3930
3931 fib_node = fib6_entry->common.fib_node;
3932
3933 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3934 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3935 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3936}
3937
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003938static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3939 enum mlxsw_reg_ralxx_protocol proto,
3940 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003941{
3942 char ralta_pl[MLXSW_REG_RALTA_LEN];
3943 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003944 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003945
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003946 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003947 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3948 if (err)
3949 return err;
3950
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003951 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003952 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3953 if (err)
3954 return err;
3955
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003956 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003957 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003958 char raltb_pl[MLXSW_REG_RALTB_LEN];
3959 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003960
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003961 if (!mlxsw_sp_vr_is_used(vr))
3962 continue;
3963
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003964 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003965 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3966 raltb_pl);
3967 if (err)
3968 return err;
3969
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003970 mlxsw_reg_ralue_pack(ralue_pl, proto,
3971 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003972 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3973 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3974 ralue_pl);
3975 if (err)
3976 return err;
3977 }
3978
3979 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003980}
3981
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003982static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3983{
3984 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3985 int err;
3986
3987 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3988 MLXSW_SP_LPM_TREE_MIN);
3989 if (err)
3990 return err;
3991
3992 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3993 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3994 MLXSW_SP_LPM_TREE_MIN + 1);
3995}
3996
Ido Schimmel9aecce12017-02-09 10:28:42 +01003997static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3998 struct mlxsw_sp_fib_node *fib_node)
3999{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004000 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004001
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004002 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4003 common.list) {
4004 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004005
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004006 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4007 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004008 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004009 /* Break when entry list is empty and node was freed.
4010 * Otherwise, we'll access freed memory in the next
4011 * iteration.
4012 */
4013 if (do_break)
4014 break;
4015 }
4016}
4017
Ido Schimmel428b8512017-08-03 13:28:28 +02004018static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4019 struct mlxsw_sp_fib_node *fib_node)
4020{
4021 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4022
4023 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4024 common.list) {
4025 bool do_break = &tmp->common.list == &fib_node->entry_list;
4026
4027 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4028 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4029 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4030 if (do_break)
4031 break;
4032 }
4033}
4034
Ido Schimmel9aecce12017-02-09 10:28:42 +01004035static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4036 struct mlxsw_sp_fib_node *fib_node)
4037{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004038 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004039 case MLXSW_SP_L3_PROTO_IPV4:
4040 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4041 break;
4042 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004043 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004044 break;
4045 }
4046}
4047
Ido Schimmel76610eb2017-03-10 08:53:41 +01004048static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4049 struct mlxsw_sp_vr *vr,
4050 enum mlxsw_sp_l3proto proto)
4051{
4052 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4053 struct mlxsw_sp_fib_node *fib_node, *tmp;
4054
4055 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4056 bool do_break = &tmp->list == &fib->node_list;
4057
4058 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4059 if (do_break)
4060 break;
4061 }
4062}
4063
Ido Schimmelac571de2016-11-14 11:26:32 +01004064static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004065{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004066 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004067
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004068 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004069 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004070
Ido Schimmel76610eb2017-03-10 08:53:41 +01004071 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004072 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004073 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004074
4075 /* If virtual router was only used for IPv4, then it's no
4076 * longer used.
4077 */
4078 if (!mlxsw_sp_vr_is_used(vr))
4079 continue;
4080 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004081 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004082}
4083
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004084static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004085{
4086 int err;
4087
Ido Schimmel9011b672017-05-16 19:38:25 +02004088 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004089 return;
4090 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 +01004091 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004092 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004093 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4094 if (err)
4095 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4096}
4097
Ido Schimmel30572242016-12-03 16:45:01 +01004098struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004099 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004100 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004101 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004102 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004103 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004104 struct fib_nh_notifier_info fnh_info;
4105 };
Ido Schimmel30572242016-12-03 16:45:01 +01004106 struct mlxsw_sp *mlxsw_sp;
4107 unsigned long event;
4108};
4109
Ido Schimmel66a57632017-08-03 13:28:26 +02004110static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004111{
Ido Schimmel30572242016-12-03 16:45:01 +01004112 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004113 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004114 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004115 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004116 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004117 int err;
4118
Ido Schimmel30572242016-12-03 16:45:01 +01004119 /* Protect internal structures from changes */
4120 rtnl_lock();
4121 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004122 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004123 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004124 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004125 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004126 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4127 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004128 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004129 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004130 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004131 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004132 break;
4133 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004134 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4135 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004136 break;
4137 case FIB_EVENT_RULE_ADD: /* fall through */
4138 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004139 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004140 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004141 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004142 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004143 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004144 case FIB_EVENT_NH_ADD: /* fall through */
4145 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004146 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4147 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004148 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4149 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004150 }
Ido Schimmel30572242016-12-03 16:45:01 +01004151 rtnl_unlock();
4152 kfree(fib_work);
4153}
4154
Ido Schimmel66a57632017-08-03 13:28:26 +02004155static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4156{
Ido Schimmel583419f2017-08-03 13:28:27 +02004157 struct mlxsw_sp_fib_event_work *fib_work =
4158 container_of(work, struct mlxsw_sp_fib_event_work, work);
4159 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4160 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004161 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004162 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004163
4164 rtnl_lock();
4165 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:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004168 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004169 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004170 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004171 if (err)
4172 mlxsw_sp_router_fib_abort(mlxsw_sp);
4173 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4174 break;
4175 case FIB_EVENT_ENTRY_DEL:
4176 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4177 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4178 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004179 case FIB_EVENT_RULE_ADD: /* fall through */
4180 case FIB_EVENT_RULE_DEL:
4181 rule = fib_work->fr_info.rule;
4182 if (!fib6_rule_default(rule) && !rule->l3mdev)
4183 mlxsw_sp_router_fib_abort(mlxsw_sp);
4184 fib_rule_put(rule);
4185 break;
4186 }
4187 rtnl_unlock();
4188 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004189}
4190
4191static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4192 struct fib_notifier_info *info)
4193{
4194 switch (fib_work->event) {
4195 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4196 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4197 case FIB_EVENT_ENTRY_ADD: /* fall through */
4198 case FIB_EVENT_ENTRY_DEL:
4199 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4200 /* Take referece on fib_info to prevent it from being
4201 * freed while work is queued. Release it afterwards.
4202 */
4203 fib_info_hold(fib_work->fen_info.fi);
4204 break;
4205 case FIB_EVENT_RULE_ADD: /* fall through */
4206 case FIB_EVENT_RULE_DEL:
4207 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4208 fib_rule_get(fib_work->fr_info.rule);
4209 break;
4210 case FIB_EVENT_NH_ADD: /* fall through */
4211 case FIB_EVENT_NH_DEL:
4212 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4213 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4214 break;
4215 }
4216}
4217
4218static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4219 struct fib_notifier_info *info)
4220{
Ido Schimmel583419f2017-08-03 13:28:27 +02004221 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004222 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004223 case FIB_EVENT_ENTRY_ADD: /* fall through */
4224 case FIB_EVENT_ENTRY_DEL:
4225 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4226 rt6_hold(fib_work->fen6_info.rt);
4227 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004228 case FIB_EVENT_RULE_ADD: /* fall through */
4229 case FIB_EVENT_RULE_DEL:
4230 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4231 fib_rule_get(fib_work->fr_info.rule);
4232 break;
4233 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004234}
4235
Ido Schimmel30572242016-12-03 16:45:01 +01004236/* Called with rcu_read_lock() */
4237static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4238 unsigned long event, void *ptr)
4239{
Ido Schimmel30572242016-12-03 16:45:01 +01004240 struct mlxsw_sp_fib_event_work *fib_work;
4241 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004242 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004243
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004244 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004245 return NOTIFY_DONE;
4246
4247 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4248 if (WARN_ON(!fib_work))
4249 return NOTIFY_BAD;
4250
Ido Schimmel7e39d112017-05-16 19:38:28 +02004251 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4252 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004253 fib_work->event = event;
4254
Ido Schimmel66a57632017-08-03 13:28:26 +02004255 switch (info->family) {
4256 case AF_INET:
4257 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4258 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004259 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004260 case AF_INET6:
4261 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4262 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004263 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004264 }
4265
Ido Schimmela0e47612017-02-06 16:20:10 +01004266 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004267
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004268 return NOTIFY_DONE;
4269}
4270
Ido Schimmel4724ba562017-03-10 08:53:39 +01004271static struct mlxsw_sp_rif *
4272mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4273 const struct net_device *dev)
4274{
4275 int i;
4276
4277 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004278 if (mlxsw_sp->router->rifs[i] &&
4279 mlxsw_sp->router->rifs[i]->dev == dev)
4280 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004281
4282 return NULL;
4283}
4284
4285static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4286{
4287 char ritr_pl[MLXSW_REG_RITR_LEN];
4288 int err;
4289
4290 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4291 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4292 if (WARN_ON_ONCE(err))
4293 return err;
4294
4295 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4296 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4297}
4298
4299static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004300 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004301{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004302 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4303 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4304 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004305}
4306
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004307static bool
4308mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4309 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004310{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004311 struct inet6_dev *inet6_dev;
4312 bool addr_list_empty = true;
4313 struct in_device *idev;
4314
Ido Schimmel4724ba562017-03-10 08:53:39 +01004315 switch (event) {
4316 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004317 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004318 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004319 idev = __in_dev_get_rtnl(dev);
4320 if (idev && idev->ifa_list)
4321 addr_list_empty = false;
4322
4323 inet6_dev = __in6_dev_get(dev);
4324 if (addr_list_empty && inet6_dev &&
4325 !list_empty(&inet6_dev->addr_list))
4326 addr_list_empty = false;
4327
4328 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004329 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004330 return true;
4331 /* It is possible we already removed the RIF ourselves
4332 * if it was assigned to a netdev that is now a bridge
4333 * or LAG slave.
4334 */
4335 return false;
4336 }
4337
4338 return false;
4339}
4340
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004341static enum mlxsw_sp_rif_type
4342mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4343 const struct net_device *dev)
4344{
4345 enum mlxsw_sp_fid_type type;
4346
4347 /* RIF type is derived from the type of the underlying FID */
4348 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4349 type = MLXSW_SP_FID_TYPE_8021Q;
4350 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4351 type = MLXSW_SP_FID_TYPE_8021Q;
4352 else if (netif_is_bridge_master(dev))
4353 type = MLXSW_SP_FID_TYPE_8021D;
4354 else
4355 type = MLXSW_SP_FID_TYPE_RFID;
4356
4357 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4358}
4359
Ido Schimmelde5ed992017-06-04 16:53:40 +02004360static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004361{
4362 int i;
4363
Ido Schimmelde5ed992017-06-04 16:53:40 +02004364 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4365 if (!mlxsw_sp->router->rifs[i]) {
4366 *p_rif_index = i;
4367 return 0;
4368 }
4369 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004370
Ido Schimmelde5ed992017-06-04 16:53:40 +02004371 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004372}
4373
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004374static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4375 u16 vr_id,
4376 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004377{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004378 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004379
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004380 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004381 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004382 return NULL;
4383
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004384 INIT_LIST_HEAD(&rif->nexthop_list);
4385 INIT_LIST_HEAD(&rif->neigh_list);
4386 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4387 rif->mtu = l3_dev->mtu;
4388 rif->vr_id = vr_id;
4389 rif->dev = l3_dev;
4390 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004391
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004392 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004393}
4394
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004395struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4396 u16 rif_index)
4397{
4398 return mlxsw_sp->router->rifs[rif_index];
4399}
4400
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004401u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4402{
4403 return rif->rif_index;
4404}
4405
4406int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4407{
4408 return rif->dev->ifindex;
4409}
4410
Ido Schimmel4724ba562017-03-10 08:53:39 +01004411static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004412mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4413 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004414{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004415 u32 tb_id = l3mdev_fib_table(params->dev);
4416 const struct mlxsw_sp_rif_ops *ops;
4417 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004418 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004419 struct mlxsw_sp_fid *fid;
4420 struct mlxsw_sp_vr *vr;
4421 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004422 int err;
4423
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004424 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4425 ops = mlxsw_sp->router->rif_ops_arr[type];
4426
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004427 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4428 if (IS_ERR(vr))
4429 return ERR_CAST(vr);
4430
Ido Schimmelde5ed992017-06-04 16:53:40 +02004431 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4432 if (err)
4433 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004434
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004435 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004436 if (!rif) {
4437 err = -ENOMEM;
4438 goto err_rif_alloc;
4439 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004440 rif->mlxsw_sp = mlxsw_sp;
4441 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004442
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004443 fid = ops->fid_get(rif);
4444 if (IS_ERR(fid)) {
4445 err = PTR_ERR(fid);
4446 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004447 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004448 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004449
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004450 if (ops->setup)
4451 ops->setup(rif, params);
4452
4453 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004454 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004455 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004456
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004457 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004458 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004459 if (err)
4460 goto err_rif_fdb_op;
4461
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004462 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004463 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004464 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004465 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004466
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004467 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004468
Ido Schimmel4724ba562017-03-10 08:53:39 +01004469err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004470 ops->deconfigure(rif);
4471err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004472 mlxsw_sp_fid_put(fid);
4473err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004474 kfree(rif);
4475err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004476err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004477 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004478 return ERR_PTR(err);
4479}
4480
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004481void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004482{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004483 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4484 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004485 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004486 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004487
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004488 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004489 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004490
Ido Schimmel69132292017-03-10 08:53:42 +01004491 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004492 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004493 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004494 mlxsw_sp_rif_counters_free(rif);
4495 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4496 mlxsw_sp_fid_index(fid), false);
4497 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004498 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004499 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004500 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004501}
4502
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004503static void
4504mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4505 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4506{
4507 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4508
4509 params->vid = mlxsw_sp_port_vlan->vid;
4510 params->lag = mlxsw_sp_port->lagged;
4511 if (params->lag)
4512 params->lag_id = mlxsw_sp_port->lag_id;
4513 else
4514 params->system_port = mlxsw_sp_port->local_port;
4515}
4516
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004517static int
Ido Schimmela1107482017-05-26 08:37:39 +02004518mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004519 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004520{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004521 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004522 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004523 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004524 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004525 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004526 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004527
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004528 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004529 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004530 struct mlxsw_sp_rif_params params = {
4531 .dev = l3_dev,
4532 };
4533
4534 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4535 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004536 if (IS_ERR(rif))
4537 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004538 }
4539
Ido Schimmela1107482017-05-26 08:37:39 +02004540 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004541 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004542 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4543 if (err)
4544 goto err_fid_port_vid_map;
4545
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004546 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004547 if (err)
4548 goto err_port_vid_learning_set;
4549
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004550 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004551 BR_STATE_FORWARDING);
4552 if (err)
4553 goto err_port_vid_stp_set;
4554
Ido Schimmela1107482017-05-26 08:37:39 +02004555 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004556
Ido Schimmel4724ba562017-03-10 08:53:39 +01004557 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004558
4559err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004560 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004561err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004562 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4563err_fid_port_vid_map:
4564 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004565 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004566}
4567
Ido Schimmela1107482017-05-26 08:37:39 +02004568void
4569mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004570{
Ido Schimmelce95e152017-05-26 08:37:27 +02004571 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004572 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004573 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004574
Ido Schimmela1107482017-05-26 08:37:39 +02004575 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4576 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004577
Ido Schimmela1107482017-05-26 08:37:39 +02004578 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004579 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4580 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004581 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4582 /* If router port holds the last reference on the rFID, then the
4583 * associated Sub-port RIF will be destroyed.
4584 */
4585 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004586}
4587
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004588static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4589 struct net_device *port_dev,
4590 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004591{
4592 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004593 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004594
Ido Schimmelce95e152017-05-26 08:37:27 +02004595 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004596 if (WARN_ON(!mlxsw_sp_port_vlan))
4597 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004598
4599 switch (event) {
4600 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004601 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004602 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004603 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004604 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004605 break;
4606 }
4607
4608 return 0;
4609}
4610
4611static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4612 unsigned long event)
4613{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004614 if (netif_is_bridge_port(port_dev) ||
4615 netif_is_lag_port(port_dev) ||
4616 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004617 return 0;
4618
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004619 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004620}
4621
4622static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4623 struct net_device *lag_dev,
4624 unsigned long event, u16 vid)
4625{
4626 struct net_device *port_dev;
4627 struct list_head *iter;
4628 int err;
4629
4630 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4631 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004632 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4633 port_dev,
4634 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004635 if (err)
4636 return err;
4637 }
4638 }
4639
4640 return 0;
4641}
4642
4643static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4644 unsigned long event)
4645{
4646 if (netif_is_bridge_port(lag_dev))
4647 return 0;
4648
4649 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4650}
4651
Ido Schimmel4724ba562017-03-10 08:53:39 +01004652static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004653 unsigned long event)
4654{
4655 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004656 struct mlxsw_sp_rif_params params = {
4657 .dev = l3_dev,
4658 };
Ido Schimmela1107482017-05-26 08:37:39 +02004659 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004660
4661 switch (event) {
4662 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004663 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4664 if (IS_ERR(rif))
4665 return PTR_ERR(rif);
4666 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004667 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004668 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004669 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004670 break;
4671 }
4672
4673 return 0;
4674}
4675
4676static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4677 unsigned long event)
4678{
4679 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004680 u16 vid = vlan_dev_vlan_id(vlan_dev);
4681
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004682 if (netif_is_bridge_port(vlan_dev))
4683 return 0;
4684
Ido Schimmel4724ba562017-03-10 08:53:39 +01004685 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004686 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4687 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004688 else if (netif_is_lag_master(real_dev))
4689 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4690 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004691 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004692 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004693
4694 return 0;
4695}
4696
Ido Schimmelb1e45522017-04-30 19:47:14 +03004697static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4698 unsigned long event)
4699{
4700 if (mlxsw_sp_port_dev_check(dev))
4701 return mlxsw_sp_inetaddr_port_event(dev, event);
4702 else if (netif_is_lag_master(dev))
4703 return mlxsw_sp_inetaddr_lag_event(dev, event);
4704 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004705 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004706 else if (is_vlan_dev(dev))
4707 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4708 else
4709 return 0;
4710}
4711
Ido Schimmel4724ba562017-03-10 08:53:39 +01004712int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4713 unsigned long event, void *ptr)
4714{
4715 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4716 struct net_device *dev = ifa->ifa_dev->dev;
4717 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004718 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004719 int err = 0;
4720
4721 mlxsw_sp = mlxsw_sp_lower_get(dev);
4722 if (!mlxsw_sp)
4723 goto out;
4724
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004725 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004726 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004727 goto out;
4728
Ido Schimmelb1e45522017-04-30 19:47:14 +03004729 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004730out:
4731 return notifier_from_errno(err);
4732}
4733
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004734struct mlxsw_sp_inet6addr_event_work {
4735 struct work_struct work;
4736 struct net_device *dev;
4737 unsigned long event;
4738};
4739
4740static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4741{
4742 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4743 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4744 struct net_device *dev = inet6addr_work->dev;
4745 unsigned long event = inet6addr_work->event;
4746 struct mlxsw_sp *mlxsw_sp;
4747 struct mlxsw_sp_rif *rif;
4748
4749 rtnl_lock();
4750 mlxsw_sp = mlxsw_sp_lower_get(dev);
4751 if (!mlxsw_sp)
4752 goto out;
4753
4754 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4755 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4756 goto out;
4757
4758 __mlxsw_sp_inetaddr_event(dev, event);
4759out:
4760 rtnl_unlock();
4761 dev_put(dev);
4762 kfree(inet6addr_work);
4763}
4764
4765/* Called with rcu_read_lock() */
4766int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4767 unsigned long event, void *ptr)
4768{
4769 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4770 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4771 struct net_device *dev = if6->idev->dev;
4772
4773 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4774 return NOTIFY_DONE;
4775
4776 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4777 if (!inet6addr_work)
4778 return NOTIFY_BAD;
4779
4780 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4781 inet6addr_work->dev = dev;
4782 inet6addr_work->event = event;
4783 dev_hold(dev);
4784 mlxsw_core_schedule_work(&inet6addr_work->work);
4785
4786 return NOTIFY_DONE;
4787}
4788
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004789static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004790 const char *mac, int mtu)
4791{
4792 char ritr_pl[MLXSW_REG_RITR_LEN];
4793 int err;
4794
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004795 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004796 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4797 if (err)
4798 return err;
4799
4800 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4801 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4802 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4803 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4804}
4805
4806int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4807{
4808 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004809 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004810 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004811 int err;
4812
4813 mlxsw_sp = mlxsw_sp_lower_get(dev);
4814 if (!mlxsw_sp)
4815 return 0;
4816
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004817 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4818 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004819 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004820 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004821
Ido Schimmela1107482017-05-26 08:37:39 +02004822 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004823 if (err)
4824 return err;
4825
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004826 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4827 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004828 if (err)
4829 goto err_rif_edit;
4830
Ido Schimmela1107482017-05-26 08:37:39 +02004831 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004832 if (err)
4833 goto err_rif_fdb_op;
4834
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004835 ether_addr_copy(rif->addr, dev->dev_addr);
4836 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004837
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004838 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004839
4840 return 0;
4841
4842err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004843 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004844err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004845 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004846 return err;
4847}
4848
Ido Schimmelb1e45522017-04-30 19:47:14 +03004849static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4850 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004851{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004852 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004853
Ido Schimmelb1e45522017-04-30 19:47:14 +03004854 /* If netdev is already associated with a RIF, then we need to
4855 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004856 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004857 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4858 if (rif)
4859 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004860
Ido Schimmelb1e45522017-04-30 19:47:14 +03004861 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004862}
4863
Ido Schimmelb1e45522017-04-30 19:47:14 +03004864static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4865 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004866{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004867 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004868
Ido Schimmelb1e45522017-04-30 19:47:14 +03004869 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4870 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004871 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004872 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004873}
4874
Ido Schimmelb1e45522017-04-30 19:47:14 +03004875int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4876 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004877{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004878 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4879 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004880
Ido Schimmelb1e45522017-04-30 19:47:14 +03004881 if (!mlxsw_sp)
4882 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004883
Ido Schimmelb1e45522017-04-30 19:47:14 +03004884 switch (event) {
4885 case NETDEV_PRECHANGEUPPER:
4886 return 0;
4887 case NETDEV_CHANGEUPPER:
4888 if (info->linking)
4889 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4890 else
4891 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4892 break;
4893 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004894
Ido Schimmelb1e45522017-04-30 19:47:14 +03004895 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004896}
4897
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004898static struct mlxsw_sp_rif_subport *
4899mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004900{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004901 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004902}
4903
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004904static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4905 const struct mlxsw_sp_rif_params *params)
4906{
4907 struct mlxsw_sp_rif_subport *rif_subport;
4908
4909 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4910 rif_subport->vid = params->vid;
4911 rif_subport->lag = params->lag;
4912 if (params->lag)
4913 rif_subport->lag_id = params->lag_id;
4914 else
4915 rif_subport->system_port = params->system_port;
4916}
4917
4918static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4919{
4920 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4921 struct mlxsw_sp_rif_subport *rif_subport;
4922 char ritr_pl[MLXSW_REG_RITR_LEN];
4923
4924 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4925 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4926 rif->rif_index, rif->vr_id, rif->dev->mtu,
4927 rif->dev->dev_addr);
4928 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4929 rif_subport->lag ? rif_subport->lag_id :
4930 rif_subport->system_port,
4931 rif_subport->vid);
4932
4933 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4934}
4935
4936static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4937{
4938 return mlxsw_sp_rif_subport_op(rif, true);
4939}
4940
4941static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4942{
4943 mlxsw_sp_rif_subport_op(rif, false);
4944}
4945
4946static struct mlxsw_sp_fid *
4947mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4948{
4949 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4950}
4951
4952static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4953 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4954 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4955 .setup = mlxsw_sp_rif_subport_setup,
4956 .configure = mlxsw_sp_rif_subport_configure,
4957 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4958 .fid_get = mlxsw_sp_rif_subport_fid_get,
4959};
4960
4961static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4962 enum mlxsw_reg_ritr_if_type type,
4963 u16 vid_fid, bool enable)
4964{
4965 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4966 char ritr_pl[MLXSW_REG_RITR_LEN];
4967
4968 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4969 rif->dev->mtu, rif->dev->dev_addr);
4970 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4971
4972 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4973}
4974
4975static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4976{
4977 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4978}
4979
4980static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4981{
4982 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4983 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4984 int err;
4985
4986 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4987 if (err)
4988 return err;
4989
Ido Schimmel0d284812017-07-18 10:10:12 +02004990 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4991 mlxsw_sp_router_port(mlxsw_sp), true);
4992 if (err)
4993 goto err_fid_mc_flood_set;
4994
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004995 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4996 mlxsw_sp_router_port(mlxsw_sp), true);
4997 if (err)
4998 goto err_fid_bc_flood_set;
4999
5000 return 0;
5001
5002err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005003 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5004 mlxsw_sp_router_port(mlxsw_sp), false);
5005err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005006 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5007 return err;
5008}
5009
5010static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5011{
5012 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5013 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5014
5015 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5016 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005017 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5018 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005019 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5020}
5021
5022static struct mlxsw_sp_fid *
5023mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5024{
5025 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5026
5027 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5028}
5029
5030static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5031 .type = MLXSW_SP_RIF_TYPE_VLAN,
5032 .rif_size = sizeof(struct mlxsw_sp_rif),
5033 .configure = mlxsw_sp_rif_vlan_configure,
5034 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5035 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5036};
5037
5038static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5039{
5040 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5041 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5042 int err;
5043
5044 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5045 true);
5046 if (err)
5047 return err;
5048
Ido Schimmel0d284812017-07-18 10:10:12 +02005049 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5050 mlxsw_sp_router_port(mlxsw_sp), true);
5051 if (err)
5052 goto err_fid_mc_flood_set;
5053
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005054 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5055 mlxsw_sp_router_port(mlxsw_sp), true);
5056 if (err)
5057 goto err_fid_bc_flood_set;
5058
5059 return 0;
5060
5061err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005062 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5063 mlxsw_sp_router_port(mlxsw_sp), false);
5064err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005065 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5066 return err;
5067}
5068
5069static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5070{
5071 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5072 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5073
5074 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5075 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005076 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5077 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005078 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5079}
5080
5081static struct mlxsw_sp_fid *
5082mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5083{
5084 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5085}
5086
5087static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5088 .type = MLXSW_SP_RIF_TYPE_FID,
5089 .rif_size = sizeof(struct mlxsw_sp_rif),
5090 .configure = mlxsw_sp_rif_fid_configure,
5091 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5092 .fid_get = mlxsw_sp_rif_fid_fid_get,
5093};
5094
5095static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5096 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5097 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5098 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
5099};
5100
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005101static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5102{
5103 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5104
5105 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5106 sizeof(struct mlxsw_sp_rif *),
5107 GFP_KERNEL);
5108 if (!mlxsw_sp->router->rifs)
5109 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005110
5111 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5112
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005113 return 0;
5114}
5115
5116static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5117{
5118 int i;
5119
5120 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5121 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5122
5123 kfree(mlxsw_sp->router->rifs);
5124}
5125
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005126static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5127{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005128 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005129
5130 /* Flush pending FIB notifications and then flush the device's
5131 * table before requesting another dump. The FIB notification
5132 * block is unregistered, so no need to take RTNL.
5133 */
5134 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005135 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5136 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005137}
5138
Ido Schimmel4724ba562017-03-10 08:53:39 +01005139static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5140{
5141 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5142 u64 max_rifs;
5143 int err;
5144
5145 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5146 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005147 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005148
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005149 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005150 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5151 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5152 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005153 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005154 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005155}
5156
5157static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5158{
5159 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005160
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005161 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005162 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005163}
5164
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005165int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5166{
Ido Schimmel9011b672017-05-16 19:38:25 +02005167 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005168 int err;
5169
Ido Schimmel9011b672017-05-16 19:38:25 +02005170 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5171 if (!router)
5172 return -ENOMEM;
5173 mlxsw_sp->router = router;
5174 router->mlxsw_sp = mlxsw_sp;
5175
5176 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005177 err = __mlxsw_sp_router_init(mlxsw_sp);
5178 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005179 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005180
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005181 err = mlxsw_sp_rifs_init(mlxsw_sp);
5182 if (err)
5183 goto err_rifs_init;
5184
Ido Schimmel9011b672017-05-16 19:38:25 +02005185 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005186 &mlxsw_sp_nexthop_ht_params);
5187 if (err)
5188 goto err_nexthop_ht_init;
5189
Ido Schimmel9011b672017-05-16 19:38:25 +02005190 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005191 &mlxsw_sp_nexthop_group_ht_params);
5192 if (err)
5193 goto err_nexthop_group_ht_init;
5194
Ido Schimmel8494ab02017-03-24 08:02:47 +01005195 err = mlxsw_sp_lpm_init(mlxsw_sp);
5196 if (err)
5197 goto err_lpm_init;
5198
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005199 err = mlxsw_sp_vrs_init(mlxsw_sp);
5200 if (err)
5201 goto err_vrs_init;
5202
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005203 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005204 if (err)
5205 goto err_neigh_init;
5206
Ido Schimmel7e39d112017-05-16 19:38:28 +02005207 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5208 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005209 mlxsw_sp_router_fib_dump_flush);
5210 if (err)
5211 goto err_register_fib_notifier;
5212
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005213 return 0;
5214
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005215err_register_fib_notifier:
5216 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005217err_neigh_init:
5218 mlxsw_sp_vrs_fini(mlxsw_sp);
5219err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005220 mlxsw_sp_lpm_fini(mlxsw_sp);
5221err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005222 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005223err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005224 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005225err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005226 mlxsw_sp_rifs_fini(mlxsw_sp);
5227err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005228 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005229err_router_init:
5230 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005231 return err;
5232}
5233
5234void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5235{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005236 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005237 mlxsw_sp_neigh_fini(mlxsw_sp);
5238 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005239 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005240 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5241 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005242 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005243 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005244 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005245}