blob: 3ddfbe3d0dbe22d6abc2a16ecb1e2d7508422951 [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
Yotam Gigic723c7352016-07-05 11:27:43 +02006 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020039#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020042#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010043#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010044#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020045#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020046#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020047#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020048#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020049#include <net/neighbour.h>
50#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020051#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020052#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010053#include <net/fib_rules.h>
Ido Schimmel57837882017-03-16 09:08:16 +010054#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020055#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020056#include <net/ndisc.h>
57#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020058#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020059
60#include "spectrum.h"
61#include "core.h"
62#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020063#include "spectrum_cnt.h"
64#include "spectrum_dpipe.h"
65#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020066
Ido Schimmel9011b672017-05-16 19:38:25 +020067struct mlxsw_sp_vr;
68struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020069struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020070
71struct mlxsw_sp_router {
72 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020073 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020074 struct mlxsw_sp_vr *vrs;
75 struct rhashtable neigh_ht;
76 struct rhashtable nexthop_group_ht;
77 struct rhashtable nexthop_ht;
78 struct {
79 struct mlxsw_sp_lpm_tree *trees;
80 unsigned int tree_count;
81 } lpm;
82 struct {
83 struct delayed_work dw;
84 unsigned long interval; /* ms */
85 } neighs_update;
86 struct delayed_work nexthop_probe_dw;
87#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
88 struct list_head nexthop_neighs_list;
89 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020090 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020091 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020092};
93
Ido Schimmel4724ba562017-03-10 08:53:39 +010094struct mlxsw_sp_rif {
95 struct list_head nexthop_list;
96 struct list_head neigh_list;
97 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +020098 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +010099 unsigned char addr[ETH_ALEN];
100 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100101 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100102 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200103 const struct mlxsw_sp_rif_ops *ops;
104 struct mlxsw_sp *mlxsw_sp;
105
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200106 unsigned int counter_ingress;
107 bool counter_ingress_valid;
108 unsigned int counter_egress;
109 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100110};
111
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200112struct mlxsw_sp_rif_params {
113 struct net_device *dev;
114 union {
115 u16 system_port;
116 u16 lag_id;
117 };
118 u16 vid;
119 bool lag;
120};
121
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200122struct mlxsw_sp_rif_subport {
123 struct mlxsw_sp_rif common;
124 union {
125 u16 system_port;
126 u16 lag_id;
127 };
128 u16 vid;
129 bool lag;
130};
131
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200132struct mlxsw_sp_rif_ops {
133 enum mlxsw_sp_rif_type type;
134 size_t rif_size;
135
136 void (*setup)(struct mlxsw_sp_rif *rif,
137 const struct mlxsw_sp_rif_params *params);
138 int (*configure)(struct mlxsw_sp_rif *rif);
139 void (*deconfigure)(struct mlxsw_sp_rif *rif);
140 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
141};
142
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200143static unsigned int *
144mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
145 enum mlxsw_sp_rif_counter_dir dir)
146{
147 switch (dir) {
148 case MLXSW_SP_RIF_COUNTER_EGRESS:
149 return &rif->counter_egress;
150 case MLXSW_SP_RIF_COUNTER_INGRESS:
151 return &rif->counter_ingress;
152 }
153 return NULL;
154}
155
156static bool
157mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
158 enum mlxsw_sp_rif_counter_dir dir)
159{
160 switch (dir) {
161 case MLXSW_SP_RIF_COUNTER_EGRESS:
162 return rif->counter_egress_valid;
163 case MLXSW_SP_RIF_COUNTER_INGRESS:
164 return rif->counter_ingress_valid;
165 }
166 return false;
167}
168
169static void
170mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
171 enum mlxsw_sp_rif_counter_dir dir,
172 bool valid)
173{
174 switch (dir) {
175 case MLXSW_SP_RIF_COUNTER_EGRESS:
176 rif->counter_egress_valid = valid;
177 break;
178 case MLXSW_SP_RIF_COUNTER_INGRESS:
179 rif->counter_ingress_valid = valid;
180 break;
181 }
182}
183
184static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
185 unsigned int counter_index, bool enable,
186 enum mlxsw_sp_rif_counter_dir dir)
187{
188 char ritr_pl[MLXSW_REG_RITR_LEN];
189 bool is_egress = false;
190 int err;
191
192 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
193 is_egress = true;
194 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
195 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
196 if (err)
197 return err;
198
199 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
200 is_egress);
201 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
202}
203
204int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
205 struct mlxsw_sp_rif *rif,
206 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
207{
208 char ricnt_pl[MLXSW_REG_RICNT_LEN];
209 unsigned int *p_counter_index;
210 bool valid;
211 int err;
212
213 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
214 if (!valid)
215 return -EINVAL;
216
217 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
218 if (!p_counter_index)
219 return -EINVAL;
220 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
221 MLXSW_REG_RICNT_OPCODE_NOP);
222 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
223 if (err)
224 return err;
225 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
226 return 0;
227}
228
229static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
230 unsigned int counter_index)
231{
232 char ricnt_pl[MLXSW_REG_RICNT_LEN];
233
234 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
235 MLXSW_REG_RICNT_OPCODE_CLEAR);
236 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
237}
238
239int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
240 struct mlxsw_sp_rif *rif,
241 enum mlxsw_sp_rif_counter_dir dir)
242{
243 unsigned int *p_counter_index;
244 int err;
245
246 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
247 if (!p_counter_index)
248 return -EINVAL;
249 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
250 p_counter_index);
251 if (err)
252 return err;
253
254 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
255 if (err)
256 goto err_counter_clear;
257
258 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
259 *p_counter_index, true, dir);
260 if (err)
261 goto err_counter_edit;
262 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
263 return 0;
264
265err_counter_edit:
266err_counter_clear:
267 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
268 *p_counter_index);
269 return err;
270}
271
272void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
273 struct mlxsw_sp_rif *rif,
274 enum mlxsw_sp_rif_counter_dir dir)
275{
276 unsigned int *p_counter_index;
277
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200278 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
279 return;
280
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200281 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
282 if (WARN_ON(!p_counter_index))
283 return;
284 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
285 *p_counter_index, false, dir);
286 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
287 *p_counter_index);
288 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
289}
290
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200291static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
292{
293 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
294 struct devlink *devlink;
295
296 devlink = priv_to_devlink(mlxsw_sp->core);
297 if (!devlink_dpipe_table_counter_enabled(devlink,
298 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
299 return;
300 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
301}
302
303static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
304{
305 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
306
307 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
308}
309
Ido Schimmel4724ba562017-03-10 08:53:39 +0100310static struct mlxsw_sp_rif *
311mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
312 const struct net_device *dev);
313
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200314#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200315
316struct mlxsw_sp_prefix_usage {
317 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
318};
319
Jiri Pirko53342022016-07-04 08:23:08 +0200320#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
321 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
322
323static bool
324mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
325 struct mlxsw_sp_prefix_usage *prefix_usage2)
326{
327 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
328}
329
Jiri Pirko6b75c482016-07-04 08:23:09 +0200330static bool
331mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
332{
333 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
334
335 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
336}
337
338static void
339mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
340 struct mlxsw_sp_prefix_usage *prefix_usage2)
341{
342 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
343}
344
345static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200346mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
347 unsigned char prefix_len)
348{
349 set_bit(prefix_len, prefix_usage->b);
350}
351
352static void
353mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
354 unsigned char prefix_len)
355{
356 clear_bit(prefix_len, prefix_usage->b);
357}
358
359struct mlxsw_sp_fib_key {
360 unsigned char addr[sizeof(struct in6_addr)];
361 unsigned char prefix_len;
362};
363
Jiri Pirko61c503f2016-07-04 08:23:11 +0200364enum mlxsw_sp_fib_entry_type {
365 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
366 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
367 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
368};
369
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200370struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200371struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200372
Ido Schimmel9aecce12017-02-09 10:28:42 +0100373struct mlxsw_sp_fib_node {
374 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200375 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100376 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100377 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100378 struct mlxsw_sp_fib_key key;
379};
380
Ido Schimmel9aecce12017-02-09 10:28:42 +0100381struct mlxsw_sp_fib_entry {
382 struct list_head list;
383 struct mlxsw_sp_fib_node *fib_node;
384 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200385 struct list_head nexthop_group_node;
386 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200387};
388
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200389struct mlxsw_sp_fib4_entry {
390 struct mlxsw_sp_fib_entry common;
391 u32 tb_id;
392 u32 prio;
393 u8 tos;
394 u8 type;
395};
396
Ido Schimmel428b8512017-08-03 13:28:28 +0200397struct mlxsw_sp_fib6_entry {
398 struct mlxsw_sp_fib_entry common;
399 struct list_head rt6_list;
400 unsigned int nrt6;
401};
402
403struct mlxsw_sp_rt6 {
404 struct list_head list;
405 struct rt6_info *rt;
406};
407
Ido Schimmel9011b672017-05-16 19:38:25 +0200408enum mlxsw_sp_l3proto {
409 MLXSW_SP_L3_PROTO_IPV4,
410 MLXSW_SP_L3_PROTO_IPV6,
411};
412
413struct mlxsw_sp_lpm_tree {
414 u8 id; /* tree ID */
415 unsigned int ref_count;
416 enum mlxsw_sp_l3proto proto;
417 struct mlxsw_sp_prefix_usage prefix_usage;
418};
419
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200420struct mlxsw_sp_fib {
421 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100422 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100423 struct mlxsw_sp_vr *vr;
424 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200425 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
426 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100427 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200428};
429
Ido Schimmel9011b672017-05-16 19:38:25 +0200430struct mlxsw_sp_vr {
431 u16 id; /* virtual router ID */
432 u32 tb_id; /* kernel fib table id */
433 unsigned int rif_count;
434 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200435 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200436};
437
Ido Schimmel9aecce12017-02-09 10:28:42 +0100438static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200439
Ido Schimmel76610eb2017-03-10 08:53:41 +0100440static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
441 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200442{
443 struct mlxsw_sp_fib *fib;
444 int err;
445
446 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
447 if (!fib)
448 return ERR_PTR(-ENOMEM);
449 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
450 if (err)
451 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100452 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100453 fib->proto = proto;
454 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200455 return fib;
456
457err_rhashtable_init:
458 kfree(fib);
459 return ERR_PTR(err);
460}
461
462static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
463{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100464 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100465 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200466 rhashtable_destroy(&fib->ht);
467 kfree(fib);
468}
469
Jiri Pirko53342022016-07-04 08:23:08 +0200470static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100471mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200472{
473 static struct mlxsw_sp_lpm_tree *lpm_tree;
474 int i;
475
Ido Schimmel9011b672017-05-16 19:38:25 +0200476 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
477 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100478 if (lpm_tree->ref_count == 0)
479 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200480 }
481 return NULL;
482}
483
484static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
485 struct mlxsw_sp_lpm_tree *lpm_tree)
486{
487 char ralta_pl[MLXSW_REG_RALTA_LEN];
488
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200489 mlxsw_reg_ralta_pack(ralta_pl, true,
490 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
491 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200492 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
493}
494
Ido Schimmelcc702672017-08-14 10:54:03 +0200495static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
496 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200497{
498 char ralta_pl[MLXSW_REG_RALTA_LEN];
499
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200500 mlxsw_reg_ralta_pack(ralta_pl, false,
501 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
502 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200503 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200504}
505
506static int
507mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
508 struct mlxsw_sp_prefix_usage *prefix_usage,
509 struct mlxsw_sp_lpm_tree *lpm_tree)
510{
511 char ralst_pl[MLXSW_REG_RALST_LEN];
512 u8 root_bin = 0;
513 u8 prefix;
514 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
515
516 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
517 root_bin = prefix;
518
519 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
520 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
521 if (prefix == 0)
522 continue;
523 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
524 MLXSW_REG_RALST_BIN_NO_CHILD);
525 last_prefix = prefix;
526 }
527 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
528}
529
530static struct mlxsw_sp_lpm_tree *
531mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
532 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100533 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200534{
535 struct mlxsw_sp_lpm_tree *lpm_tree;
536 int err;
537
Ido Schimmel382dbb42017-03-10 08:53:40 +0100538 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200539 if (!lpm_tree)
540 return ERR_PTR(-EBUSY);
541 lpm_tree->proto = proto;
542 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
543 if (err)
544 return ERR_PTR(err);
545
546 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
547 lpm_tree);
548 if (err)
549 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200550 memcpy(&lpm_tree->prefix_usage, prefix_usage,
551 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200552 return lpm_tree;
553
554err_left_struct_set:
555 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
556 return ERR_PTR(err);
557}
558
Ido Schimmelcc702672017-08-14 10:54:03 +0200559static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
560 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200561{
Ido Schimmelcc702672017-08-14 10:54:03 +0200562 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200563}
564
565static struct mlxsw_sp_lpm_tree *
566mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
567 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100568 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200569{
570 struct mlxsw_sp_lpm_tree *lpm_tree;
571 int i;
572
Ido Schimmel9011b672017-05-16 19:38:25 +0200573 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
574 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200575 if (lpm_tree->ref_count != 0 &&
576 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200577 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
578 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200579 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200580 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200581 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
582}
Jiri Pirko53342022016-07-04 08:23:08 +0200583
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200584static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
585{
Jiri Pirko53342022016-07-04 08:23:08 +0200586 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200587}
588
Ido Schimmelcc702672017-08-14 10:54:03 +0200589static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
590 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200591{
592 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200593 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200594}
595
Ido Schimmeld7a60302017-06-08 08:47:43 +0200596#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100597
598static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200599{
600 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100601 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200602 int i;
603
Ido Schimmel8494ab02017-03-24 08:02:47 +0100604 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
605 return -EIO;
606
607 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200608 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
609 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100610 sizeof(struct mlxsw_sp_lpm_tree),
611 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200612 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100613 return -ENOMEM;
614
Ido Schimmel9011b672017-05-16 19:38:25 +0200615 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
616 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200617 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
618 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100619
620 return 0;
621}
622
623static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
624{
Ido Schimmel9011b672017-05-16 19:38:25 +0200625 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200626}
627
Ido Schimmel76610eb2017-03-10 08:53:41 +0100628static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
629{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200630 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100631}
632
Jiri Pirko6b75c482016-07-04 08:23:09 +0200633static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
634{
635 struct mlxsw_sp_vr *vr;
636 int i;
637
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200638 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200639 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100640 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200641 return vr;
642 }
643 return NULL;
644}
645
646static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200647 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200648{
649 char raltb_pl[MLXSW_REG_RALTB_LEN];
650
Ido Schimmel76610eb2017-03-10 08:53:41 +0100651 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
652 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200653 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200654 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
655}
656
657static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100658 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200659{
660 char raltb_pl[MLXSW_REG_RALTB_LEN];
661
662 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100663 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
664 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200665 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
666}
667
668static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
669{
670 /* For our purpose, squash main and local table into one */
671 if (tb_id == RT_TABLE_LOCAL)
672 tb_id = RT_TABLE_MAIN;
673 return tb_id;
674}
675
676static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100677 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200678{
679 struct mlxsw_sp_vr *vr;
680 int i;
681
682 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200683
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200684 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200685 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100686 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200687 return vr;
688 }
689 return NULL;
690}
691
Ido Schimmel76610eb2017-03-10 08:53:41 +0100692static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
693 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200694{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100695 switch (proto) {
696 case MLXSW_SP_L3_PROTO_IPV4:
697 return vr->fib4;
698 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200699 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100700 }
701 return NULL;
702}
703
704static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
705 u32 tb_id)
706{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200707 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200708 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200709
710 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
711 if (!vr)
712 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100713 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
714 if (IS_ERR(vr->fib4))
715 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200716 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
717 if (IS_ERR(vr->fib6)) {
718 err = PTR_ERR(vr->fib6);
719 goto err_fib6_create;
720 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200721 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200722 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200723
724err_fib6_create:
725 mlxsw_sp_fib_destroy(vr->fib4);
726 vr->fib4 = NULL;
727 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200728}
729
Ido Schimmel76610eb2017-03-10 08:53:41 +0100730static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200731{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200732 mlxsw_sp_fib_destroy(vr->fib6);
733 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100734 mlxsw_sp_fib_destroy(vr->fib4);
735 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200736}
737
Ido Schimmel76610eb2017-03-10 08:53:41 +0100738static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200739{
740 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200741
742 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100743 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
744 if (!vr)
745 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746 return vr;
747}
748
Ido Schimmel76610eb2017-03-10 08:53:41 +0100749static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200750{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200751 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
752 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100753 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200754}
755
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200756static bool
757mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
758 enum mlxsw_sp_l3proto proto, u8 tree_id)
759{
760 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
761
762 if (!mlxsw_sp_vr_is_used(vr))
763 return false;
764 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
765 return true;
766 return false;
767}
768
769static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
770 struct mlxsw_sp_fib *fib,
771 struct mlxsw_sp_lpm_tree *new_tree)
772{
773 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
774 int err;
775
776 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
777 if (err)
778 return err;
779 fib->lpm_tree = new_tree;
780 mlxsw_sp_lpm_tree_hold(new_tree);
781 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
782 return 0;
783}
784
785static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
786 struct mlxsw_sp_fib *fib,
787 struct mlxsw_sp_lpm_tree *new_tree)
788{
789 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
790 enum mlxsw_sp_l3proto proto = fib->proto;
791 u8 old_id, new_id = new_tree->id;
792 struct mlxsw_sp_vr *vr;
793 int i, err;
794
795 if (!old_tree)
796 goto no_replace;
797 old_id = old_tree->id;
798
799 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
800 vr = &mlxsw_sp->router->vrs[i];
801 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
802 continue;
803 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
804 mlxsw_sp_vr_fib(vr, proto),
805 new_tree);
806 if (err)
807 goto err_tree_replace;
808 }
809
810 return 0;
811
812err_tree_replace:
813 for (i--; i >= 0; i--) {
814 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
815 continue;
816 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
817 mlxsw_sp_vr_fib(vr, proto),
818 old_tree);
819 }
820 return err;
821
822no_replace:
823 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
824 if (err)
825 return err;
826 fib->lpm_tree = new_tree;
827 mlxsw_sp_lpm_tree_hold(new_tree);
828 return 0;
829}
830
831static void
832mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
833 enum mlxsw_sp_l3proto proto,
834 struct mlxsw_sp_prefix_usage *req_prefix_usage)
835{
836 int i;
837
838 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
839 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
840 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
841 unsigned char prefix;
842
843 if (!mlxsw_sp_vr_is_used(vr))
844 continue;
845 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
846 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
847 }
848}
849
Nogah Frankel9497c042016-09-20 11:16:54 +0200850static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200851{
852 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200853 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200854 int i;
855
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200856 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200857 return -EIO;
858
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200859 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200860 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
861 GFP_KERNEL);
862 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200863 return -ENOMEM;
864
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200865 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200866 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200867 vr->id = i;
868 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200869
870 return 0;
871}
872
Ido Schimmelac571de2016-11-14 11:26:32 +0100873static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
874
Nogah Frankel9497c042016-09-20 11:16:54 +0200875static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
876{
Ido Schimmel30572242016-12-03 16:45:01 +0100877 /* At this stage we're guaranteed not to have new incoming
878 * FIB notifications and the work queue is free from FIBs
879 * sitting on top of mlxsw netdevs. However, we can still
880 * have other FIBs queued. Flush the queue before flushing
881 * the device's tables. No need for locks, as we're the only
882 * writer.
883 */
884 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100885 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200886 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200887}
888
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200889struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100890 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200891};
892
893struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100894 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200895 struct rhash_head ht_node;
896 struct mlxsw_sp_neigh_key key;
897 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100898 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200899 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200900 struct list_head nexthop_list; /* list of nexthops using
901 * this neigh entry
902 */
Yotam Gigib2157142016-07-05 11:27:51 +0200903 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200904 unsigned int counter_index;
905 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200906};
907
908static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
909 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
910 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
911 .key_len = sizeof(struct mlxsw_sp_neigh_key),
912};
913
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +0200914struct mlxsw_sp_neigh_entry *
915mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
916 struct mlxsw_sp_neigh_entry *neigh_entry)
917{
918 if (!neigh_entry) {
919 if (list_empty(&rif->neigh_list))
920 return NULL;
921 else
922 return list_first_entry(&rif->neigh_list,
923 typeof(*neigh_entry),
924 rif_list_node);
925 }
926 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
927 return NULL;
928 return list_next_entry(neigh_entry, rif_list_node);
929}
930
931int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
932{
933 return neigh_entry->key.n->tbl->family;
934}
935
936unsigned char *
937mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
938{
939 return neigh_entry->ha;
940}
941
942u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
943{
944 struct neighbour *n;
945
946 n = neigh_entry->key.n;
947 return ntohl(*((__be32 *) n->primary_key));
948}
949
Arkadi Sharshevsky02507682017-08-31 17:59:15 +0200950struct in6_addr *
951mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
952{
953 struct neighbour *n;
954
955 n = neigh_entry->key.n;
956 return (struct in6_addr *) &n->primary_key;
957}
958
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200959int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
960 struct mlxsw_sp_neigh_entry *neigh_entry,
961 u64 *p_counter)
962{
963 if (!neigh_entry->counter_valid)
964 return -EINVAL;
965
966 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
967 p_counter, NULL);
968}
969
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100970static struct mlxsw_sp_neigh_entry *
971mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
972 u16 rif)
973{
974 struct mlxsw_sp_neigh_entry *neigh_entry;
975
976 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
977 if (!neigh_entry)
978 return NULL;
979
980 neigh_entry->key.n = n;
981 neigh_entry->rif = rif;
982 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
983
984 return neigh_entry;
985}
986
987static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
988{
989 kfree(neigh_entry);
990}
991
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200992static int
993mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
994 struct mlxsw_sp_neigh_entry *neigh_entry)
995{
Ido Schimmel9011b672017-05-16 19:38:25 +0200996 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200997 &neigh_entry->ht_node,
998 mlxsw_sp_neigh_ht_params);
999}
1000
1001static void
1002mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1003 struct mlxsw_sp_neigh_entry *neigh_entry)
1004{
Ido Schimmel9011b672017-05-16 19:38:25 +02001005 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001006 &neigh_entry->ht_node,
1007 mlxsw_sp_neigh_ht_params);
1008}
1009
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001010static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001011mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1012 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001013{
1014 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001015 const char *table_name;
1016
1017 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1018 case AF_INET:
1019 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1020 break;
1021 case AF_INET6:
1022 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1023 break;
1024 default:
1025 WARN_ON(1);
1026 return false;
1027 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001028
1029 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001030 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001031}
1032
1033static void
1034mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1035 struct mlxsw_sp_neigh_entry *neigh_entry)
1036{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001037 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001038 return;
1039
1040 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1041 return;
1042
1043 neigh_entry->counter_valid = true;
1044}
1045
1046static void
1047mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1048 struct mlxsw_sp_neigh_entry *neigh_entry)
1049{
1050 if (!neigh_entry->counter_valid)
1051 return;
1052 mlxsw_sp_flow_counter_free(mlxsw_sp,
1053 neigh_entry->counter_index);
1054 neigh_entry->counter_valid = false;
1055}
1056
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001057static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001058mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001059{
1060 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001061 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001062 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001063
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001064 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1065 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001066 return ERR_PTR(-EINVAL);
1067
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001068 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001069 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001070 return ERR_PTR(-ENOMEM);
1071
1072 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1073 if (err)
1074 goto err_neigh_entry_insert;
1075
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001076 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001077 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001078
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001079 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001080
1081err_neigh_entry_insert:
1082 mlxsw_sp_neigh_entry_free(neigh_entry);
1083 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001084}
1085
1086static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001087mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1088 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001089{
Ido Schimmel9665b742017-02-08 11:16:42 +01001090 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001091 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001092 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1093 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001094}
1095
1096static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001097mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001098{
Jiri Pirko33b13412016-11-10 12:31:04 +01001099 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001100
Jiri Pirko33b13412016-11-10 12:31:04 +01001101 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001102 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001103 &key, mlxsw_sp_neigh_ht_params);
1104}
1105
Yotam Gigic723c7352016-07-05 11:27:43 +02001106static void
1107mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1108{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001109 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001110
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001111#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001112 interval = min_t(unsigned long,
1113 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1114 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001115#else
1116 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1117#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001118 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001119}
1120
1121static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1122 char *rauhtd_pl,
1123 int ent_index)
1124{
1125 struct net_device *dev;
1126 struct neighbour *n;
1127 __be32 dipn;
1128 u32 dip;
1129 u16 rif;
1130
1131 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1132
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001133 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001134 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1135 return;
1136 }
1137
1138 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001139 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001140 n = neigh_lookup(&arp_tbl, &dipn, dev);
1141 if (!n) {
1142 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1143 &dip);
1144 return;
1145 }
1146
1147 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1148 neigh_event_send(n, NULL);
1149 neigh_release(n);
1150}
1151
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001152#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001153static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1154 char *rauhtd_pl,
1155 int rec_index)
1156{
1157 struct net_device *dev;
1158 struct neighbour *n;
1159 struct in6_addr dip;
1160 u16 rif;
1161
1162 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1163 (char *) &dip);
1164
1165 if (!mlxsw_sp->router->rifs[rif]) {
1166 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1167 return;
1168 }
1169
1170 dev = mlxsw_sp->router->rifs[rif]->dev;
1171 n = neigh_lookup(&nd_tbl, &dip, dev);
1172 if (!n) {
1173 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1174 &dip);
1175 return;
1176 }
1177
1178 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1179 neigh_event_send(n, NULL);
1180 neigh_release(n);
1181}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001182#else
1183static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1184 char *rauhtd_pl,
1185 int rec_index)
1186{
1187}
1188#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001189
Yotam Gigic723c7352016-07-05 11:27:43 +02001190static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1191 char *rauhtd_pl,
1192 int rec_index)
1193{
1194 u8 num_entries;
1195 int i;
1196
1197 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1198 rec_index);
1199 /* Hardware starts counting at 0, so add 1. */
1200 num_entries++;
1201
1202 /* Each record consists of several neighbour entries. */
1203 for (i = 0; i < num_entries; i++) {
1204 int ent_index;
1205
1206 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1207 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1208 ent_index);
1209 }
1210
1211}
1212
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001213static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1214 char *rauhtd_pl,
1215 int rec_index)
1216{
1217 /* One record contains one entry. */
1218 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1219 rec_index);
1220}
1221
Yotam Gigic723c7352016-07-05 11:27:43 +02001222static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1223 char *rauhtd_pl, int rec_index)
1224{
1225 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1226 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1227 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1228 rec_index);
1229 break;
1230 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001231 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1232 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001233 break;
1234 }
1235}
1236
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001237static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1238{
1239 u8 num_rec, last_rec_index, num_entries;
1240
1241 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1242 last_rec_index = num_rec - 1;
1243
1244 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1245 return false;
1246 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1247 MLXSW_REG_RAUHTD_TYPE_IPV6)
1248 return true;
1249
1250 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1251 last_rec_index);
1252 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1253 return true;
1254 return false;
1255}
1256
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001257static int
1258__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1259 char *rauhtd_pl,
1260 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001261{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001262 int i, num_rec;
1263 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001264
1265 /* Make sure the neighbour's netdev isn't removed in the
1266 * process.
1267 */
1268 rtnl_lock();
1269 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001270 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001271 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1272 rauhtd_pl);
1273 if (err) {
1274 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1275 break;
1276 }
1277 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1278 for (i = 0; i < num_rec; i++)
1279 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1280 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001281 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001282 rtnl_unlock();
1283
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001284 return err;
1285}
1286
1287static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1288{
1289 enum mlxsw_reg_rauhtd_type type;
1290 char *rauhtd_pl;
1291 int err;
1292
1293 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1294 if (!rauhtd_pl)
1295 return -ENOMEM;
1296
1297 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1298 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1299 if (err)
1300 goto out;
1301
1302 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1303 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1304out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001305 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001306 return err;
1307}
1308
1309static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1310{
1311 struct mlxsw_sp_neigh_entry *neigh_entry;
1312
1313 /* Take RTNL mutex here to prevent lists from changes */
1314 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001315 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001316 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001317 /* If this neigh have nexthops, make the kernel think this neigh
1318 * is active regardless of the traffic.
1319 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001320 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001321 rtnl_unlock();
1322}
1323
1324static void
1325mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1326{
Ido Schimmel9011b672017-05-16 19:38:25 +02001327 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001328
Ido Schimmel9011b672017-05-16 19:38:25 +02001329 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001330 msecs_to_jiffies(interval));
1331}
1332
1333static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1334{
Ido Schimmel9011b672017-05-16 19:38:25 +02001335 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001336 int err;
1337
Ido Schimmel9011b672017-05-16 19:38:25 +02001338 router = container_of(work, struct mlxsw_sp_router,
1339 neighs_update.dw.work);
1340 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001341 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001342 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001343
Ido Schimmel9011b672017-05-16 19:38:25 +02001344 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001345
Ido Schimmel9011b672017-05-16 19:38:25 +02001346 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001347}
1348
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001349static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1350{
1351 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001352 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001353
Ido Schimmel9011b672017-05-16 19:38:25 +02001354 router = container_of(work, struct mlxsw_sp_router,
1355 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001356 /* Iterate over nexthop neighbours, find those who are unresolved and
1357 * send arp on them. This solves the chicken-egg problem when
1358 * the nexthop wouldn't get offloaded until the neighbor is resolved
1359 * but it wouldn't get resolved ever in case traffic is flowing in HW
1360 * using different nexthop.
1361 *
1362 * Take RTNL mutex here to prevent lists from changes.
1363 */
1364 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001365 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001366 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001367 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001368 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001369 rtnl_unlock();
1370
Ido Schimmel9011b672017-05-16 19:38:25 +02001371 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001372 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1373}
1374
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001375static void
1376mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1377 struct mlxsw_sp_neigh_entry *neigh_entry,
1378 bool removing);
1379
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001380static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001381{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001382 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1383 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1384}
1385
1386static void
1387mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1388 struct mlxsw_sp_neigh_entry *neigh_entry,
1389 enum mlxsw_reg_rauht_op op)
1390{
Jiri Pirko33b13412016-11-10 12:31:04 +01001391 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001392 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001393 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001394
1395 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1396 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001397 if (neigh_entry->counter_valid)
1398 mlxsw_reg_rauht_pack_counter(rauht_pl,
1399 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001400 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1401}
1402
1403static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001404mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1405 struct mlxsw_sp_neigh_entry *neigh_entry,
1406 enum mlxsw_reg_rauht_op op)
1407{
1408 struct neighbour *n = neigh_entry->key.n;
1409 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1410 const char *dip = n->primary_key;
1411
1412 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1413 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001414 if (neigh_entry->counter_valid)
1415 mlxsw_reg_rauht_pack_counter(rauht_pl,
1416 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001417 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1418}
1419
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001420bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001421{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001422 struct neighbour *n = neigh_entry->key.n;
1423
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001424 /* Packets with a link-local destination address are trapped
1425 * after LPM lookup and never reach the neighbour table, so
1426 * there is no need to program such neighbours to the device.
1427 */
1428 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1429 IPV6_ADDR_LINKLOCAL)
1430 return true;
1431 return false;
1432}
1433
1434static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001435mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1436 struct mlxsw_sp_neigh_entry *neigh_entry,
1437 bool adding)
1438{
1439 if (!adding && !neigh_entry->connected)
1440 return;
1441 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001442 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001443 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1444 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001445 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001446 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001447 return;
1448 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1449 mlxsw_sp_rauht_op(adding));
1450 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001451 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001452 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001453}
1454
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001455void
1456mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1457 struct mlxsw_sp_neigh_entry *neigh_entry,
1458 bool adding)
1459{
1460 if (adding)
1461 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1462 else
1463 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1464 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1465}
1466
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001467struct mlxsw_sp_neigh_event_work {
1468 struct work_struct work;
1469 struct mlxsw_sp *mlxsw_sp;
1470 struct neighbour *n;
1471};
1472
1473static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1474{
1475 struct mlxsw_sp_neigh_event_work *neigh_work =
1476 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1477 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1478 struct mlxsw_sp_neigh_entry *neigh_entry;
1479 struct neighbour *n = neigh_work->n;
1480 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001481 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001482 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001483
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001484 /* If these parameters are changed after we release the lock,
1485 * then we are guaranteed to receive another event letting us
1486 * know about it.
1487 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001488 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001489 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001490 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001491 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001492 read_unlock_bh(&n->lock);
1493
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001494 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001495 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001496 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1497 if (!entry_connected && !neigh_entry)
1498 goto out;
1499 if (!neigh_entry) {
1500 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1501 if (IS_ERR(neigh_entry))
1502 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001503 }
1504
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001505 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1506 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1507 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1508
1509 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1510 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1511
1512out:
1513 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001514 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001515 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001516}
1517
Jiri Pirkoe7322632016-09-01 10:37:43 +02001518int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1519 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001520{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001521 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001522 struct mlxsw_sp_port *mlxsw_sp_port;
1523 struct mlxsw_sp *mlxsw_sp;
1524 unsigned long interval;
1525 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001526 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001527
1528 switch (event) {
1529 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1530 p = ptr;
1531
1532 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001533 if (!p->dev || (p->tbl->family != AF_INET &&
1534 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001535 return NOTIFY_DONE;
1536
1537 /* We are in atomic context and can't take RTNL mutex,
1538 * so use RCU variant to walk the device chain.
1539 */
1540 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1541 if (!mlxsw_sp_port)
1542 return NOTIFY_DONE;
1543
1544 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1545 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001546 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001547
1548 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1549 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001550 case NETEVENT_NEIGH_UPDATE:
1551 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001552
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001553 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001554 return NOTIFY_DONE;
1555
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001556 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001557 if (!mlxsw_sp_port)
1558 return NOTIFY_DONE;
1559
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001560 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1561 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001562 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001563 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001564 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001565
1566 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1567 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1568 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001569
1570 /* Take a reference to ensure the neighbour won't be
1571 * destructed until we drop the reference in delayed
1572 * work.
1573 */
1574 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001575 mlxsw_core_schedule_work(&neigh_work->work);
1576 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001577 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001578 }
1579
1580 return NOTIFY_DONE;
1581}
1582
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001583static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1584{
Yotam Gigic723c7352016-07-05 11:27:43 +02001585 int err;
1586
Ido Schimmel9011b672017-05-16 19:38:25 +02001587 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001588 &mlxsw_sp_neigh_ht_params);
1589 if (err)
1590 return err;
1591
1592 /* Initialize the polling interval according to the default
1593 * table.
1594 */
1595 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1596
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001597 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001598 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001599 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001600 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001601 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001602 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1603 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001604 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001605}
1606
1607static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1608{
Ido Schimmel9011b672017-05-16 19:38:25 +02001609 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1610 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1611 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001612}
1613
Ido Schimmel9665b742017-02-08 11:16:42 +01001614static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001615 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001616{
1617 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1618
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001619 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001620 rif_list_node) {
1621 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001622 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001623 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001624}
1625
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001626struct mlxsw_sp_nexthop_key {
1627 struct fib_nh *fib_nh;
1628};
1629
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001630struct mlxsw_sp_nexthop {
1631 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001632 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001633 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1634 * this belongs to
1635 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001636 struct rhash_head ht_node;
1637 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001638 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001639 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001640 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001641 u8 should_offload:1, /* set indicates this neigh is connected and
1642 * should be put to KVD linear area of this group.
1643 */
1644 offloaded:1, /* set in case the neigh is actually put into
1645 * KVD linear area of this group.
1646 */
1647 update:1; /* set indicates that MAC of this neigh should be
1648 * updated in HW
1649 */
1650 struct mlxsw_sp_neigh_entry *neigh_entry;
1651};
1652
1653struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001654 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001655 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001656 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001657 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001658 u8 adj_index_valid:1,
1659 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001660 u32 adj_index;
1661 u16 ecmp_size;
1662 u16 count;
1663 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001664#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001665};
1666
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001667static struct fib_info *
1668mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1669{
1670 return nh_grp->priv;
1671}
1672
1673struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001674 enum mlxsw_sp_l3proto proto;
1675 union {
1676 struct fib_info *fi;
1677 struct mlxsw_sp_fib6_entry *fib6_entry;
1678 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001679};
1680
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001681static bool
1682mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
1683 const struct in6_addr *gw, int ifindex)
1684{
1685 int i;
1686
1687 for (i = 0; i < nh_grp->count; i++) {
1688 const struct mlxsw_sp_nexthop *nh;
1689
1690 nh = &nh_grp->nexthops[i];
1691 if (nh->ifindex == ifindex &&
1692 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
1693 return true;
1694 }
1695
1696 return false;
1697}
1698
1699static bool
1700mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
1701 const struct mlxsw_sp_fib6_entry *fib6_entry)
1702{
1703 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1704
1705 if (nh_grp->count != fib6_entry->nrt6)
1706 return false;
1707
1708 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1709 struct in6_addr *gw;
1710 int ifindex;
1711
1712 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
1713 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
1714 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
1715 return false;
1716 }
1717
1718 return true;
1719}
1720
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001721static int
1722mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1723{
1724 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1725 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1726
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001727 switch (cmp_arg->proto) {
1728 case MLXSW_SP_L3_PROTO_IPV4:
1729 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1730 case MLXSW_SP_L3_PROTO_IPV6:
1731 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
1732 cmp_arg->fib6_entry);
1733 default:
1734 WARN_ON(1);
1735 return 1;
1736 }
1737}
1738
1739static int
1740mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
1741{
1742 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001743}
1744
1745static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1746{
1747 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001748 const struct mlxsw_sp_nexthop *nh;
1749 struct fib_info *fi;
1750 unsigned int val;
1751 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001752
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001753 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
1754 case AF_INET:
1755 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1756 return jhash(&fi, sizeof(fi), seed);
1757 case AF_INET6:
1758 val = nh_grp->count;
1759 for (i = 0; i < nh_grp->count; i++) {
1760 nh = &nh_grp->nexthops[i];
1761 val ^= nh->ifindex;
1762 }
1763 return jhash(&val, sizeof(val), seed);
1764 default:
1765 WARN_ON(1);
1766 return 0;
1767 }
1768}
1769
1770static u32
1771mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
1772{
1773 unsigned int val = fib6_entry->nrt6;
1774 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1775 struct net_device *dev;
1776
1777 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1778 dev = mlxsw_sp_rt6->rt->dst.dev;
1779 val ^= dev->ifindex;
1780 }
1781
1782 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001783}
1784
1785static u32
1786mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1787{
1788 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1789
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001790 switch (cmp_arg->proto) {
1791 case MLXSW_SP_L3_PROTO_IPV4:
1792 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1793 case MLXSW_SP_L3_PROTO_IPV6:
1794 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
1795 default:
1796 WARN_ON(1);
1797 return 0;
1798 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001799}
1800
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001801static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001802 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001803 .hashfn = mlxsw_sp_nexthop_group_hash,
1804 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1805 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001806};
1807
1808static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1809 struct mlxsw_sp_nexthop_group *nh_grp)
1810{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001811 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1812 !nh_grp->gateway)
1813 return 0;
1814
Ido Schimmel9011b672017-05-16 19:38:25 +02001815 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001816 &nh_grp->ht_node,
1817 mlxsw_sp_nexthop_group_ht_params);
1818}
1819
1820static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1821 struct mlxsw_sp_nexthop_group *nh_grp)
1822{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001823 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1824 !nh_grp->gateway)
1825 return;
1826
Ido Schimmel9011b672017-05-16 19:38:25 +02001827 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001828 &nh_grp->ht_node,
1829 mlxsw_sp_nexthop_group_ht_params);
1830}
1831
1832static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001833mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1834 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001835{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001836 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1837
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001838 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001839 cmp_arg.fi = fi;
1840 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1841 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001842 mlxsw_sp_nexthop_group_ht_params);
1843}
1844
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001845static struct mlxsw_sp_nexthop_group *
1846mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
1847 struct mlxsw_sp_fib6_entry *fib6_entry)
1848{
1849 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1850
1851 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
1852 cmp_arg.fib6_entry = fib6_entry;
1853 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1854 &cmp_arg,
1855 mlxsw_sp_nexthop_group_ht_params);
1856}
1857
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001858static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1859 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1860 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1861 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1862};
1863
1864static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1865 struct mlxsw_sp_nexthop *nh)
1866{
Ido Schimmel9011b672017-05-16 19:38:25 +02001867 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001868 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1869}
1870
1871static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1872 struct mlxsw_sp_nexthop *nh)
1873{
Ido Schimmel9011b672017-05-16 19:38:25 +02001874 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001875 mlxsw_sp_nexthop_ht_params);
1876}
1877
Ido Schimmelad178c82017-02-08 11:16:40 +01001878static struct mlxsw_sp_nexthop *
1879mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1880 struct mlxsw_sp_nexthop_key key)
1881{
Ido Schimmel9011b672017-05-16 19:38:25 +02001882 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001883 mlxsw_sp_nexthop_ht_params);
1884}
1885
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001886static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001887 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001888 u32 adj_index, u16 ecmp_size,
1889 u32 new_adj_index,
1890 u16 new_ecmp_size)
1891{
1892 char raleu_pl[MLXSW_REG_RALEU_LEN];
1893
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001894 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001895 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1896 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001897 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001898 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1899}
1900
1901static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1902 struct mlxsw_sp_nexthop_group *nh_grp,
1903 u32 old_adj_index, u16 old_ecmp_size)
1904{
1905 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001906 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001907 int err;
1908
1909 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001910 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001911 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001912 fib = fib_entry->fib_node->fib;
1913 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001914 old_adj_index,
1915 old_ecmp_size,
1916 nh_grp->adj_index,
1917 nh_grp->ecmp_size);
1918 if (err)
1919 return err;
1920 }
1921 return 0;
1922}
1923
1924static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1925 struct mlxsw_sp_nexthop *nh)
1926{
1927 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1928 char ratr_pl[MLXSW_REG_RATR_LEN];
1929
1930 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1931 true, adj_index, neigh_entry->rif);
1932 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1933 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1934}
1935
1936static int
1937mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001938 struct mlxsw_sp_nexthop_group *nh_grp,
1939 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001940{
1941 u32 adj_index = nh_grp->adj_index; /* base */
1942 struct mlxsw_sp_nexthop *nh;
1943 int i;
1944 int err;
1945
1946 for (i = 0; i < nh_grp->count; i++) {
1947 nh = &nh_grp->nexthops[i];
1948
1949 if (!nh->should_offload) {
1950 nh->offloaded = 0;
1951 continue;
1952 }
1953
Ido Schimmela59b7e02017-01-23 11:11:42 +01001954 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001955 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1956 adj_index, nh);
1957 if (err)
1958 return err;
1959 nh->update = 0;
1960 nh->offloaded = 1;
1961 }
1962 adj_index++;
1963 }
1964 return 0;
1965}
1966
1967static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1968 struct mlxsw_sp_fib_entry *fib_entry);
1969
Ido Schimmel1819ae32017-07-21 18:04:28 +02001970static bool
1971mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1972 const struct mlxsw_sp_fib_entry *fib_entry);
1973
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001974static int
1975mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1976 struct mlxsw_sp_nexthop_group *nh_grp)
1977{
1978 struct mlxsw_sp_fib_entry *fib_entry;
1979 int err;
1980
1981 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001982 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1983 fib_entry))
1984 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001985 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1986 if (err)
1987 return err;
1988 }
1989 return 0;
1990}
1991
1992static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001993mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1994 enum mlxsw_reg_ralue_op op, int err);
1995
1996static void
1997mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1998{
1999 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2000 struct mlxsw_sp_fib_entry *fib_entry;
2001
2002 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2003 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2004 fib_entry))
2005 continue;
2006 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2007 }
2008}
2009
2010static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002011mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2012 struct mlxsw_sp_nexthop_group *nh_grp)
2013{
2014 struct mlxsw_sp_nexthop *nh;
2015 bool offload_change = false;
2016 u32 adj_index;
2017 u16 ecmp_size = 0;
2018 bool old_adj_index_valid;
2019 u32 old_adj_index;
2020 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002021 int i;
2022 int err;
2023
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002024 if (!nh_grp->gateway) {
2025 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2026 return;
2027 }
2028
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002029 for (i = 0; i < nh_grp->count; i++) {
2030 nh = &nh_grp->nexthops[i];
2031
Petr Machata56b8a9e2017-07-31 09:27:29 +02002032 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002033 offload_change = true;
2034 if (nh->should_offload)
2035 nh->update = 1;
2036 }
2037 if (nh->should_offload)
2038 ecmp_size++;
2039 }
2040 if (!offload_change) {
2041 /* Nothing was added or removed, so no need to reallocate. Just
2042 * update MAC on existing adjacency indexes.
2043 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01002044 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
2045 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002046 if (err) {
2047 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2048 goto set_trap;
2049 }
2050 return;
2051 }
2052 if (!ecmp_size)
2053 /* No neigh of this group is connected so we just set
2054 * the trap and let everthing flow through kernel.
2055 */
2056 goto set_trap;
2057
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002058 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2059 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002060 /* We ran out of KVD linear space, just set the
2061 * trap and let everything flow through kernel.
2062 */
2063 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2064 goto set_trap;
2065 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002066 old_adj_index_valid = nh_grp->adj_index_valid;
2067 old_adj_index = nh_grp->adj_index;
2068 old_ecmp_size = nh_grp->ecmp_size;
2069 nh_grp->adj_index_valid = 1;
2070 nh_grp->adj_index = adj_index;
2071 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01002072 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002073 if (err) {
2074 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2075 goto set_trap;
2076 }
2077
2078 if (!old_adj_index_valid) {
2079 /* The trap was set for fib entries, so we have to call
2080 * fib entry update to unset it and use adjacency index.
2081 */
2082 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2083 if (err) {
2084 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2085 goto set_trap;
2086 }
2087 return;
2088 }
2089
2090 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2091 old_adj_index, old_ecmp_size);
2092 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2093 if (err) {
2094 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2095 goto set_trap;
2096 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002097
2098 /* Offload state within the group changed, so update the flags. */
2099 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2100
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002101 return;
2102
2103set_trap:
2104 old_adj_index_valid = nh_grp->adj_index_valid;
2105 nh_grp->adj_index_valid = 0;
2106 for (i = 0; i < nh_grp->count; i++) {
2107 nh = &nh_grp->nexthops[i];
2108 nh->offloaded = 0;
2109 }
2110 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2111 if (err)
2112 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2113 if (old_adj_index_valid)
2114 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2115}
2116
2117static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2118 bool removing)
2119{
Petr Machata213666a2017-07-31 09:27:30 +02002120 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002121 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002122 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002123 nh->should_offload = 0;
2124 nh->update = 1;
2125}
2126
2127static void
2128mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2129 struct mlxsw_sp_neigh_entry *neigh_entry,
2130 bool removing)
2131{
2132 struct mlxsw_sp_nexthop *nh;
2133
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002134 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2135 neigh_list_node) {
2136 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2137 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2138 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002139}
2140
Ido Schimmel9665b742017-02-08 11:16:42 +01002141static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002142 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002143{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002144 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002145 return;
2146
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002147 nh->rif = rif;
2148 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002149}
2150
2151static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2152{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002153 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002154 return;
2155
2156 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002157 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002158}
2159
Ido Schimmela8c97012017-02-08 11:16:35 +01002160static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2161 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002162{
2163 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002164 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002165 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002166 int err;
2167
Ido Schimmelad178c82017-02-08 11:16:40 +01002168 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002169 return 0;
2170
Jiri Pirko33b13412016-11-10 12:31:04 +01002171 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002172 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002173 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002174 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002175 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002176 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002177 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002178 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2179 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002180 if (IS_ERR(n))
2181 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002182 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002183 }
2184 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2185 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002186 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2187 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002188 err = -EINVAL;
2189 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002190 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002191 }
Yotam Gigib2157142016-07-05 11:27:51 +02002192
2193 /* If that is the first nexthop connected to that neigh, add to
2194 * nexthop_neighs_list
2195 */
2196 if (list_empty(&neigh_entry->nexthop_list))
2197 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002198 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002199
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002200 nh->neigh_entry = neigh_entry;
2201 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2202 read_lock_bh(&n->lock);
2203 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002204 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002205 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002206 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002207
2208 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002209
2210err_neigh_entry_create:
2211 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002212 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002213}
2214
Ido Schimmela8c97012017-02-08 11:16:35 +01002215static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2216 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002217{
2218 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002219 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002220
Ido Schimmelb8399a12017-02-08 11:16:33 +01002221 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002222 return;
2223 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002224
Ido Schimmel58312122016-12-23 09:32:50 +01002225 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002226 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002227 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002228
2229 /* If that is the last nexthop connected to that neigh, remove from
2230 * nexthop_neighs_list
2231 */
Ido Schimmele58be792017-02-08 11:16:28 +01002232 if (list_empty(&neigh_entry->nexthop_list))
2233 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002234
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002235 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2236 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2237
2238 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002239}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002240
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002241static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2242 struct mlxsw_sp_nexthop_group *nh_grp,
2243 struct mlxsw_sp_nexthop *nh,
2244 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002245{
2246 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002247 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002248 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002249 int err;
2250
2251 nh->nh_grp = nh_grp;
2252 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002253 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002254 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2255 if (err)
2256 return err;
2257
Ido Schimmel97989ee2017-03-10 08:53:38 +01002258 if (!dev)
2259 return 0;
2260
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002261 in_dev = __in_dev_get_rtnl(dev);
2262 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2263 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2264 return 0;
2265
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002266 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2267 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002268 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002269 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002270
2271 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2272 if (err)
2273 goto err_nexthop_neigh_init;
2274
2275 return 0;
2276
2277err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002278 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002279 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2280 return err;
2281}
2282
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002283static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2284 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002285{
2286 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002287 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002288 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002289}
2290
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002291static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2292 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002293{
2294 struct mlxsw_sp_nexthop_key key;
2295 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002296 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002297
Ido Schimmel9011b672017-05-16 19:38:25 +02002298 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002299 return;
2300
2301 key.fib_nh = fib_nh;
2302 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2303 if (WARN_ON_ONCE(!nh))
2304 return;
2305
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002306 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2307 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002308 return;
2309
2310 switch (event) {
2311 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002312 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002313 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2314 break;
2315 case FIB_EVENT_NH_DEL:
2316 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002317 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002318 break;
2319 }
2320
2321 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2322}
2323
Ido Schimmel9665b742017-02-08 11:16:42 +01002324static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002325 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002326{
2327 struct mlxsw_sp_nexthop *nh, *tmp;
2328
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002329 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002330 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2331 mlxsw_sp_nexthop_rif_fini(nh);
2332 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2333 }
2334}
2335
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002336static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002337mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002338{
2339 struct mlxsw_sp_nexthop_group *nh_grp;
2340 struct mlxsw_sp_nexthop *nh;
2341 struct fib_nh *fib_nh;
2342 size_t alloc_size;
2343 int i;
2344 int err;
2345
2346 alloc_size = sizeof(*nh_grp) +
2347 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2348 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2349 if (!nh_grp)
2350 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002351 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002352 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002353 nh_grp->neigh_tbl = &arp_tbl;
2354
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002355 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002356 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002357 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002358 for (i = 0; i < nh_grp->count; i++) {
2359 nh = &nh_grp->nexthops[i];
2360 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002361 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002362 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002363 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002364 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002365 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2366 if (err)
2367 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002368 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2369 return nh_grp;
2370
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002371err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002372err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002373 for (i--; i >= 0; i--) {
2374 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002375 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002376 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002377 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002378 kfree(nh_grp);
2379 return ERR_PTR(err);
2380}
2381
2382static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002383mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2384 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002385{
2386 struct mlxsw_sp_nexthop *nh;
2387 int i;
2388
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002389 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002390 for (i = 0; i < nh_grp->count; i++) {
2391 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002392 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002393 }
Ido Schimmel58312122016-12-23 09:32:50 +01002394 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2395 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002396 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002397 kfree(nh_grp);
2398}
2399
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002400static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2401 struct mlxsw_sp_fib_entry *fib_entry,
2402 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002403{
2404 struct mlxsw_sp_nexthop_group *nh_grp;
2405
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002406 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002407 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002408 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002409 if (IS_ERR(nh_grp))
2410 return PTR_ERR(nh_grp);
2411 }
2412 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2413 fib_entry->nh_group = nh_grp;
2414 return 0;
2415}
2416
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002417static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2418 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002419{
2420 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2421
2422 list_del(&fib_entry->nexthop_group_node);
2423 if (!list_empty(&nh_grp->fib_list))
2424 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002425 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002426}
2427
Ido Schimmel013b20f2017-02-08 11:16:36 +01002428static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002429mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2430{
2431 struct mlxsw_sp_fib4_entry *fib4_entry;
2432
2433 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2434 common);
2435 return !fib4_entry->tos;
2436}
2437
2438static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002439mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2440{
2441 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2442
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002443 switch (fib_entry->fib_node->fib->proto) {
2444 case MLXSW_SP_L3_PROTO_IPV4:
2445 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2446 return false;
2447 break;
2448 case MLXSW_SP_L3_PROTO_IPV6:
2449 break;
2450 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002451
Ido Schimmel013b20f2017-02-08 11:16:36 +01002452 switch (fib_entry->type) {
2453 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2454 return !!nh_group->adj_index_valid;
2455 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002456 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002457 default:
2458 return false;
2459 }
2460}
2461
Ido Schimmel428b8512017-08-03 13:28:28 +02002462static struct mlxsw_sp_nexthop *
2463mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2464 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2465{
2466 int i;
2467
2468 for (i = 0; i < nh_grp->count; i++) {
2469 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2470 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2471
2472 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2473 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2474 &rt->rt6i_gateway))
2475 return nh;
2476 continue;
2477 }
2478
2479 return NULL;
2480}
2481
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002482static void
2483mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2484{
2485 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2486 int i;
2487
2488 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2489 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2490 return;
2491 }
2492
2493 for (i = 0; i < nh_grp->count; i++) {
2494 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2495
2496 if (nh->offloaded)
2497 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2498 else
2499 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2500 }
2501}
2502
2503static void
2504mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2505{
2506 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2507 int i;
2508
2509 for (i = 0; i < nh_grp->count; i++) {
2510 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2511
2512 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2513 }
2514}
2515
Ido Schimmel428b8512017-08-03 13:28:28 +02002516static void
2517mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2518{
2519 struct mlxsw_sp_fib6_entry *fib6_entry;
2520 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2521
2522 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2523 common);
2524
2525 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2526 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02002527 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002528 return;
2529 }
2530
2531 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2532 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2533 struct mlxsw_sp_nexthop *nh;
2534
2535 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2536 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02002537 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002538 else
Ido Schimmelfe400792017-08-15 09:09:49 +02002539 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002540 }
2541}
2542
2543static void
2544mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2545{
2546 struct mlxsw_sp_fib6_entry *fib6_entry;
2547 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2548
2549 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2550 common);
2551 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2552 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2553
Ido Schimmelfe400792017-08-15 09:09:49 +02002554 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002555 }
2556}
2557
Ido Schimmel013b20f2017-02-08 11:16:36 +01002558static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2559{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002560 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002561 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002562 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002563 break;
2564 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002565 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2566 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002567 }
2568}
2569
2570static void
2571mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2572{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002573 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002574 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002575 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002576 break;
2577 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002578 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2579 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002580 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002581}
2582
2583static void
2584mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2585 enum mlxsw_reg_ralue_op op, int err)
2586{
2587 switch (op) {
2588 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002589 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2590 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2591 if (err)
2592 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002593 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002594 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002595 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002596 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2597 return;
2598 default:
2599 return;
2600 }
2601}
2602
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002603static void
2604mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2605 const struct mlxsw_sp_fib_entry *fib_entry,
2606 enum mlxsw_reg_ralue_op op)
2607{
2608 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2609 enum mlxsw_reg_ralxx_protocol proto;
2610 u32 *p_dip;
2611
2612 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2613
2614 switch (fib->proto) {
2615 case MLXSW_SP_L3_PROTO_IPV4:
2616 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2617 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2618 fib_entry->fib_node->key.prefix_len,
2619 *p_dip);
2620 break;
2621 case MLXSW_SP_L3_PROTO_IPV6:
2622 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2623 fib_entry->fib_node->key.prefix_len,
2624 fib_entry->fib_node->key.addr);
2625 break;
2626 }
2627}
2628
2629static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2630 struct mlxsw_sp_fib_entry *fib_entry,
2631 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002632{
2633 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002634 enum mlxsw_reg_ralue_trap_action trap_action;
2635 u16 trap_id = 0;
2636 u32 adjacency_index = 0;
2637 u16 ecmp_size = 0;
2638
2639 /* In case the nexthop group adjacency index is valid, use it
2640 * with provided ECMP size. Otherwise, setup trap and pass
2641 * traffic to kernel.
2642 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002643 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002644 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2645 adjacency_index = fib_entry->nh_group->adj_index;
2646 ecmp_size = fib_entry->nh_group->ecmp_size;
2647 } else {
2648 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2649 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2650 }
2651
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002652 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002653 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2654 adjacency_index, ecmp_size);
2655 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2656}
2657
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002658static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2659 struct mlxsw_sp_fib_entry *fib_entry,
2660 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002661{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002662 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002663 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002664 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002665 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002666 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002667
2668 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2669 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002670 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002671 } else {
2672 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2673 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2674 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002675
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002676 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002677 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2678 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002679 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2680}
2681
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002682static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2683 struct mlxsw_sp_fib_entry *fib_entry,
2684 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002685{
2686 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002687
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002688 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002689 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2690 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2691}
2692
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002693static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2694 struct mlxsw_sp_fib_entry *fib_entry,
2695 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002696{
2697 switch (fib_entry->type) {
2698 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002699 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002700 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002701 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002702 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002703 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002704 }
2705 return -EINVAL;
2706}
2707
2708static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2709 struct mlxsw_sp_fib_entry *fib_entry,
2710 enum mlxsw_reg_ralue_op op)
2711{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002712 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002713
Ido Schimmel013b20f2017-02-08 11:16:36 +01002714 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002715
Ido Schimmel013b20f2017-02-08 11:16:36 +01002716 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002717}
2718
2719static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2720 struct mlxsw_sp_fib_entry *fib_entry)
2721{
Jiri Pirko7146da32016-09-01 10:37:41 +02002722 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2723 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002724}
2725
2726static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2727 struct mlxsw_sp_fib_entry *fib_entry)
2728{
2729 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2730 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2731}
2732
Jiri Pirko61c503f2016-07-04 08:23:11 +02002733static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002734mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2735 const struct fib_entry_notifier_info *fen_info,
2736 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002737{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002738 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002739
Ido Schimmel97989ee2017-03-10 08:53:38 +01002740 switch (fen_info->type) {
2741 case RTN_BROADCAST: /* fall through */
2742 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002743 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2744 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002745 case RTN_UNREACHABLE: /* fall through */
2746 case RTN_BLACKHOLE: /* fall through */
2747 case RTN_PROHIBIT:
2748 /* Packets hitting these routes need to be trapped, but
2749 * can do so with a lower priority than packets directed
2750 * at the host, so use action type local instead of trap.
2751 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002752 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002753 return 0;
2754 case RTN_UNICAST:
2755 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2756 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2757 else
2758 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2759 return 0;
2760 default:
2761 return -EINVAL;
2762 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002763}
2764
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002765static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002766mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2767 struct mlxsw_sp_fib_node *fib_node,
2768 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002769{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002770 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002771 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002772 int err;
2773
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002774 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2775 if (!fib4_entry)
2776 return ERR_PTR(-ENOMEM);
2777 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002778
2779 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2780 if (err)
2781 goto err_fib4_entry_type_set;
2782
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002783 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002784 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002785 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002786
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002787 fib4_entry->prio = fen_info->fi->fib_priority;
2788 fib4_entry->tb_id = fen_info->tb_id;
2789 fib4_entry->type = fen_info->type;
2790 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002791
2792 fib_entry->fib_node = fib_node;
2793
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002794 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002795
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002796err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002797err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002798 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002799 return ERR_PTR(err);
2800}
2801
2802static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002803 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002804{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002805 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002806 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002807}
2808
2809static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002810mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2811 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002812
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002813static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002814mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2815 const struct fib_entry_notifier_info *fen_info)
2816{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002817 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002818 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002819 struct mlxsw_sp_fib *fib;
2820 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002821
Ido Schimmel160e22a2017-07-18 10:10:20 +02002822 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2823 if (!vr)
2824 return NULL;
2825 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2826
2827 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2828 sizeof(fen_info->dst),
2829 fen_info->dst_len);
2830 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002831 return NULL;
2832
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002833 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2834 if (fib4_entry->tb_id == fen_info->tb_id &&
2835 fib4_entry->tos == fen_info->tos &&
2836 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002837 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2838 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002839 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002840 }
2841 }
2842
2843 return NULL;
2844}
2845
2846static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2847 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2848 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2849 .key_len = sizeof(struct mlxsw_sp_fib_key),
2850 .automatic_shrinking = true,
2851};
2852
2853static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2854 struct mlxsw_sp_fib_node *fib_node)
2855{
2856 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2857 mlxsw_sp_fib_ht_params);
2858}
2859
2860static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2861 struct mlxsw_sp_fib_node *fib_node)
2862{
2863 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2864 mlxsw_sp_fib_ht_params);
2865}
2866
2867static struct mlxsw_sp_fib_node *
2868mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2869 size_t addr_len, unsigned char prefix_len)
2870{
2871 struct mlxsw_sp_fib_key key;
2872
2873 memset(&key, 0, sizeof(key));
2874 memcpy(key.addr, addr, addr_len);
2875 key.prefix_len = prefix_len;
2876 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2877}
2878
2879static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002880mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002881 size_t addr_len, unsigned char prefix_len)
2882{
2883 struct mlxsw_sp_fib_node *fib_node;
2884
2885 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2886 if (!fib_node)
2887 return NULL;
2888
2889 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002890 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002891 memcpy(fib_node->key.addr, addr, addr_len);
2892 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002893
2894 return fib_node;
2895}
2896
2897static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2898{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002899 list_del(&fib_node->list);
2900 WARN_ON(!list_empty(&fib_node->entry_list));
2901 kfree(fib_node);
2902}
2903
2904static bool
2905mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2906 const struct mlxsw_sp_fib_entry *fib_entry)
2907{
2908 return list_first_entry(&fib_node->entry_list,
2909 struct mlxsw_sp_fib_entry, list) == fib_entry;
2910}
2911
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002912static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2913 struct mlxsw_sp_fib *fib,
2914 struct mlxsw_sp_fib_node *fib_node)
2915{
2916 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2917 struct mlxsw_sp_lpm_tree *lpm_tree;
2918 int err;
2919
2920 /* Since the tree is shared between all virtual routers we must
2921 * make sure it contains all the required prefix lengths. This
2922 * can be computed by either adding the new prefix length to the
2923 * existing prefix usage of a bound tree, or by aggregating the
2924 * prefix lengths across all virtual routers and adding the new
2925 * one as well.
2926 */
2927 if (fib->lpm_tree)
2928 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2929 &fib->lpm_tree->prefix_usage);
2930 else
2931 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2932 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2933
2934 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2935 fib->proto);
2936 if (IS_ERR(lpm_tree))
2937 return PTR_ERR(lpm_tree);
2938
2939 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2940 return 0;
2941
2942 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2943 if (err)
2944 return err;
2945
2946 return 0;
2947}
2948
2949static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2950 struct mlxsw_sp_fib *fib)
2951{
2952 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2953 struct mlxsw_sp_lpm_tree *lpm_tree;
2954
2955 /* Aggregate prefix lengths across all virtual routers to make
2956 * sure we only have used prefix lengths in the LPM tree.
2957 */
2958 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2959 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2960 fib->proto);
2961 if (IS_ERR(lpm_tree))
2962 goto err_tree_get;
2963 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2964
2965err_tree_get:
2966 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2967 return;
2968 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2969 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2970 fib->lpm_tree = NULL;
2971}
2972
Ido Schimmel9aecce12017-02-09 10:28:42 +01002973static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2974{
2975 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002976 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002977
2978 if (fib->prefix_ref_count[prefix_len]++ == 0)
2979 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2980}
2981
2982static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2983{
2984 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002985 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002986
2987 if (--fib->prefix_ref_count[prefix_len] == 0)
2988 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2989}
2990
Ido Schimmel76610eb2017-03-10 08:53:41 +01002991static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2992 struct mlxsw_sp_fib_node *fib_node,
2993 struct mlxsw_sp_fib *fib)
2994{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002995 int err;
2996
2997 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2998 if (err)
2999 return err;
3000 fib_node->fib = fib;
3001
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003002 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3003 if (err)
3004 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003005
3006 mlxsw_sp_fib_node_prefix_inc(fib_node);
3007
3008 return 0;
3009
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003010err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003011 fib_node->fib = NULL;
3012 mlxsw_sp_fib_node_remove(fib, fib_node);
3013 return err;
3014}
3015
3016static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3017 struct mlxsw_sp_fib_node *fib_node)
3018{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003019 struct mlxsw_sp_fib *fib = fib_node->fib;
3020
3021 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003022 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003023 fib_node->fib = NULL;
3024 mlxsw_sp_fib_node_remove(fib, fib_node);
3025}
3026
Ido Schimmel9aecce12017-02-09 10:28:42 +01003027static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003028mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3029 size_t addr_len, unsigned char prefix_len,
3030 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003031{
3032 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003033 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003034 struct mlxsw_sp_vr *vr;
3035 int err;
3036
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003037 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003038 if (IS_ERR(vr))
3039 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003040 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003041
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003042 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003043 if (fib_node)
3044 return fib_node;
3045
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003046 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003047 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003048 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003049 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003050 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003051
Ido Schimmel76610eb2017-03-10 08:53:41 +01003052 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3053 if (err)
3054 goto err_fib_node_init;
3055
Ido Schimmel9aecce12017-02-09 10:28:42 +01003056 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003057
Ido Schimmel76610eb2017-03-10 08:53:41 +01003058err_fib_node_init:
3059 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003060err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003061 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003062 return ERR_PTR(err);
3063}
3064
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003065static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3066 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003067{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003068 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003069
Ido Schimmel9aecce12017-02-09 10:28:42 +01003070 if (!list_empty(&fib_node->entry_list))
3071 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003072 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003073 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003074 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003075}
3076
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003077static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003078mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003079 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003080{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003081 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003082
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003083 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3084 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003085 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003086 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003087 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003088 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003089 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003090 if (fib4_entry->prio >= new4_entry->prio ||
3091 fib4_entry->tos < new4_entry->tos)
3092 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003093 }
3094
3095 return NULL;
3096}
3097
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003098static int
3099mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3100 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003101{
3102 struct mlxsw_sp_fib_node *fib_node;
3103
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003104 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003105 return -EINVAL;
3106
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003107 fib_node = fib4_entry->common.fib_node;
3108 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3109 common.list) {
3110 if (fib4_entry->tb_id != new4_entry->tb_id ||
3111 fib4_entry->tos != new4_entry->tos ||
3112 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003113 break;
3114 }
3115
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003116 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003117 return 0;
3118}
3119
Ido Schimmel9aecce12017-02-09 10:28:42 +01003120static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003121mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003122 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003123{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003124 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003125 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003126
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003127 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003128
Ido Schimmel4283bce2017-02-09 10:28:43 +01003129 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003130 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3131 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003132 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003133
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003134 /* Insert new entry before replaced one, so that we can later
3135 * remove the second.
3136 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003137 if (fib4_entry) {
3138 list_add_tail(&new4_entry->common.list,
3139 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003140 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003141 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003142
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003143 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3144 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003145 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003146 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003147 }
3148
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003149 if (fib4_entry)
3150 list_add(&new4_entry->common.list,
3151 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003152 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003153 list_add(&new4_entry->common.list,
3154 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003155 }
3156
3157 return 0;
3158}
3159
3160static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003161mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003162{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003163 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003164}
3165
Ido Schimmel80c238f2017-07-18 10:10:29 +02003166static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3167 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003168{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003169 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3170
Ido Schimmel9aecce12017-02-09 10:28:42 +01003171 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3172 return 0;
3173
3174 /* To prevent packet loss, overwrite the previously offloaded
3175 * entry.
3176 */
3177 if (!list_is_singular(&fib_node->entry_list)) {
3178 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3179 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3180
3181 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3182 }
3183
3184 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3185}
3186
Ido Schimmel80c238f2017-07-18 10:10:29 +02003187static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3188 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003189{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003190 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3191
Ido Schimmel9aecce12017-02-09 10:28:42 +01003192 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3193 return;
3194
3195 /* Promote the next entry by overwriting the deleted entry */
3196 if (!list_is_singular(&fib_node->entry_list)) {
3197 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3198 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3199
3200 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3201 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3202 return;
3203 }
3204
3205 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3206}
3207
3208static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003209 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003210 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003211{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003212 int err;
3213
Ido Schimmel9efbee62017-07-18 10:10:28 +02003214 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003215 if (err)
3216 return err;
3217
Ido Schimmel80c238f2017-07-18 10:10:29 +02003218 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003219 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003220 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003221
Ido Schimmel9aecce12017-02-09 10:28:42 +01003222 return 0;
3223
Ido Schimmel80c238f2017-07-18 10:10:29 +02003224err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003225 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003226 return err;
3227}
3228
3229static void
3230mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003231 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003232{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003233 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003234 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003235}
3236
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003237static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003238 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003239 bool replace)
3240{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003241 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3242 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003243
3244 if (!replace)
3245 return;
3246
3247 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003248 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003249
3250 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3251 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003252 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003253}
3254
Ido Schimmel9aecce12017-02-09 10:28:42 +01003255static int
3256mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003257 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003258 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003259{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003260 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003261 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003262 int err;
3263
Ido Schimmel9011b672017-05-16 19:38:25 +02003264 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003265 return 0;
3266
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003267 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3268 &fen_info->dst, sizeof(fen_info->dst),
3269 fen_info->dst_len,
3270 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003271 if (IS_ERR(fib_node)) {
3272 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3273 return PTR_ERR(fib_node);
3274 }
3275
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003276 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3277 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003278 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003279 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003280 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003281 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003282
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003283 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003284 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003285 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003286 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3287 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003288 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003289
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003290 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003291
Jiri Pirko61c503f2016-07-04 08:23:11 +02003292 return 0;
3293
Ido Schimmel9aecce12017-02-09 10:28:42 +01003294err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003295 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003296err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003297 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003298 return err;
3299}
3300
Jiri Pirko37956d72016-10-20 16:05:43 +02003301static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3302 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003303{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003304 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003305 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003306
Ido Schimmel9011b672017-05-16 19:38:25 +02003307 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003308 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003309
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003310 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3311 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003312 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003313 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003314
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003315 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3316 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003317 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003318}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003319
Ido Schimmel428b8512017-08-03 13:28:28 +02003320static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3321{
3322 /* Packets with link-local destination IP arriving to the router
3323 * are trapped to the CPU, so no need to program specific routes
3324 * for them.
3325 */
3326 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3327 return true;
3328
3329 /* Multicast routes aren't supported, so ignore them. Neighbour
3330 * Discovery packets are specifically trapped.
3331 */
3332 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3333 return true;
3334
3335 /* Cloned routes are irrelevant in the forwarding path. */
3336 if (rt->rt6i_flags & RTF_CACHE)
3337 return true;
3338
3339 return false;
3340}
3341
3342static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3343{
3344 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3345
3346 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3347 if (!mlxsw_sp_rt6)
3348 return ERR_PTR(-ENOMEM);
3349
3350 /* In case of route replace, replaced route is deleted with
3351 * no notification. Take reference to prevent accessing freed
3352 * memory.
3353 */
3354 mlxsw_sp_rt6->rt = rt;
3355 rt6_hold(rt);
3356
3357 return mlxsw_sp_rt6;
3358}
3359
3360#if IS_ENABLED(CONFIG_IPV6)
3361static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3362{
3363 rt6_release(rt);
3364}
3365#else
3366static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3367{
3368}
3369#endif
3370
3371static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3372{
3373 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3374 kfree(mlxsw_sp_rt6);
3375}
3376
3377static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3378{
3379 /* RTF_CACHE routes are ignored */
3380 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3381}
3382
3383static struct rt6_info *
3384mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3385{
3386 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3387 list)->rt;
3388}
3389
3390static struct mlxsw_sp_fib6_entry *
3391mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003392 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003393{
3394 struct mlxsw_sp_fib6_entry *fib6_entry;
3395
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003396 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003397 return NULL;
3398
3399 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3400 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3401
3402 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3403 * virtual router.
3404 */
3405 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3406 continue;
3407 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3408 break;
3409 if (rt->rt6i_metric < nrt->rt6i_metric)
3410 continue;
3411 if (rt->rt6i_metric == nrt->rt6i_metric &&
3412 mlxsw_sp_fib6_rt_can_mp(rt))
3413 return fib6_entry;
3414 if (rt->rt6i_metric > nrt->rt6i_metric)
3415 break;
3416 }
3417
3418 return NULL;
3419}
3420
3421static struct mlxsw_sp_rt6 *
3422mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3423 const struct rt6_info *rt)
3424{
3425 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3426
3427 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3428 if (mlxsw_sp_rt6->rt == rt)
3429 return mlxsw_sp_rt6;
3430 }
3431
3432 return NULL;
3433}
3434
3435static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3436 struct mlxsw_sp_nexthop_group *nh_grp,
3437 struct mlxsw_sp_nexthop *nh,
3438 const struct rt6_info *rt)
3439{
3440 struct net_device *dev = rt->dst.dev;
3441 struct mlxsw_sp_rif *rif;
3442 int err;
3443
3444 nh->nh_grp = nh_grp;
3445 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3446
3447 if (!dev)
3448 return 0;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003449 nh->ifindex = dev->ifindex;
Ido Schimmel428b8512017-08-03 13:28:28 +02003450
3451 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3452 if (!rif)
3453 return 0;
3454 mlxsw_sp_nexthop_rif_init(nh, rif);
3455
3456 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3457 if (err)
3458 goto err_nexthop_neigh_init;
3459
3460 return 0;
3461
3462err_nexthop_neigh_init:
3463 mlxsw_sp_nexthop_rif_fini(nh);
3464 return err;
3465}
3466
3467static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3468 struct mlxsw_sp_nexthop *nh)
3469{
3470 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3471 mlxsw_sp_nexthop_rif_fini(nh);
3472}
3473
3474static struct mlxsw_sp_nexthop_group *
3475mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3476 struct mlxsw_sp_fib6_entry *fib6_entry)
3477{
3478 struct mlxsw_sp_nexthop_group *nh_grp;
3479 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3480 struct mlxsw_sp_nexthop *nh;
3481 size_t alloc_size;
3482 int i = 0;
3483 int err;
3484
3485 alloc_size = sizeof(*nh_grp) +
3486 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3487 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3488 if (!nh_grp)
3489 return ERR_PTR(-ENOMEM);
3490 INIT_LIST_HEAD(&nh_grp->fib_list);
3491#if IS_ENABLED(CONFIG_IPV6)
3492 nh_grp->neigh_tbl = &nd_tbl;
3493#endif
3494 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3495 struct mlxsw_sp_rt6, list);
3496 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3497 nh_grp->count = fib6_entry->nrt6;
3498 for (i = 0; i < nh_grp->count; i++) {
3499 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3500
3501 nh = &nh_grp->nexthops[i];
3502 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3503 if (err)
3504 goto err_nexthop6_init;
3505 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3506 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003507
3508 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3509 if (err)
3510 goto err_nexthop_group_insert;
3511
Ido Schimmel428b8512017-08-03 13:28:28 +02003512 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3513 return nh_grp;
3514
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003515err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02003516err_nexthop6_init:
3517 for (i--; i >= 0; i--) {
3518 nh = &nh_grp->nexthops[i];
3519 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3520 }
3521 kfree(nh_grp);
3522 return ERR_PTR(err);
3523}
3524
3525static void
3526mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3527 struct mlxsw_sp_nexthop_group *nh_grp)
3528{
3529 struct mlxsw_sp_nexthop *nh;
3530 int i = nh_grp->count;
3531
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003532 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02003533 for (i--; i >= 0; i--) {
3534 nh = &nh_grp->nexthops[i];
3535 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3536 }
3537 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3538 WARN_ON(nh_grp->adj_index_valid);
3539 kfree(nh_grp);
3540}
3541
3542static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3543 struct mlxsw_sp_fib6_entry *fib6_entry)
3544{
3545 struct mlxsw_sp_nexthop_group *nh_grp;
3546
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003547 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
3548 if (!nh_grp) {
3549 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3550 if (IS_ERR(nh_grp))
3551 return PTR_ERR(nh_grp);
3552 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003553
3554 list_add_tail(&fib6_entry->common.nexthop_group_node,
3555 &nh_grp->fib_list);
3556 fib6_entry->common.nh_group = nh_grp;
3557
3558 return 0;
3559}
3560
3561static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3562 struct mlxsw_sp_fib_entry *fib_entry)
3563{
3564 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3565
3566 list_del(&fib_entry->nexthop_group_node);
3567 if (!list_empty(&nh_grp->fib_list))
3568 return;
3569 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3570}
3571
3572static int
3573mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3574 struct mlxsw_sp_fib6_entry *fib6_entry)
3575{
3576 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3577 int err;
3578
3579 fib6_entry->common.nh_group = NULL;
3580 list_del(&fib6_entry->common.nexthop_group_node);
3581
3582 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3583 if (err)
3584 goto err_nexthop6_group_get;
3585
3586 /* In case this entry is offloaded, then the adjacency index
3587 * currently associated with it in the device's table is that
3588 * of the old group. Start using the new one instead.
3589 */
3590 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3591 if (err)
3592 goto err_fib_node_entry_add;
3593
3594 if (list_empty(&old_nh_grp->fib_list))
3595 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3596
3597 return 0;
3598
3599err_fib_node_entry_add:
3600 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3601err_nexthop6_group_get:
3602 list_add_tail(&fib6_entry->common.nexthop_group_node,
3603 &old_nh_grp->fib_list);
3604 fib6_entry->common.nh_group = old_nh_grp;
3605 return err;
3606}
3607
3608static int
3609mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3610 struct mlxsw_sp_fib6_entry *fib6_entry,
3611 struct rt6_info *rt)
3612{
3613 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3614 int err;
3615
3616 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3617 if (IS_ERR(mlxsw_sp_rt6))
3618 return PTR_ERR(mlxsw_sp_rt6);
3619
3620 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3621 fib6_entry->nrt6++;
3622
3623 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3624 if (err)
3625 goto err_nexthop6_group_update;
3626
3627 return 0;
3628
3629err_nexthop6_group_update:
3630 fib6_entry->nrt6--;
3631 list_del(&mlxsw_sp_rt6->list);
3632 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3633 return err;
3634}
3635
3636static void
3637mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3638 struct mlxsw_sp_fib6_entry *fib6_entry,
3639 struct rt6_info *rt)
3640{
3641 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3642
3643 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3644 if (WARN_ON(!mlxsw_sp_rt6))
3645 return;
3646
3647 fib6_entry->nrt6--;
3648 list_del(&mlxsw_sp_rt6->list);
3649 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3650 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3651}
3652
3653static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3654 const struct rt6_info *rt)
3655{
3656 /* Packets hitting RTF_REJECT routes need to be discarded by the
3657 * stack. We can rely on their destination device not having a
3658 * RIF (it's the loopback device) and can thus use action type
3659 * local, which will cause them to be trapped with a lower
3660 * priority than packets that need to be locally received.
3661 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02003662 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02003663 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3664 else if (rt->rt6i_flags & RTF_REJECT)
3665 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3666 else if (rt->rt6i_flags & RTF_GATEWAY)
3667 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3668 else
3669 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3670}
3671
3672static void
3673mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3674{
3675 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3676
3677 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3678 list) {
3679 fib6_entry->nrt6--;
3680 list_del(&mlxsw_sp_rt6->list);
3681 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3682 }
3683}
3684
3685static struct mlxsw_sp_fib6_entry *
3686mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3687 struct mlxsw_sp_fib_node *fib_node,
3688 struct rt6_info *rt)
3689{
3690 struct mlxsw_sp_fib6_entry *fib6_entry;
3691 struct mlxsw_sp_fib_entry *fib_entry;
3692 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3693 int err;
3694
3695 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3696 if (!fib6_entry)
3697 return ERR_PTR(-ENOMEM);
3698 fib_entry = &fib6_entry->common;
3699
3700 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3701 if (IS_ERR(mlxsw_sp_rt6)) {
3702 err = PTR_ERR(mlxsw_sp_rt6);
3703 goto err_rt6_create;
3704 }
3705
3706 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3707
3708 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3709 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3710 fib6_entry->nrt6 = 1;
3711 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3712 if (err)
3713 goto err_nexthop6_group_get;
3714
3715 fib_entry->fib_node = fib_node;
3716
3717 return fib6_entry;
3718
3719err_nexthop6_group_get:
3720 list_del(&mlxsw_sp_rt6->list);
3721 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3722err_rt6_create:
3723 kfree(fib6_entry);
3724 return ERR_PTR(err);
3725}
3726
3727static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3728 struct mlxsw_sp_fib6_entry *fib6_entry)
3729{
3730 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3731 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3732 WARN_ON(fib6_entry->nrt6);
3733 kfree(fib6_entry);
3734}
3735
3736static struct mlxsw_sp_fib6_entry *
3737mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003738 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003739{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003740 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003741
3742 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3743 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3744
3745 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3746 continue;
3747 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3748 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003749 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3750 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3751 mlxsw_sp_fib6_rt_can_mp(nrt))
3752 return fib6_entry;
3753 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3754 fallback = fallback ?: fib6_entry;
3755 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003756 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003757 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003758 }
3759
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003760 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003761}
3762
3763static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003764mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3765 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003766{
3767 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3768 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3769 struct mlxsw_sp_fib6_entry *fib6_entry;
3770
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003771 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3772
3773 if (replace && WARN_ON(!fib6_entry))
3774 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003775
3776 if (fib6_entry) {
3777 list_add_tail(&new6_entry->common.list,
3778 &fib6_entry->common.list);
3779 } else {
3780 struct mlxsw_sp_fib6_entry *last;
3781
3782 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3783 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3784
3785 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3786 break;
3787 fib6_entry = last;
3788 }
3789
3790 if (fib6_entry)
3791 list_add(&new6_entry->common.list,
3792 &fib6_entry->common.list);
3793 else
3794 list_add(&new6_entry->common.list,
3795 &fib_node->entry_list);
3796 }
3797
3798 return 0;
3799}
3800
3801static void
3802mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3803{
3804 list_del(&fib6_entry->common.list);
3805}
3806
3807static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003808 struct mlxsw_sp_fib6_entry *fib6_entry,
3809 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003810{
3811 int err;
3812
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003813 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003814 if (err)
3815 return err;
3816
3817 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3818 if (err)
3819 goto err_fib_node_entry_add;
3820
3821 return 0;
3822
3823err_fib_node_entry_add:
3824 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3825 return err;
3826}
3827
3828static void
3829mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3830 struct mlxsw_sp_fib6_entry *fib6_entry)
3831{
3832 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3833 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3834}
3835
3836static struct mlxsw_sp_fib6_entry *
3837mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3838 const struct rt6_info *rt)
3839{
3840 struct mlxsw_sp_fib6_entry *fib6_entry;
3841 struct mlxsw_sp_fib_node *fib_node;
3842 struct mlxsw_sp_fib *fib;
3843 struct mlxsw_sp_vr *vr;
3844
3845 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3846 if (!vr)
3847 return NULL;
3848 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3849
3850 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3851 sizeof(rt->rt6i_dst.addr),
3852 rt->rt6i_dst.plen);
3853 if (!fib_node)
3854 return NULL;
3855
3856 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3857 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3858
3859 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3860 rt->rt6i_metric == iter_rt->rt6i_metric &&
3861 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3862 return fib6_entry;
3863 }
3864
3865 return NULL;
3866}
3867
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003868static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3869 struct mlxsw_sp_fib6_entry *fib6_entry,
3870 bool replace)
3871{
3872 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3873 struct mlxsw_sp_fib6_entry *replaced;
3874
3875 if (!replace)
3876 return;
3877
3878 replaced = list_next_entry(fib6_entry, common.list);
3879
3880 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3881 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3882 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3883}
3884
Ido Schimmel428b8512017-08-03 13:28:28 +02003885static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003886 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003887{
3888 struct mlxsw_sp_fib6_entry *fib6_entry;
3889 struct mlxsw_sp_fib_node *fib_node;
3890 int err;
3891
3892 if (mlxsw_sp->router->aborted)
3893 return 0;
3894
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003895 if (rt->rt6i_src.plen)
3896 return -EINVAL;
3897
Ido Schimmel428b8512017-08-03 13:28:28 +02003898 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3899 return 0;
3900
3901 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3902 &rt->rt6i_dst.addr,
3903 sizeof(rt->rt6i_dst.addr),
3904 rt->rt6i_dst.plen,
3905 MLXSW_SP_L3_PROTO_IPV6);
3906 if (IS_ERR(fib_node))
3907 return PTR_ERR(fib_node);
3908
3909 /* Before creating a new entry, try to append route to an existing
3910 * multipath entry.
3911 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003912 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003913 if (fib6_entry) {
3914 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3915 if (err)
3916 goto err_fib6_entry_nexthop_add;
3917 return 0;
3918 }
3919
3920 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3921 if (IS_ERR(fib6_entry)) {
3922 err = PTR_ERR(fib6_entry);
3923 goto err_fib6_entry_create;
3924 }
3925
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003926 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003927 if (err)
3928 goto err_fib6_node_entry_link;
3929
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003930 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3931
Ido Schimmel428b8512017-08-03 13:28:28 +02003932 return 0;
3933
3934err_fib6_node_entry_link:
3935 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3936err_fib6_entry_create:
3937err_fib6_entry_nexthop_add:
3938 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3939 return err;
3940}
3941
3942static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3943 struct rt6_info *rt)
3944{
3945 struct mlxsw_sp_fib6_entry *fib6_entry;
3946 struct mlxsw_sp_fib_node *fib_node;
3947
3948 if (mlxsw_sp->router->aborted)
3949 return;
3950
3951 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3952 return;
3953
3954 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3955 if (WARN_ON(!fib6_entry))
3956 return;
3957
3958 /* If route is part of a multipath entry, but not the last one
3959 * removed, then only reduce its nexthop group.
3960 */
3961 if (!list_is_singular(&fib6_entry->rt6_list)) {
3962 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3963 return;
3964 }
3965
3966 fib_node = fib6_entry->common.fib_node;
3967
3968 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3969 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3970 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3971}
3972
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003973static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3974 enum mlxsw_reg_ralxx_protocol proto,
3975 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003976{
3977 char ralta_pl[MLXSW_REG_RALTA_LEN];
3978 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003979 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003980
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003981 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003982 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3983 if (err)
3984 return err;
3985
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003986 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003987 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3988 if (err)
3989 return err;
3990
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003991 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003992 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003993 char raltb_pl[MLXSW_REG_RALTB_LEN];
3994 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003995
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003996 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003997 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3998 raltb_pl);
3999 if (err)
4000 return err;
4001
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004002 mlxsw_reg_ralue_pack(ralue_pl, proto,
4003 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004004 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4005 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4006 ralue_pl);
4007 if (err)
4008 return err;
4009 }
4010
4011 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004012}
4013
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004014static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4015{
4016 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4017 int err;
4018
4019 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4020 MLXSW_SP_LPM_TREE_MIN);
4021 if (err)
4022 return err;
4023
4024 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4025 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4026 MLXSW_SP_LPM_TREE_MIN + 1);
4027}
4028
Ido Schimmel9aecce12017-02-09 10:28:42 +01004029static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4030 struct mlxsw_sp_fib_node *fib_node)
4031{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004032 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004033
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004034 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4035 common.list) {
4036 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004037
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004038 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4039 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004040 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004041 /* Break when entry list is empty and node was freed.
4042 * Otherwise, we'll access freed memory in the next
4043 * iteration.
4044 */
4045 if (do_break)
4046 break;
4047 }
4048}
4049
Ido Schimmel428b8512017-08-03 13:28:28 +02004050static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4051 struct mlxsw_sp_fib_node *fib_node)
4052{
4053 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4054
4055 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4056 common.list) {
4057 bool do_break = &tmp->common.list == &fib_node->entry_list;
4058
4059 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4060 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4061 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4062 if (do_break)
4063 break;
4064 }
4065}
4066
Ido Schimmel9aecce12017-02-09 10:28:42 +01004067static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4068 struct mlxsw_sp_fib_node *fib_node)
4069{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004070 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004071 case MLXSW_SP_L3_PROTO_IPV4:
4072 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4073 break;
4074 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004075 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004076 break;
4077 }
4078}
4079
Ido Schimmel76610eb2017-03-10 08:53:41 +01004080static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4081 struct mlxsw_sp_vr *vr,
4082 enum mlxsw_sp_l3proto proto)
4083{
4084 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4085 struct mlxsw_sp_fib_node *fib_node, *tmp;
4086
4087 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4088 bool do_break = &tmp->list == &fib->node_list;
4089
4090 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4091 if (do_break)
4092 break;
4093 }
4094}
4095
Ido Schimmelac571de2016-11-14 11:26:32 +01004096static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004097{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004098 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004099
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004100 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004101 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004102
Ido Schimmel76610eb2017-03-10 08:53:41 +01004103 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004104 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004105 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004106
4107 /* If virtual router was only used for IPv4, then it's no
4108 * longer used.
4109 */
4110 if (!mlxsw_sp_vr_is_used(vr))
4111 continue;
4112 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004113 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004114}
4115
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004116static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004117{
4118 int err;
4119
Ido Schimmel9011b672017-05-16 19:38:25 +02004120 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004121 return;
4122 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 +01004123 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004124 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004125 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4126 if (err)
4127 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4128}
4129
Ido Schimmel30572242016-12-03 16:45:01 +01004130struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004131 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004132 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004133 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004134 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004135 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004136 struct fib_nh_notifier_info fnh_info;
4137 };
Ido Schimmel30572242016-12-03 16:45:01 +01004138 struct mlxsw_sp *mlxsw_sp;
4139 unsigned long event;
4140};
4141
Ido Schimmel66a57632017-08-03 13:28:26 +02004142static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004143{
Ido Schimmel30572242016-12-03 16:45:01 +01004144 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004145 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004146 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004147 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004148 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004149 int err;
4150
Ido Schimmel30572242016-12-03 16:45:01 +01004151 /* Protect internal structures from changes */
4152 rtnl_lock();
4153 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004154 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004155 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004156 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004157 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004158 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4159 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004160 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004161 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004162 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004163 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004164 break;
4165 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004166 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4167 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004168 break;
4169 case FIB_EVENT_RULE_ADD: /* fall through */
4170 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004171 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004172 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004173 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004174 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004175 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004176 case FIB_EVENT_NH_ADD: /* fall through */
4177 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004178 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4179 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004180 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4181 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004182 }
Ido Schimmel30572242016-12-03 16:45:01 +01004183 rtnl_unlock();
4184 kfree(fib_work);
4185}
4186
Ido Schimmel66a57632017-08-03 13:28:26 +02004187static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4188{
Ido Schimmel583419f2017-08-03 13:28:27 +02004189 struct mlxsw_sp_fib_event_work *fib_work =
4190 container_of(work, struct mlxsw_sp_fib_event_work, work);
4191 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4192 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004193 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004194 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004195
4196 rtnl_lock();
4197 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004198 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004199 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004200 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004201 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004202 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004203 if (err)
4204 mlxsw_sp_router_fib_abort(mlxsw_sp);
4205 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4206 break;
4207 case FIB_EVENT_ENTRY_DEL:
4208 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4209 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4210 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004211 case FIB_EVENT_RULE_ADD: /* fall through */
4212 case FIB_EVENT_RULE_DEL:
4213 rule = fib_work->fr_info.rule;
4214 if (!fib6_rule_default(rule) && !rule->l3mdev)
4215 mlxsw_sp_router_fib_abort(mlxsw_sp);
4216 fib_rule_put(rule);
4217 break;
4218 }
4219 rtnl_unlock();
4220 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004221}
4222
4223static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4224 struct fib_notifier_info *info)
4225{
4226 switch (fib_work->event) {
4227 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4228 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4229 case FIB_EVENT_ENTRY_ADD: /* fall through */
4230 case FIB_EVENT_ENTRY_DEL:
4231 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4232 /* Take referece on fib_info to prevent it from being
4233 * freed while work is queued. Release it afterwards.
4234 */
4235 fib_info_hold(fib_work->fen_info.fi);
4236 break;
4237 case FIB_EVENT_RULE_ADD: /* fall through */
4238 case FIB_EVENT_RULE_DEL:
4239 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4240 fib_rule_get(fib_work->fr_info.rule);
4241 break;
4242 case FIB_EVENT_NH_ADD: /* fall through */
4243 case FIB_EVENT_NH_DEL:
4244 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4245 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4246 break;
4247 }
4248}
4249
4250static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4251 struct fib_notifier_info *info)
4252{
Ido Schimmel583419f2017-08-03 13:28:27 +02004253 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004254 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004255 case FIB_EVENT_ENTRY_ADD: /* fall through */
4256 case FIB_EVENT_ENTRY_DEL:
4257 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4258 rt6_hold(fib_work->fen6_info.rt);
4259 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004260 case FIB_EVENT_RULE_ADD: /* fall through */
4261 case FIB_EVENT_RULE_DEL:
4262 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4263 fib_rule_get(fib_work->fr_info.rule);
4264 break;
4265 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004266}
4267
Ido Schimmel30572242016-12-03 16:45:01 +01004268/* Called with rcu_read_lock() */
4269static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4270 unsigned long event, void *ptr)
4271{
Ido Schimmel30572242016-12-03 16:45:01 +01004272 struct mlxsw_sp_fib_event_work *fib_work;
4273 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004274 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004275
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004276 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004277 return NOTIFY_DONE;
4278
4279 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4280 if (WARN_ON(!fib_work))
4281 return NOTIFY_BAD;
4282
Ido Schimmel7e39d112017-05-16 19:38:28 +02004283 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4284 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004285 fib_work->event = event;
4286
Ido Schimmel66a57632017-08-03 13:28:26 +02004287 switch (info->family) {
4288 case AF_INET:
4289 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4290 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004291 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004292 case AF_INET6:
4293 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4294 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004295 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004296 }
4297
Ido Schimmela0e47612017-02-06 16:20:10 +01004298 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004299
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004300 return NOTIFY_DONE;
4301}
4302
Ido Schimmel4724ba562017-03-10 08:53:39 +01004303static struct mlxsw_sp_rif *
4304mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4305 const struct net_device *dev)
4306{
4307 int i;
4308
4309 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004310 if (mlxsw_sp->router->rifs[i] &&
4311 mlxsw_sp->router->rifs[i]->dev == dev)
4312 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004313
4314 return NULL;
4315}
4316
4317static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4318{
4319 char ritr_pl[MLXSW_REG_RITR_LEN];
4320 int err;
4321
4322 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4323 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4324 if (WARN_ON_ONCE(err))
4325 return err;
4326
4327 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4328 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4329}
4330
4331static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004332 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004333{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004334 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4335 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4336 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004337}
4338
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004339static bool
4340mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4341 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004342{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004343 struct inet6_dev *inet6_dev;
4344 bool addr_list_empty = true;
4345 struct in_device *idev;
4346
Ido Schimmel4724ba562017-03-10 08:53:39 +01004347 switch (event) {
4348 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004349 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004350 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004351 idev = __in_dev_get_rtnl(dev);
4352 if (idev && idev->ifa_list)
4353 addr_list_empty = false;
4354
4355 inet6_dev = __in6_dev_get(dev);
4356 if (addr_list_empty && inet6_dev &&
4357 !list_empty(&inet6_dev->addr_list))
4358 addr_list_empty = false;
4359
4360 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004361 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004362 return true;
4363 /* It is possible we already removed the RIF ourselves
4364 * if it was assigned to a netdev that is now a bridge
4365 * or LAG slave.
4366 */
4367 return false;
4368 }
4369
4370 return false;
4371}
4372
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004373static enum mlxsw_sp_rif_type
4374mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4375 const struct net_device *dev)
4376{
4377 enum mlxsw_sp_fid_type type;
4378
4379 /* RIF type is derived from the type of the underlying FID */
4380 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4381 type = MLXSW_SP_FID_TYPE_8021Q;
4382 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4383 type = MLXSW_SP_FID_TYPE_8021Q;
4384 else if (netif_is_bridge_master(dev))
4385 type = MLXSW_SP_FID_TYPE_8021D;
4386 else
4387 type = MLXSW_SP_FID_TYPE_RFID;
4388
4389 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4390}
4391
Ido Schimmelde5ed992017-06-04 16:53:40 +02004392static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004393{
4394 int i;
4395
Ido Schimmelde5ed992017-06-04 16:53:40 +02004396 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4397 if (!mlxsw_sp->router->rifs[i]) {
4398 *p_rif_index = i;
4399 return 0;
4400 }
4401 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004402
Ido Schimmelde5ed992017-06-04 16:53:40 +02004403 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004404}
4405
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004406static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4407 u16 vr_id,
4408 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004409{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004410 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004411
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004412 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004413 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004414 return NULL;
4415
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004416 INIT_LIST_HEAD(&rif->nexthop_list);
4417 INIT_LIST_HEAD(&rif->neigh_list);
4418 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4419 rif->mtu = l3_dev->mtu;
4420 rif->vr_id = vr_id;
4421 rif->dev = l3_dev;
4422 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004423
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004424 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004425}
4426
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004427struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4428 u16 rif_index)
4429{
4430 return mlxsw_sp->router->rifs[rif_index];
4431}
4432
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004433u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4434{
4435 return rif->rif_index;
4436}
4437
4438int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4439{
4440 return rif->dev->ifindex;
4441}
4442
Ido Schimmel4724ba562017-03-10 08:53:39 +01004443static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004444mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4445 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004446{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004447 u32 tb_id = l3mdev_fib_table(params->dev);
4448 const struct mlxsw_sp_rif_ops *ops;
4449 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004450 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004451 struct mlxsw_sp_fid *fid;
4452 struct mlxsw_sp_vr *vr;
4453 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004454 int err;
4455
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004456 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4457 ops = mlxsw_sp->router->rif_ops_arr[type];
4458
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004459 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4460 if (IS_ERR(vr))
4461 return ERR_CAST(vr);
4462
Ido Schimmelde5ed992017-06-04 16:53:40 +02004463 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4464 if (err)
4465 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004466
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004467 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004468 if (!rif) {
4469 err = -ENOMEM;
4470 goto err_rif_alloc;
4471 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004472 rif->mlxsw_sp = mlxsw_sp;
4473 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004474
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004475 fid = ops->fid_get(rif);
4476 if (IS_ERR(fid)) {
4477 err = PTR_ERR(fid);
4478 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004479 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004480 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004481
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004482 if (ops->setup)
4483 ops->setup(rif, params);
4484
4485 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004486 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004487 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004488
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004489 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004490 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004491 if (err)
4492 goto err_rif_fdb_op;
4493
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004494 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004495 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004496 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004497 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004498
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004499 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004500
Ido Schimmel4724ba562017-03-10 08:53:39 +01004501err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004502 ops->deconfigure(rif);
4503err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004504 mlxsw_sp_fid_put(fid);
4505err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004506 kfree(rif);
4507err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004508err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004509 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004510 return ERR_PTR(err);
4511}
4512
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004513void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004514{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004515 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4516 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004517 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004518 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004519
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004520 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004521 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004522
Ido Schimmel69132292017-03-10 08:53:42 +01004523 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004524 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004525 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004526 mlxsw_sp_rif_counters_free(rif);
4527 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4528 mlxsw_sp_fid_index(fid), false);
4529 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004530 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004531 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004532 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004533}
4534
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004535static void
4536mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4537 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4538{
4539 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4540
4541 params->vid = mlxsw_sp_port_vlan->vid;
4542 params->lag = mlxsw_sp_port->lagged;
4543 if (params->lag)
4544 params->lag_id = mlxsw_sp_port->lag_id;
4545 else
4546 params->system_port = mlxsw_sp_port->local_port;
4547}
4548
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004549static int
Ido Schimmela1107482017-05-26 08:37:39 +02004550mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004551 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004552{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004553 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004554 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004555 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004556 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004557 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004558 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004559
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004560 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004561 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004562 struct mlxsw_sp_rif_params params = {
4563 .dev = l3_dev,
4564 };
4565
4566 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4567 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004568 if (IS_ERR(rif))
4569 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004570 }
4571
Ido Schimmela1107482017-05-26 08:37:39 +02004572 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004573 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004574 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4575 if (err)
4576 goto err_fid_port_vid_map;
4577
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004578 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004579 if (err)
4580 goto err_port_vid_learning_set;
4581
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004582 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004583 BR_STATE_FORWARDING);
4584 if (err)
4585 goto err_port_vid_stp_set;
4586
Ido Schimmela1107482017-05-26 08:37:39 +02004587 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004588
Ido Schimmel4724ba562017-03-10 08:53:39 +01004589 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004590
4591err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004592 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004593err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004594 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4595err_fid_port_vid_map:
4596 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004597 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004598}
4599
Ido Schimmela1107482017-05-26 08:37:39 +02004600void
4601mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004602{
Ido Schimmelce95e152017-05-26 08:37:27 +02004603 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004604 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004605 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004606
Ido Schimmela1107482017-05-26 08:37:39 +02004607 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4608 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004609
Ido Schimmela1107482017-05-26 08:37:39 +02004610 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004611 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4612 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004613 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4614 /* If router port holds the last reference on the rFID, then the
4615 * associated Sub-port RIF will be destroyed.
4616 */
4617 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004618}
4619
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004620static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4621 struct net_device *port_dev,
4622 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004623{
4624 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004625 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004626
Ido Schimmelce95e152017-05-26 08:37:27 +02004627 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004628 if (WARN_ON(!mlxsw_sp_port_vlan))
4629 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004630
4631 switch (event) {
4632 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004633 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004634 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004635 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004636 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004637 break;
4638 }
4639
4640 return 0;
4641}
4642
4643static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4644 unsigned long event)
4645{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004646 if (netif_is_bridge_port(port_dev) ||
4647 netif_is_lag_port(port_dev) ||
4648 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004649 return 0;
4650
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004651 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004652}
4653
4654static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4655 struct net_device *lag_dev,
4656 unsigned long event, u16 vid)
4657{
4658 struct net_device *port_dev;
4659 struct list_head *iter;
4660 int err;
4661
4662 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4663 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004664 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4665 port_dev,
4666 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004667 if (err)
4668 return err;
4669 }
4670 }
4671
4672 return 0;
4673}
4674
4675static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4676 unsigned long event)
4677{
4678 if (netif_is_bridge_port(lag_dev))
4679 return 0;
4680
4681 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4682}
4683
Ido Schimmel4724ba562017-03-10 08:53:39 +01004684static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004685 unsigned long event)
4686{
4687 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004688 struct mlxsw_sp_rif_params params = {
4689 .dev = l3_dev,
4690 };
Ido Schimmela1107482017-05-26 08:37:39 +02004691 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004692
4693 switch (event) {
4694 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004695 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4696 if (IS_ERR(rif))
4697 return PTR_ERR(rif);
4698 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004699 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004700 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004701 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004702 break;
4703 }
4704
4705 return 0;
4706}
4707
4708static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4709 unsigned long event)
4710{
4711 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004712 u16 vid = vlan_dev_vlan_id(vlan_dev);
4713
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004714 if (netif_is_bridge_port(vlan_dev))
4715 return 0;
4716
Ido Schimmel4724ba562017-03-10 08:53:39 +01004717 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004718 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4719 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004720 else if (netif_is_lag_master(real_dev))
4721 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4722 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004723 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004724 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004725
4726 return 0;
4727}
4728
Ido Schimmelb1e45522017-04-30 19:47:14 +03004729static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4730 unsigned long event)
4731{
4732 if (mlxsw_sp_port_dev_check(dev))
4733 return mlxsw_sp_inetaddr_port_event(dev, event);
4734 else if (netif_is_lag_master(dev))
4735 return mlxsw_sp_inetaddr_lag_event(dev, event);
4736 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004737 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004738 else if (is_vlan_dev(dev))
4739 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4740 else
4741 return 0;
4742}
4743
Ido Schimmel4724ba562017-03-10 08:53:39 +01004744int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4745 unsigned long event, void *ptr)
4746{
4747 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4748 struct net_device *dev = ifa->ifa_dev->dev;
4749 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004750 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004751 int err = 0;
4752
4753 mlxsw_sp = mlxsw_sp_lower_get(dev);
4754 if (!mlxsw_sp)
4755 goto out;
4756
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004757 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004758 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004759 goto out;
4760
Ido Schimmelb1e45522017-04-30 19:47:14 +03004761 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004762out:
4763 return notifier_from_errno(err);
4764}
4765
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004766struct mlxsw_sp_inet6addr_event_work {
4767 struct work_struct work;
4768 struct net_device *dev;
4769 unsigned long event;
4770};
4771
4772static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4773{
4774 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4775 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4776 struct net_device *dev = inet6addr_work->dev;
4777 unsigned long event = inet6addr_work->event;
4778 struct mlxsw_sp *mlxsw_sp;
4779 struct mlxsw_sp_rif *rif;
4780
4781 rtnl_lock();
4782 mlxsw_sp = mlxsw_sp_lower_get(dev);
4783 if (!mlxsw_sp)
4784 goto out;
4785
4786 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4787 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4788 goto out;
4789
4790 __mlxsw_sp_inetaddr_event(dev, event);
4791out:
4792 rtnl_unlock();
4793 dev_put(dev);
4794 kfree(inet6addr_work);
4795}
4796
4797/* Called with rcu_read_lock() */
4798int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4799 unsigned long event, void *ptr)
4800{
4801 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4802 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4803 struct net_device *dev = if6->idev->dev;
4804
4805 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4806 return NOTIFY_DONE;
4807
4808 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4809 if (!inet6addr_work)
4810 return NOTIFY_BAD;
4811
4812 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4813 inet6addr_work->dev = dev;
4814 inet6addr_work->event = event;
4815 dev_hold(dev);
4816 mlxsw_core_schedule_work(&inet6addr_work->work);
4817
4818 return NOTIFY_DONE;
4819}
4820
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004821static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004822 const char *mac, int mtu)
4823{
4824 char ritr_pl[MLXSW_REG_RITR_LEN];
4825 int err;
4826
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004827 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004828 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4829 if (err)
4830 return err;
4831
4832 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4833 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4834 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4835 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4836}
4837
4838int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4839{
4840 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004841 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004842 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004843 int err;
4844
4845 mlxsw_sp = mlxsw_sp_lower_get(dev);
4846 if (!mlxsw_sp)
4847 return 0;
4848
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004849 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4850 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004851 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004852 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004853
Ido Schimmela1107482017-05-26 08:37:39 +02004854 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004855 if (err)
4856 return err;
4857
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004858 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4859 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004860 if (err)
4861 goto err_rif_edit;
4862
Ido Schimmela1107482017-05-26 08:37:39 +02004863 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004864 if (err)
4865 goto err_rif_fdb_op;
4866
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004867 ether_addr_copy(rif->addr, dev->dev_addr);
4868 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004869
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004870 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004871
4872 return 0;
4873
4874err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004875 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004876err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004877 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004878 return err;
4879}
4880
Ido Schimmelb1e45522017-04-30 19:47:14 +03004881static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4882 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004883{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004884 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004885
Ido Schimmelb1e45522017-04-30 19:47:14 +03004886 /* If netdev is already associated with a RIF, then we need to
4887 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004888 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004889 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4890 if (rif)
4891 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004892
Ido Schimmelb1e45522017-04-30 19:47:14 +03004893 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004894}
4895
Ido Schimmelb1e45522017-04-30 19:47:14 +03004896static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4897 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004898{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004899 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004900
Ido Schimmelb1e45522017-04-30 19:47:14 +03004901 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4902 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004903 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004904 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004905}
4906
Ido Schimmelb1e45522017-04-30 19:47:14 +03004907int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4908 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004909{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004910 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4911 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004912
Ido Schimmelb1e45522017-04-30 19:47:14 +03004913 if (!mlxsw_sp)
4914 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004915
Ido Schimmelb1e45522017-04-30 19:47:14 +03004916 switch (event) {
4917 case NETDEV_PRECHANGEUPPER:
4918 return 0;
4919 case NETDEV_CHANGEUPPER:
4920 if (info->linking)
4921 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4922 else
4923 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4924 break;
4925 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004926
Ido Schimmelb1e45522017-04-30 19:47:14 +03004927 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004928}
4929
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004930static struct mlxsw_sp_rif_subport *
4931mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004932{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004933 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004934}
4935
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004936static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4937 const struct mlxsw_sp_rif_params *params)
4938{
4939 struct mlxsw_sp_rif_subport *rif_subport;
4940
4941 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4942 rif_subport->vid = params->vid;
4943 rif_subport->lag = params->lag;
4944 if (params->lag)
4945 rif_subport->lag_id = params->lag_id;
4946 else
4947 rif_subport->system_port = params->system_port;
4948}
4949
4950static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4951{
4952 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4953 struct mlxsw_sp_rif_subport *rif_subport;
4954 char ritr_pl[MLXSW_REG_RITR_LEN];
4955
4956 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4957 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02004958 rif->rif_index, rif->vr_id, rif->dev->mtu);
4959 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004960 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4961 rif_subport->lag ? rif_subport->lag_id :
4962 rif_subport->system_port,
4963 rif_subport->vid);
4964
4965 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4966}
4967
4968static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4969{
4970 return mlxsw_sp_rif_subport_op(rif, true);
4971}
4972
4973static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4974{
4975 mlxsw_sp_rif_subport_op(rif, false);
4976}
4977
4978static struct mlxsw_sp_fid *
4979mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4980{
4981 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4982}
4983
4984static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4985 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4986 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4987 .setup = mlxsw_sp_rif_subport_setup,
4988 .configure = mlxsw_sp_rif_subport_configure,
4989 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4990 .fid_get = mlxsw_sp_rif_subport_fid_get,
4991};
4992
4993static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4994 enum mlxsw_reg_ritr_if_type type,
4995 u16 vid_fid, bool enable)
4996{
4997 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4998 char ritr_pl[MLXSW_REG_RITR_LEN];
4999
5000 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005001 rif->dev->mtu);
5002 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005003 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5004
5005 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5006}
5007
5008static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5009{
5010 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5011}
5012
5013static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5014{
5015 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5016 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5017 int err;
5018
5019 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5020 if (err)
5021 return err;
5022
Ido Schimmel0d284812017-07-18 10:10:12 +02005023 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5024 mlxsw_sp_router_port(mlxsw_sp), true);
5025 if (err)
5026 goto err_fid_mc_flood_set;
5027
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005028 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5029 mlxsw_sp_router_port(mlxsw_sp), true);
5030 if (err)
5031 goto err_fid_bc_flood_set;
5032
5033 return 0;
5034
5035err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005036 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5037 mlxsw_sp_router_port(mlxsw_sp), false);
5038err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005039 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5040 return err;
5041}
5042
5043static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5044{
5045 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5046 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5047
5048 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5049 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005050 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5051 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005052 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5053}
5054
5055static struct mlxsw_sp_fid *
5056mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5057{
5058 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5059
5060 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5061}
5062
5063static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5064 .type = MLXSW_SP_RIF_TYPE_VLAN,
5065 .rif_size = sizeof(struct mlxsw_sp_rif),
5066 .configure = mlxsw_sp_rif_vlan_configure,
5067 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5068 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5069};
5070
5071static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5072{
5073 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5074 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5075 int err;
5076
5077 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5078 true);
5079 if (err)
5080 return err;
5081
Ido Schimmel0d284812017-07-18 10:10:12 +02005082 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5083 mlxsw_sp_router_port(mlxsw_sp), true);
5084 if (err)
5085 goto err_fid_mc_flood_set;
5086
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005087 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5088 mlxsw_sp_router_port(mlxsw_sp), true);
5089 if (err)
5090 goto err_fid_bc_flood_set;
5091
5092 return 0;
5093
5094err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005095 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5096 mlxsw_sp_router_port(mlxsw_sp), false);
5097err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005098 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5099 return err;
5100}
5101
5102static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5103{
5104 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5105 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5106
5107 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5108 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005109 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5110 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005111 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5112}
5113
5114static struct mlxsw_sp_fid *
5115mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5116{
5117 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5118}
5119
5120static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5121 .type = MLXSW_SP_RIF_TYPE_FID,
5122 .rif_size = sizeof(struct mlxsw_sp_rif),
5123 .configure = mlxsw_sp_rif_fid_configure,
5124 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5125 .fid_get = mlxsw_sp_rif_fid_fid_get,
5126};
5127
5128static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5129 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5130 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5131 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
5132};
5133
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005134static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5135{
5136 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5137
5138 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5139 sizeof(struct mlxsw_sp_rif *),
5140 GFP_KERNEL);
5141 if (!mlxsw_sp->router->rifs)
5142 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005143
5144 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5145
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005146 return 0;
5147}
5148
5149static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5150{
5151 int i;
5152
5153 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5154 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5155
5156 kfree(mlxsw_sp->router->rifs);
5157}
5158
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005159static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5160{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005161 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005162
5163 /* Flush pending FIB notifications and then flush the device's
5164 * table before requesting another dump. The FIB notification
5165 * block is unregistered, so no need to take RTNL.
5166 */
5167 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005168 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5169 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005170}
5171
Ido Schimmel4724ba562017-03-10 08:53:39 +01005172static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5173{
5174 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5175 u64 max_rifs;
5176 int err;
5177
5178 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5179 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005180 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005181
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005182 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005183 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5184 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5185 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005186 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005187 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005188}
5189
5190static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5191{
5192 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005193
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005194 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005195 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005196}
5197
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005198int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5199{
Ido Schimmel9011b672017-05-16 19:38:25 +02005200 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005201 int err;
5202
Ido Schimmel9011b672017-05-16 19:38:25 +02005203 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5204 if (!router)
5205 return -ENOMEM;
5206 mlxsw_sp->router = router;
5207 router->mlxsw_sp = mlxsw_sp;
5208
5209 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005210 err = __mlxsw_sp_router_init(mlxsw_sp);
5211 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005212 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005213
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005214 err = mlxsw_sp_rifs_init(mlxsw_sp);
5215 if (err)
5216 goto err_rifs_init;
5217
Ido Schimmel9011b672017-05-16 19:38:25 +02005218 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005219 &mlxsw_sp_nexthop_ht_params);
5220 if (err)
5221 goto err_nexthop_ht_init;
5222
Ido Schimmel9011b672017-05-16 19:38:25 +02005223 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005224 &mlxsw_sp_nexthop_group_ht_params);
5225 if (err)
5226 goto err_nexthop_group_ht_init;
5227
Ido Schimmel8494ab02017-03-24 08:02:47 +01005228 err = mlxsw_sp_lpm_init(mlxsw_sp);
5229 if (err)
5230 goto err_lpm_init;
5231
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005232 err = mlxsw_sp_vrs_init(mlxsw_sp);
5233 if (err)
5234 goto err_vrs_init;
5235
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005236 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005237 if (err)
5238 goto err_neigh_init;
5239
Ido Schimmel7e39d112017-05-16 19:38:28 +02005240 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5241 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005242 mlxsw_sp_router_fib_dump_flush);
5243 if (err)
5244 goto err_register_fib_notifier;
5245
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005246 return 0;
5247
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005248err_register_fib_notifier:
5249 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005250err_neigh_init:
5251 mlxsw_sp_vrs_fini(mlxsw_sp);
5252err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005253 mlxsw_sp_lpm_fini(mlxsw_sp);
5254err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005255 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005256err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005257 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005258err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005259 mlxsw_sp_rifs_fini(mlxsw_sp);
5260err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005261 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005262err_router_init:
5263 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005264 return err;
5265}
5266
5267void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5268{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005269 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005270 mlxsw_sp_neigh_fini(mlxsw_sp);
5271 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005272 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005273 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5274 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005275 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005276 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005277 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005278}