blob: 3d9be36965f687ba0d93cd1637a36ee3fcae5add [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
Yotam Gigic723c7352016-07-05 11:27:43 +02006 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020039#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020042#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010043#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010044#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020045#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020046#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020047#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020048#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020049#include <net/neighbour.h>
50#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020051#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020052#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010053#include <net/fib_rules.h>
Ido Schimmel57837882017-03-16 09:08:16 +010054#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020055#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020056#include <net/ndisc.h>
57#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020058#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020059
60#include "spectrum.h"
61#include "core.h"
62#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020063#include "spectrum_cnt.h"
64#include "spectrum_dpipe.h"
65#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020066
Ido Schimmel9011b672017-05-16 19:38:25 +020067struct mlxsw_sp_vr;
68struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020069struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020070
71struct mlxsw_sp_router {
72 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020073 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020074 struct mlxsw_sp_vr *vrs;
75 struct rhashtable neigh_ht;
76 struct rhashtable nexthop_group_ht;
77 struct rhashtable nexthop_ht;
78 struct {
79 struct mlxsw_sp_lpm_tree *trees;
80 unsigned int tree_count;
81 } lpm;
82 struct {
83 struct delayed_work dw;
84 unsigned long interval; /* ms */
85 } neighs_update;
86 struct delayed_work nexthop_probe_dw;
87#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
88 struct list_head nexthop_neighs_list;
89 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020090 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020091 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020092};
93
Ido Schimmel4724ba562017-03-10 08:53:39 +010094struct mlxsw_sp_rif {
95 struct list_head nexthop_list;
96 struct list_head neigh_list;
97 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +020098 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +010099 unsigned char addr[ETH_ALEN];
100 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100101 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100102 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200103 const struct mlxsw_sp_rif_ops *ops;
104 struct mlxsw_sp *mlxsw_sp;
105
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200106 unsigned int counter_ingress;
107 bool counter_ingress_valid;
108 unsigned int counter_egress;
109 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100110};
111
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200112struct mlxsw_sp_rif_params {
113 struct net_device *dev;
114 union {
115 u16 system_port;
116 u16 lag_id;
117 };
118 u16 vid;
119 bool lag;
120};
121
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200122struct mlxsw_sp_rif_subport {
123 struct mlxsw_sp_rif common;
124 union {
125 u16 system_port;
126 u16 lag_id;
127 };
128 u16 vid;
129 bool lag;
130};
131
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200132struct mlxsw_sp_rif_ops {
133 enum mlxsw_sp_rif_type type;
134 size_t rif_size;
135
136 void (*setup)(struct mlxsw_sp_rif *rif,
137 const struct mlxsw_sp_rif_params *params);
138 int (*configure)(struct mlxsw_sp_rif *rif);
139 void (*deconfigure)(struct mlxsw_sp_rif *rif);
140 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
141};
142
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200143static unsigned int *
144mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
145 enum mlxsw_sp_rif_counter_dir dir)
146{
147 switch (dir) {
148 case MLXSW_SP_RIF_COUNTER_EGRESS:
149 return &rif->counter_egress;
150 case MLXSW_SP_RIF_COUNTER_INGRESS:
151 return &rif->counter_ingress;
152 }
153 return NULL;
154}
155
156static bool
157mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
158 enum mlxsw_sp_rif_counter_dir dir)
159{
160 switch (dir) {
161 case MLXSW_SP_RIF_COUNTER_EGRESS:
162 return rif->counter_egress_valid;
163 case MLXSW_SP_RIF_COUNTER_INGRESS:
164 return rif->counter_ingress_valid;
165 }
166 return false;
167}
168
169static void
170mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
171 enum mlxsw_sp_rif_counter_dir dir,
172 bool valid)
173{
174 switch (dir) {
175 case MLXSW_SP_RIF_COUNTER_EGRESS:
176 rif->counter_egress_valid = valid;
177 break;
178 case MLXSW_SP_RIF_COUNTER_INGRESS:
179 rif->counter_ingress_valid = valid;
180 break;
181 }
182}
183
184static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
185 unsigned int counter_index, bool enable,
186 enum mlxsw_sp_rif_counter_dir dir)
187{
188 char ritr_pl[MLXSW_REG_RITR_LEN];
189 bool is_egress = false;
190 int err;
191
192 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
193 is_egress = true;
194 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
195 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
196 if (err)
197 return err;
198
199 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
200 is_egress);
201 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
202}
203
204int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
205 struct mlxsw_sp_rif *rif,
206 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
207{
208 char ricnt_pl[MLXSW_REG_RICNT_LEN];
209 unsigned int *p_counter_index;
210 bool valid;
211 int err;
212
213 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
214 if (!valid)
215 return -EINVAL;
216
217 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
218 if (!p_counter_index)
219 return -EINVAL;
220 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
221 MLXSW_REG_RICNT_OPCODE_NOP);
222 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
223 if (err)
224 return err;
225 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
226 return 0;
227}
228
229static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
230 unsigned int counter_index)
231{
232 char ricnt_pl[MLXSW_REG_RICNT_LEN];
233
234 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
235 MLXSW_REG_RICNT_OPCODE_CLEAR);
236 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
237}
238
239int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
240 struct mlxsw_sp_rif *rif,
241 enum mlxsw_sp_rif_counter_dir dir)
242{
243 unsigned int *p_counter_index;
244 int err;
245
246 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
247 if (!p_counter_index)
248 return -EINVAL;
249 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
250 p_counter_index);
251 if (err)
252 return err;
253
254 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
255 if (err)
256 goto err_counter_clear;
257
258 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
259 *p_counter_index, true, dir);
260 if (err)
261 goto err_counter_edit;
262 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
263 return 0;
264
265err_counter_edit:
266err_counter_clear:
267 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
268 *p_counter_index);
269 return err;
270}
271
272void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
273 struct mlxsw_sp_rif *rif,
274 enum mlxsw_sp_rif_counter_dir dir)
275{
276 unsigned int *p_counter_index;
277
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200278 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
279 return;
280
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200281 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
282 if (WARN_ON(!p_counter_index))
283 return;
284 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
285 *p_counter_index, false, dir);
286 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
287 *p_counter_index);
288 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
289}
290
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200291static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
292{
293 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
294 struct devlink *devlink;
295
296 devlink = priv_to_devlink(mlxsw_sp->core);
297 if (!devlink_dpipe_table_counter_enabled(devlink,
298 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
299 return;
300 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
301}
302
303static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
304{
305 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
306
307 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
308}
309
Ido Schimmel4724ba562017-03-10 08:53:39 +0100310static struct mlxsw_sp_rif *
311mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
312 const struct net_device *dev);
313
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200314#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200315
316struct mlxsw_sp_prefix_usage {
317 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
318};
319
Jiri Pirko53342022016-07-04 08:23:08 +0200320#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
321 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
322
323static bool
324mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
325 struct mlxsw_sp_prefix_usage *prefix_usage2)
326{
327 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
328}
329
Jiri Pirko6b75c482016-07-04 08:23:09 +0200330static bool
331mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
332{
333 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
334
335 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
336}
337
338static void
339mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
340 struct mlxsw_sp_prefix_usage *prefix_usage2)
341{
342 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
343}
344
345static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200346mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
347 unsigned char prefix_len)
348{
349 set_bit(prefix_len, prefix_usage->b);
350}
351
352static void
353mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
354 unsigned char prefix_len)
355{
356 clear_bit(prefix_len, prefix_usage->b);
357}
358
359struct mlxsw_sp_fib_key {
360 unsigned char addr[sizeof(struct in6_addr)];
361 unsigned char prefix_len;
362};
363
Jiri Pirko61c503f2016-07-04 08:23:11 +0200364enum mlxsw_sp_fib_entry_type {
365 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
366 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
367 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
368};
369
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200370struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200371struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200372
Ido Schimmel9aecce12017-02-09 10:28:42 +0100373struct mlxsw_sp_fib_node {
374 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200375 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100376 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100377 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100378 struct mlxsw_sp_fib_key key;
379};
380
Ido Schimmel9aecce12017-02-09 10:28:42 +0100381struct mlxsw_sp_fib_entry {
382 struct list_head list;
383 struct mlxsw_sp_fib_node *fib_node;
384 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200385 struct list_head nexthop_group_node;
386 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200387};
388
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200389struct mlxsw_sp_fib4_entry {
390 struct mlxsw_sp_fib_entry common;
391 u32 tb_id;
392 u32 prio;
393 u8 tos;
394 u8 type;
395};
396
Ido Schimmel428b8512017-08-03 13:28:28 +0200397struct mlxsw_sp_fib6_entry {
398 struct mlxsw_sp_fib_entry common;
399 struct list_head rt6_list;
400 unsigned int nrt6;
401};
402
403struct mlxsw_sp_rt6 {
404 struct list_head list;
405 struct rt6_info *rt;
406};
407
Ido Schimmel9011b672017-05-16 19:38:25 +0200408enum mlxsw_sp_l3proto {
409 MLXSW_SP_L3_PROTO_IPV4,
410 MLXSW_SP_L3_PROTO_IPV6,
411};
412
413struct mlxsw_sp_lpm_tree {
414 u8 id; /* tree ID */
415 unsigned int ref_count;
416 enum mlxsw_sp_l3proto proto;
417 struct mlxsw_sp_prefix_usage prefix_usage;
418};
419
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200420struct mlxsw_sp_fib {
421 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100422 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100423 struct mlxsw_sp_vr *vr;
424 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200425 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
426 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100427 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200428};
429
Ido Schimmel9011b672017-05-16 19:38:25 +0200430struct mlxsw_sp_vr {
431 u16 id; /* virtual router ID */
432 u32 tb_id; /* kernel fib table id */
433 unsigned int rif_count;
434 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200435 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200436};
437
Ido Schimmel9aecce12017-02-09 10:28:42 +0100438static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200439
Ido Schimmel76610eb2017-03-10 08:53:41 +0100440static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
441 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200442{
443 struct mlxsw_sp_fib *fib;
444 int err;
445
446 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
447 if (!fib)
448 return ERR_PTR(-ENOMEM);
449 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
450 if (err)
451 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100452 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100453 fib->proto = proto;
454 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200455 return fib;
456
457err_rhashtable_init:
458 kfree(fib);
459 return ERR_PTR(err);
460}
461
462static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
463{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100464 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100465 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200466 rhashtable_destroy(&fib->ht);
467 kfree(fib);
468}
469
Jiri Pirko53342022016-07-04 08:23:08 +0200470static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100471mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200472{
473 static struct mlxsw_sp_lpm_tree *lpm_tree;
474 int i;
475
Ido Schimmel9011b672017-05-16 19:38:25 +0200476 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
477 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100478 if (lpm_tree->ref_count == 0)
479 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200480 }
481 return NULL;
482}
483
484static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
485 struct mlxsw_sp_lpm_tree *lpm_tree)
486{
487 char ralta_pl[MLXSW_REG_RALTA_LEN];
488
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200489 mlxsw_reg_ralta_pack(ralta_pl, true,
490 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
491 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200492 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
493}
494
Ido Schimmelcc702672017-08-14 10:54:03 +0200495static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
496 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200497{
498 char ralta_pl[MLXSW_REG_RALTA_LEN];
499
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200500 mlxsw_reg_ralta_pack(ralta_pl, false,
501 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
502 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200503 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200504}
505
506static int
507mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
508 struct mlxsw_sp_prefix_usage *prefix_usage,
509 struct mlxsw_sp_lpm_tree *lpm_tree)
510{
511 char ralst_pl[MLXSW_REG_RALST_LEN];
512 u8 root_bin = 0;
513 u8 prefix;
514 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
515
516 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
517 root_bin = prefix;
518
519 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
520 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
521 if (prefix == 0)
522 continue;
523 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
524 MLXSW_REG_RALST_BIN_NO_CHILD);
525 last_prefix = prefix;
526 }
527 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
528}
529
530static struct mlxsw_sp_lpm_tree *
531mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
532 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100533 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200534{
535 struct mlxsw_sp_lpm_tree *lpm_tree;
536 int err;
537
Ido Schimmel382dbb42017-03-10 08:53:40 +0100538 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200539 if (!lpm_tree)
540 return ERR_PTR(-EBUSY);
541 lpm_tree->proto = proto;
542 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
543 if (err)
544 return ERR_PTR(err);
545
546 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
547 lpm_tree);
548 if (err)
549 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200550 memcpy(&lpm_tree->prefix_usage, prefix_usage,
551 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200552 return lpm_tree;
553
554err_left_struct_set:
555 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
556 return ERR_PTR(err);
557}
558
Ido Schimmelcc702672017-08-14 10:54:03 +0200559static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
560 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200561{
Ido Schimmelcc702672017-08-14 10:54:03 +0200562 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200563}
564
565static struct mlxsw_sp_lpm_tree *
566mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
567 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100568 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200569{
570 struct mlxsw_sp_lpm_tree *lpm_tree;
571 int i;
572
Ido Schimmel9011b672017-05-16 19:38:25 +0200573 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
574 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200575 if (lpm_tree->ref_count != 0 &&
576 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200577 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
578 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200579 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200580 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200581 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
582}
Jiri Pirko53342022016-07-04 08:23:08 +0200583
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200584static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
585{
Jiri Pirko53342022016-07-04 08:23:08 +0200586 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200587}
588
Ido Schimmelcc702672017-08-14 10:54:03 +0200589static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
590 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200591{
592 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200593 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200594}
595
Ido Schimmeld7a60302017-06-08 08:47:43 +0200596#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100597
598static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200599{
600 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100601 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200602 int i;
603
Ido Schimmel8494ab02017-03-24 08:02:47 +0100604 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
605 return -EIO;
606
607 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200608 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
609 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100610 sizeof(struct mlxsw_sp_lpm_tree),
611 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200612 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100613 return -ENOMEM;
614
Ido Schimmel9011b672017-05-16 19:38:25 +0200615 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
616 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200617 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
618 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100619
620 return 0;
621}
622
623static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
624{
Ido Schimmel9011b672017-05-16 19:38:25 +0200625 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200626}
627
Ido Schimmel76610eb2017-03-10 08:53:41 +0100628static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
629{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200630 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100631}
632
Jiri Pirko6b75c482016-07-04 08:23:09 +0200633static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
634{
635 struct mlxsw_sp_vr *vr;
636 int i;
637
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200638 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200639 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100640 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200641 return vr;
642 }
643 return NULL;
644}
645
646static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200647 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200648{
649 char raltb_pl[MLXSW_REG_RALTB_LEN];
650
Ido Schimmel76610eb2017-03-10 08:53:41 +0100651 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
652 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200653 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200654 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
655}
656
657static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100658 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200659{
660 char raltb_pl[MLXSW_REG_RALTB_LEN];
661
662 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100663 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
664 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200665 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
666}
667
668static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
669{
670 /* For our purpose, squash main and local table into one */
671 if (tb_id == RT_TABLE_LOCAL)
672 tb_id = RT_TABLE_MAIN;
673 return tb_id;
674}
675
676static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100677 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200678{
679 struct mlxsw_sp_vr *vr;
680 int i;
681
682 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200683
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200684 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200685 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100686 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200687 return vr;
688 }
689 return NULL;
690}
691
Ido Schimmel76610eb2017-03-10 08:53:41 +0100692static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
693 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200694{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100695 switch (proto) {
696 case MLXSW_SP_L3_PROTO_IPV4:
697 return vr->fib4;
698 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200699 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100700 }
701 return NULL;
702}
703
704static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
705 u32 tb_id)
706{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200707 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200708 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200709
710 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
711 if (!vr)
712 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100713 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
714 if (IS_ERR(vr->fib4))
715 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200716 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
717 if (IS_ERR(vr->fib6)) {
718 err = PTR_ERR(vr->fib6);
719 goto err_fib6_create;
720 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200721 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200722 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200723
724err_fib6_create:
725 mlxsw_sp_fib_destroy(vr->fib4);
726 vr->fib4 = NULL;
727 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200728}
729
Ido Schimmel76610eb2017-03-10 08:53:41 +0100730static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200731{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200732 mlxsw_sp_fib_destroy(vr->fib6);
733 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100734 mlxsw_sp_fib_destroy(vr->fib4);
735 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200736}
737
Ido Schimmel76610eb2017-03-10 08:53:41 +0100738static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200739{
740 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200741
742 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100743 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
744 if (!vr)
745 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746 return vr;
747}
748
Ido Schimmel76610eb2017-03-10 08:53:41 +0100749static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200750{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200751 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
752 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100753 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200754}
755
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200756static bool
757mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
758 enum mlxsw_sp_l3proto proto, u8 tree_id)
759{
760 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
761
762 if (!mlxsw_sp_vr_is_used(vr))
763 return false;
764 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
765 return true;
766 return false;
767}
768
769static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
770 struct mlxsw_sp_fib *fib,
771 struct mlxsw_sp_lpm_tree *new_tree)
772{
773 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
774 int err;
775
776 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
777 if (err)
778 return err;
779 fib->lpm_tree = new_tree;
780 mlxsw_sp_lpm_tree_hold(new_tree);
781 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
782 return 0;
783}
784
785static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
786 struct mlxsw_sp_fib *fib,
787 struct mlxsw_sp_lpm_tree *new_tree)
788{
789 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
790 enum mlxsw_sp_l3proto proto = fib->proto;
791 u8 old_id, new_id = new_tree->id;
792 struct mlxsw_sp_vr *vr;
793 int i, err;
794
795 if (!old_tree)
796 goto no_replace;
797 old_id = old_tree->id;
798
799 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
800 vr = &mlxsw_sp->router->vrs[i];
801 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
802 continue;
803 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
804 mlxsw_sp_vr_fib(vr, proto),
805 new_tree);
806 if (err)
807 goto err_tree_replace;
808 }
809
810 return 0;
811
812err_tree_replace:
813 for (i--; i >= 0; i--) {
814 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
815 continue;
816 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
817 mlxsw_sp_vr_fib(vr, proto),
818 old_tree);
819 }
820 return err;
821
822no_replace:
823 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
824 if (err)
825 return err;
826 fib->lpm_tree = new_tree;
827 mlxsw_sp_lpm_tree_hold(new_tree);
828 return 0;
829}
830
831static void
832mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
833 enum mlxsw_sp_l3proto proto,
834 struct mlxsw_sp_prefix_usage *req_prefix_usage)
835{
836 int i;
837
838 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
839 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
840 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
841 unsigned char prefix;
842
843 if (!mlxsw_sp_vr_is_used(vr))
844 continue;
845 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
846 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
847 }
848}
849
Nogah Frankel9497c042016-09-20 11:16:54 +0200850static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200851{
852 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200853 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200854 int i;
855
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200856 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200857 return -EIO;
858
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200859 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200860 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
861 GFP_KERNEL);
862 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200863 return -ENOMEM;
864
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200865 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200866 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200867 vr->id = i;
868 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200869
870 return 0;
871}
872
Ido Schimmelac571de2016-11-14 11:26:32 +0100873static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
874
Nogah Frankel9497c042016-09-20 11:16:54 +0200875static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
876{
Ido Schimmel30572242016-12-03 16:45:01 +0100877 /* At this stage we're guaranteed not to have new incoming
878 * FIB notifications and the work queue is free from FIBs
879 * sitting on top of mlxsw netdevs. However, we can still
880 * have other FIBs queued. Flush the queue before flushing
881 * the device's tables. No need for locks, as we're the only
882 * writer.
883 */
884 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100885 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200886 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200887}
888
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200889struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100890 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200891};
892
893struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100894 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200895 struct rhash_head ht_node;
896 struct mlxsw_sp_neigh_key key;
897 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100898 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200899 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200900 struct list_head nexthop_list; /* list of nexthops using
901 * this neigh entry
902 */
Yotam Gigib2157142016-07-05 11:27:51 +0200903 struct list_head nexthop_neighs_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200904};
905
906static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
907 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
908 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
909 .key_len = sizeof(struct mlxsw_sp_neigh_key),
910};
911
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100912static struct mlxsw_sp_neigh_entry *
913mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
914 u16 rif)
915{
916 struct mlxsw_sp_neigh_entry *neigh_entry;
917
918 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
919 if (!neigh_entry)
920 return NULL;
921
922 neigh_entry->key.n = n;
923 neigh_entry->rif = rif;
924 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
925
926 return neigh_entry;
927}
928
929static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
930{
931 kfree(neigh_entry);
932}
933
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200934static int
935mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
936 struct mlxsw_sp_neigh_entry *neigh_entry)
937{
Ido Schimmel9011b672017-05-16 19:38:25 +0200938 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200939 &neigh_entry->ht_node,
940 mlxsw_sp_neigh_ht_params);
941}
942
943static void
944mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
945 struct mlxsw_sp_neigh_entry *neigh_entry)
946{
Ido Schimmel9011b672017-05-16 19:38:25 +0200947 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200948 &neigh_entry->ht_node,
949 mlxsw_sp_neigh_ht_params);
950}
951
952static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100953mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200954{
955 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100956 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100957 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200958
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100959 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
960 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100961 return ERR_PTR(-EINVAL);
962
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100963 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200964 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100965 return ERR_PTR(-ENOMEM);
966
967 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
968 if (err)
969 goto err_neigh_entry_insert;
970
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100971 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +0100972
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200973 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100974
975err_neigh_entry_insert:
976 mlxsw_sp_neigh_entry_free(neigh_entry);
977 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200978}
979
980static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100981mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
982 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200983{
Ido Schimmel9665b742017-02-08 11:16:42 +0100984 list_del(&neigh_entry->rif_list_node);
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100985 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
986 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200987}
988
989static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +0100990mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200991{
Jiri Pirko33b13412016-11-10 12:31:04 +0100992 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200993
Jiri Pirko33b13412016-11-10 12:31:04 +0100994 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +0200995 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200996 &key, mlxsw_sp_neigh_ht_params);
997}
998
Yotam Gigic723c7352016-07-05 11:27:43 +0200999static void
1000mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1001{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001002 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001003
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001004#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001005 interval = min_t(unsigned long,
1006 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1007 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001008#else
1009 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1010#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001011 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001012}
1013
1014static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1015 char *rauhtd_pl,
1016 int ent_index)
1017{
1018 struct net_device *dev;
1019 struct neighbour *n;
1020 __be32 dipn;
1021 u32 dip;
1022 u16 rif;
1023
1024 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1025
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001026 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001027 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1028 return;
1029 }
1030
1031 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001032 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001033 n = neigh_lookup(&arp_tbl, &dipn, dev);
1034 if (!n) {
1035 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1036 &dip);
1037 return;
1038 }
1039
1040 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1041 neigh_event_send(n, NULL);
1042 neigh_release(n);
1043}
1044
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001045#if IS_ENABLED(IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001046static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1047 char *rauhtd_pl,
1048 int rec_index)
1049{
1050 struct net_device *dev;
1051 struct neighbour *n;
1052 struct in6_addr dip;
1053 u16 rif;
1054
1055 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1056 (char *) &dip);
1057
1058 if (!mlxsw_sp->router->rifs[rif]) {
1059 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1060 return;
1061 }
1062
1063 dev = mlxsw_sp->router->rifs[rif]->dev;
1064 n = neigh_lookup(&nd_tbl, &dip, dev);
1065 if (!n) {
1066 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1067 &dip);
1068 return;
1069 }
1070
1071 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1072 neigh_event_send(n, NULL);
1073 neigh_release(n);
1074}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001075#else
1076static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1077 char *rauhtd_pl,
1078 int rec_index)
1079{
1080}
1081#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001082
Yotam Gigic723c7352016-07-05 11:27:43 +02001083static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1084 char *rauhtd_pl,
1085 int rec_index)
1086{
1087 u8 num_entries;
1088 int i;
1089
1090 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1091 rec_index);
1092 /* Hardware starts counting at 0, so add 1. */
1093 num_entries++;
1094
1095 /* Each record consists of several neighbour entries. */
1096 for (i = 0; i < num_entries; i++) {
1097 int ent_index;
1098
1099 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1100 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1101 ent_index);
1102 }
1103
1104}
1105
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001106static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1107 char *rauhtd_pl,
1108 int rec_index)
1109{
1110 /* One record contains one entry. */
1111 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1112 rec_index);
1113}
1114
Yotam Gigic723c7352016-07-05 11:27:43 +02001115static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1116 char *rauhtd_pl, int rec_index)
1117{
1118 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1119 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1120 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1121 rec_index);
1122 break;
1123 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001124 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1125 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001126 break;
1127 }
1128}
1129
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001130static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1131{
1132 u8 num_rec, last_rec_index, num_entries;
1133
1134 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1135 last_rec_index = num_rec - 1;
1136
1137 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1138 return false;
1139 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1140 MLXSW_REG_RAUHTD_TYPE_IPV6)
1141 return true;
1142
1143 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1144 last_rec_index);
1145 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1146 return true;
1147 return false;
1148}
1149
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001150static int
1151__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1152 char *rauhtd_pl,
1153 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001154{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001155 int i, num_rec;
1156 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001157
1158 /* Make sure the neighbour's netdev isn't removed in the
1159 * process.
1160 */
1161 rtnl_lock();
1162 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001163 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001164 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1165 rauhtd_pl);
1166 if (err) {
1167 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1168 break;
1169 }
1170 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1171 for (i = 0; i < num_rec; i++)
1172 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1173 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001174 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001175 rtnl_unlock();
1176
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001177 return err;
1178}
1179
1180static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1181{
1182 enum mlxsw_reg_rauhtd_type type;
1183 char *rauhtd_pl;
1184 int err;
1185
1186 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1187 if (!rauhtd_pl)
1188 return -ENOMEM;
1189
1190 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1191 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1192 if (err)
1193 goto out;
1194
1195 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1196 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1197out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001198 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001199 return err;
1200}
1201
1202static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1203{
1204 struct mlxsw_sp_neigh_entry *neigh_entry;
1205
1206 /* Take RTNL mutex here to prevent lists from changes */
1207 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001208 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001209 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001210 /* If this neigh have nexthops, make the kernel think this neigh
1211 * is active regardless of the traffic.
1212 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001213 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001214 rtnl_unlock();
1215}
1216
1217static void
1218mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1219{
Ido Schimmel9011b672017-05-16 19:38:25 +02001220 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001221
Ido Schimmel9011b672017-05-16 19:38:25 +02001222 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001223 msecs_to_jiffies(interval));
1224}
1225
1226static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1227{
Ido Schimmel9011b672017-05-16 19:38:25 +02001228 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001229 int err;
1230
Ido Schimmel9011b672017-05-16 19:38:25 +02001231 router = container_of(work, struct mlxsw_sp_router,
1232 neighs_update.dw.work);
1233 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001234 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001235 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001236
Ido Schimmel9011b672017-05-16 19:38:25 +02001237 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001238
Ido Schimmel9011b672017-05-16 19:38:25 +02001239 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001240}
1241
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001242static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1243{
1244 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001245 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001246
Ido Schimmel9011b672017-05-16 19:38:25 +02001247 router = container_of(work, struct mlxsw_sp_router,
1248 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001249 /* Iterate over nexthop neighbours, find those who are unresolved and
1250 * send arp on them. This solves the chicken-egg problem when
1251 * the nexthop wouldn't get offloaded until the neighbor is resolved
1252 * but it wouldn't get resolved ever in case traffic is flowing in HW
1253 * using different nexthop.
1254 *
1255 * Take RTNL mutex here to prevent lists from changes.
1256 */
1257 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001258 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001259 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001260 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001261 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001262 rtnl_unlock();
1263
Ido Schimmel9011b672017-05-16 19:38:25 +02001264 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001265 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1266}
1267
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001268static void
1269mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1270 struct mlxsw_sp_neigh_entry *neigh_entry,
1271 bool removing);
1272
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001273static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001274{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001275 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1276 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1277}
1278
1279static void
1280mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1281 struct mlxsw_sp_neigh_entry *neigh_entry,
1282 enum mlxsw_reg_rauht_op op)
1283{
Jiri Pirko33b13412016-11-10 12:31:04 +01001284 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001285 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001286 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001287
1288 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1289 dip);
1290 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1291}
1292
1293static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001294mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1295 struct mlxsw_sp_neigh_entry *neigh_entry,
1296 enum mlxsw_reg_rauht_op op)
1297{
1298 struct neighbour *n = neigh_entry->key.n;
1299 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1300 const char *dip = n->primary_key;
1301
1302 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1303 dip);
1304 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1305}
1306
1307static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1308{
1309 /* Packets with a link-local destination address are trapped
1310 * after LPM lookup and never reach the neighbour table, so
1311 * there is no need to program such neighbours to the device.
1312 */
1313 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1314 IPV6_ADDR_LINKLOCAL)
1315 return true;
1316 return false;
1317}
1318
1319static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001320mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1321 struct mlxsw_sp_neigh_entry *neigh_entry,
1322 bool adding)
1323{
1324 if (!adding && !neigh_entry->connected)
1325 return;
1326 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001327 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001328 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1329 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001330 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001331 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1332 return;
1333 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1334 mlxsw_sp_rauht_op(adding));
1335 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001336 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001337 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001338}
1339
1340struct mlxsw_sp_neigh_event_work {
1341 struct work_struct work;
1342 struct mlxsw_sp *mlxsw_sp;
1343 struct neighbour *n;
1344};
1345
1346static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1347{
1348 struct mlxsw_sp_neigh_event_work *neigh_work =
1349 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1350 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1351 struct mlxsw_sp_neigh_entry *neigh_entry;
1352 struct neighbour *n = neigh_work->n;
1353 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001354 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001355 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001356
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001357 /* If these parameters are changed after we release the lock,
1358 * then we are guaranteed to receive another event letting us
1359 * know about it.
1360 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001361 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001362 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001363 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001364 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001365 read_unlock_bh(&n->lock);
1366
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001367 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001368 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001369 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1370 if (!entry_connected && !neigh_entry)
1371 goto out;
1372 if (!neigh_entry) {
1373 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1374 if (IS_ERR(neigh_entry))
1375 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001376 }
1377
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001378 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1379 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1380 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1381
1382 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1383 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1384
1385out:
1386 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001387 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001388 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001389}
1390
Jiri Pirkoe7322632016-09-01 10:37:43 +02001391int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1392 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001393{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001394 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001395 struct mlxsw_sp_port *mlxsw_sp_port;
1396 struct mlxsw_sp *mlxsw_sp;
1397 unsigned long interval;
1398 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001399 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001400
1401 switch (event) {
1402 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1403 p = ptr;
1404
1405 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001406 if (!p->dev || (p->tbl->family != AF_INET &&
1407 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001408 return NOTIFY_DONE;
1409
1410 /* We are in atomic context and can't take RTNL mutex,
1411 * so use RCU variant to walk the device chain.
1412 */
1413 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1414 if (!mlxsw_sp_port)
1415 return NOTIFY_DONE;
1416
1417 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1418 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001419 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001420
1421 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1422 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001423 case NETEVENT_NEIGH_UPDATE:
1424 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001425
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001426 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001427 return NOTIFY_DONE;
1428
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001429 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001430 if (!mlxsw_sp_port)
1431 return NOTIFY_DONE;
1432
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001433 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1434 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001435 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001436 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001437 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001438
1439 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1440 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1441 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001442
1443 /* Take a reference to ensure the neighbour won't be
1444 * destructed until we drop the reference in delayed
1445 * work.
1446 */
1447 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001448 mlxsw_core_schedule_work(&neigh_work->work);
1449 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001450 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001451 }
1452
1453 return NOTIFY_DONE;
1454}
1455
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001456static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1457{
Yotam Gigic723c7352016-07-05 11:27:43 +02001458 int err;
1459
Ido Schimmel9011b672017-05-16 19:38:25 +02001460 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001461 &mlxsw_sp_neigh_ht_params);
1462 if (err)
1463 return err;
1464
1465 /* Initialize the polling interval according to the default
1466 * table.
1467 */
1468 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1469
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001470 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001471 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001472 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001473 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001474 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001475 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1476 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001477 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001478}
1479
1480static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1481{
Ido Schimmel9011b672017-05-16 19:38:25 +02001482 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1483 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1484 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001485}
1486
Ido Schimmel9665b742017-02-08 11:16:42 +01001487static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001488 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001489{
1490 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1491
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001492 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001493 rif_list_node) {
1494 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001495 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001496 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001497}
1498
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001499struct mlxsw_sp_nexthop_key {
1500 struct fib_nh *fib_nh;
1501};
1502
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001503struct mlxsw_sp_nexthop {
1504 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001505 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001506 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1507 * this belongs to
1508 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001509 struct rhash_head ht_node;
1510 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001511 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001512 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001513 u8 should_offload:1, /* set indicates this neigh is connected and
1514 * should be put to KVD linear area of this group.
1515 */
1516 offloaded:1, /* set in case the neigh is actually put into
1517 * KVD linear area of this group.
1518 */
1519 update:1; /* set indicates that MAC of this neigh should be
1520 * updated in HW
1521 */
1522 struct mlxsw_sp_neigh_entry *neigh_entry;
1523};
1524
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001525struct mlxsw_sp_nexthop_group_key {
1526 struct fib_info *fi;
1527};
1528
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001529struct mlxsw_sp_nexthop_group {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001530 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001531 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001532 struct neigh_table *neigh_tbl;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001533 struct mlxsw_sp_nexthop_group_key key;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001534 u8 adj_index_valid:1,
1535 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001536 u32 adj_index;
1537 u16 ecmp_size;
1538 u16 count;
1539 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001540#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001541};
1542
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001543static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1544 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1545 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1546 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1547};
1548
1549static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1550 struct mlxsw_sp_nexthop_group *nh_grp)
1551{
Ido Schimmel9011b672017-05-16 19:38:25 +02001552 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001553 &nh_grp->ht_node,
1554 mlxsw_sp_nexthop_group_ht_params);
1555}
1556
1557static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1558 struct mlxsw_sp_nexthop_group *nh_grp)
1559{
Ido Schimmel9011b672017-05-16 19:38:25 +02001560 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001561 &nh_grp->ht_node,
1562 mlxsw_sp_nexthop_group_ht_params);
1563}
1564
1565static struct mlxsw_sp_nexthop_group *
1566mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1567 struct mlxsw_sp_nexthop_group_key key)
1568{
Ido Schimmel9011b672017-05-16 19:38:25 +02001569 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001570 mlxsw_sp_nexthop_group_ht_params);
1571}
1572
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001573static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1574 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1575 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1576 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1577};
1578
1579static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1580 struct mlxsw_sp_nexthop *nh)
1581{
Ido Schimmel9011b672017-05-16 19:38:25 +02001582 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001583 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1584}
1585
1586static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1587 struct mlxsw_sp_nexthop *nh)
1588{
Ido Schimmel9011b672017-05-16 19:38:25 +02001589 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001590 mlxsw_sp_nexthop_ht_params);
1591}
1592
Ido Schimmelad178c82017-02-08 11:16:40 +01001593static struct mlxsw_sp_nexthop *
1594mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1595 struct mlxsw_sp_nexthop_key key)
1596{
Ido Schimmel9011b672017-05-16 19:38:25 +02001597 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001598 mlxsw_sp_nexthop_ht_params);
1599}
1600
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001601static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001602 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001603 u32 adj_index, u16 ecmp_size,
1604 u32 new_adj_index,
1605 u16 new_ecmp_size)
1606{
1607 char raleu_pl[MLXSW_REG_RALEU_LEN];
1608
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001609 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001610 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1611 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001612 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001613 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1614}
1615
1616static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1617 struct mlxsw_sp_nexthop_group *nh_grp,
1618 u32 old_adj_index, u16 old_ecmp_size)
1619{
1620 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001621 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001622 int err;
1623
1624 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001625 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001626 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001627 fib = fib_entry->fib_node->fib;
1628 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001629 old_adj_index,
1630 old_ecmp_size,
1631 nh_grp->adj_index,
1632 nh_grp->ecmp_size);
1633 if (err)
1634 return err;
1635 }
1636 return 0;
1637}
1638
1639static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1640 struct mlxsw_sp_nexthop *nh)
1641{
1642 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1643 char ratr_pl[MLXSW_REG_RATR_LEN];
1644
1645 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1646 true, adj_index, neigh_entry->rif);
1647 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1648 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1649}
1650
1651static int
1652mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001653 struct mlxsw_sp_nexthop_group *nh_grp,
1654 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001655{
1656 u32 adj_index = nh_grp->adj_index; /* base */
1657 struct mlxsw_sp_nexthop *nh;
1658 int i;
1659 int err;
1660
1661 for (i = 0; i < nh_grp->count; i++) {
1662 nh = &nh_grp->nexthops[i];
1663
1664 if (!nh->should_offload) {
1665 nh->offloaded = 0;
1666 continue;
1667 }
1668
Ido Schimmela59b7e02017-01-23 11:11:42 +01001669 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001670 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1671 adj_index, nh);
1672 if (err)
1673 return err;
1674 nh->update = 0;
1675 nh->offloaded = 1;
1676 }
1677 adj_index++;
1678 }
1679 return 0;
1680}
1681
1682static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1683 struct mlxsw_sp_fib_entry *fib_entry);
1684
Ido Schimmel1819ae32017-07-21 18:04:28 +02001685static bool
1686mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1687 const struct mlxsw_sp_fib_entry *fib_entry);
1688
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001689static int
1690mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1691 struct mlxsw_sp_nexthop_group *nh_grp)
1692{
1693 struct mlxsw_sp_fib_entry *fib_entry;
1694 int err;
1695
1696 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001697 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1698 fib_entry))
1699 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001700 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1701 if (err)
1702 return err;
1703 }
1704 return 0;
1705}
1706
1707static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001708mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1709 enum mlxsw_reg_ralue_op op, int err);
1710
1711static void
1712mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1713{
1714 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1715 struct mlxsw_sp_fib_entry *fib_entry;
1716
1717 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1718 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1719 fib_entry))
1720 continue;
1721 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1722 }
1723}
1724
1725static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001726mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1727 struct mlxsw_sp_nexthop_group *nh_grp)
1728{
1729 struct mlxsw_sp_nexthop *nh;
1730 bool offload_change = false;
1731 u32 adj_index;
1732 u16 ecmp_size = 0;
1733 bool old_adj_index_valid;
1734 u32 old_adj_index;
1735 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001736 int i;
1737 int err;
1738
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001739 if (!nh_grp->gateway) {
1740 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1741 return;
1742 }
1743
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001744 for (i = 0; i < nh_grp->count; i++) {
1745 nh = &nh_grp->nexthops[i];
1746
Petr Machata56b8a9e2017-07-31 09:27:29 +02001747 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001748 offload_change = true;
1749 if (nh->should_offload)
1750 nh->update = 1;
1751 }
1752 if (nh->should_offload)
1753 ecmp_size++;
1754 }
1755 if (!offload_change) {
1756 /* Nothing was added or removed, so no need to reallocate. Just
1757 * update MAC on existing adjacency indexes.
1758 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001759 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1760 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001761 if (err) {
1762 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1763 goto set_trap;
1764 }
1765 return;
1766 }
1767 if (!ecmp_size)
1768 /* No neigh of this group is connected so we just set
1769 * the trap and let everthing flow through kernel.
1770 */
1771 goto set_trap;
1772
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001773 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1774 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001775 /* We ran out of KVD linear space, just set the
1776 * trap and let everything flow through kernel.
1777 */
1778 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1779 goto set_trap;
1780 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001781 old_adj_index_valid = nh_grp->adj_index_valid;
1782 old_adj_index = nh_grp->adj_index;
1783 old_ecmp_size = nh_grp->ecmp_size;
1784 nh_grp->adj_index_valid = 1;
1785 nh_grp->adj_index = adj_index;
1786 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001787 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001788 if (err) {
1789 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1790 goto set_trap;
1791 }
1792
1793 if (!old_adj_index_valid) {
1794 /* The trap was set for fib entries, so we have to call
1795 * fib entry update to unset it and use adjacency index.
1796 */
1797 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1798 if (err) {
1799 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1800 goto set_trap;
1801 }
1802 return;
1803 }
1804
1805 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1806 old_adj_index, old_ecmp_size);
1807 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1808 if (err) {
1809 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1810 goto set_trap;
1811 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02001812
1813 /* Offload state within the group changed, so update the flags. */
1814 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
1815
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001816 return;
1817
1818set_trap:
1819 old_adj_index_valid = nh_grp->adj_index_valid;
1820 nh_grp->adj_index_valid = 0;
1821 for (i = 0; i < nh_grp->count; i++) {
1822 nh = &nh_grp->nexthops[i];
1823 nh->offloaded = 0;
1824 }
1825 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1826 if (err)
1827 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1828 if (old_adj_index_valid)
1829 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1830}
1831
1832static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1833 bool removing)
1834{
Petr Machata213666a2017-07-31 09:27:30 +02001835 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001836 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02001837 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001838 nh->should_offload = 0;
1839 nh->update = 1;
1840}
1841
1842static void
1843mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1844 struct mlxsw_sp_neigh_entry *neigh_entry,
1845 bool removing)
1846{
1847 struct mlxsw_sp_nexthop *nh;
1848
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001849 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1850 neigh_list_node) {
1851 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1852 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1853 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001854}
1855
Ido Schimmel9665b742017-02-08 11:16:42 +01001856static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001857 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001858{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001859 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001860 return;
1861
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001862 nh->rif = rif;
1863 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001864}
1865
1866static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1867{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001868 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001869 return;
1870
1871 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001872 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01001873}
1874
Ido Schimmela8c97012017-02-08 11:16:35 +01001875static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1876 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001877{
1878 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001879 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001880 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001881 int err;
1882
Ido Schimmelad178c82017-02-08 11:16:40 +01001883 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01001884 return 0;
1885
Jiri Pirko33b13412016-11-10 12:31:04 +01001886 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02001887 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01001888 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01001889 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01001890 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001891 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01001892 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001893 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1894 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01001895 if (IS_ERR(n))
1896 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001897 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01001898 }
1899 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1900 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001901 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1902 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001903 err = -EINVAL;
1904 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001905 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001906 }
Yotam Gigib2157142016-07-05 11:27:51 +02001907
1908 /* If that is the first nexthop connected to that neigh, add to
1909 * nexthop_neighs_list
1910 */
1911 if (list_empty(&neigh_entry->nexthop_list))
1912 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02001913 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02001914
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001915 nh->neigh_entry = neigh_entry;
1916 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1917 read_lock_bh(&n->lock);
1918 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001919 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001920 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01001921 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001922
1923 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001924
1925err_neigh_entry_create:
1926 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001927 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001928}
1929
Ido Schimmela8c97012017-02-08 11:16:35 +01001930static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1931 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001932{
1933 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01001934 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001935
Ido Schimmelb8399a12017-02-08 11:16:33 +01001936 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01001937 return;
1938 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01001939
Ido Schimmel58312122016-12-23 09:32:50 +01001940 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001941 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01001942 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02001943
1944 /* If that is the last nexthop connected to that neigh, remove from
1945 * nexthop_neighs_list
1946 */
Ido Schimmele58be792017-02-08 11:16:28 +01001947 if (list_empty(&neigh_entry->nexthop_list))
1948 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02001949
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001950 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1951 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1952
1953 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01001954}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001955
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001956static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1957 struct mlxsw_sp_nexthop_group *nh_grp,
1958 struct mlxsw_sp_nexthop *nh,
1959 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001960{
1961 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001962 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001963 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01001964 int err;
1965
1966 nh->nh_grp = nh_grp;
1967 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001968 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01001969 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1970 if (err)
1971 return err;
1972
Ido Schimmel97989ee2017-03-10 08:53:38 +01001973 if (!dev)
1974 return 0;
1975
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001976 in_dev = __in_dev_get_rtnl(dev);
1977 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1978 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1979 return 0;
1980
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001981 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1982 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01001983 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001984 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01001985
1986 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1987 if (err)
1988 goto err_nexthop_neigh_init;
1989
1990 return 0;
1991
1992err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02001993 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01001994 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1995 return err;
1996}
1997
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001998static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
1999 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002000{
2001 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002002 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002003 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002004}
2005
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002006static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2007 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002008{
2009 struct mlxsw_sp_nexthop_key key;
2010 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002011 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002012
Ido Schimmel9011b672017-05-16 19:38:25 +02002013 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002014 return;
2015
2016 key.fib_nh = fib_nh;
2017 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2018 if (WARN_ON_ONCE(!nh))
2019 return;
2020
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002021 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2022 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002023 return;
2024
2025 switch (event) {
2026 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002027 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002028 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2029 break;
2030 case FIB_EVENT_NH_DEL:
2031 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002032 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002033 break;
2034 }
2035
2036 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2037}
2038
Ido Schimmel9665b742017-02-08 11:16:42 +01002039static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002040 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002041{
2042 struct mlxsw_sp_nexthop *nh, *tmp;
2043
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002044 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002045 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2046 mlxsw_sp_nexthop_rif_fini(nh);
2047 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2048 }
2049}
2050
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002051static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002052mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002053{
2054 struct mlxsw_sp_nexthop_group *nh_grp;
2055 struct mlxsw_sp_nexthop *nh;
2056 struct fib_nh *fib_nh;
2057 size_t alloc_size;
2058 int i;
2059 int err;
2060
2061 alloc_size = sizeof(*nh_grp) +
2062 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2063 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2064 if (!nh_grp)
2065 return ERR_PTR(-ENOMEM);
2066 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002067 nh_grp->neigh_tbl = &arp_tbl;
2068
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002069 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002070 nh_grp->count = fi->fib_nhs;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002071 nh_grp->key.fi = fi;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002072 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002073 for (i = 0; i < nh_grp->count; i++) {
2074 nh = &nh_grp->nexthops[i];
2075 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002076 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002077 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002078 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002079 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002080 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2081 if (err)
2082 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002083 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2084 return nh_grp;
2085
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002086err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002087err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002088 for (i--; i >= 0; i--) {
2089 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002090 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002091 }
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002092 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002093 kfree(nh_grp);
2094 return ERR_PTR(err);
2095}
2096
2097static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002098mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2099 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002100{
2101 struct mlxsw_sp_nexthop *nh;
2102 int i;
2103
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002104 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002105 for (i = 0; i < nh_grp->count; i++) {
2106 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002107 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002108 }
Ido Schimmel58312122016-12-23 09:32:50 +01002109 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2110 WARN_ON_ONCE(nh_grp->adj_index_valid);
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002111 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002112 kfree(nh_grp);
2113}
2114
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002115static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2116 struct mlxsw_sp_fib_entry *fib_entry,
2117 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002118{
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002119 struct mlxsw_sp_nexthop_group_key key;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002120 struct mlxsw_sp_nexthop_group *nh_grp;
2121
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002122 key.fi = fi;
2123 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002124 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002125 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002126 if (IS_ERR(nh_grp))
2127 return PTR_ERR(nh_grp);
2128 }
2129 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2130 fib_entry->nh_group = nh_grp;
2131 return 0;
2132}
2133
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002134static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2135 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002136{
2137 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2138
2139 list_del(&fib_entry->nexthop_group_node);
2140 if (!list_empty(&nh_grp->fib_list))
2141 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002142 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002143}
2144
Ido Schimmel013b20f2017-02-08 11:16:36 +01002145static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002146mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2147{
2148 struct mlxsw_sp_fib4_entry *fib4_entry;
2149
2150 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2151 common);
2152 return !fib4_entry->tos;
2153}
2154
2155static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002156mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2157{
2158 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2159
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002160 switch (fib_entry->fib_node->fib->proto) {
2161 case MLXSW_SP_L3_PROTO_IPV4:
2162 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2163 return false;
2164 break;
2165 case MLXSW_SP_L3_PROTO_IPV6:
2166 break;
2167 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002168
Ido Schimmel013b20f2017-02-08 11:16:36 +01002169 switch (fib_entry->type) {
2170 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2171 return !!nh_group->adj_index_valid;
2172 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002173 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002174 default:
2175 return false;
2176 }
2177}
2178
Ido Schimmel428b8512017-08-03 13:28:28 +02002179static struct mlxsw_sp_nexthop *
2180mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2181 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2182{
2183 int i;
2184
2185 for (i = 0; i < nh_grp->count; i++) {
2186 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2187 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2188
2189 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2190 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2191 &rt->rt6i_gateway))
2192 return nh;
2193 continue;
2194 }
2195
2196 return NULL;
2197}
2198
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002199static void
2200mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2201{
2202 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2203 int i;
2204
2205 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2206 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2207 return;
2208 }
2209
2210 for (i = 0; i < nh_grp->count; i++) {
2211 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2212
2213 if (nh->offloaded)
2214 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2215 else
2216 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2217 }
2218}
2219
2220static void
2221mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2222{
2223 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2224 int i;
2225
2226 for (i = 0; i < nh_grp->count; i++) {
2227 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2228
2229 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2230 }
2231}
2232
Ido Schimmel428b8512017-08-03 13:28:28 +02002233static void
2234mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2235{
2236 struct mlxsw_sp_fib6_entry *fib6_entry;
2237 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2238
2239 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2240 common);
2241
2242 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2243 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
2244 list)->rt->rt6i_flags |= RTF_OFFLOAD;
2245 return;
2246 }
2247
2248 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2249 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2250 struct mlxsw_sp_nexthop *nh;
2251
2252 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2253 if (nh && nh->offloaded)
2254 mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD;
2255 else
2256 mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD;
2257 }
2258}
2259
2260static void
2261mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2262{
2263 struct mlxsw_sp_fib6_entry *fib6_entry;
2264 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2265
2266 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2267 common);
2268 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2269 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2270
2271 rt->rt6i_flags &= ~RTF_OFFLOAD;
2272 }
2273}
2274
Ido Schimmel013b20f2017-02-08 11:16:36 +01002275static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2276{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002277 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002278 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002279 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002280 break;
2281 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002282 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2283 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002284 }
2285}
2286
2287static void
2288mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2289{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002290 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002291 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002292 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002293 break;
2294 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002295 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2296 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002297 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002298}
2299
2300static void
2301mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2302 enum mlxsw_reg_ralue_op op, int err)
2303{
2304 switch (op) {
2305 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002306 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2307 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2308 if (err)
2309 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002310 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002311 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002312 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002313 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2314 return;
2315 default:
2316 return;
2317 }
2318}
2319
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002320static void
2321mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2322 const struct mlxsw_sp_fib_entry *fib_entry,
2323 enum mlxsw_reg_ralue_op op)
2324{
2325 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2326 enum mlxsw_reg_ralxx_protocol proto;
2327 u32 *p_dip;
2328
2329 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2330
2331 switch (fib->proto) {
2332 case MLXSW_SP_L3_PROTO_IPV4:
2333 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2334 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2335 fib_entry->fib_node->key.prefix_len,
2336 *p_dip);
2337 break;
2338 case MLXSW_SP_L3_PROTO_IPV6:
2339 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2340 fib_entry->fib_node->key.prefix_len,
2341 fib_entry->fib_node->key.addr);
2342 break;
2343 }
2344}
2345
2346static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2347 struct mlxsw_sp_fib_entry *fib_entry,
2348 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002349{
2350 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002351 enum mlxsw_reg_ralue_trap_action trap_action;
2352 u16 trap_id = 0;
2353 u32 adjacency_index = 0;
2354 u16 ecmp_size = 0;
2355
2356 /* In case the nexthop group adjacency index is valid, use it
2357 * with provided ECMP size. Otherwise, setup trap and pass
2358 * traffic to kernel.
2359 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002360 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002361 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2362 adjacency_index = fib_entry->nh_group->adj_index;
2363 ecmp_size = fib_entry->nh_group->ecmp_size;
2364 } else {
2365 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2366 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2367 }
2368
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002369 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002370 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2371 adjacency_index, ecmp_size);
2372 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2373}
2374
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002375static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2376 struct mlxsw_sp_fib_entry *fib_entry,
2377 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002378{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002379 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002380 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002381 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002382 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002383 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002384
2385 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2386 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002387 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002388 } else {
2389 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2390 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2391 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002392
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002393 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002394 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2395 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002396 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2397}
2398
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002399static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2400 struct mlxsw_sp_fib_entry *fib_entry,
2401 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002402{
2403 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002404
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002405 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002406 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2407 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2408}
2409
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002410static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2411 struct mlxsw_sp_fib_entry *fib_entry,
2412 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002413{
2414 switch (fib_entry->type) {
2415 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002416 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002417 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002418 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002419 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002420 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002421 }
2422 return -EINVAL;
2423}
2424
2425static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2426 struct mlxsw_sp_fib_entry *fib_entry,
2427 enum mlxsw_reg_ralue_op op)
2428{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002429 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002430
Ido Schimmel013b20f2017-02-08 11:16:36 +01002431 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002432
Ido Schimmel013b20f2017-02-08 11:16:36 +01002433 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002434}
2435
2436static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2437 struct mlxsw_sp_fib_entry *fib_entry)
2438{
Jiri Pirko7146da32016-09-01 10:37:41 +02002439 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2440 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002441}
2442
2443static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2444 struct mlxsw_sp_fib_entry *fib_entry)
2445{
2446 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2447 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2448}
2449
Jiri Pirko61c503f2016-07-04 08:23:11 +02002450static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002451mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2452 const struct fib_entry_notifier_info *fen_info,
2453 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002454{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002455 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002456
Ido Schimmel97989ee2017-03-10 08:53:38 +01002457 switch (fen_info->type) {
2458 case RTN_BROADCAST: /* fall through */
2459 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002460 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2461 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002462 case RTN_UNREACHABLE: /* fall through */
2463 case RTN_BLACKHOLE: /* fall through */
2464 case RTN_PROHIBIT:
2465 /* Packets hitting these routes need to be trapped, but
2466 * can do so with a lower priority than packets directed
2467 * at the host, so use action type local instead of trap.
2468 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002469 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002470 return 0;
2471 case RTN_UNICAST:
2472 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2473 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2474 else
2475 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2476 return 0;
2477 default:
2478 return -EINVAL;
2479 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002480}
2481
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002482static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002483mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2484 struct mlxsw_sp_fib_node *fib_node,
2485 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002486{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002487 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002488 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002489 int err;
2490
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002491 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2492 if (!fib4_entry)
2493 return ERR_PTR(-ENOMEM);
2494 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002495
2496 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2497 if (err)
2498 goto err_fib4_entry_type_set;
2499
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002500 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002501 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002502 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002503
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002504 fib4_entry->prio = fen_info->fi->fib_priority;
2505 fib4_entry->tb_id = fen_info->tb_id;
2506 fib4_entry->type = fen_info->type;
2507 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002508
2509 fib_entry->fib_node = fib_node;
2510
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002511 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002512
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002513err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002514err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002515 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002516 return ERR_PTR(err);
2517}
2518
2519static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002520 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002521{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002522 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002523 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002524}
2525
2526static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002527mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2528 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002529
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002530static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002531mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2532 const struct fib_entry_notifier_info *fen_info)
2533{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002534 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002535 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002536 struct mlxsw_sp_fib *fib;
2537 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002538
Ido Schimmel160e22a2017-07-18 10:10:20 +02002539 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2540 if (!vr)
2541 return NULL;
2542 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2543
2544 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2545 sizeof(fen_info->dst),
2546 fen_info->dst_len);
2547 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002548 return NULL;
2549
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002550 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2551 if (fib4_entry->tb_id == fen_info->tb_id &&
2552 fib4_entry->tos == fen_info->tos &&
2553 fib4_entry->type == fen_info->type &&
2554 fib4_entry->common.nh_group->key.fi == fen_info->fi) {
2555 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002556 }
2557 }
2558
2559 return NULL;
2560}
2561
2562static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2563 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2564 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2565 .key_len = sizeof(struct mlxsw_sp_fib_key),
2566 .automatic_shrinking = true,
2567};
2568
2569static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2570 struct mlxsw_sp_fib_node *fib_node)
2571{
2572 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2573 mlxsw_sp_fib_ht_params);
2574}
2575
2576static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2577 struct mlxsw_sp_fib_node *fib_node)
2578{
2579 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2580 mlxsw_sp_fib_ht_params);
2581}
2582
2583static struct mlxsw_sp_fib_node *
2584mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2585 size_t addr_len, unsigned char prefix_len)
2586{
2587 struct mlxsw_sp_fib_key key;
2588
2589 memset(&key, 0, sizeof(key));
2590 memcpy(key.addr, addr, addr_len);
2591 key.prefix_len = prefix_len;
2592 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2593}
2594
2595static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002596mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002597 size_t addr_len, unsigned char prefix_len)
2598{
2599 struct mlxsw_sp_fib_node *fib_node;
2600
2601 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2602 if (!fib_node)
2603 return NULL;
2604
2605 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002606 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002607 memcpy(fib_node->key.addr, addr, addr_len);
2608 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002609
2610 return fib_node;
2611}
2612
2613static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2614{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002615 list_del(&fib_node->list);
2616 WARN_ON(!list_empty(&fib_node->entry_list));
2617 kfree(fib_node);
2618}
2619
2620static bool
2621mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2622 const struct mlxsw_sp_fib_entry *fib_entry)
2623{
2624 return list_first_entry(&fib_node->entry_list,
2625 struct mlxsw_sp_fib_entry, list) == fib_entry;
2626}
2627
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002628static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2629 struct mlxsw_sp_fib *fib,
2630 struct mlxsw_sp_fib_node *fib_node)
2631{
2632 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2633 struct mlxsw_sp_lpm_tree *lpm_tree;
2634 int err;
2635
2636 /* Since the tree is shared between all virtual routers we must
2637 * make sure it contains all the required prefix lengths. This
2638 * can be computed by either adding the new prefix length to the
2639 * existing prefix usage of a bound tree, or by aggregating the
2640 * prefix lengths across all virtual routers and adding the new
2641 * one as well.
2642 */
2643 if (fib->lpm_tree)
2644 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2645 &fib->lpm_tree->prefix_usage);
2646 else
2647 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2648 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2649
2650 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2651 fib->proto);
2652 if (IS_ERR(lpm_tree))
2653 return PTR_ERR(lpm_tree);
2654
2655 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2656 return 0;
2657
2658 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2659 if (err)
2660 return err;
2661
2662 return 0;
2663}
2664
2665static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2666 struct mlxsw_sp_fib *fib)
2667{
2668 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2669 struct mlxsw_sp_lpm_tree *lpm_tree;
2670
2671 /* Aggregate prefix lengths across all virtual routers to make
2672 * sure we only have used prefix lengths in the LPM tree.
2673 */
2674 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2675 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2676 fib->proto);
2677 if (IS_ERR(lpm_tree))
2678 goto err_tree_get;
2679 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2680
2681err_tree_get:
2682 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2683 return;
2684 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2685 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2686 fib->lpm_tree = NULL;
2687}
2688
Ido Schimmel9aecce12017-02-09 10:28:42 +01002689static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2690{
2691 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002692 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002693
2694 if (fib->prefix_ref_count[prefix_len]++ == 0)
2695 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2696}
2697
2698static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2699{
2700 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002701 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002702
2703 if (--fib->prefix_ref_count[prefix_len] == 0)
2704 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2705}
2706
Ido Schimmel76610eb2017-03-10 08:53:41 +01002707static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2708 struct mlxsw_sp_fib_node *fib_node,
2709 struct mlxsw_sp_fib *fib)
2710{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002711 int err;
2712
2713 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2714 if (err)
2715 return err;
2716 fib_node->fib = fib;
2717
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002718 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
2719 if (err)
2720 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002721
2722 mlxsw_sp_fib_node_prefix_inc(fib_node);
2723
2724 return 0;
2725
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002726err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002727 fib_node->fib = NULL;
2728 mlxsw_sp_fib_node_remove(fib, fib_node);
2729 return err;
2730}
2731
2732static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2733 struct mlxsw_sp_fib_node *fib_node)
2734{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002735 struct mlxsw_sp_fib *fib = fib_node->fib;
2736
2737 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002738 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002739 fib_node->fib = NULL;
2740 mlxsw_sp_fib_node_remove(fib, fib_node);
2741}
2742
Ido Schimmel9aecce12017-02-09 10:28:42 +01002743static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002744mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2745 size_t addr_len, unsigned char prefix_len,
2746 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002747{
2748 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002749 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002750 struct mlxsw_sp_vr *vr;
2751 int err;
2752
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002753 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002754 if (IS_ERR(vr))
2755 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002756 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002757
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002758 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002759 if (fib_node)
2760 return fib_node;
2761
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002762 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002763 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002764 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002765 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002766 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002767
Ido Schimmel76610eb2017-03-10 08:53:41 +01002768 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2769 if (err)
2770 goto err_fib_node_init;
2771
Ido Schimmel9aecce12017-02-09 10:28:42 +01002772 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002773
Ido Schimmel76610eb2017-03-10 08:53:41 +01002774err_fib_node_init:
2775 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002776err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002777 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002778 return ERR_PTR(err);
2779}
2780
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002781static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2782 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002783{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002784 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002785
Ido Schimmel9aecce12017-02-09 10:28:42 +01002786 if (!list_empty(&fib_node->entry_list))
2787 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002788 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002789 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002790 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002791}
2792
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002793static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002794mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002795 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002796{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002797 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002798
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002799 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2800 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002801 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002802 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002803 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002804 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002805 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002806 if (fib4_entry->prio >= new4_entry->prio ||
2807 fib4_entry->tos < new4_entry->tos)
2808 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002809 }
2810
2811 return NULL;
2812}
2813
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002814static int
2815mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2816 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002817{
2818 struct mlxsw_sp_fib_node *fib_node;
2819
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002820 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01002821 return -EINVAL;
2822
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002823 fib_node = fib4_entry->common.fib_node;
2824 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2825 common.list) {
2826 if (fib4_entry->tb_id != new4_entry->tb_id ||
2827 fib4_entry->tos != new4_entry->tos ||
2828 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002829 break;
2830 }
2831
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002832 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01002833 return 0;
2834}
2835
Ido Schimmel9aecce12017-02-09 10:28:42 +01002836static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02002837mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002838 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002839{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002840 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002841 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002842
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002843 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002844
Ido Schimmel4283bce2017-02-09 10:28:43 +01002845 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002846 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2847 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002848 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01002849
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002850 /* Insert new entry before replaced one, so that we can later
2851 * remove the second.
2852 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002853 if (fib4_entry) {
2854 list_add_tail(&new4_entry->common.list,
2855 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002856 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002857 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002858
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002859 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2860 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002861 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002862 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002863 }
2864
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002865 if (fib4_entry)
2866 list_add(&new4_entry->common.list,
2867 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002868 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002869 list_add(&new4_entry->common.list,
2870 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002871 }
2872
2873 return 0;
2874}
2875
2876static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002877mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002878{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002879 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002880}
2881
Ido Schimmel80c238f2017-07-18 10:10:29 +02002882static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2883 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002884{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002885 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2886
Ido Schimmel9aecce12017-02-09 10:28:42 +01002887 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2888 return 0;
2889
2890 /* To prevent packet loss, overwrite the previously offloaded
2891 * entry.
2892 */
2893 if (!list_is_singular(&fib_node->entry_list)) {
2894 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2895 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2896
2897 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2898 }
2899
2900 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2901}
2902
Ido Schimmel80c238f2017-07-18 10:10:29 +02002903static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2904 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002905{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002906 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2907
Ido Schimmel9aecce12017-02-09 10:28:42 +01002908 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2909 return;
2910
2911 /* Promote the next entry by overwriting the deleted entry */
2912 if (!list_is_singular(&fib_node->entry_list)) {
2913 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2914 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2915
2916 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2917 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2918 return;
2919 }
2920
2921 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2922}
2923
2924static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002925 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002926 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002927{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002928 int err;
2929
Ido Schimmel9efbee62017-07-18 10:10:28 +02002930 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002931 if (err)
2932 return err;
2933
Ido Schimmel80c238f2017-07-18 10:10:29 +02002934 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002935 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02002936 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002937
Ido Schimmel9aecce12017-02-09 10:28:42 +01002938 return 0;
2939
Ido Schimmel80c238f2017-07-18 10:10:29 +02002940err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002941 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002942 return err;
2943}
2944
2945static void
2946mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002947 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002948{
Ido Schimmel80c238f2017-07-18 10:10:29 +02002949 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002950 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002951}
2952
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002953static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002954 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002955 bool replace)
2956{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002957 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2958 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002959
2960 if (!replace)
2961 return;
2962
2963 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002964 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002965
2966 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2967 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002968 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002969}
2970
Ido Schimmel9aecce12017-02-09 10:28:42 +01002971static int
2972mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01002973 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002974 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002975{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002976 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002977 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002978 int err;
2979
Ido Schimmel9011b672017-05-16 19:38:25 +02002980 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002981 return 0;
2982
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002983 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
2984 &fen_info->dst, sizeof(fen_info->dst),
2985 fen_info->dst_len,
2986 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002987 if (IS_ERR(fib_node)) {
2988 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2989 return PTR_ERR(fib_node);
2990 }
2991
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002992 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2993 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002994 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002995 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002996 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002997 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002998
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002999 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003000 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003001 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003002 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3003 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003004 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003005
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003006 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003007
Jiri Pirko61c503f2016-07-04 08:23:11 +02003008 return 0;
3009
Ido Schimmel9aecce12017-02-09 10:28:42 +01003010err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003011 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003012err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003013 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003014 return err;
3015}
3016
Jiri Pirko37956d72016-10-20 16:05:43 +02003017static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3018 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003019{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003020 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003021 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003022
Ido Schimmel9011b672017-05-16 19:38:25 +02003023 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003024 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003025
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003026 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3027 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003028 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003029 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003030
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003031 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3032 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003033 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003034}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003035
Ido Schimmel428b8512017-08-03 13:28:28 +02003036static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3037{
3038 /* Packets with link-local destination IP arriving to the router
3039 * are trapped to the CPU, so no need to program specific routes
3040 * for them.
3041 */
3042 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3043 return true;
3044
3045 /* Multicast routes aren't supported, so ignore them. Neighbour
3046 * Discovery packets are specifically trapped.
3047 */
3048 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3049 return true;
3050
3051 /* Cloned routes are irrelevant in the forwarding path. */
3052 if (rt->rt6i_flags & RTF_CACHE)
3053 return true;
3054
3055 return false;
3056}
3057
3058static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3059{
3060 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3061
3062 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3063 if (!mlxsw_sp_rt6)
3064 return ERR_PTR(-ENOMEM);
3065
3066 /* In case of route replace, replaced route is deleted with
3067 * no notification. Take reference to prevent accessing freed
3068 * memory.
3069 */
3070 mlxsw_sp_rt6->rt = rt;
3071 rt6_hold(rt);
3072
3073 return mlxsw_sp_rt6;
3074}
3075
3076#if IS_ENABLED(CONFIG_IPV6)
3077static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3078{
3079 rt6_release(rt);
3080}
3081#else
3082static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3083{
3084}
3085#endif
3086
3087static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3088{
3089 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3090 kfree(mlxsw_sp_rt6);
3091}
3092
3093static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3094{
3095 /* RTF_CACHE routes are ignored */
3096 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3097}
3098
3099static struct rt6_info *
3100mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3101{
3102 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3103 list)->rt;
3104}
3105
3106static struct mlxsw_sp_fib6_entry *
3107mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003108 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003109{
3110 struct mlxsw_sp_fib6_entry *fib6_entry;
3111
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003112 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003113 return NULL;
3114
3115 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3116 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3117
3118 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3119 * virtual router.
3120 */
3121 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3122 continue;
3123 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3124 break;
3125 if (rt->rt6i_metric < nrt->rt6i_metric)
3126 continue;
3127 if (rt->rt6i_metric == nrt->rt6i_metric &&
3128 mlxsw_sp_fib6_rt_can_mp(rt))
3129 return fib6_entry;
3130 if (rt->rt6i_metric > nrt->rt6i_metric)
3131 break;
3132 }
3133
3134 return NULL;
3135}
3136
3137static struct mlxsw_sp_rt6 *
3138mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3139 const struct rt6_info *rt)
3140{
3141 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3142
3143 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3144 if (mlxsw_sp_rt6->rt == rt)
3145 return mlxsw_sp_rt6;
3146 }
3147
3148 return NULL;
3149}
3150
3151static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3152 struct mlxsw_sp_nexthop_group *nh_grp,
3153 struct mlxsw_sp_nexthop *nh,
3154 const struct rt6_info *rt)
3155{
3156 struct net_device *dev = rt->dst.dev;
3157 struct mlxsw_sp_rif *rif;
3158 int err;
3159
3160 nh->nh_grp = nh_grp;
3161 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3162
3163 if (!dev)
3164 return 0;
3165
3166 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3167 if (!rif)
3168 return 0;
3169 mlxsw_sp_nexthop_rif_init(nh, rif);
3170
3171 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3172 if (err)
3173 goto err_nexthop_neigh_init;
3174
3175 return 0;
3176
3177err_nexthop_neigh_init:
3178 mlxsw_sp_nexthop_rif_fini(nh);
3179 return err;
3180}
3181
3182static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3183 struct mlxsw_sp_nexthop *nh)
3184{
3185 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3186 mlxsw_sp_nexthop_rif_fini(nh);
3187}
3188
3189static struct mlxsw_sp_nexthop_group *
3190mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3191 struct mlxsw_sp_fib6_entry *fib6_entry)
3192{
3193 struct mlxsw_sp_nexthop_group *nh_grp;
3194 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3195 struct mlxsw_sp_nexthop *nh;
3196 size_t alloc_size;
3197 int i = 0;
3198 int err;
3199
3200 alloc_size = sizeof(*nh_grp) +
3201 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3202 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3203 if (!nh_grp)
3204 return ERR_PTR(-ENOMEM);
3205 INIT_LIST_HEAD(&nh_grp->fib_list);
3206#if IS_ENABLED(CONFIG_IPV6)
3207 nh_grp->neigh_tbl = &nd_tbl;
3208#endif
3209 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3210 struct mlxsw_sp_rt6, list);
3211 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3212 nh_grp->count = fib6_entry->nrt6;
3213 for (i = 0; i < nh_grp->count; i++) {
3214 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3215
3216 nh = &nh_grp->nexthops[i];
3217 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3218 if (err)
3219 goto err_nexthop6_init;
3220 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3221 }
3222 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3223 return nh_grp;
3224
3225err_nexthop6_init:
3226 for (i--; i >= 0; i--) {
3227 nh = &nh_grp->nexthops[i];
3228 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3229 }
3230 kfree(nh_grp);
3231 return ERR_PTR(err);
3232}
3233
3234static void
3235mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3236 struct mlxsw_sp_nexthop_group *nh_grp)
3237{
3238 struct mlxsw_sp_nexthop *nh;
3239 int i = nh_grp->count;
3240
3241 for (i--; i >= 0; i--) {
3242 nh = &nh_grp->nexthops[i];
3243 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3244 }
3245 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3246 WARN_ON(nh_grp->adj_index_valid);
3247 kfree(nh_grp);
3248}
3249
3250static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3251 struct mlxsw_sp_fib6_entry *fib6_entry)
3252{
3253 struct mlxsw_sp_nexthop_group *nh_grp;
3254
3255 /* For now, don't consolidate nexthop groups */
3256 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3257 if (IS_ERR(nh_grp))
3258 return PTR_ERR(nh_grp);
3259
3260 list_add_tail(&fib6_entry->common.nexthop_group_node,
3261 &nh_grp->fib_list);
3262 fib6_entry->common.nh_group = nh_grp;
3263
3264 return 0;
3265}
3266
3267static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3268 struct mlxsw_sp_fib_entry *fib_entry)
3269{
3270 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3271
3272 list_del(&fib_entry->nexthop_group_node);
3273 if (!list_empty(&nh_grp->fib_list))
3274 return;
3275 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3276}
3277
3278static int
3279mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3280 struct mlxsw_sp_fib6_entry *fib6_entry)
3281{
3282 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3283 int err;
3284
3285 fib6_entry->common.nh_group = NULL;
3286 list_del(&fib6_entry->common.nexthop_group_node);
3287
3288 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3289 if (err)
3290 goto err_nexthop6_group_get;
3291
3292 /* In case this entry is offloaded, then the adjacency index
3293 * currently associated with it in the device's table is that
3294 * of the old group. Start using the new one instead.
3295 */
3296 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3297 if (err)
3298 goto err_fib_node_entry_add;
3299
3300 if (list_empty(&old_nh_grp->fib_list))
3301 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3302
3303 return 0;
3304
3305err_fib_node_entry_add:
3306 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3307err_nexthop6_group_get:
3308 list_add_tail(&fib6_entry->common.nexthop_group_node,
3309 &old_nh_grp->fib_list);
3310 fib6_entry->common.nh_group = old_nh_grp;
3311 return err;
3312}
3313
3314static int
3315mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3316 struct mlxsw_sp_fib6_entry *fib6_entry,
3317 struct rt6_info *rt)
3318{
3319 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3320 int err;
3321
3322 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3323 if (IS_ERR(mlxsw_sp_rt6))
3324 return PTR_ERR(mlxsw_sp_rt6);
3325
3326 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3327 fib6_entry->nrt6++;
3328
3329 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3330 if (err)
3331 goto err_nexthop6_group_update;
3332
3333 return 0;
3334
3335err_nexthop6_group_update:
3336 fib6_entry->nrt6--;
3337 list_del(&mlxsw_sp_rt6->list);
3338 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3339 return err;
3340}
3341
3342static void
3343mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3344 struct mlxsw_sp_fib6_entry *fib6_entry,
3345 struct rt6_info *rt)
3346{
3347 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3348
3349 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3350 if (WARN_ON(!mlxsw_sp_rt6))
3351 return;
3352
3353 fib6_entry->nrt6--;
3354 list_del(&mlxsw_sp_rt6->list);
3355 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3356 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3357}
3358
3359static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3360 const struct rt6_info *rt)
3361{
3362 /* Packets hitting RTF_REJECT routes need to be discarded by the
3363 * stack. We can rely on their destination device not having a
3364 * RIF (it's the loopback device) and can thus use action type
3365 * local, which will cause them to be trapped with a lower
3366 * priority than packets that need to be locally received.
3367 */
3368 if (rt->rt6i_flags & RTF_LOCAL)
3369 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3370 else if (rt->rt6i_flags & RTF_REJECT)
3371 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3372 else if (rt->rt6i_flags & RTF_GATEWAY)
3373 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3374 else
3375 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3376}
3377
3378static void
3379mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3380{
3381 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3382
3383 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3384 list) {
3385 fib6_entry->nrt6--;
3386 list_del(&mlxsw_sp_rt6->list);
3387 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3388 }
3389}
3390
3391static struct mlxsw_sp_fib6_entry *
3392mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3393 struct mlxsw_sp_fib_node *fib_node,
3394 struct rt6_info *rt)
3395{
3396 struct mlxsw_sp_fib6_entry *fib6_entry;
3397 struct mlxsw_sp_fib_entry *fib_entry;
3398 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3399 int err;
3400
3401 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3402 if (!fib6_entry)
3403 return ERR_PTR(-ENOMEM);
3404 fib_entry = &fib6_entry->common;
3405
3406 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3407 if (IS_ERR(mlxsw_sp_rt6)) {
3408 err = PTR_ERR(mlxsw_sp_rt6);
3409 goto err_rt6_create;
3410 }
3411
3412 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3413
3414 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3415 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3416 fib6_entry->nrt6 = 1;
3417 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3418 if (err)
3419 goto err_nexthop6_group_get;
3420
3421 fib_entry->fib_node = fib_node;
3422
3423 return fib6_entry;
3424
3425err_nexthop6_group_get:
3426 list_del(&mlxsw_sp_rt6->list);
3427 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3428err_rt6_create:
3429 kfree(fib6_entry);
3430 return ERR_PTR(err);
3431}
3432
3433static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3434 struct mlxsw_sp_fib6_entry *fib6_entry)
3435{
3436 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3437 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3438 WARN_ON(fib6_entry->nrt6);
3439 kfree(fib6_entry);
3440}
3441
3442static struct mlxsw_sp_fib6_entry *
3443mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003444 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003445{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003446 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003447
3448 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3449 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3450
3451 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3452 continue;
3453 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3454 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003455 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3456 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3457 mlxsw_sp_fib6_rt_can_mp(nrt))
3458 return fib6_entry;
3459 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3460 fallback = fallback ?: fib6_entry;
3461 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003462 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003463 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003464 }
3465
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003466 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003467}
3468
3469static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003470mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3471 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003472{
3473 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3474 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3475 struct mlxsw_sp_fib6_entry *fib6_entry;
3476
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003477 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3478
3479 if (replace && WARN_ON(!fib6_entry))
3480 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003481
3482 if (fib6_entry) {
3483 list_add_tail(&new6_entry->common.list,
3484 &fib6_entry->common.list);
3485 } else {
3486 struct mlxsw_sp_fib6_entry *last;
3487
3488 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3489 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3490
3491 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3492 break;
3493 fib6_entry = last;
3494 }
3495
3496 if (fib6_entry)
3497 list_add(&new6_entry->common.list,
3498 &fib6_entry->common.list);
3499 else
3500 list_add(&new6_entry->common.list,
3501 &fib_node->entry_list);
3502 }
3503
3504 return 0;
3505}
3506
3507static void
3508mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3509{
3510 list_del(&fib6_entry->common.list);
3511}
3512
3513static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003514 struct mlxsw_sp_fib6_entry *fib6_entry,
3515 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003516{
3517 int err;
3518
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003519 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003520 if (err)
3521 return err;
3522
3523 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3524 if (err)
3525 goto err_fib_node_entry_add;
3526
3527 return 0;
3528
3529err_fib_node_entry_add:
3530 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3531 return err;
3532}
3533
3534static void
3535mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3536 struct mlxsw_sp_fib6_entry *fib6_entry)
3537{
3538 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3539 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3540}
3541
3542static struct mlxsw_sp_fib6_entry *
3543mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3544 const struct rt6_info *rt)
3545{
3546 struct mlxsw_sp_fib6_entry *fib6_entry;
3547 struct mlxsw_sp_fib_node *fib_node;
3548 struct mlxsw_sp_fib *fib;
3549 struct mlxsw_sp_vr *vr;
3550
3551 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3552 if (!vr)
3553 return NULL;
3554 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3555
3556 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3557 sizeof(rt->rt6i_dst.addr),
3558 rt->rt6i_dst.plen);
3559 if (!fib_node)
3560 return NULL;
3561
3562 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3563 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3564
3565 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3566 rt->rt6i_metric == iter_rt->rt6i_metric &&
3567 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3568 return fib6_entry;
3569 }
3570
3571 return NULL;
3572}
3573
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003574static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3575 struct mlxsw_sp_fib6_entry *fib6_entry,
3576 bool replace)
3577{
3578 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3579 struct mlxsw_sp_fib6_entry *replaced;
3580
3581 if (!replace)
3582 return;
3583
3584 replaced = list_next_entry(fib6_entry, common.list);
3585
3586 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3587 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3588 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3589}
3590
Ido Schimmel428b8512017-08-03 13:28:28 +02003591static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003592 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003593{
3594 struct mlxsw_sp_fib6_entry *fib6_entry;
3595 struct mlxsw_sp_fib_node *fib_node;
3596 int err;
3597
3598 if (mlxsw_sp->router->aborted)
3599 return 0;
3600
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003601 if (rt->rt6i_src.plen)
3602 return -EINVAL;
3603
Ido Schimmel428b8512017-08-03 13:28:28 +02003604 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3605 return 0;
3606
3607 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3608 &rt->rt6i_dst.addr,
3609 sizeof(rt->rt6i_dst.addr),
3610 rt->rt6i_dst.plen,
3611 MLXSW_SP_L3_PROTO_IPV6);
3612 if (IS_ERR(fib_node))
3613 return PTR_ERR(fib_node);
3614
3615 /* Before creating a new entry, try to append route to an existing
3616 * multipath entry.
3617 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003618 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003619 if (fib6_entry) {
3620 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3621 if (err)
3622 goto err_fib6_entry_nexthop_add;
3623 return 0;
3624 }
3625
3626 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3627 if (IS_ERR(fib6_entry)) {
3628 err = PTR_ERR(fib6_entry);
3629 goto err_fib6_entry_create;
3630 }
3631
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003632 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003633 if (err)
3634 goto err_fib6_node_entry_link;
3635
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003636 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3637
Ido Schimmel428b8512017-08-03 13:28:28 +02003638 return 0;
3639
3640err_fib6_node_entry_link:
3641 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3642err_fib6_entry_create:
3643err_fib6_entry_nexthop_add:
3644 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3645 return err;
3646}
3647
3648static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3649 struct rt6_info *rt)
3650{
3651 struct mlxsw_sp_fib6_entry *fib6_entry;
3652 struct mlxsw_sp_fib_node *fib_node;
3653
3654 if (mlxsw_sp->router->aborted)
3655 return;
3656
3657 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3658 return;
3659
3660 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3661 if (WARN_ON(!fib6_entry))
3662 return;
3663
3664 /* If route is part of a multipath entry, but not the last one
3665 * removed, then only reduce its nexthop group.
3666 */
3667 if (!list_is_singular(&fib6_entry->rt6_list)) {
3668 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3669 return;
3670 }
3671
3672 fib_node = fib6_entry->common.fib_node;
3673
3674 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3675 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3676 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3677}
3678
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003679static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3680 enum mlxsw_reg_ralxx_protocol proto,
3681 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003682{
3683 char ralta_pl[MLXSW_REG_RALTA_LEN];
3684 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003685 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003686
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003687 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003688 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3689 if (err)
3690 return err;
3691
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003692 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003693 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3694 if (err)
3695 return err;
3696
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003697 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003698 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003699 char raltb_pl[MLXSW_REG_RALTB_LEN];
3700 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003701
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003702 if (!mlxsw_sp_vr_is_used(vr))
3703 continue;
3704
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003705 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003706 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3707 raltb_pl);
3708 if (err)
3709 return err;
3710
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003711 mlxsw_reg_ralue_pack(ralue_pl, proto,
3712 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003713 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3714 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3715 ralue_pl);
3716 if (err)
3717 return err;
3718 }
3719
3720 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003721}
3722
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003723static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3724{
3725 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3726 int err;
3727
3728 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3729 MLXSW_SP_LPM_TREE_MIN);
3730 if (err)
3731 return err;
3732
3733 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3734 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3735 MLXSW_SP_LPM_TREE_MIN + 1);
3736}
3737
Ido Schimmel9aecce12017-02-09 10:28:42 +01003738static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3739 struct mlxsw_sp_fib_node *fib_node)
3740{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003741 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003742
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003743 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
3744 common.list) {
3745 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003746
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003747 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3748 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003749 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003750 /* Break when entry list is empty and node was freed.
3751 * Otherwise, we'll access freed memory in the next
3752 * iteration.
3753 */
3754 if (do_break)
3755 break;
3756 }
3757}
3758
Ido Schimmel428b8512017-08-03 13:28:28 +02003759static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
3760 struct mlxsw_sp_fib_node *fib_node)
3761{
3762 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
3763
3764 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
3765 common.list) {
3766 bool do_break = &tmp->common.list == &fib_node->entry_list;
3767
3768 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3769 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3770 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3771 if (do_break)
3772 break;
3773 }
3774}
3775
Ido Schimmel9aecce12017-02-09 10:28:42 +01003776static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
3777 struct mlxsw_sp_fib_node *fib_node)
3778{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003779 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003780 case MLXSW_SP_L3_PROTO_IPV4:
3781 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
3782 break;
3783 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003784 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003785 break;
3786 }
3787}
3788
Ido Schimmel76610eb2017-03-10 08:53:41 +01003789static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
3790 struct mlxsw_sp_vr *vr,
3791 enum mlxsw_sp_l3proto proto)
3792{
3793 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
3794 struct mlxsw_sp_fib_node *fib_node, *tmp;
3795
3796 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
3797 bool do_break = &tmp->list == &fib->node_list;
3798
3799 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
3800 if (do_break)
3801 break;
3802 }
3803}
3804
Ido Schimmelac571de2016-11-14 11:26:32 +01003805static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003806{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003807 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003808
Jiri Pirkoc1a38312016-10-21 16:07:23 +02003809 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003810 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01003811
Ido Schimmel76610eb2017-03-10 08:53:41 +01003812 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003813 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003814 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02003815
3816 /* If virtual router was only used for IPv4, then it's no
3817 * longer used.
3818 */
3819 if (!mlxsw_sp_vr_is_used(vr))
3820 continue;
3821 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003822 }
Ido Schimmelac571de2016-11-14 11:26:32 +01003823}
3824
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003825static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01003826{
3827 int err;
3828
Ido Schimmel9011b672017-05-16 19:38:25 +02003829 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003830 return;
3831 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 +01003832 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003833 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003834 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3835 if (err)
3836 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3837}
3838
Ido Schimmel30572242016-12-03 16:45:01 +01003839struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003840 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003841 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02003842 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003843 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003844 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003845 struct fib_nh_notifier_info fnh_info;
3846 };
Ido Schimmel30572242016-12-03 16:45:01 +01003847 struct mlxsw_sp *mlxsw_sp;
3848 unsigned long event;
3849};
3850
Ido Schimmel66a57632017-08-03 13:28:26 +02003851static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003852{
Ido Schimmel30572242016-12-03 16:45:01 +01003853 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003854 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003855 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003856 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003857 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003858 int err;
3859
Ido Schimmel30572242016-12-03 16:45:01 +01003860 /* Protect internal structures from changes */
3861 rtnl_lock();
3862 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003863 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003864 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003865 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003866 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003867 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3868 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003869 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003870 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003871 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003872 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003873 break;
3874 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003875 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3876 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003877 break;
3878 case FIB_EVENT_RULE_ADD: /* fall through */
3879 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003880 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003881 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003882 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003883 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003884 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003885 case FIB_EVENT_NH_ADD: /* fall through */
3886 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003887 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3888 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003889 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3890 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003891 }
Ido Schimmel30572242016-12-03 16:45:01 +01003892 rtnl_unlock();
3893 kfree(fib_work);
3894}
3895
Ido Schimmel66a57632017-08-03 13:28:26 +02003896static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
3897{
Ido Schimmel583419f2017-08-03 13:28:27 +02003898 struct mlxsw_sp_fib_event_work *fib_work =
3899 container_of(work, struct mlxsw_sp_fib_event_work, work);
3900 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
3901 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003902 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02003903 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02003904
3905 rtnl_lock();
3906 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003907 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003908 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003909 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02003910 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003911 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003912 if (err)
3913 mlxsw_sp_router_fib_abort(mlxsw_sp);
3914 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3915 break;
3916 case FIB_EVENT_ENTRY_DEL:
3917 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
3918 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3919 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003920 case FIB_EVENT_RULE_ADD: /* fall through */
3921 case FIB_EVENT_RULE_DEL:
3922 rule = fib_work->fr_info.rule;
3923 if (!fib6_rule_default(rule) && !rule->l3mdev)
3924 mlxsw_sp_router_fib_abort(mlxsw_sp);
3925 fib_rule_put(rule);
3926 break;
3927 }
3928 rtnl_unlock();
3929 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02003930}
3931
3932static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
3933 struct fib_notifier_info *info)
3934{
3935 switch (fib_work->event) {
3936 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
3937 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3938 case FIB_EVENT_ENTRY_ADD: /* fall through */
3939 case FIB_EVENT_ENTRY_DEL:
3940 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
3941 /* Take referece on fib_info to prevent it from being
3942 * freed while work is queued. Release it afterwards.
3943 */
3944 fib_info_hold(fib_work->fen_info.fi);
3945 break;
3946 case FIB_EVENT_RULE_ADD: /* fall through */
3947 case FIB_EVENT_RULE_DEL:
3948 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3949 fib_rule_get(fib_work->fr_info.rule);
3950 break;
3951 case FIB_EVENT_NH_ADD: /* fall through */
3952 case FIB_EVENT_NH_DEL:
3953 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
3954 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3955 break;
3956 }
3957}
3958
3959static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
3960 struct fib_notifier_info *info)
3961{
Ido Schimmel583419f2017-08-03 13:28:27 +02003962 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003963 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003964 case FIB_EVENT_ENTRY_ADD: /* fall through */
3965 case FIB_EVENT_ENTRY_DEL:
3966 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
3967 rt6_hold(fib_work->fen6_info.rt);
3968 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003969 case FIB_EVENT_RULE_ADD: /* fall through */
3970 case FIB_EVENT_RULE_DEL:
3971 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3972 fib_rule_get(fib_work->fr_info.rule);
3973 break;
3974 }
Ido Schimmel66a57632017-08-03 13:28:26 +02003975}
3976
Ido Schimmel30572242016-12-03 16:45:01 +01003977/* Called with rcu_read_lock() */
3978static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3979 unsigned long event, void *ptr)
3980{
Ido Schimmel30572242016-12-03 16:45:01 +01003981 struct mlxsw_sp_fib_event_work *fib_work;
3982 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02003983 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01003984
Ido Schimmel65e65ec2017-08-03 13:28:31 +02003985 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01003986 return NOTIFY_DONE;
3987
3988 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3989 if (WARN_ON(!fib_work))
3990 return NOTIFY_BAD;
3991
Ido Schimmel7e39d112017-05-16 19:38:28 +02003992 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3993 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01003994 fib_work->event = event;
3995
Ido Schimmel66a57632017-08-03 13:28:26 +02003996 switch (info->family) {
3997 case AF_INET:
3998 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
3999 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004000 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004001 case AF_INET6:
4002 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4003 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004004 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004005 }
4006
Ido Schimmela0e47612017-02-06 16:20:10 +01004007 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004008
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004009 return NOTIFY_DONE;
4010}
4011
Ido Schimmel4724ba562017-03-10 08:53:39 +01004012static struct mlxsw_sp_rif *
4013mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4014 const struct net_device *dev)
4015{
4016 int i;
4017
4018 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004019 if (mlxsw_sp->router->rifs[i] &&
4020 mlxsw_sp->router->rifs[i]->dev == dev)
4021 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004022
4023 return NULL;
4024}
4025
4026static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4027{
4028 char ritr_pl[MLXSW_REG_RITR_LEN];
4029 int err;
4030
4031 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4032 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4033 if (WARN_ON_ONCE(err))
4034 return err;
4035
4036 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4037 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4038}
4039
4040static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004041 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004042{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004043 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4044 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4045 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004046}
4047
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004048static bool
4049mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4050 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004051{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004052 struct inet6_dev *inet6_dev;
4053 bool addr_list_empty = true;
4054 struct in_device *idev;
4055
Ido Schimmel4724ba562017-03-10 08:53:39 +01004056 switch (event) {
4057 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004058 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004059 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004060 idev = __in_dev_get_rtnl(dev);
4061 if (idev && idev->ifa_list)
4062 addr_list_empty = false;
4063
4064 inet6_dev = __in6_dev_get(dev);
4065 if (addr_list_empty && inet6_dev &&
4066 !list_empty(&inet6_dev->addr_list))
4067 addr_list_empty = false;
4068
4069 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004070 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004071 return true;
4072 /* It is possible we already removed the RIF ourselves
4073 * if it was assigned to a netdev that is now a bridge
4074 * or LAG slave.
4075 */
4076 return false;
4077 }
4078
4079 return false;
4080}
4081
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004082static enum mlxsw_sp_rif_type
4083mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4084 const struct net_device *dev)
4085{
4086 enum mlxsw_sp_fid_type type;
4087
4088 /* RIF type is derived from the type of the underlying FID */
4089 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4090 type = MLXSW_SP_FID_TYPE_8021Q;
4091 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4092 type = MLXSW_SP_FID_TYPE_8021Q;
4093 else if (netif_is_bridge_master(dev))
4094 type = MLXSW_SP_FID_TYPE_8021D;
4095 else
4096 type = MLXSW_SP_FID_TYPE_RFID;
4097
4098 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4099}
4100
Ido Schimmelde5ed992017-06-04 16:53:40 +02004101static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004102{
4103 int i;
4104
Ido Schimmelde5ed992017-06-04 16:53:40 +02004105 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4106 if (!mlxsw_sp->router->rifs[i]) {
4107 *p_rif_index = i;
4108 return 0;
4109 }
4110 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004111
Ido Schimmelde5ed992017-06-04 16:53:40 +02004112 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004113}
4114
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004115static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4116 u16 vr_id,
4117 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004118{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004119 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004120
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004121 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004122 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004123 return NULL;
4124
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004125 INIT_LIST_HEAD(&rif->nexthop_list);
4126 INIT_LIST_HEAD(&rif->neigh_list);
4127 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4128 rif->mtu = l3_dev->mtu;
4129 rif->vr_id = vr_id;
4130 rif->dev = l3_dev;
4131 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004132
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004133 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004134}
4135
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004136struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4137 u16 rif_index)
4138{
4139 return mlxsw_sp->router->rifs[rif_index];
4140}
4141
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004142u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4143{
4144 return rif->rif_index;
4145}
4146
4147int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4148{
4149 return rif->dev->ifindex;
4150}
4151
Ido Schimmel4724ba562017-03-10 08:53:39 +01004152static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004153mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4154 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004155{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004156 u32 tb_id = l3mdev_fib_table(params->dev);
4157 const struct mlxsw_sp_rif_ops *ops;
4158 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004159 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004160 struct mlxsw_sp_fid *fid;
4161 struct mlxsw_sp_vr *vr;
4162 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004163 int err;
4164
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004165 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4166 ops = mlxsw_sp->router->rif_ops_arr[type];
4167
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004168 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4169 if (IS_ERR(vr))
4170 return ERR_CAST(vr);
4171
Ido Schimmelde5ed992017-06-04 16:53:40 +02004172 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4173 if (err)
4174 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004175
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004176 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004177 if (!rif) {
4178 err = -ENOMEM;
4179 goto err_rif_alloc;
4180 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004181 rif->mlxsw_sp = mlxsw_sp;
4182 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004183
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004184 fid = ops->fid_get(rif);
4185 if (IS_ERR(fid)) {
4186 err = PTR_ERR(fid);
4187 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004188 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004189 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004190
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004191 if (ops->setup)
4192 ops->setup(rif, params);
4193
4194 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004195 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004196 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004197
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004198 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004199 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004200 if (err)
4201 goto err_rif_fdb_op;
4202
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004203 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004204 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004205 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004206 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004207
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004208 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004209
Ido Schimmel4724ba562017-03-10 08:53:39 +01004210err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004211 ops->deconfigure(rif);
4212err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004213 mlxsw_sp_fid_put(fid);
4214err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004215 kfree(rif);
4216err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004217err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004218 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004219 return ERR_PTR(err);
4220}
4221
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004222void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004223{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004224 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4225 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004226 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004227 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004228
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004229 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004230 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004231
Ido Schimmel69132292017-03-10 08:53:42 +01004232 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004233 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004234 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004235 mlxsw_sp_rif_counters_free(rif);
4236 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4237 mlxsw_sp_fid_index(fid), false);
4238 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004239 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004240 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004241 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004242}
4243
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004244static void
4245mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4246 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4247{
4248 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4249
4250 params->vid = mlxsw_sp_port_vlan->vid;
4251 params->lag = mlxsw_sp_port->lagged;
4252 if (params->lag)
4253 params->lag_id = mlxsw_sp_port->lag_id;
4254 else
4255 params->system_port = mlxsw_sp_port->local_port;
4256}
4257
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004258static int
Ido Schimmela1107482017-05-26 08:37:39 +02004259mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004260 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004261{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004262 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004263 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004264 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004265 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004266 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004267 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004268
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004269 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004270 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004271 struct mlxsw_sp_rif_params params = {
4272 .dev = l3_dev,
4273 };
4274
4275 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4276 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004277 if (IS_ERR(rif))
4278 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004279 }
4280
Ido Schimmela1107482017-05-26 08:37:39 +02004281 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004282 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004283 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4284 if (err)
4285 goto err_fid_port_vid_map;
4286
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004287 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004288 if (err)
4289 goto err_port_vid_learning_set;
4290
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004291 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004292 BR_STATE_FORWARDING);
4293 if (err)
4294 goto err_port_vid_stp_set;
4295
Ido Schimmela1107482017-05-26 08:37:39 +02004296 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004297
Ido Schimmel4724ba562017-03-10 08:53:39 +01004298 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004299
4300err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004301 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004302err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004303 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4304err_fid_port_vid_map:
4305 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004306 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004307}
4308
Ido Schimmela1107482017-05-26 08:37:39 +02004309void
4310mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004311{
Ido Schimmelce95e152017-05-26 08:37:27 +02004312 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004313 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004314 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004315
Ido Schimmela1107482017-05-26 08:37:39 +02004316 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4317 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004318
Ido Schimmela1107482017-05-26 08:37:39 +02004319 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004320 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4321 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004322 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4323 /* If router port holds the last reference on the rFID, then the
4324 * associated Sub-port RIF will be destroyed.
4325 */
4326 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004327}
4328
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004329static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4330 struct net_device *port_dev,
4331 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004332{
4333 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004334 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004335
Ido Schimmelce95e152017-05-26 08:37:27 +02004336 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004337 if (WARN_ON(!mlxsw_sp_port_vlan))
4338 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004339
4340 switch (event) {
4341 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004342 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004343 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004344 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004345 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004346 break;
4347 }
4348
4349 return 0;
4350}
4351
4352static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4353 unsigned long event)
4354{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004355 if (netif_is_bridge_port(port_dev) ||
4356 netif_is_lag_port(port_dev) ||
4357 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004358 return 0;
4359
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004360 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004361}
4362
4363static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4364 struct net_device *lag_dev,
4365 unsigned long event, u16 vid)
4366{
4367 struct net_device *port_dev;
4368 struct list_head *iter;
4369 int err;
4370
4371 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4372 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004373 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4374 port_dev,
4375 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004376 if (err)
4377 return err;
4378 }
4379 }
4380
4381 return 0;
4382}
4383
4384static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4385 unsigned long event)
4386{
4387 if (netif_is_bridge_port(lag_dev))
4388 return 0;
4389
4390 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4391}
4392
Ido Schimmel4724ba562017-03-10 08:53:39 +01004393static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004394 unsigned long event)
4395{
4396 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004397 struct mlxsw_sp_rif_params params = {
4398 .dev = l3_dev,
4399 };
Ido Schimmela1107482017-05-26 08:37:39 +02004400 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004401
4402 switch (event) {
4403 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004404 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4405 if (IS_ERR(rif))
4406 return PTR_ERR(rif);
4407 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004408 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004409 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004410 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004411 break;
4412 }
4413
4414 return 0;
4415}
4416
4417static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4418 unsigned long event)
4419{
4420 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004421 u16 vid = vlan_dev_vlan_id(vlan_dev);
4422
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004423 if (netif_is_bridge_port(vlan_dev))
4424 return 0;
4425
Ido Schimmel4724ba562017-03-10 08:53:39 +01004426 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004427 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4428 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004429 else if (netif_is_lag_master(real_dev))
4430 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4431 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004432 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004433 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004434
4435 return 0;
4436}
4437
Ido Schimmelb1e45522017-04-30 19:47:14 +03004438static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4439 unsigned long event)
4440{
4441 if (mlxsw_sp_port_dev_check(dev))
4442 return mlxsw_sp_inetaddr_port_event(dev, event);
4443 else if (netif_is_lag_master(dev))
4444 return mlxsw_sp_inetaddr_lag_event(dev, event);
4445 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004446 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004447 else if (is_vlan_dev(dev))
4448 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4449 else
4450 return 0;
4451}
4452
Ido Schimmel4724ba562017-03-10 08:53:39 +01004453int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4454 unsigned long event, void *ptr)
4455{
4456 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4457 struct net_device *dev = ifa->ifa_dev->dev;
4458 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004459 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004460 int err = 0;
4461
4462 mlxsw_sp = mlxsw_sp_lower_get(dev);
4463 if (!mlxsw_sp)
4464 goto out;
4465
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004466 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004467 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004468 goto out;
4469
Ido Schimmelb1e45522017-04-30 19:47:14 +03004470 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004471out:
4472 return notifier_from_errno(err);
4473}
4474
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004475struct mlxsw_sp_inet6addr_event_work {
4476 struct work_struct work;
4477 struct net_device *dev;
4478 unsigned long event;
4479};
4480
4481static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4482{
4483 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4484 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4485 struct net_device *dev = inet6addr_work->dev;
4486 unsigned long event = inet6addr_work->event;
4487 struct mlxsw_sp *mlxsw_sp;
4488 struct mlxsw_sp_rif *rif;
4489
4490 rtnl_lock();
4491 mlxsw_sp = mlxsw_sp_lower_get(dev);
4492 if (!mlxsw_sp)
4493 goto out;
4494
4495 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4496 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4497 goto out;
4498
4499 __mlxsw_sp_inetaddr_event(dev, event);
4500out:
4501 rtnl_unlock();
4502 dev_put(dev);
4503 kfree(inet6addr_work);
4504}
4505
4506/* Called with rcu_read_lock() */
4507int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4508 unsigned long event, void *ptr)
4509{
4510 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4511 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4512 struct net_device *dev = if6->idev->dev;
4513
4514 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4515 return NOTIFY_DONE;
4516
4517 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4518 if (!inet6addr_work)
4519 return NOTIFY_BAD;
4520
4521 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4522 inet6addr_work->dev = dev;
4523 inet6addr_work->event = event;
4524 dev_hold(dev);
4525 mlxsw_core_schedule_work(&inet6addr_work->work);
4526
4527 return NOTIFY_DONE;
4528}
4529
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004530static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004531 const char *mac, int mtu)
4532{
4533 char ritr_pl[MLXSW_REG_RITR_LEN];
4534 int err;
4535
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004536 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004537 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4538 if (err)
4539 return err;
4540
4541 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4542 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4543 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4544 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4545}
4546
4547int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4548{
4549 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004550 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004551 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004552 int err;
4553
4554 mlxsw_sp = mlxsw_sp_lower_get(dev);
4555 if (!mlxsw_sp)
4556 return 0;
4557
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004558 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4559 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004560 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004561 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004562
Ido Schimmela1107482017-05-26 08:37:39 +02004563 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004564 if (err)
4565 return err;
4566
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004567 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4568 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004569 if (err)
4570 goto err_rif_edit;
4571
Ido Schimmela1107482017-05-26 08:37:39 +02004572 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004573 if (err)
4574 goto err_rif_fdb_op;
4575
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004576 ether_addr_copy(rif->addr, dev->dev_addr);
4577 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004578
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004579 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004580
4581 return 0;
4582
4583err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004584 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004585err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004586 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004587 return err;
4588}
4589
Ido Schimmelb1e45522017-04-30 19:47:14 +03004590static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4591 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004592{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004593 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004594
Ido Schimmelb1e45522017-04-30 19:47:14 +03004595 /* If netdev is already associated with a RIF, then we need to
4596 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004597 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004598 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4599 if (rif)
4600 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004601
Ido Schimmelb1e45522017-04-30 19:47:14 +03004602 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004603}
4604
Ido Schimmelb1e45522017-04-30 19:47:14 +03004605static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4606 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004607{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004608 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004609
Ido Schimmelb1e45522017-04-30 19:47:14 +03004610 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4611 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004612 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004613 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004614}
4615
Ido Schimmelb1e45522017-04-30 19:47:14 +03004616int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4617 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004618{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004619 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4620 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004621
Ido Schimmelb1e45522017-04-30 19:47:14 +03004622 if (!mlxsw_sp)
4623 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004624
Ido Schimmelb1e45522017-04-30 19:47:14 +03004625 switch (event) {
4626 case NETDEV_PRECHANGEUPPER:
4627 return 0;
4628 case NETDEV_CHANGEUPPER:
4629 if (info->linking)
4630 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4631 else
4632 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4633 break;
4634 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004635
Ido Schimmelb1e45522017-04-30 19:47:14 +03004636 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004637}
4638
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004639static struct mlxsw_sp_rif_subport *
4640mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004641{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004642 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004643}
4644
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004645static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4646 const struct mlxsw_sp_rif_params *params)
4647{
4648 struct mlxsw_sp_rif_subport *rif_subport;
4649
4650 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4651 rif_subport->vid = params->vid;
4652 rif_subport->lag = params->lag;
4653 if (params->lag)
4654 rif_subport->lag_id = params->lag_id;
4655 else
4656 rif_subport->system_port = params->system_port;
4657}
4658
4659static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4660{
4661 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4662 struct mlxsw_sp_rif_subport *rif_subport;
4663 char ritr_pl[MLXSW_REG_RITR_LEN];
4664
4665 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4666 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4667 rif->rif_index, rif->vr_id, rif->dev->mtu,
4668 rif->dev->dev_addr);
4669 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4670 rif_subport->lag ? rif_subport->lag_id :
4671 rif_subport->system_port,
4672 rif_subport->vid);
4673
4674 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4675}
4676
4677static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4678{
4679 return mlxsw_sp_rif_subport_op(rif, true);
4680}
4681
4682static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4683{
4684 mlxsw_sp_rif_subport_op(rif, false);
4685}
4686
4687static struct mlxsw_sp_fid *
4688mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4689{
4690 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4691}
4692
4693static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4694 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4695 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4696 .setup = mlxsw_sp_rif_subport_setup,
4697 .configure = mlxsw_sp_rif_subport_configure,
4698 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4699 .fid_get = mlxsw_sp_rif_subport_fid_get,
4700};
4701
4702static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4703 enum mlxsw_reg_ritr_if_type type,
4704 u16 vid_fid, bool enable)
4705{
4706 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4707 char ritr_pl[MLXSW_REG_RITR_LEN];
4708
4709 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4710 rif->dev->mtu, rif->dev->dev_addr);
4711 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4712
4713 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4714}
4715
4716static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4717{
4718 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4719}
4720
4721static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4722{
4723 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4724 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4725 int err;
4726
4727 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4728 if (err)
4729 return err;
4730
Ido Schimmel0d284812017-07-18 10:10:12 +02004731 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4732 mlxsw_sp_router_port(mlxsw_sp), true);
4733 if (err)
4734 goto err_fid_mc_flood_set;
4735
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004736 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4737 mlxsw_sp_router_port(mlxsw_sp), true);
4738 if (err)
4739 goto err_fid_bc_flood_set;
4740
4741 return 0;
4742
4743err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004744 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4745 mlxsw_sp_router_port(mlxsw_sp), false);
4746err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004747 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4748 return err;
4749}
4750
4751static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
4752{
4753 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4754 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4755
4756 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4757 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004758 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4759 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004760 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4761}
4762
4763static struct mlxsw_sp_fid *
4764mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
4765{
4766 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
4767
4768 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
4769}
4770
4771static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
4772 .type = MLXSW_SP_RIF_TYPE_VLAN,
4773 .rif_size = sizeof(struct mlxsw_sp_rif),
4774 .configure = mlxsw_sp_rif_vlan_configure,
4775 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
4776 .fid_get = mlxsw_sp_rif_vlan_fid_get,
4777};
4778
4779static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
4780{
4781 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4782 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4783 int err;
4784
4785 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
4786 true);
4787 if (err)
4788 return err;
4789
Ido Schimmel0d284812017-07-18 10:10:12 +02004790 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4791 mlxsw_sp_router_port(mlxsw_sp), true);
4792 if (err)
4793 goto err_fid_mc_flood_set;
4794
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004795 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4796 mlxsw_sp_router_port(mlxsw_sp), true);
4797 if (err)
4798 goto err_fid_bc_flood_set;
4799
4800 return 0;
4801
4802err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004803 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4804 mlxsw_sp_router_port(mlxsw_sp), false);
4805err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004806 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4807 return err;
4808}
4809
4810static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
4811{
4812 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4813 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4814
4815 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4816 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004817 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4818 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004819 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4820}
4821
4822static struct mlxsw_sp_fid *
4823mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
4824{
4825 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
4826}
4827
4828static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
4829 .type = MLXSW_SP_RIF_TYPE_FID,
4830 .rif_size = sizeof(struct mlxsw_sp_rif),
4831 .configure = mlxsw_sp_rif_fid_configure,
4832 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
4833 .fid_get = mlxsw_sp_rif_fid_fid_get,
4834};
4835
4836static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
4837 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
4838 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
4839 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
4840};
4841
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004842static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
4843{
4844 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4845
4846 mlxsw_sp->router->rifs = kcalloc(max_rifs,
4847 sizeof(struct mlxsw_sp_rif *),
4848 GFP_KERNEL);
4849 if (!mlxsw_sp->router->rifs)
4850 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004851
4852 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
4853
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004854 return 0;
4855}
4856
4857static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
4858{
4859 int i;
4860
4861 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
4862 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
4863
4864 kfree(mlxsw_sp->router->rifs);
4865}
4866
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004867static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
4868{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004869 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004870
4871 /* Flush pending FIB notifications and then flush the device's
4872 * table before requesting another dump. The FIB notification
4873 * block is unregistered, so no need to take RTNL.
4874 */
4875 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02004876 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4877 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004878}
4879
Ido Schimmel4724ba562017-03-10 08:53:39 +01004880static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4881{
4882 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4883 u64 max_rifs;
4884 int err;
4885
4886 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
4887 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004888 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004889
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004890 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004891 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
4892 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4893 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004894 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004895 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004896}
4897
4898static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4899{
4900 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004901
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004902 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004903 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004904}
4905
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004906int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4907{
Ido Schimmel9011b672017-05-16 19:38:25 +02004908 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004909 int err;
4910
Ido Schimmel9011b672017-05-16 19:38:25 +02004911 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4912 if (!router)
4913 return -ENOMEM;
4914 mlxsw_sp->router = router;
4915 router->mlxsw_sp = mlxsw_sp;
4916
4917 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004918 err = __mlxsw_sp_router_init(mlxsw_sp);
4919 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004920 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004921
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004922 err = mlxsw_sp_rifs_init(mlxsw_sp);
4923 if (err)
4924 goto err_rifs_init;
4925
Ido Schimmel9011b672017-05-16 19:38:25 +02004926 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004927 &mlxsw_sp_nexthop_ht_params);
4928 if (err)
4929 goto err_nexthop_ht_init;
4930
Ido Schimmel9011b672017-05-16 19:38:25 +02004931 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004932 &mlxsw_sp_nexthop_group_ht_params);
4933 if (err)
4934 goto err_nexthop_group_ht_init;
4935
Ido Schimmel8494ab02017-03-24 08:02:47 +01004936 err = mlxsw_sp_lpm_init(mlxsw_sp);
4937 if (err)
4938 goto err_lpm_init;
4939
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004940 err = mlxsw_sp_vrs_init(mlxsw_sp);
4941 if (err)
4942 goto err_vrs_init;
4943
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004944 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004945 if (err)
4946 goto err_neigh_init;
4947
Ido Schimmel7e39d112017-05-16 19:38:28 +02004948 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4949 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004950 mlxsw_sp_router_fib_dump_flush);
4951 if (err)
4952 goto err_register_fib_notifier;
4953
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004954 return 0;
4955
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004956err_register_fib_notifier:
4957 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004958err_neigh_init:
4959 mlxsw_sp_vrs_fini(mlxsw_sp);
4960err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004961 mlxsw_sp_lpm_fini(mlxsw_sp);
4962err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004963 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004964err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004965 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004966err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004967 mlxsw_sp_rifs_fini(mlxsw_sp);
4968err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004969 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004970err_router_init:
4971 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004972 return err;
4973}
4974
4975void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4976{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004977 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004978 mlxsw_sp_neigh_fini(mlxsw_sp);
4979 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01004980 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004981 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4982 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004983 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004984 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004985 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004986}