blob: 510042905f22aee4adddf5a5cc4fc8f6c060479f [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
1525struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001526 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001527 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001528 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001529 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001530 u8 adj_index_valid:1,
1531 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001532 u32 adj_index;
1533 u16 ecmp_size;
1534 u16 count;
1535 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001536#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001537};
1538
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001539static struct fib_info *
1540mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1541{
1542 return nh_grp->priv;
1543}
1544
1545struct mlxsw_sp_nexthop_group_cmp_arg {
1546 struct fib_info *fi;
1547};
1548
1549static int
1550mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1551{
1552 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1553 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1554
1555 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1556}
1557
1558static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1559{
1560 const struct mlxsw_sp_nexthop_group *nh_grp = data;
1561 struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1562
1563 return jhash(&fi, sizeof(fi), seed);
1564}
1565
1566static u32
1567mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1568{
1569 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1570
1571 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1572}
1573
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001574static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001575 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001576 .hashfn = mlxsw_sp_nexthop_group_hash,
1577 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1578 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001579};
1580
1581static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1582 struct mlxsw_sp_nexthop_group *nh_grp)
1583{
Ido Schimmel9011b672017-05-16 19:38:25 +02001584 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001585 &nh_grp->ht_node,
1586 mlxsw_sp_nexthop_group_ht_params);
1587}
1588
1589static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1590 struct mlxsw_sp_nexthop_group *nh_grp)
1591{
Ido Schimmel9011b672017-05-16 19:38:25 +02001592 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001593 &nh_grp->ht_node,
1594 mlxsw_sp_nexthop_group_ht_params);
1595}
1596
1597static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001598mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1599 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001600{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001601 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1602
1603 cmp_arg.fi = fi;
1604 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1605 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001606 mlxsw_sp_nexthop_group_ht_params);
1607}
1608
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001609static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1610 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1611 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1612 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1613};
1614
1615static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1616 struct mlxsw_sp_nexthop *nh)
1617{
Ido Schimmel9011b672017-05-16 19:38:25 +02001618 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001619 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1620}
1621
1622static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1623 struct mlxsw_sp_nexthop *nh)
1624{
Ido Schimmel9011b672017-05-16 19:38:25 +02001625 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001626 mlxsw_sp_nexthop_ht_params);
1627}
1628
Ido Schimmelad178c82017-02-08 11:16:40 +01001629static struct mlxsw_sp_nexthop *
1630mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1631 struct mlxsw_sp_nexthop_key key)
1632{
Ido Schimmel9011b672017-05-16 19:38:25 +02001633 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001634 mlxsw_sp_nexthop_ht_params);
1635}
1636
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001637static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001638 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001639 u32 adj_index, u16 ecmp_size,
1640 u32 new_adj_index,
1641 u16 new_ecmp_size)
1642{
1643 char raleu_pl[MLXSW_REG_RALEU_LEN];
1644
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001645 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001646 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1647 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001648 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001649 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1650}
1651
1652static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1653 struct mlxsw_sp_nexthop_group *nh_grp,
1654 u32 old_adj_index, u16 old_ecmp_size)
1655{
1656 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001657 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001658 int err;
1659
1660 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001661 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001662 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001663 fib = fib_entry->fib_node->fib;
1664 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001665 old_adj_index,
1666 old_ecmp_size,
1667 nh_grp->adj_index,
1668 nh_grp->ecmp_size);
1669 if (err)
1670 return err;
1671 }
1672 return 0;
1673}
1674
1675static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1676 struct mlxsw_sp_nexthop *nh)
1677{
1678 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1679 char ratr_pl[MLXSW_REG_RATR_LEN];
1680
1681 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1682 true, adj_index, neigh_entry->rif);
1683 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1684 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1685}
1686
1687static int
1688mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001689 struct mlxsw_sp_nexthop_group *nh_grp,
1690 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001691{
1692 u32 adj_index = nh_grp->adj_index; /* base */
1693 struct mlxsw_sp_nexthop *nh;
1694 int i;
1695 int err;
1696
1697 for (i = 0; i < nh_grp->count; i++) {
1698 nh = &nh_grp->nexthops[i];
1699
1700 if (!nh->should_offload) {
1701 nh->offloaded = 0;
1702 continue;
1703 }
1704
Ido Schimmela59b7e02017-01-23 11:11:42 +01001705 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001706 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1707 adj_index, nh);
1708 if (err)
1709 return err;
1710 nh->update = 0;
1711 nh->offloaded = 1;
1712 }
1713 adj_index++;
1714 }
1715 return 0;
1716}
1717
1718static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1719 struct mlxsw_sp_fib_entry *fib_entry);
1720
Ido Schimmel1819ae32017-07-21 18:04:28 +02001721static bool
1722mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1723 const struct mlxsw_sp_fib_entry *fib_entry);
1724
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001725static int
1726mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1727 struct mlxsw_sp_nexthop_group *nh_grp)
1728{
1729 struct mlxsw_sp_fib_entry *fib_entry;
1730 int err;
1731
1732 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001733 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1734 fib_entry))
1735 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001736 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1737 if (err)
1738 return err;
1739 }
1740 return 0;
1741}
1742
1743static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001744mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1745 enum mlxsw_reg_ralue_op op, int err);
1746
1747static void
1748mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1749{
1750 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1751 struct mlxsw_sp_fib_entry *fib_entry;
1752
1753 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1754 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1755 fib_entry))
1756 continue;
1757 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1758 }
1759}
1760
1761static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001762mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1763 struct mlxsw_sp_nexthop_group *nh_grp)
1764{
1765 struct mlxsw_sp_nexthop *nh;
1766 bool offload_change = false;
1767 u32 adj_index;
1768 u16 ecmp_size = 0;
1769 bool old_adj_index_valid;
1770 u32 old_adj_index;
1771 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001772 int i;
1773 int err;
1774
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001775 if (!nh_grp->gateway) {
1776 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1777 return;
1778 }
1779
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001780 for (i = 0; i < nh_grp->count; i++) {
1781 nh = &nh_grp->nexthops[i];
1782
Petr Machata56b8a9e2017-07-31 09:27:29 +02001783 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001784 offload_change = true;
1785 if (nh->should_offload)
1786 nh->update = 1;
1787 }
1788 if (nh->should_offload)
1789 ecmp_size++;
1790 }
1791 if (!offload_change) {
1792 /* Nothing was added or removed, so no need to reallocate. Just
1793 * update MAC on existing adjacency indexes.
1794 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001795 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1796 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001797 if (err) {
1798 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1799 goto set_trap;
1800 }
1801 return;
1802 }
1803 if (!ecmp_size)
1804 /* No neigh of this group is connected so we just set
1805 * the trap and let everthing flow through kernel.
1806 */
1807 goto set_trap;
1808
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001809 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1810 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001811 /* We ran out of KVD linear space, just set the
1812 * trap and let everything flow through kernel.
1813 */
1814 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1815 goto set_trap;
1816 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001817 old_adj_index_valid = nh_grp->adj_index_valid;
1818 old_adj_index = nh_grp->adj_index;
1819 old_ecmp_size = nh_grp->ecmp_size;
1820 nh_grp->adj_index_valid = 1;
1821 nh_grp->adj_index = adj_index;
1822 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001823 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001824 if (err) {
1825 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1826 goto set_trap;
1827 }
1828
1829 if (!old_adj_index_valid) {
1830 /* The trap was set for fib entries, so we have to call
1831 * fib entry update to unset it and use adjacency index.
1832 */
1833 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1834 if (err) {
1835 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1836 goto set_trap;
1837 }
1838 return;
1839 }
1840
1841 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1842 old_adj_index, old_ecmp_size);
1843 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1844 if (err) {
1845 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1846 goto set_trap;
1847 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02001848
1849 /* Offload state within the group changed, so update the flags. */
1850 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
1851
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001852 return;
1853
1854set_trap:
1855 old_adj_index_valid = nh_grp->adj_index_valid;
1856 nh_grp->adj_index_valid = 0;
1857 for (i = 0; i < nh_grp->count; i++) {
1858 nh = &nh_grp->nexthops[i];
1859 nh->offloaded = 0;
1860 }
1861 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1862 if (err)
1863 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1864 if (old_adj_index_valid)
1865 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1866}
1867
1868static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1869 bool removing)
1870{
Petr Machata213666a2017-07-31 09:27:30 +02001871 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001872 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02001873 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001874 nh->should_offload = 0;
1875 nh->update = 1;
1876}
1877
1878static void
1879mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1880 struct mlxsw_sp_neigh_entry *neigh_entry,
1881 bool removing)
1882{
1883 struct mlxsw_sp_nexthop *nh;
1884
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001885 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1886 neigh_list_node) {
1887 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1888 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1889 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001890}
1891
Ido Schimmel9665b742017-02-08 11:16:42 +01001892static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001893 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001894{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001895 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001896 return;
1897
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001898 nh->rif = rif;
1899 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001900}
1901
1902static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1903{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001904 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001905 return;
1906
1907 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001908 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01001909}
1910
Ido Schimmela8c97012017-02-08 11:16:35 +01001911static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1912 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001913{
1914 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001915 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001916 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001917 int err;
1918
Ido Schimmelad178c82017-02-08 11:16:40 +01001919 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01001920 return 0;
1921
Jiri Pirko33b13412016-11-10 12:31:04 +01001922 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02001923 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01001924 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01001925 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01001926 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001927 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01001928 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001929 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1930 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01001931 if (IS_ERR(n))
1932 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001933 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01001934 }
1935 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1936 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001937 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1938 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001939 err = -EINVAL;
1940 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001941 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001942 }
Yotam Gigib2157142016-07-05 11:27:51 +02001943
1944 /* If that is the first nexthop connected to that neigh, add to
1945 * nexthop_neighs_list
1946 */
1947 if (list_empty(&neigh_entry->nexthop_list))
1948 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02001949 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02001950
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001951 nh->neigh_entry = neigh_entry;
1952 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1953 read_lock_bh(&n->lock);
1954 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001955 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001956 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01001957 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001958
1959 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001960
1961err_neigh_entry_create:
1962 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001963 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001964}
1965
Ido Schimmela8c97012017-02-08 11:16:35 +01001966static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1967 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001968{
1969 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01001970 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001971
Ido Schimmelb8399a12017-02-08 11:16:33 +01001972 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01001973 return;
1974 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01001975
Ido Schimmel58312122016-12-23 09:32:50 +01001976 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001977 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01001978 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02001979
1980 /* If that is the last nexthop connected to that neigh, remove from
1981 * nexthop_neighs_list
1982 */
Ido Schimmele58be792017-02-08 11:16:28 +01001983 if (list_empty(&neigh_entry->nexthop_list))
1984 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02001985
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001986 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1987 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1988
1989 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01001990}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001991
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001992static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1993 struct mlxsw_sp_nexthop_group *nh_grp,
1994 struct mlxsw_sp_nexthop *nh,
1995 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001996{
1997 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001998 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001999 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002000 int err;
2001
2002 nh->nh_grp = nh_grp;
2003 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002004 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002005 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2006 if (err)
2007 return err;
2008
Ido Schimmel97989ee2017-03-10 08:53:38 +01002009 if (!dev)
2010 return 0;
2011
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002012 in_dev = __in_dev_get_rtnl(dev);
2013 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2014 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2015 return 0;
2016
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002017 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2018 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002019 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002020 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002021
2022 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2023 if (err)
2024 goto err_nexthop_neigh_init;
2025
2026 return 0;
2027
2028err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002029 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002030 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2031 return err;
2032}
2033
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002034static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2035 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002036{
2037 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002038 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002039 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002040}
2041
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002042static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2043 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002044{
2045 struct mlxsw_sp_nexthop_key key;
2046 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002047 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002048
Ido Schimmel9011b672017-05-16 19:38:25 +02002049 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002050 return;
2051
2052 key.fib_nh = fib_nh;
2053 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2054 if (WARN_ON_ONCE(!nh))
2055 return;
2056
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002057 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2058 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002059 return;
2060
2061 switch (event) {
2062 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002063 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002064 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2065 break;
2066 case FIB_EVENT_NH_DEL:
2067 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002068 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002069 break;
2070 }
2071
2072 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2073}
2074
Ido Schimmel9665b742017-02-08 11:16:42 +01002075static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002076 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002077{
2078 struct mlxsw_sp_nexthop *nh, *tmp;
2079
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002080 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002081 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2082 mlxsw_sp_nexthop_rif_fini(nh);
2083 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2084 }
2085}
2086
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002087static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002088mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002089{
2090 struct mlxsw_sp_nexthop_group *nh_grp;
2091 struct mlxsw_sp_nexthop *nh;
2092 struct fib_nh *fib_nh;
2093 size_t alloc_size;
2094 int i;
2095 int err;
2096
2097 alloc_size = sizeof(*nh_grp) +
2098 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2099 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2100 if (!nh_grp)
2101 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002102 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002103 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002104 nh_grp->neigh_tbl = &arp_tbl;
2105
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002106 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002107 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002108 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002109 for (i = 0; i < nh_grp->count; i++) {
2110 nh = &nh_grp->nexthops[i];
2111 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002112 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002113 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002114 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002115 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002116 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2117 if (err)
2118 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002119 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2120 return nh_grp;
2121
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002122err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002123err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002124 for (i--; i >= 0; i--) {
2125 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002126 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002127 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002128 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002129 kfree(nh_grp);
2130 return ERR_PTR(err);
2131}
2132
2133static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002134mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2135 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002136{
2137 struct mlxsw_sp_nexthop *nh;
2138 int i;
2139
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002140 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002141 for (i = 0; i < nh_grp->count; i++) {
2142 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002143 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002144 }
Ido Schimmel58312122016-12-23 09:32:50 +01002145 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2146 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002147 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002148 kfree(nh_grp);
2149}
2150
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002151static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2152 struct mlxsw_sp_fib_entry *fib_entry,
2153 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002154{
2155 struct mlxsw_sp_nexthop_group *nh_grp;
2156
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002157 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002158 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002159 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002160 if (IS_ERR(nh_grp))
2161 return PTR_ERR(nh_grp);
2162 }
2163 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2164 fib_entry->nh_group = nh_grp;
2165 return 0;
2166}
2167
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002168static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2169 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002170{
2171 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2172
2173 list_del(&fib_entry->nexthop_group_node);
2174 if (!list_empty(&nh_grp->fib_list))
2175 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002176 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002177}
2178
Ido Schimmel013b20f2017-02-08 11:16:36 +01002179static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002180mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2181{
2182 struct mlxsw_sp_fib4_entry *fib4_entry;
2183
2184 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2185 common);
2186 return !fib4_entry->tos;
2187}
2188
2189static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002190mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2191{
2192 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2193
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002194 switch (fib_entry->fib_node->fib->proto) {
2195 case MLXSW_SP_L3_PROTO_IPV4:
2196 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2197 return false;
2198 break;
2199 case MLXSW_SP_L3_PROTO_IPV6:
2200 break;
2201 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002202
Ido Schimmel013b20f2017-02-08 11:16:36 +01002203 switch (fib_entry->type) {
2204 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2205 return !!nh_group->adj_index_valid;
2206 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002207 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002208 default:
2209 return false;
2210 }
2211}
2212
Ido Schimmel428b8512017-08-03 13:28:28 +02002213static struct mlxsw_sp_nexthop *
2214mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2215 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2216{
2217 int i;
2218
2219 for (i = 0; i < nh_grp->count; i++) {
2220 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2221 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2222
2223 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2224 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2225 &rt->rt6i_gateway))
2226 return nh;
2227 continue;
2228 }
2229
2230 return NULL;
2231}
2232
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002233static void
2234mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2235{
2236 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2237 int i;
2238
2239 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2240 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2241 return;
2242 }
2243
2244 for (i = 0; i < nh_grp->count; i++) {
2245 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2246
2247 if (nh->offloaded)
2248 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2249 else
2250 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2251 }
2252}
2253
2254static void
2255mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2256{
2257 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2258 int i;
2259
2260 for (i = 0; i < nh_grp->count; i++) {
2261 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2262
2263 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2264 }
2265}
2266
Ido Schimmel428b8512017-08-03 13:28:28 +02002267static void
2268mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2269{
2270 struct mlxsw_sp_fib6_entry *fib6_entry;
2271 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2272
2273 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2274 common);
2275
2276 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2277 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
2278 list)->rt->rt6i_flags |= RTF_OFFLOAD;
2279 return;
2280 }
2281
2282 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2283 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2284 struct mlxsw_sp_nexthop *nh;
2285
2286 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2287 if (nh && nh->offloaded)
2288 mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD;
2289 else
2290 mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD;
2291 }
2292}
2293
2294static void
2295mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2296{
2297 struct mlxsw_sp_fib6_entry *fib6_entry;
2298 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2299
2300 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2301 common);
2302 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2303 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2304
2305 rt->rt6i_flags &= ~RTF_OFFLOAD;
2306 }
2307}
2308
Ido Schimmel013b20f2017-02-08 11:16:36 +01002309static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2310{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002311 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002312 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002313 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002314 break;
2315 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002316 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2317 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002318 }
2319}
2320
2321static void
2322mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2323{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002324 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002325 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002326 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002327 break;
2328 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002329 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2330 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002331 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002332}
2333
2334static void
2335mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2336 enum mlxsw_reg_ralue_op op, int err)
2337{
2338 switch (op) {
2339 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002340 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2341 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2342 if (err)
2343 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002344 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002345 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002346 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002347 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2348 return;
2349 default:
2350 return;
2351 }
2352}
2353
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002354static void
2355mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2356 const struct mlxsw_sp_fib_entry *fib_entry,
2357 enum mlxsw_reg_ralue_op op)
2358{
2359 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2360 enum mlxsw_reg_ralxx_protocol proto;
2361 u32 *p_dip;
2362
2363 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2364
2365 switch (fib->proto) {
2366 case MLXSW_SP_L3_PROTO_IPV4:
2367 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2368 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2369 fib_entry->fib_node->key.prefix_len,
2370 *p_dip);
2371 break;
2372 case MLXSW_SP_L3_PROTO_IPV6:
2373 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2374 fib_entry->fib_node->key.prefix_len,
2375 fib_entry->fib_node->key.addr);
2376 break;
2377 }
2378}
2379
2380static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2381 struct mlxsw_sp_fib_entry *fib_entry,
2382 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002383{
2384 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002385 enum mlxsw_reg_ralue_trap_action trap_action;
2386 u16 trap_id = 0;
2387 u32 adjacency_index = 0;
2388 u16 ecmp_size = 0;
2389
2390 /* In case the nexthop group adjacency index is valid, use it
2391 * with provided ECMP size. Otherwise, setup trap and pass
2392 * traffic to kernel.
2393 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002394 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002395 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2396 adjacency_index = fib_entry->nh_group->adj_index;
2397 ecmp_size = fib_entry->nh_group->ecmp_size;
2398 } else {
2399 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2400 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2401 }
2402
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002403 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002404 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2405 adjacency_index, ecmp_size);
2406 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2407}
2408
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002409static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2410 struct mlxsw_sp_fib_entry *fib_entry,
2411 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002412{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002413 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002414 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002415 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002416 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002417 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002418
2419 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2420 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002421 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002422 } else {
2423 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2424 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2425 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002426
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002427 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002428 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2429 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002430 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2431}
2432
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002433static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2434 struct mlxsw_sp_fib_entry *fib_entry,
2435 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002436{
2437 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002438
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002439 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002440 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2441 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2442}
2443
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002444static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2445 struct mlxsw_sp_fib_entry *fib_entry,
2446 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002447{
2448 switch (fib_entry->type) {
2449 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002450 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002451 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002452 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002453 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002454 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002455 }
2456 return -EINVAL;
2457}
2458
2459static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2460 struct mlxsw_sp_fib_entry *fib_entry,
2461 enum mlxsw_reg_ralue_op op)
2462{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002463 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002464
Ido Schimmel013b20f2017-02-08 11:16:36 +01002465 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002466
Ido Schimmel013b20f2017-02-08 11:16:36 +01002467 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002468}
2469
2470static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2471 struct mlxsw_sp_fib_entry *fib_entry)
2472{
Jiri Pirko7146da32016-09-01 10:37:41 +02002473 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2474 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002475}
2476
2477static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2478 struct mlxsw_sp_fib_entry *fib_entry)
2479{
2480 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2481 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2482}
2483
Jiri Pirko61c503f2016-07-04 08:23:11 +02002484static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002485mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2486 const struct fib_entry_notifier_info *fen_info,
2487 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002488{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002489 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002490
Ido Schimmel97989ee2017-03-10 08:53:38 +01002491 switch (fen_info->type) {
2492 case RTN_BROADCAST: /* fall through */
2493 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002494 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2495 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002496 case RTN_UNREACHABLE: /* fall through */
2497 case RTN_BLACKHOLE: /* fall through */
2498 case RTN_PROHIBIT:
2499 /* Packets hitting these routes need to be trapped, but
2500 * can do so with a lower priority than packets directed
2501 * at the host, so use action type local instead of trap.
2502 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002503 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002504 return 0;
2505 case RTN_UNICAST:
2506 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2507 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2508 else
2509 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2510 return 0;
2511 default:
2512 return -EINVAL;
2513 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002514}
2515
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002516static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002517mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2518 struct mlxsw_sp_fib_node *fib_node,
2519 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002520{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002521 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002522 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002523 int err;
2524
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002525 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2526 if (!fib4_entry)
2527 return ERR_PTR(-ENOMEM);
2528 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002529
2530 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2531 if (err)
2532 goto err_fib4_entry_type_set;
2533
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002534 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002535 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002536 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002537
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002538 fib4_entry->prio = fen_info->fi->fib_priority;
2539 fib4_entry->tb_id = fen_info->tb_id;
2540 fib4_entry->type = fen_info->type;
2541 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002542
2543 fib_entry->fib_node = fib_node;
2544
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002545 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002546
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002547err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002548err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002549 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002550 return ERR_PTR(err);
2551}
2552
2553static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002554 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002555{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002556 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002557 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002558}
2559
2560static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002561mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2562 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002563
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002564static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002565mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2566 const struct fib_entry_notifier_info *fen_info)
2567{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002568 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002569 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002570 struct mlxsw_sp_fib *fib;
2571 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002572
Ido Schimmel160e22a2017-07-18 10:10:20 +02002573 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2574 if (!vr)
2575 return NULL;
2576 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2577
2578 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2579 sizeof(fen_info->dst),
2580 fen_info->dst_len);
2581 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002582 return NULL;
2583
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002584 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2585 if (fib4_entry->tb_id == fen_info->tb_id &&
2586 fib4_entry->tos == fen_info->tos &&
2587 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002588 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2589 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002590 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002591 }
2592 }
2593
2594 return NULL;
2595}
2596
2597static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2598 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2599 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2600 .key_len = sizeof(struct mlxsw_sp_fib_key),
2601 .automatic_shrinking = true,
2602};
2603
2604static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2605 struct mlxsw_sp_fib_node *fib_node)
2606{
2607 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2608 mlxsw_sp_fib_ht_params);
2609}
2610
2611static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2612 struct mlxsw_sp_fib_node *fib_node)
2613{
2614 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2615 mlxsw_sp_fib_ht_params);
2616}
2617
2618static struct mlxsw_sp_fib_node *
2619mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2620 size_t addr_len, unsigned char prefix_len)
2621{
2622 struct mlxsw_sp_fib_key key;
2623
2624 memset(&key, 0, sizeof(key));
2625 memcpy(key.addr, addr, addr_len);
2626 key.prefix_len = prefix_len;
2627 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2628}
2629
2630static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002631mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002632 size_t addr_len, unsigned char prefix_len)
2633{
2634 struct mlxsw_sp_fib_node *fib_node;
2635
2636 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2637 if (!fib_node)
2638 return NULL;
2639
2640 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002641 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002642 memcpy(fib_node->key.addr, addr, addr_len);
2643 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002644
2645 return fib_node;
2646}
2647
2648static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2649{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002650 list_del(&fib_node->list);
2651 WARN_ON(!list_empty(&fib_node->entry_list));
2652 kfree(fib_node);
2653}
2654
2655static bool
2656mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2657 const struct mlxsw_sp_fib_entry *fib_entry)
2658{
2659 return list_first_entry(&fib_node->entry_list,
2660 struct mlxsw_sp_fib_entry, list) == fib_entry;
2661}
2662
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002663static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2664 struct mlxsw_sp_fib *fib,
2665 struct mlxsw_sp_fib_node *fib_node)
2666{
2667 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2668 struct mlxsw_sp_lpm_tree *lpm_tree;
2669 int err;
2670
2671 /* Since the tree is shared between all virtual routers we must
2672 * make sure it contains all the required prefix lengths. This
2673 * can be computed by either adding the new prefix length to the
2674 * existing prefix usage of a bound tree, or by aggregating the
2675 * prefix lengths across all virtual routers and adding the new
2676 * one as well.
2677 */
2678 if (fib->lpm_tree)
2679 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2680 &fib->lpm_tree->prefix_usage);
2681 else
2682 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2683 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2684
2685 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2686 fib->proto);
2687 if (IS_ERR(lpm_tree))
2688 return PTR_ERR(lpm_tree);
2689
2690 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2691 return 0;
2692
2693 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2694 if (err)
2695 return err;
2696
2697 return 0;
2698}
2699
2700static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2701 struct mlxsw_sp_fib *fib)
2702{
2703 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2704 struct mlxsw_sp_lpm_tree *lpm_tree;
2705
2706 /* Aggregate prefix lengths across all virtual routers to make
2707 * sure we only have used prefix lengths in the LPM tree.
2708 */
2709 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2710 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2711 fib->proto);
2712 if (IS_ERR(lpm_tree))
2713 goto err_tree_get;
2714 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2715
2716err_tree_get:
2717 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2718 return;
2719 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2720 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2721 fib->lpm_tree = NULL;
2722}
2723
Ido Schimmel9aecce12017-02-09 10:28:42 +01002724static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2725{
2726 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002727 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002728
2729 if (fib->prefix_ref_count[prefix_len]++ == 0)
2730 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2731}
2732
2733static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2734{
2735 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002736 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002737
2738 if (--fib->prefix_ref_count[prefix_len] == 0)
2739 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2740}
2741
Ido Schimmel76610eb2017-03-10 08:53:41 +01002742static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2743 struct mlxsw_sp_fib_node *fib_node,
2744 struct mlxsw_sp_fib *fib)
2745{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002746 int err;
2747
2748 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2749 if (err)
2750 return err;
2751 fib_node->fib = fib;
2752
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002753 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
2754 if (err)
2755 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002756
2757 mlxsw_sp_fib_node_prefix_inc(fib_node);
2758
2759 return 0;
2760
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002761err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002762 fib_node->fib = NULL;
2763 mlxsw_sp_fib_node_remove(fib, fib_node);
2764 return err;
2765}
2766
2767static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2768 struct mlxsw_sp_fib_node *fib_node)
2769{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002770 struct mlxsw_sp_fib *fib = fib_node->fib;
2771
2772 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002773 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002774 fib_node->fib = NULL;
2775 mlxsw_sp_fib_node_remove(fib, fib_node);
2776}
2777
Ido Schimmel9aecce12017-02-09 10:28:42 +01002778static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002779mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2780 size_t addr_len, unsigned char prefix_len,
2781 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002782{
2783 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002784 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002785 struct mlxsw_sp_vr *vr;
2786 int err;
2787
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002788 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002789 if (IS_ERR(vr))
2790 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002791 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002792
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002793 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002794 if (fib_node)
2795 return fib_node;
2796
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002797 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002798 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002799 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002800 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002801 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002802
Ido Schimmel76610eb2017-03-10 08:53:41 +01002803 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2804 if (err)
2805 goto err_fib_node_init;
2806
Ido Schimmel9aecce12017-02-09 10:28:42 +01002807 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002808
Ido Schimmel76610eb2017-03-10 08:53:41 +01002809err_fib_node_init:
2810 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002811err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002812 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002813 return ERR_PTR(err);
2814}
2815
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002816static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2817 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002818{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002819 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002820
Ido Schimmel9aecce12017-02-09 10:28:42 +01002821 if (!list_empty(&fib_node->entry_list))
2822 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002823 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002824 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002825 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002826}
2827
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002828static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002829mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002830 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002831{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002832 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002833
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002834 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2835 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002836 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002837 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002838 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002839 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002840 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002841 if (fib4_entry->prio >= new4_entry->prio ||
2842 fib4_entry->tos < new4_entry->tos)
2843 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002844 }
2845
2846 return NULL;
2847}
2848
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002849static int
2850mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2851 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002852{
2853 struct mlxsw_sp_fib_node *fib_node;
2854
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002855 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01002856 return -EINVAL;
2857
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002858 fib_node = fib4_entry->common.fib_node;
2859 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2860 common.list) {
2861 if (fib4_entry->tb_id != new4_entry->tb_id ||
2862 fib4_entry->tos != new4_entry->tos ||
2863 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002864 break;
2865 }
2866
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002867 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01002868 return 0;
2869}
2870
Ido Schimmel9aecce12017-02-09 10:28:42 +01002871static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02002872mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002873 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002874{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002875 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002876 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002877
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002878 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002879
Ido Schimmel4283bce2017-02-09 10:28:43 +01002880 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002881 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2882 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002883 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01002884
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002885 /* Insert new entry before replaced one, so that we can later
2886 * remove the second.
2887 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002888 if (fib4_entry) {
2889 list_add_tail(&new4_entry->common.list,
2890 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002891 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002892 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002893
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002894 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2895 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002896 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002897 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002898 }
2899
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002900 if (fib4_entry)
2901 list_add(&new4_entry->common.list,
2902 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002903 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002904 list_add(&new4_entry->common.list,
2905 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002906 }
2907
2908 return 0;
2909}
2910
2911static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002912mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002913{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002914 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002915}
2916
Ido Schimmel80c238f2017-07-18 10:10:29 +02002917static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2918 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002919{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002920 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2921
Ido Schimmel9aecce12017-02-09 10:28:42 +01002922 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2923 return 0;
2924
2925 /* To prevent packet loss, overwrite the previously offloaded
2926 * entry.
2927 */
2928 if (!list_is_singular(&fib_node->entry_list)) {
2929 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2930 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2931
2932 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2933 }
2934
2935 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2936}
2937
Ido Schimmel80c238f2017-07-18 10:10:29 +02002938static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2939 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002940{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002941 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2942
Ido Schimmel9aecce12017-02-09 10:28:42 +01002943 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2944 return;
2945
2946 /* Promote the next entry by overwriting the deleted entry */
2947 if (!list_is_singular(&fib_node->entry_list)) {
2948 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2949 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2950
2951 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2952 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2953 return;
2954 }
2955
2956 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2957}
2958
2959static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002960 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002961 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002962{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002963 int err;
2964
Ido Schimmel9efbee62017-07-18 10:10:28 +02002965 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002966 if (err)
2967 return err;
2968
Ido Schimmel80c238f2017-07-18 10:10:29 +02002969 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002970 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02002971 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002972
Ido Schimmel9aecce12017-02-09 10:28:42 +01002973 return 0;
2974
Ido Schimmel80c238f2017-07-18 10:10:29 +02002975err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002976 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002977 return err;
2978}
2979
2980static void
2981mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002982 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002983{
Ido Schimmel80c238f2017-07-18 10:10:29 +02002984 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002985 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002986}
2987
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002988static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002989 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002990 bool replace)
2991{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002992 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2993 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002994
2995 if (!replace)
2996 return;
2997
2998 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002999 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003000
3001 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3002 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003003 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003004}
3005
Ido Schimmel9aecce12017-02-09 10:28:42 +01003006static int
3007mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003008 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003009 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003010{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003011 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003012 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003013 int err;
3014
Ido Schimmel9011b672017-05-16 19:38:25 +02003015 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003016 return 0;
3017
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003018 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3019 &fen_info->dst, sizeof(fen_info->dst),
3020 fen_info->dst_len,
3021 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003022 if (IS_ERR(fib_node)) {
3023 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3024 return PTR_ERR(fib_node);
3025 }
3026
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003027 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3028 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003029 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003030 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003031 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003032 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003033
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003034 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003035 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003036 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003037 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3038 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003039 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003040
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003041 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003042
Jiri Pirko61c503f2016-07-04 08:23:11 +02003043 return 0;
3044
Ido Schimmel9aecce12017-02-09 10:28:42 +01003045err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003046 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003047err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003048 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003049 return err;
3050}
3051
Jiri Pirko37956d72016-10-20 16:05:43 +02003052static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3053 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003054{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003055 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003056 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003057
Ido Schimmel9011b672017-05-16 19:38:25 +02003058 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003059 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003060
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003061 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3062 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003063 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003064 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003065
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003066 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3067 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003068 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003069}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003070
Ido Schimmel428b8512017-08-03 13:28:28 +02003071static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3072{
3073 /* Packets with link-local destination IP arriving to the router
3074 * are trapped to the CPU, so no need to program specific routes
3075 * for them.
3076 */
3077 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3078 return true;
3079
3080 /* Multicast routes aren't supported, so ignore them. Neighbour
3081 * Discovery packets are specifically trapped.
3082 */
3083 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3084 return true;
3085
3086 /* Cloned routes are irrelevant in the forwarding path. */
3087 if (rt->rt6i_flags & RTF_CACHE)
3088 return true;
3089
3090 return false;
3091}
3092
3093static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3094{
3095 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3096
3097 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3098 if (!mlxsw_sp_rt6)
3099 return ERR_PTR(-ENOMEM);
3100
3101 /* In case of route replace, replaced route is deleted with
3102 * no notification. Take reference to prevent accessing freed
3103 * memory.
3104 */
3105 mlxsw_sp_rt6->rt = rt;
3106 rt6_hold(rt);
3107
3108 return mlxsw_sp_rt6;
3109}
3110
3111#if IS_ENABLED(CONFIG_IPV6)
3112static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3113{
3114 rt6_release(rt);
3115}
3116#else
3117static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3118{
3119}
3120#endif
3121
3122static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3123{
3124 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3125 kfree(mlxsw_sp_rt6);
3126}
3127
3128static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3129{
3130 /* RTF_CACHE routes are ignored */
3131 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3132}
3133
3134static struct rt6_info *
3135mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3136{
3137 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3138 list)->rt;
3139}
3140
3141static struct mlxsw_sp_fib6_entry *
3142mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003143 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003144{
3145 struct mlxsw_sp_fib6_entry *fib6_entry;
3146
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003147 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003148 return NULL;
3149
3150 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3151 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3152
3153 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3154 * virtual router.
3155 */
3156 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3157 continue;
3158 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3159 break;
3160 if (rt->rt6i_metric < nrt->rt6i_metric)
3161 continue;
3162 if (rt->rt6i_metric == nrt->rt6i_metric &&
3163 mlxsw_sp_fib6_rt_can_mp(rt))
3164 return fib6_entry;
3165 if (rt->rt6i_metric > nrt->rt6i_metric)
3166 break;
3167 }
3168
3169 return NULL;
3170}
3171
3172static struct mlxsw_sp_rt6 *
3173mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3174 const struct rt6_info *rt)
3175{
3176 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3177
3178 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3179 if (mlxsw_sp_rt6->rt == rt)
3180 return mlxsw_sp_rt6;
3181 }
3182
3183 return NULL;
3184}
3185
3186static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3187 struct mlxsw_sp_nexthop_group *nh_grp,
3188 struct mlxsw_sp_nexthop *nh,
3189 const struct rt6_info *rt)
3190{
3191 struct net_device *dev = rt->dst.dev;
3192 struct mlxsw_sp_rif *rif;
3193 int err;
3194
3195 nh->nh_grp = nh_grp;
3196 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3197
3198 if (!dev)
3199 return 0;
3200
3201 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3202 if (!rif)
3203 return 0;
3204 mlxsw_sp_nexthop_rif_init(nh, rif);
3205
3206 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3207 if (err)
3208 goto err_nexthop_neigh_init;
3209
3210 return 0;
3211
3212err_nexthop_neigh_init:
3213 mlxsw_sp_nexthop_rif_fini(nh);
3214 return err;
3215}
3216
3217static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3218 struct mlxsw_sp_nexthop *nh)
3219{
3220 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3221 mlxsw_sp_nexthop_rif_fini(nh);
3222}
3223
3224static struct mlxsw_sp_nexthop_group *
3225mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3226 struct mlxsw_sp_fib6_entry *fib6_entry)
3227{
3228 struct mlxsw_sp_nexthop_group *nh_grp;
3229 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3230 struct mlxsw_sp_nexthop *nh;
3231 size_t alloc_size;
3232 int i = 0;
3233 int err;
3234
3235 alloc_size = sizeof(*nh_grp) +
3236 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3237 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3238 if (!nh_grp)
3239 return ERR_PTR(-ENOMEM);
3240 INIT_LIST_HEAD(&nh_grp->fib_list);
3241#if IS_ENABLED(CONFIG_IPV6)
3242 nh_grp->neigh_tbl = &nd_tbl;
3243#endif
3244 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3245 struct mlxsw_sp_rt6, list);
3246 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3247 nh_grp->count = fib6_entry->nrt6;
3248 for (i = 0; i < nh_grp->count; i++) {
3249 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3250
3251 nh = &nh_grp->nexthops[i];
3252 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3253 if (err)
3254 goto err_nexthop6_init;
3255 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3256 }
3257 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3258 return nh_grp;
3259
3260err_nexthop6_init:
3261 for (i--; i >= 0; i--) {
3262 nh = &nh_grp->nexthops[i];
3263 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3264 }
3265 kfree(nh_grp);
3266 return ERR_PTR(err);
3267}
3268
3269static void
3270mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3271 struct mlxsw_sp_nexthop_group *nh_grp)
3272{
3273 struct mlxsw_sp_nexthop *nh;
3274 int i = nh_grp->count;
3275
3276 for (i--; i >= 0; i--) {
3277 nh = &nh_grp->nexthops[i];
3278 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3279 }
3280 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3281 WARN_ON(nh_grp->adj_index_valid);
3282 kfree(nh_grp);
3283}
3284
3285static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3286 struct mlxsw_sp_fib6_entry *fib6_entry)
3287{
3288 struct mlxsw_sp_nexthop_group *nh_grp;
3289
3290 /* For now, don't consolidate nexthop groups */
3291 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3292 if (IS_ERR(nh_grp))
3293 return PTR_ERR(nh_grp);
3294
3295 list_add_tail(&fib6_entry->common.nexthop_group_node,
3296 &nh_grp->fib_list);
3297 fib6_entry->common.nh_group = nh_grp;
3298
3299 return 0;
3300}
3301
3302static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3303 struct mlxsw_sp_fib_entry *fib_entry)
3304{
3305 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3306
3307 list_del(&fib_entry->nexthop_group_node);
3308 if (!list_empty(&nh_grp->fib_list))
3309 return;
3310 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3311}
3312
3313static int
3314mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3315 struct mlxsw_sp_fib6_entry *fib6_entry)
3316{
3317 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3318 int err;
3319
3320 fib6_entry->common.nh_group = NULL;
3321 list_del(&fib6_entry->common.nexthop_group_node);
3322
3323 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3324 if (err)
3325 goto err_nexthop6_group_get;
3326
3327 /* In case this entry is offloaded, then the adjacency index
3328 * currently associated with it in the device's table is that
3329 * of the old group. Start using the new one instead.
3330 */
3331 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3332 if (err)
3333 goto err_fib_node_entry_add;
3334
3335 if (list_empty(&old_nh_grp->fib_list))
3336 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3337
3338 return 0;
3339
3340err_fib_node_entry_add:
3341 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3342err_nexthop6_group_get:
3343 list_add_tail(&fib6_entry->common.nexthop_group_node,
3344 &old_nh_grp->fib_list);
3345 fib6_entry->common.nh_group = old_nh_grp;
3346 return err;
3347}
3348
3349static int
3350mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3351 struct mlxsw_sp_fib6_entry *fib6_entry,
3352 struct rt6_info *rt)
3353{
3354 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3355 int err;
3356
3357 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3358 if (IS_ERR(mlxsw_sp_rt6))
3359 return PTR_ERR(mlxsw_sp_rt6);
3360
3361 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3362 fib6_entry->nrt6++;
3363
3364 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3365 if (err)
3366 goto err_nexthop6_group_update;
3367
3368 return 0;
3369
3370err_nexthop6_group_update:
3371 fib6_entry->nrt6--;
3372 list_del(&mlxsw_sp_rt6->list);
3373 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3374 return err;
3375}
3376
3377static void
3378mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3379 struct mlxsw_sp_fib6_entry *fib6_entry,
3380 struct rt6_info *rt)
3381{
3382 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3383
3384 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3385 if (WARN_ON(!mlxsw_sp_rt6))
3386 return;
3387
3388 fib6_entry->nrt6--;
3389 list_del(&mlxsw_sp_rt6->list);
3390 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3391 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3392}
3393
3394static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3395 const struct rt6_info *rt)
3396{
3397 /* Packets hitting RTF_REJECT routes need to be discarded by the
3398 * stack. We can rely on their destination device not having a
3399 * RIF (it's the loopback device) and can thus use action type
3400 * local, which will cause them to be trapped with a lower
3401 * priority than packets that need to be locally received.
3402 */
3403 if (rt->rt6i_flags & RTF_LOCAL)
3404 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3405 else if (rt->rt6i_flags & RTF_REJECT)
3406 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3407 else if (rt->rt6i_flags & RTF_GATEWAY)
3408 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3409 else
3410 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3411}
3412
3413static void
3414mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3415{
3416 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3417
3418 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3419 list) {
3420 fib6_entry->nrt6--;
3421 list_del(&mlxsw_sp_rt6->list);
3422 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3423 }
3424}
3425
3426static struct mlxsw_sp_fib6_entry *
3427mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3428 struct mlxsw_sp_fib_node *fib_node,
3429 struct rt6_info *rt)
3430{
3431 struct mlxsw_sp_fib6_entry *fib6_entry;
3432 struct mlxsw_sp_fib_entry *fib_entry;
3433 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3434 int err;
3435
3436 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3437 if (!fib6_entry)
3438 return ERR_PTR(-ENOMEM);
3439 fib_entry = &fib6_entry->common;
3440
3441 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3442 if (IS_ERR(mlxsw_sp_rt6)) {
3443 err = PTR_ERR(mlxsw_sp_rt6);
3444 goto err_rt6_create;
3445 }
3446
3447 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3448
3449 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3450 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3451 fib6_entry->nrt6 = 1;
3452 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3453 if (err)
3454 goto err_nexthop6_group_get;
3455
3456 fib_entry->fib_node = fib_node;
3457
3458 return fib6_entry;
3459
3460err_nexthop6_group_get:
3461 list_del(&mlxsw_sp_rt6->list);
3462 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3463err_rt6_create:
3464 kfree(fib6_entry);
3465 return ERR_PTR(err);
3466}
3467
3468static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3469 struct mlxsw_sp_fib6_entry *fib6_entry)
3470{
3471 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3472 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3473 WARN_ON(fib6_entry->nrt6);
3474 kfree(fib6_entry);
3475}
3476
3477static struct mlxsw_sp_fib6_entry *
3478mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003479 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003480{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003481 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003482
3483 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3484 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3485
3486 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3487 continue;
3488 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3489 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003490 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3491 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3492 mlxsw_sp_fib6_rt_can_mp(nrt))
3493 return fib6_entry;
3494 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3495 fallback = fallback ?: fib6_entry;
3496 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003497 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003498 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003499 }
3500
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003501 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003502}
3503
3504static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003505mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3506 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003507{
3508 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3509 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3510 struct mlxsw_sp_fib6_entry *fib6_entry;
3511
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003512 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3513
3514 if (replace && WARN_ON(!fib6_entry))
3515 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003516
3517 if (fib6_entry) {
3518 list_add_tail(&new6_entry->common.list,
3519 &fib6_entry->common.list);
3520 } else {
3521 struct mlxsw_sp_fib6_entry *last;
3522
3523 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3524 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3525
3526 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3527 break;
3528 fib6_entry = last;
3529 }
3530
3531 if (fib6_entry)
3532 list_add(&new6_entry->common.list,
3533 &fib6_entry->common.list);
3534 else
3535 list_add(&new6_entry->common.list,
3536 &fib_node->entry_list);
3537 }
3538
3539 return 0;
3540}
3541
3542static void
3543mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3544{
3545 list_del(&fib6_entry->common.list);
3546}
3547
3548static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003549 struct mlxsw_sp_fib6_entry *fib6_entry,
3550 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003551{
3552 int err;
3553
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003554 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003555 if (err)
3556 return err;
3557
3558 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3559 if (err)
3560 goto err_fib_node_entry_add;
3561
3562 return 0;
3563
3564err_fib_node_entry_add:
3565 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3566 return err;
3567}
3568
3569static void
3570mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3571 struct mlxsw_sp_fib6_entry *fib6_entry)
3572{
3573 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3574 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3575}
3576
3577static struct mlxsw_sp_fib6_entry *
3578mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3579 const struct rt6_info *rt)
3580{
3581 struct mlxsw_sp_fib6_entry *fib6_entry;
3582 struct mlxsw_sp_fib_node *fib_node;
3583 struct mlxsw_sp_fib *fib;
3584 struct mlxsw_sp_vr *vr;
3585
3586 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3587 if (!vr)
3588 return NULL;
3589 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3590
3591 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3592 sizeof(rt->rt6i_dst.addr),
3593 rt->rt6i_dst.plen);
3594 if (!fib_node)
3595 return NULL;
3596
3597 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3598 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3599
3600 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3601 rt->rt6i_metric == iter_rt->rt6i_metric &&
3602 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3603 return fib6_entry;
3604 }
3605
3606 return NULL;
3607}
3608
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003609static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3610 struct mlxsw_sp_fib6_entry *fib6_entry,
3611 bool replace)
3612{
3613 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3614 struct mlxsw_sp_fib6_entry *replaced;
3615
3616 if (!replace)
3617 return;
3618
3619 replaced = list_next_entry(fib6_entry, common.list);
3620
3621 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3622 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3623 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3624}
3625
Ido Schimmel428b8512017-08-03 13:28:28 +02003626static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003627 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003628{
3629 struct mlxsw_sp_fib6_entry *fib6_entry;
3630 struct mlxsw_sp_fib_node *fib_node;
3631 int err;
3632
3633 if (mlxsw_sp->router->aborted)
3634 return 0;
3635
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003636 if (rt->rt6i_src.plen)
3637 return -EINVAL;
3638
Ido Schimmel428b8512017-08-03 13:28:28 +02003639 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3640 return 0;
3641
3642 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3643 &rt->rt6i_dst.addr,
3644 sizeof(rt->rt6i_dst.addr),
3645 rt->rt6i_dst.plen,
3646 MLXSW_SP_L3_PROTO_IPV6);
3647 if (IS_ERR(fib_node))
3648 return PTR_ERR(fib_node);
3649
3650 /* Before creating a new entry, try to append route to an existing
3651 * multipath entry.
3652 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003653 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003654 if (fib6_entry) {
3655 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3656 if (err)
3657 goto err_fib6_entry_nexthop_add;
3658 return 0;
3659 }
3660
3661 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3662 if (IS_ERR(fib6_entry)) {
3663 err = PTR_ERR(fib6_entry);
3664 goto err_fib6_entry_create;
3665 }
3666
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003667 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003668 if (err)
3669 goto err_fib6_node_entry_link;
3670
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003671 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3672
Ido Schimmel428b8512017-08-03 13:28:28 +02003673 return 0;
3674
3675err_fib6_node_entry_link:
3676 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3677err_fib6_entry_create:
3678err_fib6_entry_nexthop_add:
3679 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3680 return err;
3681}
3682
3683static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3684 struct rt6_info *rt)
3685{
3686 struct mlxsw_sp_fib6_entry *fib6_entry;
3687 struct mlxsw_sp_fib_node *fib_node;
3688
3689 if (mlxsw_sp->router->aborted)
3690 return;
3691
3692 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3693 return;
3694
3695 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3696 if (WARN_ON(!fib6_entry))
3697 return;
3698
3699 /* If route is part of a multipath entry, but not the last one
3700 * removed, then only reduce its nexthop group.
3701 */
3702 if (!list_is_singular(&fib6_entry->rt6_list)) {
3703 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3704 return;
3705 }
3706
3707 fib_node = fib6_entry->common.fib_node;
3708
3709 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3710 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3711 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3712}
3713
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003714static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3715 enum mlxsw_reg_ralxx_protocol proto,
3716 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003717{
3718 char ralta_pl[MLXSW_REG_RALTA_LEN];
3719 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003720 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003721
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003722 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003723 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3724 if (err)
3725 return err;
3726
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003727 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003728 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3729 if (err)
3730 return err;
3731
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003732 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003733 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003734 char raltb_pl[MLXSW_REG_RALTB_LEN];
3735 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003736
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003737 if (!mlxsw_sp_vr_is_used(vr))
3738 continue;
3739
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003740 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003741 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3742 raltb_pl);
3743 if (err)
3744 return err;
3745
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003746 mlxsw_reg_ralue_pack(ralue_pl, proto,
3747 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003748 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3749 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3750 ralue_pl);
3751 if (err)
3752 return err;
3753 }
3754
3755 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003756}
3757
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003758static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3759{
3760 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3761 int err;
3762
3763 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3764 MLXSW_SP_LPM_TREE_MIN);
3765 if (err)
3766 return err;
3767
3768 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3769 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3770 MLXSW_SP_LPM_TREE_MIN + 1);
3771}
3772
Ido Schimmel9aecce12017-02-09 10:28:42 +01003773static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3774 struct mlxsw_sp_fib_node *fib_node)
3775{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003776 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003777
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003778 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
3779 common.list) {
3780 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003781
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003782 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3783 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003784 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003785 /* Break when entry list is empty and node was freed.
3786 * Otherwise, we'll access freed memory in the next
3787 * iteration.
3788 */
3789 if (do_break)
3790 break;
3791 }
3792}
3793
Ido Schimmel428b8512017-08-03 13:28:28 +02003794static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
3795 struct mlxsw_sp_fib_node *fib_node)
3796{
3797 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
3798
3799 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
3800 common.list) {
3801 bool do_break = &tmp->common.list == &fib_node->entry_list;
3802
3803 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3804 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3805 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3806 if (do_break)
3807 break;
3808 }
3809}
3810
Ido Schimmel9aecce12017-02-09 10:28:42 +01003811static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
3812 struct mlxsw_sp_fib_node *fib_node)
3813{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003814 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003815 case MLXSW_SP_L3_PROTO_IPV4:
3816 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
3817 break;
3818 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003819 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003820 break;
3821 }
3822}
3823
Ido Schimmel76610eb2017-03-10 08:53:41 +01003824static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
3825 struct mlxsw_sp_vr *vr,
3826 enum mlxsw_sp_l3proto proto)
3827{
3828 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
3829 struct mlxsw_sp_fib_node *fib_node, *tmp;
3830
3831 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
3832 bool do_break = &tmp->list == &fib->node_list;
3833
3834 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
3835 if (do_break)
3836 break;
3837 }
3838}
3839
Ido Schimmelac571de2016-11-14 11:26:32 +01003840static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003841{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003842 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003843
Jiri Pirkoc1a38312016-10-21 16:07:23 +02003844 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003845 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01003846
Ido Schimmel76610eb2017-03-10 08:53:41 +01003847 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003848 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003849 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02003850
3851 /* If virtual router was only used for IPv4, then it's no
3852 * longer used.
3853 */
3854 if (!mlxsw_sp_vr_is_used(vr))
3855 continue;
3856 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003857 }
Ido Schimmelac571de2016-11-14 11:26:32 +01003858}
3859
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003860static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01003861{
3862 int err;
3863
Ido Schimmel9011b672017-05-16 19:38:25 +02003864 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003865 return;
3866 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 +01003867 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003868 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003869 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3870 if (err)
3871 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3872}
3873
Ido Schimmel30572242016-12-03 16:45:01 +01003874struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003875 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003876 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02003877 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003878 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003879 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003880 struct fib_nh_notifier_info fnh_info;
3881 };
Ido Schimmel30572242016-12-03 16:45:01 +01003882 struct mlxsw_sp *mlxsw_sp;
3883 unsigned long event;
3884};
3885
Ido Schimmel66a57632017-08-03 13:28:26 +02003886static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003887{
Ido Schimmel30572242016-12-03 16:45:01 +01003888 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003889 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003890 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003891 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003892 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003893 int err;
3894
Ido Schimmel30572242016-12-03 16:45:01 +01003895 /* Protect internal structures from changes */
3896 rtnl_lock();
3897 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003898 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003899 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003900 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003901 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003902 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3903 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003904 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003905 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003906 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003907 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003908 break;
3909 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003910 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3911 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003912 break;
3913 case FIB_EVENT_RULE_ADD: /* fall through */
3914 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003915 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003916 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003917 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003918 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003919 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003920 case FIB_EVENT_NH_ADD: /* fall through */
3921 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003922 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3923 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003924 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3925 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003926 }
Ido Schimmel30572242016-12-03 16:45:01 +01003927 rtnl_unlock();
3928 kfree(fib_work);
3929}
3930
Ido Schimmel66a57632017-08-03 13:28:26 +02003931static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
3932{
Ido Schimmel583419f2017-08-03 13:28:27 +02003933 struct mlxsw_sp_fib_event_work *fib_work =
3934 container_of(work, struct mlxsw_sp_fib_event_work, work);
3935 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
3936 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003937 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02003938 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02003939
3940 rtnl_lock();
3941 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003942 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003943 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003944 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02003945 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003946 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003947 if (err)
3948 mlxsw_sp_router_fib_abort(mlxsw_sp);
3949 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3950 break;
3951 case FIB_EVENT_ENTRY_DEL:
3952 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
3953 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3954 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003955 case FIB_EVENT_RULE_ADD: /* fall through */
3956 case FIB_EVENT_RULE_DEL:
3957 rule = fib_work->fr_info.rule;
3958 if (!fib6_rule_default(rule) && !rule->l3mdev)
3959 mlxsw_sp_router_fib_abort(mlxsw_sp);
3960 fib_rule_put(rule);
3961 break;
3962 }
3963 rtnl_unlock();
3964 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02003965}
3966
3967static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
3968 struct fib_notifier_info *info)
3969{
3970 switch (fib_work->event) {
3971 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
3972 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3973 case FIB_EVENT_ENTRY_ADD: /* fall through */
3974 case FIB_EVENT_ENTRY_DEL:
3975 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
3976 /* Take referece on fib_info to prevent it from being
3977 * freed while work is queued. Release it afterwards.
3978 */
3979 fib_info_hold(fib_work->fen_info.fi);
3980 break;
3981 case FIB_EVENT_RULE_ADD: /* fall through */
3982 case FIB_EVENT_RULE_DEL:
3983 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3984 fib_rule_get(fib_work->fr_info.rule);
3985 break;
3986 case FIB_EVENT_NH_ADD: /* fall through */
3987 case FIB_EVENT_NH_DEL:
3988 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
3989 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3990 break;
3991 }
3992}
3993
3994static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
3995 struct fib_notifier_info *info)
3996{
Ido Schimmel583419f2017-08-03 13:28:27 +02003997 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003998 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003999 case FIB_EVENT_ENTRY_ADD: /* fall through */
4000 case FIB_EVENT_ENTRY_DEL:
4001 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4002 rt6_hold(fib_work->fen6_info.rt);
4003 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004004 case FIB_EVENT_RULE_ADD: /* fall through */
4005 case FIB_EVENT_RULE_DEL:
4006 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4007 fib_rule_get(fib_work->fr_info.rule);
4008 break;
4009 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004010}
4011
Ido Schimmel30572242016-12-03 16:45:01 +01004012/* Called with rcu_read_lock() */
4013static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4014 unsigned long event, void *ptr)
4015{
Ido Schimmel30572242016-12-03 16:45:01 +01004016 struct mlxsw_sp_fib_event_work *fib_work;
4017 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004018 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004019
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004020 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004021 return NOTIFY_DONE;
4022
4023 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4024 if (WARN_ON(!fib_work))
4025 return NOTIFY_BAD;
4026
Ido Schimmel7e39d112017-05-16 19:38:28 +02004027 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4028 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004029 fib_work->event = event;
4030
Ido Schimmel66a57632017-08-03 13:28:26 +02004031 switch (info->family) {
4032 case AF_INET:
4033 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4034 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004035 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004036 case AF_INET6:
4037 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4038 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004039 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004040 }
4041
Ido Schimmela0e47612017-02-06 16:20:10 +01004042 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004043
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004044 return NOTIFY_DONE;
4045}
4046
Ido Schimmel4724ba562017-03-10 08:53:39 +01004047static struct mlxsw_sp_rif *
4048mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4049 const struct net_device *dev)
4050{
4051 int i;
4052
4053 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004054 if (mlxsw_sp->router->rifs[i] &&
4055 mlxsw_sp->router->rifs[i]->dev == dev)
4056 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004057
4058 return NULL;
4059}
4060
4061static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4062{
4063 char ritr_pl[MLXSW_REG_RITR_LEN];
4064 int err;
4065
4066 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4067 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4068 if (WARN_ON_ONCE(err))
4069 return err;
4070
4071 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4072 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4073}
4074
4075static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004076 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004077{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004078 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4079 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4080 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004081}
4082
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004083static bool
4084mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4085 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004086{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004087 struct inet6_dev *inet6_dev;
4088 bool addr_list_empty = true;
4089 struct in_device *idev;
4090
Ido Schimmel4724ba562017-03-10 08:53:39 +01004091 switch (event) {
4092 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004093 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004094 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004095 idev = __in_dev_get_rtnl(dev);
4096 if (idev && idev->ifa_list)
4097 addr_list_empty = false;
4098
4099 inet6_dev = __in6_dev_get(dev);
4100 if (addr_list_empty && inet6_dev &&
4101 !list_empty(&inet6_dev->addr_list))
4102 addr_list_empty = false;
4103
4104 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004105 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004106 return true;
4107 /* It is possible we already removed the RIF ourselves
4108 * if it was assigned to a netdev that is now a bridge
4109 * or LAG slave.
4110 */
4111 return false;
4112 }
4113
4114 return false;
4115}
4116
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004117static enum mlxsw_sp_rif_type
4118mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4119 const struct net_device *dev)
4120{
4121 enum mlxsw_sp_fid_type type;
4122
4123 /* RIF type is derived from the type of the underlying FID */
4124 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4125 type = MLXSW_SP_FID_TYPE_8021Q;
4126 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4127 type = MLXSW_SP_FID_TYPE_8021Q;
4128 else if (netif_is_bridge_master(dev))
4129 type = MLXSW_SP_FID_TYPE_8021D;
4130 else
4131 type = MLXSW_SP_FID_TYPE_RFID;
4132
4133 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4134}
4135
Ido Schimmelde5ed992017-06-04 16:53:40 +02004136static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004137{
4138 int i;
4139
Ido Schimmelde5ed992017-06-04 16:53:40 +02004140 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4141 if (!mlxsw_sp->router->rifs[i]) {
4142 *p_rif_index = i;
4143 return 0;
4144 }
4145 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004146
Ido Schimmelde5ed992017-06-04 16:53:40 +02004147 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004148}
4149
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004150static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4151 u16 vr_id,
4152 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004153{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004154 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004155
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004156 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004157 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004158 return NULL;
4159
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004160 INIT_LIST_HEAD(&rif->nexthop_list);
4161 INIT_LIST_HEAD(&rif->neigh_list);
4162 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4163 rif->mtu = l3_dev->mtu;
4164 rif->vr_id = vr_id;
4165 rif->dev = l3_dev;
4166 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004167
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004168 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004169}
4170
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004171struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4172 u16 rif_index)
4173{
4174 return mlxsw_sp->router->rifs[rif_index];
4175}
4176
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004177u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4178{
4179 return rif->rif_index;
4180}
4181
4182int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4183{
4184 return rif->dev->ifindex;
4185}
4186
Ido Schimmel4724ba562017-03-10 08:53:39 +01004187static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004188mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4189 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004190{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004191 u32 tb_id = l3mdev_fib_table(params->dev);
4192 const struct mlxsw_sp_rif_ops *ops;
4193 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004194 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004195 struct mlxsw_sp_fid *fid;
4196 struct mlxsw_sp_vr *vr;
4197 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004198 int err;
4199
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004200 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4201 ops = mlxsw_sp->router->rif_ops_arr[type];
4202
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004203 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4204 if (IS_ERR(vr))
4205 return ERR_CAST(vr);
4206
Ido Schimmelde5ed992017-06-04 16:53:40 +02004207 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4208 if (err)
4209 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004210
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004211 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004212 if (!rif) {
4213 err = -ENOMEM;
4214 goto err_rif_alloc;
4215 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004216 rif->mlxsw_sp = mlxsw_sp;
4217 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004218
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004219 fid = ops->fid_get(rif);
4220 if (IS_ERR(fid)) {
4221 err = PTR_ERR(fid);
4222 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004223 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004224 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004225
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004226 if (ops->setup)
4227 ops->setup(rif, params);
4228
4229 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004230 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004231 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004232
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004233 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004234 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004235 if (err)
4236 goto err_rif_fdb_op;
4237
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004238 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004239 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004240 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004241 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004242
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004243 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004244
Ido Schimmel4724ba562017-03-10 08:53:39 +01004245err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004246 ops->deconfigure(rif);
4247err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004248 mlxsw_sp_fid_put(fid);
4249err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004250 kfree(rif);
4251err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004252err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004253 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004254 return ERR_PTR(err);
4255}
4256
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004257void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004258{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004259 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4260 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004261 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004262 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004263
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004264 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004265 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004266
Ido Schimmel69132292017-03-10 08:53:42 +01004267 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004268 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004269 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004270 mlxsw_sp_rif_counters_free(rif);
4271 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4272 mlxsw_sp_fid_index(fid), false);
4273 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004274 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004275 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004276 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004277}
4278
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004279static void
4280mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4281 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4282{
4283 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4284
4285 params->vid = mlxsw_sp_port_vlan->vid;
4286 params->lag = mlxsw_sp_port->lagged;
4287 if (params->lag)
4288 params->lag_id = mlxsw_sp_port->lag_id;
4289 else
4290 params->system_port = mlxsw_sp_port->local_port;
4291}
4292
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004293static int
Ido Schimmela1107482017-05-26 08:37:39 +02004294mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004295 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004296{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004297 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004298 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004299 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004300 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004301 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004302 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004303
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004304 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004305 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004306 struct mlxsw_sp_rif_params params = {
4307 .dev = l3_dev,
4308 };
4309
4310 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4311 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004312 if (IS_ERR(rif))
4313 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004314 }
4315
Ido Schimmela1107482017-05-26 08:37:39 +02004316 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004317 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004318 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4319 if (err)
4320 goto err_fid_port_vid_map;
4321
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004322 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004323 if (err)
4324 goto err_port_vid_learning_set;
4325
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004326 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004327 BR_STATE_FORWARDING);
4328 if (err)
4329 goto err_port_vid_stp_set;
4330
Ido Schimmela1107482017-05-26 08:37:39 +02004331 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004332
Ido Schimmel4724ba562017-03-10 08:53:39 +01004333 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004334
4335err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004336 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004337err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004338 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4339err_fid_port_vid_map:
4340 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004341 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004342}
4343
Ido Schimmela1107482017-05-26 08:37:39 +02004344void
4345mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004346{
Ido Schimmelce95e152017-05-26 08:37:27 +02004347 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004348 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004349 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004350
Ido Schimmela1107482017-05-26 08:37:39 +02004351 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4352 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004353
Ido Schimmela1107482017-05-26 08:37:39 +02004354 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004355 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4356 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004357 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4358 /* If router port holds the last reference on the rFID, then the
4359 * associated Sub-port RIF will be destroyed.
4360 */
4361 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004362}
4363
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004364static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4365 struct net_device *port_dev,
4366 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004367{
4368 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004369 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004370
Ido Schimmelce95e152017-05-26 08:37:27 +02004371 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004372 if (WARN_ON(!mlxsw_sp_port_vlan))
4373 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004374
4375 switch (event) {
4376 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004377 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004378 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004379 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004380 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004381 break;
4382 }
4383
4384 return 0;
4385}
4386
4387static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4388 unsigned long event)
4389{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004390 if (netif_is_bridge_port(port_dev) ||
4391 netif_is_lag_port(port_dev) ||
4392 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004393 return 0;
4394
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004395 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004396}
4397
4398static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4399 struct net_device *lag_dev,
4400 unsigned long event, u16 vid)
4401{
4402 struct net_device *port_dev;
4403 struct list_head *iter;
4404 int err;
4405
4406 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4407 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004408 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4409 port_dev,
4410 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004411 if (err)
4412 return err;
4413 }
4414 }
4415
4416 return 0;
4417}
4418
4419static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4420 unsigned long event)
4421{
4422 if (netif_is_bridge_port(lag_dev))
4423 return 0;
4424
4425 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4426}
4427
Ido Schimmel4724ba562017-03-10 08:53:39 +01004428static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004429 unsigned long event)
4430{
4431 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004432 struct mlxsw_sp_rif_params params = {
4433 .dev = l3_dev,
4434 };
Ido Schimmela1107482017-05-26 08:37:39 +02004435 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004436
4437 switch (event) {
4438 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004439 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4440 if (IS_ERR(rif))
4441 return PTR_ERR(rif);
4442 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004443 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004444 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004445 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004446 break;
4447 }
4448
4449 return 0;
4450}
4451
4452static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4453 unsigned long event)
4454{
4455 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004456 u16 vid = vlan_dev_vlan_id(vlan_dev);
4457
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004458 if (netif_is_bridge_port(vlan_dev))
4459 return 0;
4460
Ido Schimmel4724ba562017-03-10 08:53:39 +01004461 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004462 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4463 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004464 else if (netif_is_lag_master(real_dev))
4465 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4466 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004467 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004468 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004469
4470 return 0;
4471}
4472
Ido Schimmelb1e45522017-04-30 19:47:14 +03004473static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4474 unsigned long event)
4475{
4476 if (mlxsw_sp_port_dev_check(dev))
4477 return mlxsw_sp_inetaddr_port_event(dev, event);
4478 else if (netif_is_lag_master(dev))
4479 return mlxsw_sp_inetaddr_lag_event(dev, event);
4480 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004481 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004482 else if (is_vlan_dev(dev))
4483 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4484 else
4485 return 0;
4486}
4487
Ido Schimmel4724ba562017-03-10 08:53:39 +01004488int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4489 unsigned long event, void *ptr)
4490{
4491 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4492 struct net_device *dev = ifa->ifa_dev->dev;
4493 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004494 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004495 int err = 0;
4496
4497 mlxsw_sp = mlxsw_sp_lower_get(dev);
4498 if (!mlxsw_sp)
4499 goto out;
4500
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004501 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004502 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004503 goto out;
4504
Ido Schimmelb1e45522017-04-30 19:47:14 +03004505 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004506out:
4507 return notifier_from_errno(err);
4508}
4509
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004510struct mlxsw_sp_inet6addr_event_work {
4511 struct work_struct work;
4512 struct net_device *dev;
4513 unsigned long event;
4514};
4515
4516static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4517{
4518 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4519 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4520 struct net_device *dev = inet6addr_work->dev;
4521 unsigned long event = inet6addr_work->event;
4522 struct mlxsw_sp *mlxsw_sp;
4523 struct mlxsw_sp_rif *rif;
4524
4525 rtnl_lock();
4526 mlxsw_sp = mlxsw_sp_lower_get(dev);
4527 if (!mlxsw_sp)
4528 goto out;
4529
4530 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4531 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4532 goto out;
4533
4534 __mlxsw_sp_inetaddr_event(dev, event);
4535out:
4536 rtnl_unlock();
4537 dev_put(dev);
4538 kfree(inet6addr_work);
4539}
4540
4541/* Called with rcu_read_lock() */
4542int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4543 unsigned long event, void *ptr)
4544{
4545 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4546 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4547 struct net_device *dev = if6->idev->dev;
4548
4549 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4550 return NOTIFY_DONE;
4551
4552 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4553 if (!inet6addr_work)
4554 return NOTIFY_BAD;
4555
4556 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4557 inet6addr_work->dev = dev;
4558 inet6addr_work->event = event;
4559 dev_hold(dev);
4560 mlxsw_core_schedule_work(&inet6addr_work->work);
4561
4562 return NOTIFY_DONE;
4563}
4564
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004565static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004566 const char *mac, int mtu)
4567{
4568 char ritr_pl[MLXSW_REG_RITR_LEN];
4569 int err;
4570
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004571 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004572 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4573 if (err)
4574 return err;
4575
4576 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4577 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4578 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4579 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4580}
4581
4582int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4583{
4584 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004585 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004586 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004587 int err;
4588
4589 mlxsw_sp = mlxsw_sp_lower_get(dev);
4590 if (!mlxsw_sp)
4591 return 0;
4592
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004593 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4594 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004595 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004596 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004597
Ido Schimmela1107482017-05-26 08:37:39 +02004598 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004599 if (err)
4600 return err;
4601
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004602 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4603 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004604 if (err)
4605 goto err_rif_edit;
4606
Ido Schimmela1107482017-05-26 08:37:39 +02004607 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004608 if (err)
4609 goto err_rif_fdb_op;
4610
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004611 ether_addr_copy(rif->addr, dev->dev_addr);
4612 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004613
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004614 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004615
4616 return 0;
4617
4618err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004619 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004620err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004621 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004622 return err;
4623}
4624
Ido Schimmelb1e45522017-04-30 19:47:14 +03004625static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4626 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004627{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004628 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004629
Ido Schimmelb1e45522017-04-30 19:47:14 +03004630 /* If netdev is already associated with a RIF, then we need to
4631 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004632 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004633 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4634 if (rif)
4635 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004636
Ido Schimmelb1e45522017-04-30 19:47:14 +03004637 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004638}
4639
Ido Schimmelb1e45522017-04-30 19:47:14 +03004640static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4641 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004642{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004643 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004644
Ido Schimmelb1e45522017-04-30 19:47:14 +03004645 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4646 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004647 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004648 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004649}
4650
Ido Schimmelb1e45522017-04-30 19:47:14 +03004651int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4652 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004653{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004654 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4655 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004656
Ido Schimmelb1e45522017-04-30 19:47:14 +03004657 if (!mlxsw_sp)
4658 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004659
Ido Schimmelb1e45522017-04-30 19:47:14 +03004660 switch (event) {
4661 case NETDEV_PRECHANGEUPPER:
4662 return 0;
4663 case NETDEV_CHANGEUPPER:
4664 if (info->linking)
4665 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4666 else
4667 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4668 break;
4669 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004670
Ido Schimmelb1e45522017-04-30 19:47:14 +03004671 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004672}
4673
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004674static struct mlxsw_sp_rif_subport *
4675mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004676{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004677 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004678}
4679
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004680static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4681 const struct mlxsw_sp_rif_params *params)
4682{
4683 struct mlxsw_sp_rif_subport *rif_subport;
4684
4685 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4686 rif_subport->vid = params->vid;
4687 rif_subport->lag = params->lag;
4688 if (params->lag)
4689 rif_subport->lag_id = params->lag_id;
4690 else
4691 rif_subport->system_port = params->system_port;
4692}
4693
4694static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4695{
4696 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4697 struct mlxsw_sp_rif_subport *rif_subport;
4698 char ritr_pl[MLXSW_REG_RITR_LEN];
4699
4700 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4701 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4702 rif->rif_index, rif->vr_id, rif->dev->mtu,
4703 rif->dev->dev_addr);
4704 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4705 rif_subport->lag ? rif_subport->lag_id :
4706 rif_subport->system_port,
4707 rif_subport->vid);
4708
4709 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4710}
4711
4712static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4713{
4714 return mlxsw_sp_rif_subport_op(rif, true);
4715}
4716
4717static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4718{
4719 mlxsw_sp_rif_subport_op(rif, false);
4720}
4721
4722static struct mlxsw_sp_fid *
4723mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4724{
4725 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4726}
4727
4728static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4729 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4730 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4731 .setup = mlxsw_sp_rif_subport_setup,
4732 .configure = mlxsw_sp_rif_subport_configure,
4733 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4734 .fid_get = mlxsw_sp_rif_subport_fid_get,
4735};
4736
4737static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4738 enum mlxsw_reg_ritr_if_type type,
4739 u16 vid_fid, bool enable)
4740{
4741 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4742 char ritr_pl[MLXSW_REG_RITR_LEN];
4743
4744 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4745 rif->dev->mtu, rif->dev->dev_addr);
4746 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4747
4748 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4749}
4750
4751static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4752{
4753 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4754}
4755
4756static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4757{
4758 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4759 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4760 int err;
4761
4762 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4763 if (err)
4764 return err;
4765
Ido Schimmel0d284812017-07-18 10:10:12 +02004766 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4767 mlxsw_sp_router_port(mlxsw_sp), true);
4768 if (err)
4769 goto err_fid_mc_flood_set;
4770
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004771 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4772 mlxsw_sp_router_port(mlxsw_sp), true);
4773 if (err)
4774 goto err_fid_bc_flood_set;
4775
4776 return 0;
4777
4778err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004779 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4780 mlxsw_sp_router_port(mlxsw_sp), false);
4781err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004782 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4783 return err;
4784}
4785
4786static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
4787{
4788 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4789 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4790
4791 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4792 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004793 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4794 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004795 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4796}
4797
4798static struct mlxsw_sp_fid *
4799mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
4800{
4801 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
4802
4803 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
4804}
4805
4806static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
4807 .type = MLXSW_SP_RIF_TYPE_VLAN,
4808 .rif_size = sizeof(struct mlxsw_sp_rif),
4809 .configure = mlxsw_sp_rif_vlan_configure,
4810 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
4811 .fid_get = mlxsw_sp_rif_vlan_fid_get,
4812};
4813
4814static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
4815{
4816 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4817 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4818 int err;
4819
4820 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
4821 true);
4822 if (err)
4823 return err;
4824
Ido Schimmel0d284812017-07-18 10:10:12 +02004825 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4826 mlxsw_sp_router_port(mlxsw_sp), true);
4827 if (err)
4828 goto err_fid_mc_flood_set;
4829
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004830 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4831 mlxsw_sp_router_port(mlxsw_sp), true);
4832 if (err)
4833 goto err_fid_bc_flood_set;
4834
4835 return 0;
4836
4837err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004838 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4839 mlxsw_sp_router_port(mlxsw_sp), false);
4840err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004841 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4842 return err;
4843}
4844
4845static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
4846{
4847 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4848 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4849
4850 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4851 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004852 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4853 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004854 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4855}
4856
4857static struct mlxsw_sp_fid *
4858mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
4859{
4860 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
4861}
4862
4863static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
4864 .type = MLXSW_SP_RIF_TYPE_FID,
4865 .rif_size = sizeof(struct mlxsw_sp_rif),
4866 .configure = mlxsw_sp_rif_fid_configure,
4867 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
4868 .fid_get = mlxsw_sp_rif_fid_fid_get,
4869};
4870
4871static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
4872 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
4873 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
4874 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
4875};
4876
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004877static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
4878{
4879 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4880
4881 mlxsw_sp->router->rifs = kcalloc(max_rifs,
4882 sizeof(struct mlxsw_sp_rif *),
4883 GFP_KERNEL);
4884 if (!mlxsw_sp->router->rifs)
4885 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004886
4887 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
4888
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004889 return 0;
4890}
4891
4892static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
4893{
4894 int i;
4895
4896 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
4897 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
4898
4899 kfree(mlxsw_sp->router->rifs);
4900}
4901
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004902static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
4903{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004904 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004905
4906 /* Flush pending FIB notifications and then flush the device's
4907 * table before requesting another dump. The FIB notification
4908 * block is unregistered, so no need to take RTNL.
4909 */
4910 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02004911 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4912 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004913}
4914
Ido Schimmel4724ba562017-03-10 08:53:39 +01004915static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4916{
4917 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4918 u64 max_rifs;
4919 int err;
4920
4921 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
4922 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004923 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004924
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004925 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004926 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
4927 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4928 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004929 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004930 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004931}
4932
4933static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4934{
4935 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004936
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004937 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004938 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004939}
4940
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004941int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4942{
Ido Schimmel9011b672017-05-16 19:38:25 +02004943 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004944 int err;
4945
Ido Schimmel9011b672017-05-16 19:38:25 +02004946 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4947 if (!router)
4948 return -ENOMEM;
4949 mlxsw_sp->router = router;
4950 router->mlxsw_sp = mlxsw_sp;
4951
4952 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004953 err = __mlxsw_sp_router_init(mlxsw_sp);
4954 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004955 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004956
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004957 err = mlxsw_sp_rifs_init(mlxsw_sp);
4958 if (err)
4959 goto err_rifs_init;
4960
Ido Schimmel9011b672017-05-16 19:38:25 +02004961 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004962 &mlxsw_sp_nexthop_ht_params);
4963 if (err)
4964 goto err_nexthop_ht_init;
4965
Ido Schimmel9011b672017-05-16 19:38:25 +02004966 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004967 &mlxsw_sp_nexthop_group_ht_params);
4968 if (err)
4969 goto err_nexthop_group_ht_init;
4970
Ido Schimmel8494ab02017-03-24 08:02:47 +01004971 err = mlxsw_sp_lpm_init(mlxsw_sp);
4972 if (err)
4973 goto err_lpm_init;
4974
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004975 err = mlxsw_sp_vrs_init(mlxsw_sp);
4976 if (err)
4977 goto err_vrs_init;
4978
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004979 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004980 if (err)
4981 goto err_neigh_init;
4982
Ido Schimmel7e39d112017-05-16 19:38:28 +02004983 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4984 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004985 mlxsw_sp_router_fib_dump_flush);
4986 if (err)
4987 goto err_register_fib_notifier;
4988
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004989 return 0;
4990
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004991err_register_fib_notifier:
4992 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004993err_neigh_init:
4994 mlxsw_sp_vrs_fini(mlxsw_sp);
4995err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004996 mlxsw_sp_lpm_fini(mlxsw_sp);
4997err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004998 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004999err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005000 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005001err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005002 mlxsw_sp_rifs_fini(mlxsw_sp);
5003err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005004 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005005err_router_init:
5006 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005007 return err;
5008}
5009
5010void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5011{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005012 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005013 mlxsw_sp_neigh_fini(mlxsw_sp);
5014 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005015 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005016 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5017 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005018 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005019 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005020 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005021}