blob: 38477c5f7d4dfd42a54cda697a98168c6ecad4f1 [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"
Petr Machata38ebc0f2017-09-02 23:49:17 +020065#include "spectrum_ipip.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020066#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020067
Ido Schimmel9011b672017-05-16 19:38:25 +020068struct mlxsw_sp_vr;
69struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020070struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020071
72struct mlxsw_sp_router {
73 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020074 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020075 struct mlxsw_sp_vr *vrs;
76 struct rhashtable neigh_ht;
77 struct rhashtable nexthop_group_ht;
78 struct rhashtable nexthop_ht;
79 struct {
80 struct mlxsw_sp_lpm_tree *trees;
81 unsigned int tree_count;
82 } lpm;
83 struct {
84 struct delayed_work dw;
85 unsigned long interval; /* ms */
86 } neighs_update;
87 struct delayed_work nexthop_probe_dw;
88#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
89 struct list_head nexthop_neighs_list;
90 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020091 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020092 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020093 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020094};
95
Ido Schimmel4724ba562017-03-10 08:53:39 +010096struct mlxsw_sp_rif {
97 struct list_head nexthop_list;
98 struct list_head neigh_list;
99 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200100 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100101 unsigned char addr[ETH_ALEN];
102 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100103 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100104 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200105 const struct mlxsw_sp_rif_ops *ops;
106 struct mlxsw_sp *mlxsw_sp;
107
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200108 unsigned int counter_ingress;
109 bool counter_ingress_valid;
110 unsigned int counter_egress;
111 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100112};
113
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200114struct mlxsw_sp_rif_params {
115 struct net_device *dev;
116 union {
117 u16 system_port;
118 u16 lag_id;
119 };
120 u16 vid;
121 bool lag;
122};
123
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200124struct mlxsw_sp_rif_subport {
125 struct mlxsw_sp_rif common;
126 union {
127 u16 system_port;
128 u16 lag_id;
129 };
130 u16 vid;
131 bool lag;
132};
133
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200134struct mlxsw_sp_rif_ops {
135 enum mlxsw_sp_rif_type type;
136 size_t rif_size;
137
138 void (*setup)(struct mlxsw_sp_rif *rif,
139 const struct mlxsw_sp_rif_params *params);
140 int (*configure)(struct mlxsw_sp_rif *rif);
141 void (*deconfigure)(struct mlxsw_sp_rif *rif);
142 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
143};
144
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200145static unsigned int *
146mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
147 enum mlxsw_sp_rif_counter_dir dir)
148{
149 switch (dir) {
150 case MLXSW_SP_RIF_COUNTER_EGRESS:
151 return &rif->counter_egress;
152 case MLXSW_SP_RIF_COUNTER_INGRESS:
153 return &rif->counter_ingress;
154 }
155 return NULL;
156}
157
158static bool
159mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
160 enum mlxsw_sp_rif_counter_dir dir)
161{
162 switch (dir) {
163 case MLXSW_SP_RIF_COUNTER_EGRESS:
164 return rif->counter_egress_valid;
165 case MLXSW_SP_RIF_COUNTER_INGRESS:
166 return rif->counter_ingress_valid;
167 }
168 return false;
169}
170
171static void
172mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
173 enum mlxsw_sp_rif_counter_dir dir,
174 bool valid)
175{
176 switch (dir) {
177 case MLXSW_SP_RIF_COUNTER_EGRESS:
178 rif->counter_egress_valid = valid;
179 break;
180 case MLXSW_SP_RIF_COUNTER_INGRESS:
181 rif->counter_ingress_valid = valid;
182 break;
183 }
184}
185
186static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
187 unsigned int counter_index, bool enable,
188 enum mlxsw_sp_rif_counter_dir dir)
189{
190 char ritr_pl[MLXSW_REG_RITR_LEN];
191 bool is_egress = false;
192 int err;
193
194 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
195 is_egress = true;
196 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
197 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
198 if (err)
199 return err;
200
201 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
202 is_egress);
203 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
204}
205
206int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
207 struct mlxsw_sp_rif *rif,
208 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
209{
210 char ricnt_pl[MLXSW_REG_RICNT_LEN];
211 unsigned int *p_counter_index;
212 bool valid;
213 int err;
214
215 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
216 if (!valid)
217 return -EINVAL;
218
219 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
220 if (!p_counter_index)
221 return -EINVAL;
222 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
223 MLXSW_REG_RICNT_OPCODE_NOP);
224 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
225 if (err)
226 return err;
227 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
228 return 0;
229}
230
231static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
232 unsigned int counter_index)
233{
234 char ricnt_pl[MLXSW_REG_RICNT_LEN];
235
236 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
237 MLXSW_REG_RICNT_OPCODE_CLEAR);
238 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
239}
240
241int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
242 struct mlxsw_sp_rif *rif,
243 enum mlxsw_sp_rif_counter_dir dir)
244{
245 unsigned int *p_counter_index;
246 int err;
247
248 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
249 if (!p_counter_index)
250 return -EINVAL;
251 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
252 p_counter_index);
253 if (err)
254 return err;
255
256 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
257 if (err)
258 goto err_counter_clear;
259
260 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
261 *p_counter_index, true, dir);
262 if (err)
263 goto err_counter_edit;
264 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
265 return 0;
266
267err_counter_edit:
268err_counter_clear:
269 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
270 *p_counter_index);
271 return err;
272}
273
274void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
275 struct mlxsw_sp_rif *rif,
276 enum mlxsw_sp_rif_counter_dir dir)
277{
278 unsigned int *p_counter_index;
279
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200280 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
281 return;
282
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200283 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
284 if (WARN_ON(!p_counter_index))
285 return;
286 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
287 *p_counter_index, false, dir);
288 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
289 *p_counter_index);
290 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
291}
292
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200293static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
294{
295 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
296 struct devlink *devlink;
297
298 devlink = priv_to_devlink(mlxsw_sp->core);
299 if (!devlink_dpipe_table_counter_enabled(devlink,
300 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
301 return;
302 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
303}
304
305static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
306{
307 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
308
309 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
310}
311
Ido Schimmel4724ba562017-03-10 08:53:39 +0100312static struct mlxsw_sp_rif *
313mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
314 const struct net_device *dev);
315
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200316#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200317
318struct mlxsw_sp_prefix_usage {
319 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
320};
321
Jiri Pirko53342022016-07-04 08:23:08 +0200322#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
323 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
324
325static bool
326mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
327 struct mlxsw_sp_prefix_usage *prefix_usage2)
328{
329 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
330}
331
Jiri Pirko6b75c482016-07-04 08:23:09 +0200332static bool
333mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
334{
335 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
336
337 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
338}
339
340static void
341mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
342 struct mlxsw_sp_prefix_usage *prefix_usage2)
343{
344 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
345}
346
347static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200348mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
349 unsigned char prefix_len)
350{
351 set_bit(prefix_len, prefix_usage->b);
352}
353
354static void
355mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
356 unsigned char prefix_len)
357{
358 clear_bit(prefix_len, prefix_usage->b);
359}
360
361struct mlxsw_sp_fib_key {
362 unsigned char addr[sizeof(struct in6_addr)];
363 unsigned char prefix_len;
364};
365
Jiri Pirko61c503f2016-07-04 08:23:11 +0200366enum mlxsw_sp_fib_entry_type {
367 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
368 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
369 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
370};
371
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200372struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200373struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200374
Ido Schimmel9aecce12017-02-09 10:28:42 +0100375struct mlxsw_sp_fib_node {
376 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200377 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100378 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100379 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100380 struct mlxsw_sp_fib_key key;
381};
382
Ido Schimmel9aecce12017-02-09 10:28:42 +0100383struct mlxsw_sp_fib_entry {
384 struct list_head list;
385 struct mlxsw_sp_fib_node *fib_node;
386 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200387 struct list_head nexthop_group_node;
388 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200389};
390
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200391struct mlxsw_sp_fib4_entry {
392 struct mlxsw_sp_fib_entry common;
393 u32 tb_id;
394 u32 prio;
395 u8 tos;
396 u8 type;
397};
398
Ido Schimmel428b8512017-08-03 13:28:28 +0200399struct mlxsw_sp_fib6_entry {
400 struct mlxsw_sp_fib_entry common;
401 struct list_head rt6_list;
402 unsigned int nrt6;
403};
404
405struct mlxsw_sp_rt6 {
406 struct list_head list;
407 struct rt6_info *rt;
408};
409
Ido Schimmel9011b672017-05-16 19:38:25 +0200410struct mlxsw_sp_lpm_tree {
411 u8 id; /* tree ID */
412 unsigned int ref_count;
413 enum mlxsw_sp_l3proto proto;
414 struct mlxsw_sp_prefix_usage prefix_usage;
415};
416
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200417struct mlxsw_sp_fib {
418 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100419 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100420 struct mlxsw_sp_vr *vr;
421 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200422 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
423 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100424 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200425};
426
Ido Schimmel9011b672017-05-16 19:38:25 +0200427struct mlxsw_sp_vr {
428 u16 id; /* virtual router ID */
429 u32 tb_id; /* kernel fib table id */
430 unsigned int rif_count;
431 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200432 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200433};
434
Ido Schimmel9aecce12017-02-09 10:28:42 +0100435static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200436
Ido Schimmel76610eb2017-03-10 08:53:41 +0100437static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
438 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200439{
440 struct mlxsw_sp_fib *fib;
441 int err;
442
443 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
444 if (!fib)
445 return ERR_PTR(-ENOMEM);
446 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
447 if (err)
448 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100449 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100450 fib->proto = proto;
451 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200452 return fib;
453
454err_rhashtable_init:
455 kfree(fib);
456 return ERR_PTR(err);
457}
458
459static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
460{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100461 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100462 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200463 rhashtable_destroy(&fib->ht);
464 kfree(fib);
465}
466
Jiri Pirko53342022016-07-04 08:23:08 +0200467static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100468mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200469{
470 static struct mlxsw_sp_lpm_tree *lpm_tree;
471 int i;
472
Ido Schimmel9011b672017-05-16 19:38:25 +0200473 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
474 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100475 if (lpm_tree->ref_count == 0)
476 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200477 }
478 return NULL;
479}
480
481static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
482 struct mlxsw_sp_lpm_tree *lpm_tree)
483{
484 char ralta_pl[MLXSW_REG_RALTA_LEN];
485
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200486 mlxsw_reg_ralta_pack(ralta_pl, true,
487 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
488 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200489 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
490}
491
Ido Schimmelcc702672017-08-14 10:54:03 +0200492static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
493 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200494{
495 char ralta_pl[MLXSW_REG_RALTA_LEN];
496
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200497 mlxsw_reg_ralta_pack(ralta_pl, false,
498 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
499 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200500 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200501}
502
503static int
504mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
505 struct mlxsw_sp_prefix_usage *prefix_usage,
506 struct mlxsw_sp_lpm_tree *lpm_tree)
507{
508 char ralst_pl[MLXSW_REG_RALST_LEN];
509 u8 root_bin = 0;
510 u8 prefix;
511 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
512
513 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
514 root_bin = prefix;
515
516 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
517 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
518 if (prefix == 0)
519 continue;
520 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
521 MLXSW_REG_RALST_BIN_NO_CHILD);
522 last_prefix = prefix;
523 }
524 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
525}
526
527static struct mlxsw_sp_lpm_tree *
528mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
529 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100530 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200531{
532 struct mlxsw_sp_lpm_tree *lpm_tree;
533 int err;
534
Ido Schimmel382dbb42017-03-10 08:53:40 +0100535 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200536 if (!lpm_tree)
537 return ERR_PTR(-EBUSY);
538 lpm_tree->proto = proto;
539 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
540 if (err)
541 return ERR_PTR(err);
542
543 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
544 lpm_tree);
545 if (err)
546 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200547 memcpy(&lpm_tree->prefix_usage, prefix_usage,
548 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200549 return lpm_tree;
550
551err_left_struct_set:
552 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
553 return ERR_PTR(err);
554}
555
Ido Schimmelcc702672017-08-14 10:54:03 +0200556static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
557 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200558{
Ido Schimmelcc702672017-08-14 10:54:03 +0200559 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200560}
561
562static struct mlxsw_sp_lpm_tree *
563mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
564 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100565 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200566{
567 struct mlxsw_sp_lpm_tree *lpm_tree;
568 int i;
569
Ido Schimmel9011b672017-05-16 19:38:25 +0200570 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
571 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200572 if (lpm_tree->ref_count != 0 &&
573 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200574 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
575 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200576 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200577 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200578 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
579}
Jiri Pirko53342022016-07-04 08:23:08 +0200580
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200581static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
582{
Jiri Pirko53342022016-07-04 08:23:08 +0200583 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200584}
585
Ido Schimmelcc702672017-08-14 10:54:03 +0200586static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
587 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200588{
589 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200590 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200591}
592
Ido Schimmeld7a60302017-06-08 08:47:43 +0200593#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100594
595static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200596{
597 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100598 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200599 int i;
600
Ido Schimmel8494ab02017-03-24 08:02:47 +0100601 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
602 return -EIO;
603
604 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200605 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
606 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100607 sizeof(struct mlxsw_sp_lpm_tree),
608 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200609 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100610 return -ENOMEM;
611
Ido Schimmel9011b672017-05-16 19:38:25 +0200612 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
613 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200614 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
615 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100616
617 return 0;
618}
619
620static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
621{
Ido Schimmel9011b672017-05-16 19:38:25 +0200622 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200623}
624
Ido Schimmel76610eb2017-03-10 08:53:41 +0100625static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
626{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200627 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100628}
629
Jiri Pirko6b75c482016-07-04 08:23:09 +0200630static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
631{
632 struct mlxsw_sp_vr *vr;
633 int i;
634
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200635 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200636 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100637 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200638 return vr;
639 }
640 return NULL;
641}
642
643static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200644 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200645{
646 char raltb_pl[MLXSW_REG_RALTB_LEN];
647
Ido Schimmel76610eb2017-03-10 08:53:41 +0100648 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
649 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200650 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200651 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
652}
653
654static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100655 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200656{
657 char raltb_pl[MLXSW_REG_RALTB_LEN];
658
659 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100660 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
661 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200662 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
663}
664
665static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
666{
667 /* For our purpose, squash main and local table into one */
668 if (tb_id == RT_TABLE_LOCAL)
669 tb_id = RT_TABLE_MAIN;
670 return tb_id;
671}
672
673static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100674 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200675{
676 struct mlxsw_sp_vr *vr;
677 int i;
678
679 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200680
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200681 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200682 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100683 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200684 return vr;
685 }
686 return NULL;
687}
688
Ido Schimmel76610eb2017-03-10 08:53:41 +0100689static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
690 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200691{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100692 switch (proto) {
693 case MLXSW_SP_L3_PROTO_IPV4:
694 return vr->fib4;
695 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200696 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100697 }
698 return NULL;
699}
700
701static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
702 u32 tb_id)
703{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200704 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200705 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200706
707 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
708 if (!vr)
709 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100710 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
711 if (IS_ERR(vr->fib4))
712 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200713 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
714 if (IS_ERR(vr->fib6)) {
715 err = PTR_ERR(vr->fib6);
716 goto err_fib6_create;
717 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200718 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200719 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200720
721err_fib6_create:
722 mlxsw_sp_fib_destroy(vr->fib4);
723 vr->fib4 = NULL;
724 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200725}
726
Ido Schimmel76610eb2017-03-10 08:53:41 +0100727static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200728{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200729 mlxsw_sp_fib_destroy(vr->fib6);
730 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100731 mlxsw_sp_fib_destroy(vr->fib4);
732 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200733}
734
Ido Schimmel76610eb2017-03-10 08:53:41 +0100735static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200736{
737 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200738
739 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100740 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
741 if (!vr)
742 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200743 return vr;
744}
745
Ido Schimmel76610eb2017-03-10 08:53:41 +0100746static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200748 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
749 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100750 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200751}
752
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200753static bool
754mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
755 enum mlxsw_sp_l3proto proto, u8 tree_id)
756{
757 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
758
759 if (!mlxsw_sp_vr_is_used(vr))
760 return false;
761 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
762 return true;
763 return false;
764}
765
766static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
767 struct mlxsw_sp_fib *fib,
768 struct mlxsw_sp_lpm_tree *new_tree)
769{
770 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
771 int err;
772
773 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
774 if (err)
775 return err;
776 fib->lpm_tree = new_tree;
777 mlxsw_sp_lpm_tree_hold(new_tree);
778 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
779 return 0;
780}
781
782static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
783 struct mlxsw_sp_fib *fib,
784 struct mlxsw_sp_lpm_tree *new_tree)
785{
786 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
787 enum mlxsw_sp_l3proto proto = fib->proto;
788 u8 old_id, new_id = new_tree->id;
789 struct mlxsw_sp_vr *vr;
790 int i, err;
791
792 if (!old_tree)
793 goto no_replace;
794 old_id = old_tree->id;
795
796 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
797 vr = &mlxsw_sp->router->vrs[i];
798 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
799 continue;
800 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
801 mlxsw_sp_vr_fib(vr, proto),
802 new_tree);
803 if (err)
804 goto err_tree_replace;
805 }
806
807 return 0;
808
809err_tree_replace:
810 for (i--; i >= 0; i--) {
811 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
812 continue;
813 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
814 mlxsw_sp_vr_fib(vr, proto),
815 old_tree);
816 }
817 return err;
818
819no_replace:
820 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
821 if (err)
822 return err;
823 fib->lpm_tree = new_tree;
824 mlxsw_sp_lpm_tree_hold(new_tree);
825 return 0;
826}
827
828static void
829mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
830 enum mlxsw_sp_l3proto proto,
831 struct mlxsw_sp_prefix_usage *req_prefix_usage)
832{
833 int i;
834
835 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
836 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
837 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
838 unsigned char prefix;
839
840 if (!mlxsw_sp_vr_is_used(vr))
841 continue;
842 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
843 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
844 }
845}
846
Nogah Frankel9497c042016-09-20 11:16:54 +0200847static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200848{
849 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200850 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200851 int i;
852
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200853 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200854 return -EIO;
855
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200856 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200857 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
858 GFP_KERNEL);
859 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200860 return -ENOMEM;
861
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200862 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200863 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200864 vr->id = i;
865 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200866
867 return 0;
868}
869
Ido Schimmelac571de2016-11-14 11:26:32 +0100870static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
871
Nogah Frankel9497c042016-09-20 11:16:54 +0200872static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
873{
Ido Schimmel30572242016-12-03 16:45:01 +0100874 /* At this stage we're guaranteed not to have new incoming
875 * FIB notifications and the work queue is free from FIBs
876 * sitting on top of mlxsw netdevs. However, we can still
877 * have other FIBs queued. Flush the queue before flushing
878 * the device's tables. No need for locks, as we're the only
879 * writer.
880 */
881 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100882 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200883 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200884}
885
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200886struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100887 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200888};
889
890struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100891 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200892 struct rhash_head ht_node;
893 struct mlxsw_sp_neigh_key key;
894 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100895 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200896 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200897 struct list_head nexthop_list; /* list of nexthops using
898 * this neigh entry
899 */
Yotam Gigib2157142016-07-05 11:27:51 +0200900 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200901 unsigned int counter_index;
902 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200903};
904
905static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
906 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
907 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
908 .key_len = sizeof(struct mlxsw_sp_neigh_key),
909};
910
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +0200911struct mlxsw_sp_neigh_entry *
912mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
913 struct mlxsw_sp_neigh_entry *neigh_entry)
914{
915 if (!neigh_entry) {
916 if (list_empty(&rif->neigh_list))
917 return NULL;
918 else
919 return list_first_entry(&rif->neigh_list,
920 typeof(*neigh_entry),
921 rif_list_node);
922 }
923 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
924 return NULL;
925 return list_next_entry(neigh_entry, rif_list_node);
926}
927
928int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
929{
930 return neigh_entry->key.n->tbl->family;
931}
932
933unsigned char *
934mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
935{
936 return neigh_entry->ha;
937}
938
939u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
940{
941 struct neighbour *n;
942
943 n = neigh_entry->key.n;
944 return ntohl(*((__be32 *) n->primary_key));
945}
946
Arkadi Sharshevsky02507682017-08-31 17:59:15 +0200947struct in6_addr *
948mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
949{
950 struct neighbour *n;
951
952 n = neigh_entry->key.n;
953 return (struct in6_addr *) &n->primary_key;
954}
955
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200956int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
957 struct mlxsw_sp_neigh_entry *neigh_entry,
958 u64 *p_counter)
959{
960 if (!neigh_entry->counter_valid)
961 return -EINVAL;
962
963 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
964 p_counter, NULL);
965}
966
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100967static struct mlxsw_sp_neigh_entry *
968mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
969 u16 rif)
970{
971 struct mlxsw_sp_neigh_entry *neigh_entry;
972
973 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
974 if (!neigh_entry)
975 return NULL;
976
977 neigh_entry->key.n = n;
978 neigh_entry->rif = rif;
979 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
980
981 return neigh_entry;
982}
983
984static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
985{
986 kfree(neigh_entry);
987}
988
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200989static int
990mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
991 struct mlxsw_sp_neigh_entry *neigh_entry)
992{
Ido Schimmel9011b672017-05-16 19:38:25 +0200993 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200994 &neigh_entry->ht_node,
995 mlxsw_sp_neigh_ht_params);
996}
997
998static void
999mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1000 struct mlxsw_sp_neigh_entry *neigh_entry)
1001{
Ido Schimmel9011b672017-05-16 19:38:25 +02001002 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001003 &neigh_entry->ht_node,
1004 mlxsw_sp_neigh_ht_params);
1005}
1006
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001007static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001008mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1009 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001010{
1011 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001012 const char *table_name;
1013
1014 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1015 case AF_INET:
1016 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1017 break;
1018 case AF_INET6:
1019 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1020 break;
1021 default:
1022 WARN_ON(1);
1023 return false;
1024 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001025
1026 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001027 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001028}
1029
1030static void
1031mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1032 struct mlxsw_sp_neigh_entry *neigh_entry)
1033{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001034 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001035 return;
1036
1037 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1038 return;
1039
1040 neigh_entry->counter_valid = true;
1041}
1042
1043static void
1044mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1045 struct mlxsw_sp_neigh_entry *neigh_entry)
1046{
1047 if (!neigh_entry->counter_valid)
1048 return;
1049 mlxsw_sp_flow_counter_free(mlxsw_sp,
1050 neigh_entry->counter_index);
1051 neigh_entry->counter_valid = false;
1052}
1053
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001054static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001055mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001056{
1057 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001058 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001059 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001060
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001061 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1062 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001063 return ERR_PTR(-EINVAL);
1064
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001065 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001066 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001067 return ERR_PTR(-ENOMEM);
1068
1069 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1070 if (err)
1071 goto err_neigh_entry_insert;
1072
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001073 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001074 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001075
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001076 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001077
1078err_neigh_entry_insert:
1079 mlxsw_sp_neigh_entry_free(neigh_entry);
1080 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001081}
1082
1083static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001084mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1085 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001086{
Ido Schimmel9665b742017-02-08 11:16:42 +01001087 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001088 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001089 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1090 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001091}
1092
1093static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001094mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001095{
Jiri Pirko33b13412016-11-10 12:31:04 +01001096 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001097
Jiri Pirko33b13412016-11-10 12:31:04 +01001098 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001099 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001100 &key, mlxsw_sp_neigh_ht_params);
1101}
1102
Yotam Gigic723c7352016-07-05 11:27:43 +02001103static void
1104mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1105{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001106 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001107
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001108#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001109 interval = min_t(unsigned long,
1110 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1111 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001112#else
1113 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1114#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001115 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001116}
1117
1118static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1119 char *rauhtd_pl,
1120 int ent_index)
1121{
1122 struct net_device *dev;
1123 struct neighbour *n;
1124 __be32 dipn;
1125 u32 dip;
1126 u16 rif;
1127
1128 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1129
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001130 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001131 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1132 return;
1133 }
1134
1135 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001136 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001137 n = neigh_lookup(&arp_tbl, &dipn, dev);
1138 if (!n) {
1139 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1140 &dip);
1141 return;
1142 }
1143
1144 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1145 neigh_event_send(n, NULL);
1146 neigh_release(n);
1147}
1148
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001149#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001150static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1151 char *rauhtd_pl,
1152 int rec_index)
1153{
1154 struct net_device *dev;
1155 struct neighbour *n;
1156 struct in6_addr dip;
1157 u16 rif;
1158
1159 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1160 (char *) &dip);
1161
1162 if (!mlxsw_sp->router->rifs[rif]) {
1163 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1164 return;
1165 }
1166
1167 dev = mlxsw_sp->router->rifs[rif]->dev;
1168 n = neigh_lookup(&nd_tbl, &dip, dev);
1169 if (!n) {
1170 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1171 &dip);
1172 return;
1173 }
1174
1175 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1176 neigh_event_send(n, NULL);
1177 neigh_release(n);
1178}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001179#else
1180static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1181 char *rauhtd_pl,
1182 int rec_index)
1183{
1184}
1185#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001186
Yotam Gigic723c7352016-07-05 11:27:43 +02001187static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1188 char *rauhtd_pl,
1189 int rec_index)
1190{
1191 u8 num_entries;
1192 int i;
1193
1194 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1195 rec_index);
1196 /* Hardware starts counting at 0, so add 1. */
1197 num_entries++;
1198
1199 /* Each record consists of several neighbour entries. */
1200 for (i = 0; i < num_entries; i++) {
1201 int ent_index;
1202
1203 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1204 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1205 ent_index);
1206 }
1207
1208}
1209
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001210static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1211 char *rauhtd_pl,
1212 int rec_index)
1213{
1214 /* One record contains one entry. */
1215 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1216 rec_index);
1217}
1218
Yotam Gigic723c7352016-07-05 11:27:43 +02001219static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1220 char *rauhtd_pl, int rec_index)
1221{
1222 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1223 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1224 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1225 rec_index);
1226 break;
1227 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001228 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1229 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001230 break;
1231 }
1232}
1233
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001234static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1235{
1236 u8 num_rec, last_rec_index, num_entries;
1237
1238 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1239 last_rec_index = num_rec - 1;
1240
1241 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1242 return false;
1243 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1244 MLXSW_REG_RAUHTD_TYPE_IPV6)
1245 return true;
1246
1247 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1248 last_rec_index);
1249 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1250 return true;
1251 return false;
1252}
1253
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001254static int
1255__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1256 char *rauhtd_pl,
1257 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001258{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001259 int i, num_rec;
1260 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001261
1262 /* Make sure the neighbour's netdev isn't removed in the
1263 * process.
1264 */
1265 rtnl_lock();
1266 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001267 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001268 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1269 rauhtd_pl);
1270 if (err) {
1271 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1272 break;
1273 }
1274 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1275 for (i = 0; i < num_rec; i++)
1276 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1277 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001278 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001279 rtnl_unlock();
1280
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001281 return err;
1282}
1283
1284static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1285{
1286 enum mlxsw_reg_rauhtd_type type;
1287 char *rauhtd_pl;
1288 int err;
1289
1290 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1291 if (!rauhtd_pl)
1292 return -ENOMEM;
1293
1294 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1295 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1296 if (err)
1297 goto out;
1298
1299 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1300 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1301out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001302 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001303 return err;
1304}
1305
1306static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1307{
1308 struct mlxsw_sp_neigh_entry *neigh_entry;
1309
1310 /* Take RTNL mutex here to prevent lists from changes */
1311 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001312 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001313 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001314 /* If this neigh have nexthops, make the kernel think this neigh
1315 * is active regardless of the traffic.
1316 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001317 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001318 rtnl_unlock();
1319}
1320
1321static void
1322mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1323{
Ido Schimmel9011b672017-05-16 19:38:25 +02001324 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001325
Ido Schimmel9011b672017-05-16 19:38:25 +02001326 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001327 msecs_to_jiffies(interval));
1328}
1329
1330static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1331{
Ido Schimmel9011b672017-05-16 19:38:25 +02001332 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001333 int err;
1334
Ido Schimmel9011b672017-05-16 19:38:25 +02001335 router = container_of(work, struct mlxsw_sp_router,
1336 neighs_update.dw.work);
1337 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001338 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001339 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001340
Ido Schimmel9011b672017-05-16 19:38:25 +02001341 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001342
Ido Schimmel9011b672017-05-16 19:38:25 +02001343 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001344}
1345
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001346static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1347{
1348 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001349 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001350
Ido Schimmel9011b672017-05-16 19:38:25 +02001351 router = container_of(work, struct mlxsw_sp_router,
1352 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001353 /* Iterate over nexthop neighbours, find those who are unresolved and
1354 * send arp on them. This solves the chicken-egg problem when
1355 * the nexthop wouldn't get offloaded until the neighbor is resolved
1356 * but it wouldn't get resolved ever in case traffic is flowing in HW
1357 * using different nexthop.
1358 *
1359 * Take RTNL mutex here to prevent lists from changes.
1360 */
1361 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001362 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001363 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001364 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001365 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001366 rtnl_unlock();
1367
Ido Schimmel9011b672017-05-16 19:38:25 +02001368 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001369 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1370}
1371
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001372static void
1373mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1374 struct mlxsw_sp_neigh_entry *neigh_entry,
1375 bool removing);
1376
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001377static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001378{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001379 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1380 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1381}
1382
1383static void
1384mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1385 struct mlxsw_sp_neigh_entry *neigh_entry,
1386 enum mlxsw_reg_rauht_op op)
1387{
Jiri Pirko33b13412016-11-10 12:31:04 +01001388 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001389 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001390 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001391
1392 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1393 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001394 if (neigh_entry->counter_valid)
1395 mlxsw_reg_rauht_pack_counter(rauht_pl,
1396 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001397 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1398}
1399
1400static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001401mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1402 struct mlxsw_sp_neigh_entry *neigh_entry,
1403 enum mlxsw_reg_rauht_op op)
1404{
1405 struct neighbour *n = neigh_entry->key.n;
1406 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1407 const char *dip = n->primary_key;
1408
1409 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1410 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001411 if (neigh_entry->counter_valid)
1412 mlxsw_reg_rauht_pack_counter(rauht_pl,
1413 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001414 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1415}
1416
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001417bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001418{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001419 struct neighbour *n = neigh_entry->key.n;
1420
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001421 /* Packets with a link-local destination address are trapped
1422 * after LPM lookup and never reach the neighbour table, so
1423 * there is no need to program such neighbours to the device.
1424 */
1425 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1426 IPV6_ADDR_LINKLOCAL)
1427 return true;
1428 return false;
1429}
1430
1431static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001432mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1433 struct mlxsw_sp_neigh_entry *neigh_entry,
1434 bool adding)
1435{
1436 if (!adding && !neigh_entry->connected)
1437 return;
1438 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001439 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001440 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1441 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001442 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001443 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001444 return;
1445 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1446 mlxsw_sp_rauht_op(adding));
1447 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001448 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001449 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001450}
1451
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001452void
1453mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1454 struct mlxsw_sp_neigh_entry *neigh_entry,
1455 bool adding)
1456{
1457 if (adding)
1458 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1459 else
1460 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1461 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1462}
1463
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001464struct mlxsw_sp_neigh_event_work {
1465 struct work_struct work;
1466 struct mlxsw_sp *mlxsw_sp;
1467 struct neighbour *n;
1468};
1469
1470static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1471{
1472 struct mlxsw_sp_neigh_event_work *neigh_work =
1473 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1474 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1475 struct mlxsw_sp_neigh_entry *neigh_entry;
1476 struct neighbour *n = neigh_work->n;
1477 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001478 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001479 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001480
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001481 /* If these parameters are changed after we release the lock,
1482 * then we are guaranteed to receive another event letting us
1483 * know about it.
1484 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001485 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001486 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001487 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001488 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001489 read_unlock_bh(&n->lock);
1490
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001491 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001492 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001493 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1494 if (!entry_connected && !neigh_entry)
1495 goto out;
1496 if (!neigh_entry) {
1497 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1498 if (IS_ERR(neigh_entry))
1499 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001500 }
1501
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001502 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1503 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1504 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1505
1506 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1507 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1508
1509out:
1510 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001511 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001512 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001513}
1514
Jiri Pirkoe7322632016-09-01 10:37:43 +02001515int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1516 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001517{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001518 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001519 struct mlxsw_sp_port *mlxsw_sp_port;
1520 struct mlxsw_sp *mlxsw_sp;
1521 unsigned long interval;
1522 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001523 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001524
1525 switch (event) {
1526 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1527 p = ptr;
1528
1529 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001530 if (!p->dev || (p->tbl->family != AF_INET &&
1531 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001532 return NOTIFY_DONE;
1533
1534 /* We are in atomic context and can't take RTNL mutex,
1535 * so use RCU variant to walk the device chain.
1536 */
1537 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1538 if (!mlxsw_sp_port)
1539 return NOTIFY_DONE;
1540
1541 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1542 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001543 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001544
1545 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1546 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001547 case NETEVENT_NEIGH_UPDATE:
1548 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001549
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001550 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001551 return NOTIFY_DONE;
1552
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001553 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001554 if (!mlxsw_sp_port)
1555 return NOTIFY_DONE;
1556
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001557 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1558 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001559 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001560 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001561 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001562
1563 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1564 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1565 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001566
1567 /* Take a reference to ensure the neighbour won't be
1568 * destructed until we drop the reference in delayed
1569 * work.
1570 */
1571 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001572 mlxsw_core_schedule_work(&neigh_work->work);
1573 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001574 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001575 }
1576
1577 return NOTIFY_DONE;
1578}
1579
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001580static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1581{
Yotam Gigic723c7352016-07-05 11:27:43 +02001582 int err;
1583
Ido Schimmel9011b672017-05-16 19:38:25 +02001584 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001585 &mlxsw_sp_neigh_ht_params);
1586 if (err)
1587 return err;
1588
1589 /* Initialize the polling interval according to the default
1590 * table.
1591 */
1592 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1593
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001594 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001595 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001596 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001597 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001598 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001599 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1600 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001601 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001602}
1603
1604static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1605{
Ido Schimmel9011b672017-05-16 19:38:25 +02001606 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1607 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1608 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001609}
1610
Ido Schimmel9665b742017-02-08 11:16:42 +01001611static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001612 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001613{
1614 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1615
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001616 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001617 rif_list_node) {
1618 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001619 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001620 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001621}
1622
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001623struct mlxsw_sp_nexthop_key {
1624 struct fib_nh *fib_nh;
1625};
1626
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001627struct mlxsw_sp_nexthop {
1628 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001629 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001630 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1631 * this belongs to
1632 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001633 struct rhash_head ht_node;
1634 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001635 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001636 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001637 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001638 u8 should_offload:1, /* set indicates this neigh is connected and
1639 * should be put to KVD linear area of this group.
1640 */
1641 offloaded:1, /* set in case the neigh is actually put into
1642 * KVD linear area of this group.
1643 */
1644 update:1; /* set indicates that MAC of this neigh should be
1645 * updated in HW
1646 */
1647 struct mlxsw_sp_neigh_entry *neigh_entry;
1648};
1649
1650struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001651 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001652 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001653 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001654 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001655 u8 adj_index_valid:1,
1656 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001657 u32 adj_index;
1658 u16 ecmp_size;
1659 u16 count;
1660 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001661#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001662};
1663
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001664static struct fib_info *
1665mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1666{
1667 return nh_grp->priv;
1668}
1669
1670struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001671 enum mlxsw_sp_l3proto proto;
1672 union {
1673 struct fib_info *fi;
1674 struct mlxsw_sp_fib6_entry *fib6_entry;
1675 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001676};
1677
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001678static bool
1679mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
1680 const struct in6_addr *gw, int ifindex)
1681{
1682 int i;
1683
1684 for (i = 0; i < nh_grp->count; i++) {
1685 const struct mlxsw_sp_nexthop *nh;
1686
1687 nh = &nh_grp->nexthops[i];
1688 if (nh->ifindex == ifindex &&
1689 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
1690 return true;
1691 }
1692
1693 return false;
1694}
1695
1696static bool
1697mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
1698 const struct mlxsw_sp_fib6_entry *fib6_entry)
1699{
1700 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1701
1702 if (nh_grp->count != fib6_entry->nrt6)
1703 return false;
1704
1705 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1706 struct in6_addr *gw;
1707 int ifindex;
1708
1709 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
1710 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
1711 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
1712 return false;
1713 }
1714
1715 return true;
1716}
1717
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001718static int
1719mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1720{
1721 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1722 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1723
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001724 switch (cmp_arg->proto) {
1725 case MLXSW_SP_L3_PROTO_IPV4:
1726 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1727 case MLXSW_SP_L3_PROTO_IPV6:
1728 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
1729 cmp_arg->fib6_entry);
1730 default:
1731 WARN_ON(1);
1732 return 1;
1733 }
1734}
1735
1736static int
1737mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
1738{
1739 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001740}
1741
1742static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1743{
1744 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001745 const struct mlxsw_sp_nexthop *nh;
1746 struct fib_info *fi;
1747 unsigned int val;
1748 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001749
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001750 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
1751 case AF_INET:
1752 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1753 return jhash(&fi, sizeof(fi), seed);
1754 case AF_INET6:
1755 val = nh_grp->count;
1756 for (i = 0; i < nh_grp->count; i++) {
1757 nh = &nh_grp->nexthops[i];
1758 val ^= nh->ifindex;
1759 }
1760 return jhash(&val, sizeof(val), seed);
1761 default:
1762 WARN_ON(1);
1763 return 0;
1764 }
1765}
1766
1767static u32
1768mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
1769{
1770 unsigned int val = fib6_entry->nrt6;
1771 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1772 struct net_device *dev;
1773
1774 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1775 dev = mlxsw_sp_rt6->rt->dst.dev;
1776 val ^= dev->ifindex;
1777 }
1778
1779 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001780}
1781
1782static u32
1783mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1784{
1785 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1786
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001787 switch (cmp_arg->proto) {
1788 case MLXSW_SP_L3_PROTO_IPV4:
1789 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1790 case MLXSW_SP_L3_PROTO_IPV6:
1791 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
1792 default:
1793 WARN_ON(1);
1794 return 0;
1795 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001796}
1797
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001798static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001799 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001800 .hashfn = mlxsw_sp_nexthop_group_hash,
1801 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1802 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001803};
1804
1805static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1806 struct mlxsw_sp_nexthop_group *nh_grp)
1807{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001808 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1809 !nh_grp->gateway)
1810 return 0;
1811
Ido Schimmel9011b672017-05-16 19:38:25 +02001812 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001813 &nh_grp->ht_node,
1814 mlxsw_sp_nexthop_group_ht_params);
1815}
1816
1817static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1818 struct mlxsw_sp_nexthop_group *nh_grp)
1819{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001820 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1821 !nh_grp->gateway)
1822 return;
1823
Ido Schimmel9011b672017-05-16 19:38:25 +02001824 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001825 &nh_grp->ht_node,
1826 mlxsw_sp_nexthop_group_ht_params);
1827}
1828
1829static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001830mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1831 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001832{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001833 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1834
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001835 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001836 cmp_arg.fi = fi;
1837 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1838 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001839 mlxsw_sp_nexthop_group_ht_params);
1840}
1841
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001842static struct mlxsw_sp_nexthop_group *
1843mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
1844 struct mlxsw_sp_fib6_entry *fib6_entry)
1845{
1846 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1847
1848 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
1849 cmp_arg.fib6_entry = fib6_entry;
1850 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1851 &cmp_arg,
1852 mlxsw_sp_nexthop_group_ht_params);
1853}
1854
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001855static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1856 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1857 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1858 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1859};
1860
1861static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1862 struct mlxsw_sp_nexthop *nh)
1863{
Ido Schimmel9011b672017-05-16 19:38:25 +02001864 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001865 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1866}
1867
1868static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1869 struct mlxsw_sp_nexthop *nh)
1870{
Ido Schimmel9011b672017-05-16 19:38:25 +02001871 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001872 mlxsw_sp_nexthop_ht_params);
1873}
1874
Ido Schimmelad178c82017-02-08 11:16:40 +01001875static struct mlxsw_sp_nexthop *
1876mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1877 struct mlxsw_sp_nexthop_key key)
1878{
Ido Schimmel9011b672017-05-16 19:38:25 +02001879 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001880 mlxsw_sp_nexthop_ht_params);
1881}
1882
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001883static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001884 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001885 u32 adj_index, u16 ecmp_size,
1886 u32 new_adj_index,
1887 u16 new_ecmp_size)
1888{
1889 char raleu_pl[MLXSW_REG_RALEU_LEN];
1890
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001891 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001892 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1893 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001894 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001895 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1896}
1897
1898static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1899 struct mlxsw_sp_nexthop_group *nh_grp,
1900 u32 old_adj_index, u16 old_ecmp_size)
1901{
1902 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001903 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001904 int err;
1905
1906 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001907 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001908 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001909 fib = fib_entry->fib_node->fib;
1910 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001911 old_adj_index,
1912 old_ecmp_size,
1913 nh_grp->adj_index,
1914 nh_grp->ecmp_size);
1915 if (err)
1916 return err;
1917 }
1918 return 0;
1919}
1920
1921static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1922 struct mlxsw_sp_nexthop *nh)
1923{
1924 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1925 char ratr_pl[MLXSW_REG_RATR_LEN];
1926
1927 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02001928 true, MLXSW_REG_RATR_TYPE_ETHERNET,
1929 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001930 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1931 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1932}
1933
1934static int
1935mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001936 struct mlxsw_sp_nexthop_group *nh_grp,
1937 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001938{
1939 u32 adj_index = nh_grp->adj_index; /* base */
1940 struct mlxsw_sp_nexthop *nh;
1941 int i;
1942 int err;
1943
1944 for (i = 0; i < nh_grp->count; i++) {
1945 nh = &nh_grp->nexthops[i];
1946
1947 if (!nh->should_offload) {
1948 nh->offloaded = 0;
1949 continue;
1950 }
1951
Ido Schimmela59b7e02017-01-23 11:11:42 +01001952 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001953 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1954 adj_index, nh);
1955 if (err)
1956 return err;
1957 nh->update = 0;
1958 nh->offloaded = 1;
1959 }
1960 adj_index++;
1961 }
1962 return 0;
1963}
1964
1965static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1966 struct mlxsw_sp_fib_entry *fib_entry);
1967
Ido Schimmel1819ae32017-07-21 18:04:28 +02001968static bool
1969mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1970 const struct mlxsw_sp_fib_entry *fib_entry);
1971
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001972static int
1973mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1974 struct mlxsw_sp_nexthop_group *nh_grp)
1975{
1976 struct mlxsw_sp_fib_entry *fib_entry;
1977 int err;
1978
1979 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001980 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1981 fib_entry))
1982 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001983 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1984 if (err)
1985 return err;
1986 }
1987 return 0;
1988}
1989
1990static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001991mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1992 enum mlxsw_reg_ralue_op op, int err);
1993
1994static void
1995mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1996{
1997 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1998 struct mlxsw_sp_fib_entry *fib_entry;
1999
2000 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2001 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2002 fib_entry))
2003 continue;
2004 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2005 }
2006}
2007
2008static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002009mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2010 struct mlxsw_sp_nexthop_group *nh_grp)
2011{
2012 struct mlxsw_sp_nexthop *nh;
2013 bool offload_change = false;
2014 u32 adj_index;
2015 u16 ecmp_size = 0;
2016 bool old_adj_index_valid;
2017 u32 old_adj_index;
2018 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002019 int i;
2020 int err;
2021
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002022 if (!nh_grp->gateway) {
2023 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2024 return;
2025 }
2026
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002027 for (i = 0; i < nh_grp->count; i++) {
2028 nh = &nh_grp->nexthops[i];
2029
Petr Machata56b8a9e2017-07-31 09:27:29 +02002030 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 offload_change = true;
2032 if (nh->should_offload)
2033 nh->update = 1;
2034 }
2035 if (nh->should_offload)
2036 ecmp_size++;
2037 }
2038 if (!offload_change) {
2039 /* Nothing was added or removed, so no need to reallocate. Just
2040 * update MAC on existing adjacency indexes.
2041 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01002042 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
2043 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002044 if (err) {
2045 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2046 goto set_trap;
2047 }
2048 return;
2049 }
2050 if (!ecmp_size)
2051 /* No neigh of this group is connected so we just set
2052 * the trap and let everthing flow through kernel.
2053 */
2054 goto set_trap;
2055
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002056 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2057 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002058 /* We ran out of KVD linear space, just set the
2059 * trap and let everything flow through kernel.
2060 */
2061 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2062 goto set_trap;
2063 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002064 old_adj_index_valid = nh_grp->adj_index_valid;
2065 old_adj_index = nh_grp->adj_index;
2066 old_ecmp_size = nh_grp->ecmp_size;
2067 nh_grp->adj_index_valid = 1;
2068 nh_grp->adj_index = adj_index;
2069 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01002070 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002071 if (err) {
2072 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2073 goto set_trap;
2074 }
2075
2076 if (!old_adj_index_valid) {
2077 /* The trap was set for fib entries, so we have to call
2078 * fib entry update to unset it and use adjacency index.
2079 */
2080 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2081 if (err) {
2082 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2083 goto set_trap;
2084 }
2085 return;
2086 }
2087
2088 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2089 old_adj_index, old_ecmp_size);
2090 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2091 if (err) {
2092 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2093 goto set_trap;
2094 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002095
2096 /* Offload state within the group changed, so update the flags. */
2097 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2098
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002099 return;
2100
2101set_trap:
2102 old_adj_index_valid = nh_grp->adj_index_valid;
2103 nh_grp->adj_index_valid = 0;
2104 for (i = 0; i < nh_grp->count; i++) {
2105 nh = &nh_grp->nexthops[i];
2106 nh->offloaded = 0;
2107 }
2108 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2109 if (err)
2110 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2111 if (old_adj_index_valid)
2112 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2113}
2114
2115static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2116 bool removing)
2117{
Petr Machata213666a2017-07-31 09:27:30 +02002118 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002119 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002120 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002121 nh->should_offload = 0;
2122 nh->update = 1;
2123}
2124
2125static void
2126mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2127 struct mlxsw_sp_neigh_entry *neigh_entry,
2128 bool removing)
2129{
2130 struct mlxsw_sp_nexthop *nh;
2131
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002132 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2133 neigh_list_node) {
2134 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2135 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2136 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002137}
2138
Ido Schimmel9665b742017-02-08 11:16:42 +01002139static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002140 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002141{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002142 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002143 return;
2144
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002145 nh->rif = rif;
2146 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002147}
2148
2149static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2150{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002151 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002152 return;
2153
2154 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002155 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002156}
2157
Ido Schimmela8c97012017-02-08 11:16:35 +01002158static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2159 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002160{
2161 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002162 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002163 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002164 int err;
2165
Ido Schimmelad178c82017-02-08 11:16:40 +01002166 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002167 return 0;
2168
Jiri Pirko33b13412016-11-10 12:31:04 +01002169 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002170 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002171 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002172 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002173 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002174 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002175 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002176 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2177 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002178 if (IS_ERR(n))
2179 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002180 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002181 }
2182 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2183 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002184 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2185 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002186 err = -EINVAL;
2187 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002188 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002189 }
Yotam Gigib2157142016-07-05 11:27:51 +02002190
2191 /* If that is the first nexthop connected to that neigh, add to
2192 * nexthop_neighs_list
2193 */
2194 if (list_empty(&neigh_entry->nexthop_list))
2195 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002196 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002197
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002198 nh->neigh_entry = neigh_entry;
2199 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2200 read_lock_bh(&n->lock);
2201 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002202 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002203 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002204 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002205
2206 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002207
2208err_neigh_entry_create:
2209 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002210 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002211}
2212
Ido Schimmela8c97012017-02-08 11:16:35 +01002213static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2214 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002215{
2216 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002217 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002218
Ido Schimmelb8399a12017-02-08 11:16:33 +01002219 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002220 return;
2221 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002222
Ido Schimmel58312122016-12-23 09:32:50 +01002223 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002224 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002225 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002226
2227 /* If that is the last nexthop connected to that neigh, remove from
2228 * nexthop_neighs_list
2229 */
Ido Schimmele58be792017-02-08 11:16:28 +01002230 if (list_empty(&neigh_entry->nexthop_list))
2231 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002232
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002233 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2234 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2235
2236 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002237}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002238
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002239static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2240 struct mlxsw_sp_nexthop_group *nh_grp,
2241 struct mlxsw_sp_nexthop *nh,
2242 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002243{
2244 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002245 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002246 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002247 int err;
2248
2249 nh->nh_grp = nh_grp;
2250 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002251 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002252 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2253 if (err)
2254 return err;
2255
Ido Schimmel97989ee2017-03-10 08:53:38 +01002256 if (!dev)
2257 return 0;
2258
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002259 in_dev = __in_dev_get_rtnl(dev);
2260 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2261 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2262 return 0;
2263
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002264 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2265 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002266 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002267 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002268
2269 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2270 if (err)
2271 goto err_nexthop_neigh_init;
2272
2273 return 0;
2274
2275err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002276 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002277 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2278 return err;
2279}
2280
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002281static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2282 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002283{
2284 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002285 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002286 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002287}
2288
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002289static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2290 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002291{
2292 struct mlxsw_sp_nexthop_key key;
2293 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002294 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002295
Ido Schimmel9011b672017-05-16 19:38:25 +02002296 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002297 return;
2298
2299 key.fib_nh = fib_nh;
2300 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2301 if (WARN_ON_ONCE(!nh))
2302 return;
2303
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002304 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2305 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002306 return;
2307
2308 switch (event) {
2309 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002310 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002311 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2312 break;
2313 case FIB_EVENT_NH_DEL:
2314 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002315 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002316 break;
2317 }
2318
2319 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2320}
2321
Ido Schimmel9665b742017-02-08 11:16:42 +01002322static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002323 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002324{
2325 struct mlxsw_sp_nexthop *nh, *tmp;
2326
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002327 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002328 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2329 mlxsw_sp_nexthop_rif_fini(nh);
2330 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2331 }
2332}
2333
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002334static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002335mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002336{
2337 struct mlxsw_sp_nexthop_group *nh_grp;
2338 struct mlxsw_sp_nexthop *nh;
2339 struct fib_nh *fib_nh;
2340 size_t alloc_size;
2341 int i;
2342 int err;
2343
2344 alloc_size = sizeof(*nh_grp) +
2345 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2346 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2347 if (!nh_grp)
2348 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002349 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002350 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002351 nh_grp->neigh_tbl = &arp_tbl;
2352
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002353 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002354 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002355 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002356 for (i = 0; i < nh_grp->count; i++) {
2357 nh = &nh_grp->nexthops[i];
2358 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002359 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002360 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002361 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002362 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002363 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2364 if (err)
2365 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002366 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2367 return nh_grp;
2368
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002369err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002370err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002371 for (i--; i >= 0; i--) {
2372 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002373 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002374 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002375 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002376 kfree(nh_grp);
2377 return ERR_PTR(err);
2378}
2379
2380static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002381mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2382 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002383{
2384 struct mlxsw_sp_nexthop *nh;
2385 int i;
2386
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002387 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002388 for (i = 0; i < nh_grp->count; i++) {
2389 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002390 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002391 }
Ido Schimmel58312122016-12-23 09:32:50 +01002392 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2393 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002394 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002395 kfree(nh_grp);
2396}
2397
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002398static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2399 struct mlxsw_sp_fib_entry *fib_entry,
2400 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002401{
2402 struct mlxsw_sp_nexthop_group *nh_grp;
2403
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002404 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002405 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002406 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002407 if (IS_ERR(nh_grp))
2408 return PTR_ERR(nh_grp);
2409 }
2410 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2411 fib_entry->nh_group = nh_grp;
2412 return 0;
2413}
2414
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002415static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2416 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002417{
2418 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2419
2420 list_del(&fib_entry->nexthop_group_node);
2421 if (!list_empty(&nh_grp->fib_list))
2422 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002423 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002424}
2425
Ido Schimmel013b20f2017-02-08 11:16:36 +01002426static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002427mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2428{
2429 struct mlxsw_sp_fib4_entry *fib4_entry;
2430
2431 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2432 common);
2433 return !fib4_entry->tos;
2434}
2435
2436static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002437mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2438{
2439 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2440
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002441 switch (fib_entry->fib_node->fib->proto) {
2442 case MLXSW_SP_L3_PROTO_IPV4:
2443 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2444 return false;
2445 break;
2446 case MLXSW_SP_L3_PROTO_IPV6:
2447 break;
2448 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002449
Ido Schimmel013b20f2017-02-08 11:16:36 +01002450 switch (fib_entry->type) {
2451 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2452 return !!nh_group->adj_index_valid;
2453 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002454 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002455 default:
2456 return false;
2457 }
2458}
2459
Ido Schimmel428b8512017-08-03 13:28:28 +02002460static struct mlxsw_sp_nexthop *
2461mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2462 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2463{
2464 int i;
2465
2466 for (i = 0; i < nh_grp->count; i++) {
2467 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2468 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2469
2470 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2471 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2472 &rt->rt6i_gateway))
2473 return nh;
2474 continue;
2475 }
2476
2477 return NULL;
2478}
2479
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002480static void
2481mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2482{
2483 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2484 int i;
2485
2486 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2487 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2488 return;
2489 }
2490
2491 for (i = 0; i < nh_grp->count; i++) {
2492 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2493
2494 if (nh->offloaded)
2495 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2496 else
2497 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2498 }
2499}
2500
2501static void
2502mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2503{
2504 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2505 int i;
2506
2507 for (i = 0; i < nh_grp->count; i++) {
2508 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2509
2510 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2511 }
2512}
2513
Ido Schimmel428b8512017-08-03 13:28:28 +02002514static void
2515mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2516{
2517 struct mlxsw_sp_fib6_entry *fib6_entry;
2518 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2519
2520 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2521 common);
2522
2523 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2524 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02002525 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002526 return;
2527 }
2528
2529 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2530 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2531 struct mlxsw_sp_nexthop *nh;
2532
2533 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2534 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02002535 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002536 else
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 }
2539}
2540
2541static void
2542mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2543{
2544 struct mlxsw_sp_fib6_entry *fib6_entry;
2545 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2546
2547 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2548 common);
2549 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2550 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2551
Ido Schimmelfe400792017-08-15 09:09:49 +02002552 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002553 }
2554}
2555
Ido Schimmel013b20f2017-02-08 11:16:36 +01002556static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2557{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002558 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002559 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002560 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002561 break;
2562 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002563 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2564 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002565 }
2566}
2567
2568static void
2569mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2570{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002571 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002572 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002573 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002574 break;
2575 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002576 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2577 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002578 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002579}
2580
2581static void
2582mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2583 enum mlxsw_reg_ralue_op op, int err)
2584{
2585 switch (op) {
2586 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002587 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2588 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2589 if (err)
2590 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002591 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002592 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002593 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002594 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2595 return;
2596 default:
2597 return;
2598 }
2599}
2600
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002601static void
2602mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2603 const struct mlxsw_sp_fib_entry *fib_entry,
2604 enum mlxsw_reg_ralue_op op)
2605{
2606 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2607 enum mlxsw_reg_ralxx_protocol proto;
2608 u32 *p_dip;
2609
2610 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2611
2612 switch (fib->proto) {
2613 case MLXSW_SP_L3_PROTO_IPV4:
2614 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2615 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2616 fib_entry->fib_node->key.prefix_len,
2617 *p_dip);
2618 break;
2619 case MLXSW_SP_L3_PROTO_IPV6:
2620 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2621 fib_entry->fib_node->key.prefix_len,
2622 fib_entry->fib_node->key.addr);
2623 break;
2624 }
2625}
2626
2627static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2628 struct mlxsw_sp_fib_entry *fib_entry,
2629 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002630{
2631 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002632 enum mlxsw_reg_ralue_trap_action trap_action;
2633 u16 trap_id = 0;
2634 u32 adjacency_index = 0;
2635 u16 ecmp_size = 0;
2636
2637 /* In case the nexthop group adjacency index is valid, use it
2638 * with provided ECMP size. Otherwise, setup trap and pass
2639 * traffic to kernel.
2640 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002641 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002642 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2643 adjacency_index = fib_entry->nh_group->adj_index;
2644 ecmp_size = fib_entry->nh_group->ecmp_size;
2645 } else {
2646 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2647 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2648 }
2649
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002650 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002651 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2652 adjacency_index, ecmp_size);
2653 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2654}
2655
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002656static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2657 struct mlxsw_sp_fib_entry *fib_entry,
2658 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002659{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002660 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002661 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002662 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002663 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002664 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002665
2666 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2667 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002668 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002669 } else {
2670 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2671 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2672 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002673
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002674 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002675 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2676 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002677 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2678}
2679
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002680static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2681 struct mlxsw_sp_fib_entry *fib_entry,
2682 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002683{
2684 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002685
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002686 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002687 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2688 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2689}
2690
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002691static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2692 struct mlxsw_sp_fib_entry *fib_entry,
2693 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002694{
2695 switch (fib_entry->type) {
2696 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002697 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002698 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002699 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002700 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002701 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002702 }
2703 return -EINVAL;
2704}
2705
2706static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2707 struct mlxsw_sp_fib_entry *fib_entry,
2708 enum mlxsw_reg_ralue_op op)
2709{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002710 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002711
Ido Schimmel013b20f2017-02-08 11:16:36 +01002712 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002713
Ido Schimmel013b20f2017-02-08 11:16:36 +01002714 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002715}
2716
2717static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2718 struct mlxsw_sp_fib_entry *fib_entry)
2719{
Jiri Pirko7146da32016-09-01 10:37:41 +02002720 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2721 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002722}
2723
2724static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2725 struct mlxsw_sp_fib_entry *fib_entry)
2726{
2727 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2728 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2729}
2730
Jiri Pirko61c503f2016-07-04 08:23:11 +02002731static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002732mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2733 const struct fib_entry_notifier_info *fen_info,
2734 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002735{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002736 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002737
Ido Schimmel97989ee2017-03-10 08:53:38 +01002738 switch (fen_info->type) {
2739 case RTN_BROADCAST: /* fall through */
2740 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002741 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2742 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002743 case RTN_UNREACHABLE: /* fall through */
2744 case RTN_BLACKHOLE: /* fall through */
2745 case RTN_PROHIBIT:
2746 /* Packets hitting these routes need to be trapped, but
2747 * can do so with a lower priority than packets directed
2748 * at the host, so use action type local instead of trap.
2749 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002750 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002751 return 0;
2752 case RTN_UNICAST:
2753 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2754 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2755 else
2756 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2757 return 0;
2758 default:
2759 return -EINVAL;
2760 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002761}
2762
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002763static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002764mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2765 struct mlxsw_sp_fib_node *fib_node,
2766 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002767{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002768 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002769 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002770 int err;
2771
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002772 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2773 if (!fib4_entry)
2774 return ERR_PTR(-ENOMEM);
2775 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002776
2777 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2778 if (err)
2779 goto err_fib4_entry_type_set;
2780
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002781 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002782 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002783 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002784
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002785 fib4_entry->prio = fen_info->fi->fib_priority;
2786 fib4_entry->tb_id = fen_info->tb_id;
2787 fib4_entry->type = fen_info->type;
2788 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002789
2790 fib_entry->fib_node = fib_node;
2791
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002792 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002793
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002794err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002795err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002796 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002797 return ERR_PTR(err);
2798}
2799
2800static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002801 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002802{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002803 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002804 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002805}
2806
2807static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002808mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2809 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002810
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002811static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002812mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2813 const struct fib_entry_notifier_info *fen_info)
2814{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002815 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002816 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002817 struct mlxsw_sp_fib *fib;
2818 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002819
Ido Schimmel160e22a2017-07-18 10:10:20 +02002820 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2821 if (!vr)
2822 return NULL;
2823 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2824
2825 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2826 sizeof(fen_info->dst),
2827 fen_info->dst_len);
2828 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002829 return NULL;
2830
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002831 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2832 if (fib4_entry->tb_id == fen_info->tb_id &&
2833 fib4_entry->tos == fen_info->tos &&
2834 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002835 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2836 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002837 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002838 }
2839 }
2840
2841 return NULL;
2842}
2843
2844static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2845 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2846 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2847 .key_len = sizeof(struct mlxsw_sp_fib_key),
2848 .automatic_shrinking = true,
2849};
2850
2851static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2852 struct mlxsw_sp_fib_node *fib_node)
2853{
2854 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2855 mlxsw_sp_fib_ht_params);
2856}
2857
2858static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2859 struct mlxsw_sp_fib_node *fib_node)
2860{
2861 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2862 mlxsw_sp_fib_ht_params);
2863}
2864
2865static struct mlxsw_sp_fib_node *
2866mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2867 size_t addr_len, unsigned char prefix_len)
2868{
2869 struct mlxsw_sp_fib_key key;
2870
2871 memset(&key, 0, sizeof(key));
2872 memcpy(key.addr, addr, addr_len);
2873 key.prefix_len = prefix_len;
2874 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2875}
2876
2877static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002878mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002879 size_t addr_len, unsigned char prefix_len)
2880{
2881 struct mlxsw_sp_fib_node *fib_node;
2882
2883 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2884 if (!fib_node)
2885 return NULL;
2886
2887 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002888 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002889 memcpy(fib_node->key.addr, addr, addr_len);
2890 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002891
2892 return fib_node;
2893}
2894
2895static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2896{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002897 list_del(&fib_node->list);
2898 WARN_ON(!list_empty(&fib_node->entry_list));
2899 kfree(fib_node);
2900}
2901
2902static bool
2903mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2904 const struct mlxsw_sp_fib_entry *fib_entry)
2905{
2906 return list_first_entry(&fib_node->entry_list,
2907 struct mlxsw_sp_fib_entry, list) == fib_entry;
2908}
2909
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002910static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2911 struct mlxsw_sp_fib *fib,
2912 struct mlxsw_sp_fib_node *fib_node)
2913{
2914 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2915 struct mlxsw_sp_lpm_tree *lpm_tree;
2916 int err;
2917
2918 /* Since the tree is shared between all virtual routers we must
2919 * make sure it contains all the required prefix lengths. This
2920 * can be computed by either adding the new prefix length to the
2921 * existing prefix usage of a bound tree, or by aggregating the
2922 * prefix lengths across all virtual routers and adding the new
2923 * one as well.
2924 */
2925 if (fib->lpm_tree)
2926 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2927 &fib->lpm_tree->prefix_usage);
2928 else
2929 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2930 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2931
2932 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2933 fib->proto);
2934 if (IS_ERR(lpm_tree))
2935 return PTR_ERR(lpm_tree);
2936
2937 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2938 return 0;
2939
2940 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2941 if (err)
2942 return err;
2943
2944 return 0;
2945}
2946
2947static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2948 struct mlxsw_sp_fib *fib)
2949{
2950 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2951 struct mlxsw_sp_lpm_tree *lpm_tree;
2952
2953 /* Aggregate prefix lengths across all virtual routers to make
2954 * sure we only have used prefix lengths in the LPM tree.
2955 */
2956 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2957 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2958 fib->proto);
2959 if (IS_ERR(lpm_tree))
2960 goto err_tree_get;
2961 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2962
2963err_tree_get:
2964 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
2965 return;
2966 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2967 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
2968 fib->lpm_tree = NULL;
2969}
2970
Ido Schimmel9aecce12017-02-09 10:28:42 +01002971static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2972{
2973 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002974 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002975
2976 if (fib->prefix_ref_count[prefix_len]++ == 0)
2977 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2978}
2979
2980static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2981{
2982 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002983 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002984
2985 if (--fib->prefix_ref_count[prefix_len] == 0)
2986 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2987}
2988
Ido Schimmel76610eb2017-03-10 08:53:41 +01002989static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2990 struct mlxsw_sp_fib_node *fib_node,
2991 struct mlxsw_sp_fib *fib)
2992{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002993 int err;
2994
2995 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2996 if (err)
2997 return err;
2998 fib_node->fib = fib;
2999
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003000 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3001 if (err)
3002 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003003
3004 mlxsw_sp_fib_node_prefix_inc(fib_node);
3005
3006 return 0;
3007
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003008err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003009 fib_node->fib = NULL;
3010 mlxsw_sp_fib_node_remove(fib, fib_node);
3011 return err;
3012}
3013
3014static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3015 struct mlxsw_sp_fib_node *fib_node)
3016{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003017 struct mlxsw_sp_fib *fib = fib_node->fib;
3018
3019 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003020 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003021 fib_node->fib = NULL;
3022 mlxsw_sp_fib_node_remove(fib, fib_node);
3023}
3024
Ido Schimmel9aecce12017-02-09 10:28:42 +01003025static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003026mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3027 size_t addr_len, unsigned char prefix_len,
3028 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003029{
3030 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003031 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003032 struct mlxsw_sp_vr *vr;
3033 int err;
3034
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003035 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003036 if (IS_ERR(vr))
3037 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003038 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003039
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003040 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003041 if (fib_node)
3042 return fib_node;
3043
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003044 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003045 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003046 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003047 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003048 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003049
Ido Schimmel76610eb2017-03-10 08:53:41 +01003050 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3051 if (err)
3052 goto err_fib_node_init;
3053
Ido Schimmel9aecce12017-02-09 10:28:42 +01003054 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003055
Ido Schimmel76610eb2017-03-10 08:53:41 +01003056err_fib_node_init:
3057 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003058err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003059 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003060 return ERR_PTR(err);
3061}
3062
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003063static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3064 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003065{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003066 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003067
Ido Schimmel9aecce12017-02-09 10:28:42 +01003068 if (!list_empty(&fib_node->entry_list))
3069 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003070 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003071 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003072 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003073}
3074
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003075static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003076mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003077 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003078{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003079 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003080
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003081 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3082 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003083 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003084 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003085 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003086 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003087 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003088 if (fib4_entry->prio >= new4_entry->prio ||
3089 fib4_entry->tos < new4_entry->tos)
3090 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003091 }
3092
3093 return NULL;
3094}
3095
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003096static int
3097mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3098 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003099{
3100 struct mlxsw_sp_fib_node *fib_node;
3101
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003102 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003103 return -EINVAL;
3104
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003105 fib_node = fib4_entry->common.fib_node;
3106 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3107 common.list) {
3108 if (fib4_entry->tb_id != new4_entry->tb_id ||
3109 fib4_entry->tos != new4_entry->tos ||
3110 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003111 break;
3112 }
3113
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003114 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003115 return 0;
3116}
3117
Ido Schimmel9aecce12017-02-09 10:28:42 +01003118static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003119mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003120 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003121{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003122 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003123 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003124
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003125 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003126
Ido Schimmel4283bce2017-02-09 10:28:43 +01003127 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003128 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3129 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003130 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003131
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003132 /* Insert new entry before replaced one, so that we can later
3133 * remove the second.
3134 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003135 if (fib4_entry) {
3136 list_add_tail(&new4_entry->common.list,
3137 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003138 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003139 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003140
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003141 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3142 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003143 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003144 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003145 }
3146
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003147 if (fib4_entry)
3148 list_add(&new4_entry->common.list,
3149 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003150 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003151 list_add(&new4_entry->common.list,
3152 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003153 }
3154
3155 return 0;
3156}
3157
3158static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003159mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003160{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003161 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003162}
3163
Ido Schimmel80c238f2017-07-18 10:10:29 +02003164static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3165 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003166{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003167 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3168
Ido Schimmel9aecce12017-02-09 10:28:42 +01003169 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3170 return 0;
3171
3172 /* To prevent packet loss, overwrite the previously offloaded
3173 * entry.
3174 */
3175 if (!list_is_singular(&fib_node->entry_list)) {
3176 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3177 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3178
3179 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3180 }
3181
3182 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3183}
3184
Ido Schimmel80c238f2017-07-18 10:10:29 +02003185static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3186 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003187{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003188 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3189
Ido Schimmel9aecce12017-02-09 10:28:42 +01003190 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3191 return;
3192
3193 /* Promote the next entry by overwriting the deleted entry */
3194 if (!list_is_singular(&fib_node->entry_list)) {
3195 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3196 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3197
3198 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3199 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3200 return;
3201 }
3202
3203 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3204}
3205
3206static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003207 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003208 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003209{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003210 int err;
3211
Ido Schimmel9efbee62017-07-18 10:10:28 +02003212 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003213 if (err)
3214 return err;
3215
Ido Schimmel80c238f2017-07-18 10:10:29 +02003216 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003217 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003218 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003219
Ido Schimmel9aecce12017-02-09 10:28:42 +01003220 return 0;
3221
Ido Schimmel80c238f2017-07-18 10:10:29 +02003222err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003223 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003224 return err;
3225}
3226
3227static void
3228mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003229 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003230{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003231 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003232 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003233}
3234
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003235static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003236 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003237 bool replace)
3238{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003239 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3240 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003241
3242 if (!replace)
3243 return;
3244
3245 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003246 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003247
3248 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3249 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003250 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003251}
3252
Ido Schimmel9aecce12017-02-09 10:28:42 +01003253static int
3254mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003255 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003256 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003257{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003258 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003259 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003260 int err;
3261
Ido Schimmel9011b672017-05-16 19:38:25 +02003262 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003263 return 0;
3264
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003265 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3266 &fen_info->dst, sizeof(fen_info->dst),
3267 fen_info->dst_len,
3268 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003269 if (IS_ERR(fib_node)) {
3270 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3271 return PTR_ERR(fib_node);
3272 }
3273
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003274 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3275 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003276 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003277 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003278 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003279 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003280
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003281 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003282 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003283 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003284 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3285 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003286 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003287
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003288 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003289
Jiri Pirko61c503f2016-07-04 08:23:11 +02003290 return 0;
3291
Ido Schimmel9aecce12017-02-09 10:28:42 +01003292err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003293 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003294err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003295 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003296 return err;
3297}
3298
Jiri Pirko37956d72016-10-20 16:05:43 +02003299static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3300 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003301{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003302 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003303 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003304
Ido Schimmel9011b672017-05-16 19:38:25 +02003305 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003306 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003307
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003308 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3309 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003310 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003311 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003312
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003313 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3314 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003315 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003316}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003317
Ido Schimmel428b8512017-08-03 13:28:28 +02003318static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3319{
3320 /* Packets with link-local destination IP arriving to the router
3321 * are trapped to the CPU, so no need to program specific routes
3322 * for them.
3323 */
3324 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3325 return true;
3326
3327 /* Multicast routes aren't supported, so ignore them. Neighbour
3328 * Discovery packets are specifically trapped.
3329 */
3330 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3331 return true;
3332
3333 /* Cloned routes are irrelevant in the forwarding path. */
3334 if (rt->rt6i_flags & RTF_CACHE)
3335 return true;
3336
3337 return false;
3338}
3339
3340static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3341{
3342 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3343
3344 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3345 if (!mlxsw_sp_rt6)
3346 return ERR_PTR(-ENOMEM);
3347
3348 /* In case of route replace, replaced route is deleted with
3349 * no notification. Take reference to prevent accessing freed
3350 * memory.
3351 */
3352 mlxsw_sp_rt6->rt = rt;
3353 rt6_hold(rt);
3354
3355 return mlxsw_sp_rt6;
3356}
3357
3358#if IS_ENABLED(CONFIG_IPV6)
3359static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3360{
3361 rt6_release(rt);
3362}
3363#else
3364static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3365{
3366}
3367#endif
3368
3369static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3370{
3371 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3372 kfree(mlxsw_sp_rt6);
3373}
3374
3375static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3376{
3377 /* RTF_CACHE routes are ignored */
3378 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3379}
3380
3381static struct rt6_info *
3382mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3383{
3384 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3385 list)->rt;
3386}
3387
3388static struct mlxsw_sp_fib6_entry *
3389mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003390 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003391{
3392 struct mlxsw_sp_fib6_entry *fib6_entry;
3393
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003394 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003395 return NULL;
3396
3397 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3398 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3399
3400 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3401 * virtual router.
3402 */
3403 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3404 continue;
3405 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3406 break;
3407 if (rt->rt6i_metric < nrt->rt6i_metric)
3408 continue;
3409 if (rt->rt6i_metric == nrt->rt6i_metric &&
3410 mlxsw_sp_fib6_rt_can_mp(rt))
3411 return fib6_entry;
3412 if (rt->rt6i_metric > nrt->rt6i_metric)
3413 break;
3414 }
3415
3416 return NULL;
3417}
3418
3419static struct mlxsw_sp_rt6 *
3420mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3421 const struct rt6_info *rt)
3422{
3423 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3424
3425 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3426 if (mlxsw_sp_rt6->rt == rt)
3427 return mlxsw_sp_rt6;
3428 }
3429
3430 return NULL;
3431}
3432
3433static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3434 struct mlxsw_sp_nexthop_group *nh_grp,
3435 struct mlxsw_sp_nexthop *nh,
3436 const struct rt6_info *rt)
3437{
3438 struct net_device *dev = rt->dst.dev;
3439 struct mlxsw_sp_rif *rif;
3440 int err;
3441
3442 nh->nh_grp = nh_grp;
3443 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3444
3445 if (!dev)
3446 return 0;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003447 nh->ifindex = dev->ifindex;
Ido Schimmel428b8512017-08-03 13:28:28 +02003448
3449 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3450 if (!rif)
3451 return 0;
3452 mlxsw_sp_nexthop_rif_init(nh, rif);
3453
3454 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3455 if (err)
3456 goto err_nexthop_neigh_init;
3457
3458 return 0;
3459
3460err_nexthop_neigh_init:
3461 mlxsw_sp_nexthop_rif_fini(nh);
3462 return err;
3463}
3464
3465static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3466 struct mlxsw_sp_nexthop *nh)
3467{
3468 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3469 mlxsw_sp_nexthop_rif_fini(nh);
3470}
3471
3472static struct mlxsw_sp_nexthop_group *
3473mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3474 struct mlxsw_sp_fib6_entry *fib6_entry)
3475{
3476 struct mlxsw_sp_nexthop_group *nh_grp;
3477 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3478 struct mlxsw_sp_nexthop *nh;
3479 size_t alloc_size;
3480 int i = 0;
3481 int err;
3482
3483 alloc_size = sizeof(*nh_grp) +
3484 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3485 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3486 if (!nh_grp)
3487 return ERR_PTR(-ENOMEM);
3488 INIT_LIST_HEAD(&nh_grp->fib_list);
3489#if IS_ENABLED(CONFIG_IPV6)
3490 nh_grp->neigh_tbl = &nd_tbl;
3491#endif
3492 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3493 struct mlxsw_sp_rt6, list);
3494 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3495 nh_grp->count = fib6_entry->nrt6;
3496 for (i = 0; i < nh_grp->count; i++) {
3497 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3498
3499 nh = &nh_grp->nexthops[i];
3500 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3501 if (err)
3502 goto err_nexthop6_init;
3503 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3504 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003505
3506 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3507 if (err)
3508 goto err_nexthop_group_insert;
3509
Ido Schimmel428b8512017-08-03 13:28:28 +02003510 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3511 return nh_grp;
3512
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003513err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02003514err_nexthop6_init:
3515 for (i--; i >= 0; i--) {
3516 nh = &nh_grp->nexthops[i];
3517 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3518 }
3519 kfree(nh_grp);
3520 return ERR_PTR(err);
3521}
3522
3523static void
3524mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3525 struct mlxsw_sp_nexthop_group *nh_grp)
3526{
3527 struct mlxsw_sp_nexthop *nh;
3528 int i = nh_grp->count;
3529
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003530 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02003531 for (i--; i >= 0; i--) {
3532 nh = &nh_grp->nexthops[i];
3533 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3534 }
3535 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3536 WARN_ON(nh_grp->adj_index_valid);
3537 kfree(nh_grp);
3538}
3539
3540static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3541 struct mlxsw_sp_fib6_entry *fib6_entry)
3542{
3543 struct mlxsw_sp_nexthop_group *nh_grp;
3544
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003545 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
3546 if (!nh_grp) {
3547 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3548 if (IS_ERR(nh_grp))
3549 return PTR_ERR(nh_grp);
3550 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003551
3552 list_add_tail(&fib6_entry->common.nexthop_group_node,
3553 &nh_grp->fib_list);
3554 fib6_entry->common.nh_group = nh_grp;
3555
3556 return 0;
3557}
3558
3559static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3560 struct mlxsw_sp_fib_entry *fib_entry)
3561{
3562 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3563
3564 list_del(&fib_entry->nexthop_group_node);
3565 if (!list_empty(&nh_grp->fib_list))
3566 return;
3567 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3568}
3569
3570static int
3571mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3572 struct mlxsw_sp_fib6_entry *fib6_entry)
3573{
3574 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3575 int err;
3576
3577 fib6_entry->common.nh_group = NULL;
3578 list_del(&fib6_entry->common.nexthop_group_node);
3579
3580 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3581 if (err)
3582 goto err_nexthop6_group_get;
3583
3584 /* In case this entry is offloaded, then the adjacency index
3585 * currently associated with it in the device's table is that
3586 * of the old group. Start using the new one instead.
3587 */
3588 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3589 if (err)
3590 goto err_fib_node_entry_add;
3591
3592 if (list_empty(&old_nh_grp->fib_list))
3593 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3594
3595 return 0;
3596
3597err_fib_node_entry_add:
3598 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3599err_nexthop6_group_get:
3600 list_add_tail(&fib6_entry->common.nexthop_group_node,
3601 &old_nh_grp->fib_list);
3602 fib6_entry->common.nh_group = old_nh_grp;
3603 return err;
3604}
3605
3606static int
3607mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3608 struct mlxsw_sp_fib6_entry *fib6_entry,
3609 struct rt6_info *rt)
3610{
3611 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3612 int err;
3613
3614 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3615 if (IS_ERR(mlxsw_sp_rt6))
3616 return PTR_ERR(mlxsw_sp_rt6);
3617
3618 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3619 fib6_entry->nrt6++;
3620
3621 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3622 if (err)
3623 goto err_nexthop6_group_update;
3624
3625 return 0;
3626
3627err_nexthop6_group_update:
3628 fib6_entry->nrt6--;
3629 list_del(&mlxsw_sp_rt6->list);
3630 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3631 return err;
3632}
3633
3634static void
3635mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3636 struct mlxsw_sp_fib6_entry *fib6_entry,
3637 struct rt6_info *rt)
3638{
3639 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3640
3641 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3642 if (WARN_ON(!mlxsw_sp_rt6))
3643 return;
3644
3645 fib6_entry->nrt6--;
3646 list_del(&mlxsw_sp_rt6->list);
3647 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3648 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3649}
3650
3651static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3652 const struct rt6_info *rt)
3653{
3654 /* Packets hitting RTF_REJECT routes need to be discarded by the
3655 * stack. We can rely on their destination device not having a
3656 * RIF (it's the loopback device) and can thus use action type
3657 * local, which will cause them to be trapped with a lower
3658 * priority than packets that need to be locally received.
3659 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02003660 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02003661 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3662 else if (rt->rt6i_flags & RTF_REJECT)
3663 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3664 else if (rt->rt6i_flags & RTF_GATEWAY)
3665 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3666 else
3667 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3668}
3669
3670static void
3671mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3672{
3673 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3674
3675 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3676 list) {
3677 fib6_entry->nrt6--;
3678 list_del(&mlxsw_sp_rt6->list);
3679 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3680 }
3681}
3682
3683static struct mlxsw_sp_fib6_entry *
3684mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3685 struct mlxsw_sp_fib_node *fib_node,
3686 struct rt6_info *rt)
3687{
3688 struct mlxsw_sp_fib6_entry *fib6_entry;
3689 struct mlxsw_sp_fib_entry *fib_entry;
3690 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3691 int err;
3692
3693 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3694 if (!fib6_entry)
3695 return ERR_PTR(-ENOMEM);
3696 fib_entry = &fib6_entry->common;
3697
3698 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3699 if (IS_ERR(mlxsw_sp_rt6)) {
3700 err = PTR_ERR(mlxsw_sp_rt6);
3701 goto err_rt6_create;
3702 }
3703
3704 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3705
3706 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3707 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3708 fib6_entry->nrt6 = 1;
3709 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3710 if (err)
3711 goto err_nexthop6_group_get;
3712
3713 fib_entry->fib_node = fib_node;
3714
3715 return fib6_entry;
3716
3717err_nexthop6_group_get:
3718 list_del(&mlxsw_sp_rt6->list);
3719 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3720err_rt6_create:
3721 kfree(fib6_entry);
3722 return ERR_PTR(err);
3723}
3724
3725static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3726 struct mlxsw_sp_fib6_entry *fib6_entry)
3727{
3728 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3729 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3730 WARN_ON(fib6_entry->nrt6);
3731 kfree(fib6_entry);
3732}
3733
3734static struct mlxsw_sp_fib6_entry *
3735mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003736 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003737{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003738 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003739
3740 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3741 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3742
3743 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3744 continue;
3745 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3746 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003747 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3748 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3749 mlxsw_sp_fib6_rt_can_mp(nrt))
3750 return fib6_entry;
3751 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3752 fallback = fallback ?: fib6_entry;
3753 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003754 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003755 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003756 }
3757
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003758 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003759}
3760
3761static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003762mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3763 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003764{
3765 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3766 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3767 struct mlxsw_sp_fib6_entry *fib6_entry;
3768
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003769 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3770
3771 if (replace && WARN_ON(!fib6_entry))
3772 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003773
3774 if (fib6_entry) {
3775 list_add_tail(&new6_entry->common.list,
3776 &fib6_entry->common.list);
3777 } else {
3778 struct mlxsw_sp_fib6_entry *last;
3779
3780 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3781 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3782
3783 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3784 break;
3785 fib6_entry = last;
3786 }
3787
3788 if (fib6_entry)
3789 list_add(&new6_entry->common.list,
3790 &fib6_entry->common.list);
3791 else
3792 list_add(&new6_entry->common.list,
3793 &fib_node->entry_list);
3794 }
3795
3796 return 0;
3797}
3798
3799static void
3800mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3801{
3802 list_del(&fib6_entry->common.list);
3803}
3804
3805static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003806 struct mlxsw_sp_fib6_entry *fib6_entry,
3807 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003808{
3809 int err;
3810
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003811 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003812 if (err)
3813 return err;
3814
3815 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3816 if (err)
3817 goto err_fib_node_entry_add;
3818
3819 return 0;
3820
3821err_fib_node_entry_add:
3822 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3823 return err;
3824}
3825
3826static void
3827mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3828 struct mlxsw_sp_fib6_entry *fib6_entry)
3829{
3830 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3831 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3832}
3833
3834static struct mlxsw_sp_fib6_entry *
3835mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3836 const struct rt6_info *rt)
3837{
3838 struct mlxsw_sp_fib6_entry *fib6_entry;
3839 struct mlxsw_sp_fib_node *fib_node;
3840 struct mlxsw_sp_fib *fib;
3841 struct mlxsw_sp_vr *vr;
3842
3843 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3844 if (!vr)
3845 return NULL;
3846 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3847
3848 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3849 sizeof(rt->rt6i_dst.addr),
3850 rt->rt6i_dst.plen);
3851 if (!fib_node)
3852 return NULL;
3853
3854 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3855 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3856
3857 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3858 rt->rt6i_metric == iter_rt->rt6i_metric &&
3859 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3860 return fib6_entry;
3861 }
3862
3863 return NULL;
3864}
3865
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003866static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3867 struct mlxsw_sp_fib6_entry *fib6_entry,
3868 bool replace)
3869{
3870 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3871 struct mlxsw_sp_fib6_entry *replaced;
3872
3873 if (!replace)
3874 return;
3875
3876 replaced = list_next_entry(fib6_entry, common.list);
3877
3878 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3879 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3880 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3881}
3882
Ido Schimmel428b8512017-08-03 13:28:28 +02003883static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003884 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003885{
3886 struct mlxsw_sp_fib6_entry *fib6_entry;
3887 struct mlxsw_sp_fib_node *fib_node;
3888 int err;
3889
3890 if (mlxsw_sp->router->aborted)
3891 return 0;
3892
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003893 if (rt->rt6i_src.plen)
3894 return -EINVAL;
3895
Ido Schimmel428b8512017-08-03 13:28:28 +02003896 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3897 return 0;
3898
3899 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3900 &rt->rt6i_dst.addr,
3901 sizeof(rt->rt6i_dst.addr),
3902 rt->rt6i_dst.plen,
3903 MLXSW_SP_L3_PROTO_IPV6);
3904 if (IS_ERR(fib_node))
3905 return PTR_ERR(fib_node);
3906
3907 /* Before creating a new entry, try to append route to an existing
3908 * multipath entry.
3909 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003910 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003911 if (fib6_entry) {
3912 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3913 if (err)
3914 goto err_fib6_entry_nexthop_add;
3915 return 0;
3916 }
3917
3918 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3919 if (IS_ERR(fib6_entry)) {
3920 err = PTR_ERR(fib6_entry);
3921 goto err_fib6_entry_create;
3922 }
3923
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003924 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003925 if (err)
3926 goto err_fib6_node_entry_link;
3927
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003928 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3929
Ido Schimmel428b8512017-08-03 13:28:28 +02003930 return 0;
3931
3932err_fib6_node_entry_link:
3933 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3934err_fib6_entry_create:
3935err_fib6_entry_nexthop_add:
3936 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3937 return err;
3938}
3939
3940static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3941 struct rt6_info *rt)
3942{
3943 struct mlxsw_sp_fib6_entry *fib6_entry;
3944 struct mlxsw_sp_fib_node *fib_node;
3945
3946 if (mlxsw_sp->router->aborted)
3947 return;
3948
3949 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3950 return;
3951
3952 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3953 if (WARN_ON(!fib6_entry))
3954 return;
3955
3956 /* If route is part of a multipath entry, but not the last one
3957 * removed, then only reduce its nexthop group.
3958 */
3959 if (!list_is_singular(&fib6_entry->rt6_list)) {
3960 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3961 return;
3962 }
3963
3964 fib_node = fib6_entry->common.fib_node;
3965
3966 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3967 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3968 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3969}
3970
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003971static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3972 enum mlxsw_reg_ralxx_protocol proto,
3973 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003974{
3975 char ralta_pl[MLXSW_REG_RALTA_LEN];
3976 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003977 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003978
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003979 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003980 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3981 if (err)
3982 return err;
3983
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003984 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003985 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3986 if (err)
3987 return err;
3988
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003989 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003990 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003991 char raltb_pl[MLXSW_REG_RALTB_LEN];
3992 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003993
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003994 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003995 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3996 raltb_pl);
3997 if (err)
3998 return err;
3999
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004000 mlxsw_reg_ralue_pack(ralue_pl, proto,
4001 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004002 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4003 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4004 ralue_pl);
4005 if (err)
4006 return err;
4007 }
4008
4009 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004010}
4011
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004012static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4013{
4014 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4015 int err;
4016
4017 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4018 MLXSW_SP_LPM_TREE_MIN);
4019 if (err)
4020 return err;
4021
4022 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4023 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4024 MLXSW_SP_LPM_TREE_MIN + 1);
4025}
4026
Ido Schimmel9aecce12017-02-09 10:28:42 +01004027static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4028 struct mlxsw_sp_fib_node *fib_node)
4029{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004030 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004031
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004032 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4033 common.list) {
4034 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004035
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004036 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4037 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004038 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004039 /* Break when entry list is empty and node was freed.
4040 * Otherwise, we'll access freed memory in the next
4041 * iteration.
4042 */
4043 if (do_break)
4044 break;
4045 }
4046}
4047
Ido Schimmel428b8512017-08-03 13:28:28 +02004048static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4049 struct mlxsw_sp_fib_node *fib_node)
4050{
4051 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4052
4053 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4054 common.list) {
4055 bool do_break = &tmp->common.list == &fib_node->entry_list;
4056
4057 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4058 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4059 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4060 if (do_break)
4061 break;
4062 }
4063}
4064
Ido Schimmel9aecce12017-02-09 10:28:42 +01004065static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4066 struct mlxsw_sp_fib_node *fib_node)
4067{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004068 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004069 case MLXSW_SP_L3_PROTO_IPV4:
4070 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4071 break;
4072 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004073 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004074 break;
4075 }
4076}
4077
Ido Schimmel76610eb2017-03-10 08:53:41 +01004078static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4079 struct mlxsw_sp_vr *vr,
4080 enum mlxsw_sp_l3proto proto)
4081{
4082 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4083 struct mlxsw_sp_fib_node *fib_node, *tmp;
4084
4085 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4086 bool do_break = &tmp->list == &fib->node_list;
4087
4088 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4089 if (do_break)
4090 break;
4091 }
4092}
4093
Ido Schimmelac571de2016-11-14 11:26:32 +01004094static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004095{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004096 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004097
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004098 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004099 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004100
Ido Schimmel76610eb2017-03-10 08:53:41 +01004101 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004102 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004103 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004104
4105 /* If virtual router was only used for IPv4, then it's no
4106 * longer used.
4107 */
4108 if (!mlxsw_sp_vr_is_used(vr))
4109 continue;
4110 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004111 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004112}
4113
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004114static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004115{
4116 int err;
4117
Ido Schimmel9011b672017-05-16 19:38:25 +02004118 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004119 return;
4120 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 +01004121 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004122 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004123 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4124 if (err)
4125 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4126}
4127
Ido Schimmel30572242016-12-03 16:45:01 +01004128struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004129 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004130 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004131 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004132 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004133 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004134 struct fib_nh_notifier_info fnh_info;
4135 };
Ido Schimmel30572242016-12-03 16:45:01 +01004136 struct mlxsw_sp *mlxsw_sp;
4137 unsigned long event;
4138};
4139
Ido Schimmel66a57632017-08-03 13:28:26 +02004140static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004141{
Ido Schimmel30572242016-12-03 16:45:01 +01004142 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004143 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004144 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004145 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004146 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004147 int err;
4148
Ido Schimmel30572242016-12-03 16:45:01 +01004149 /* Protect internal structures from changes */
4150 rtnl_lock();
4151 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004152 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004153 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004154 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004155 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004156 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4157 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004158 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004159 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004160 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004161 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004162 break;
4163 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004164 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4165 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004166 break;
4167 case FIB_EVENT_RULE_ADD: /* fall through */
4168 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004169 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004170 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004171 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004172 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004173 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004174 case FIB_EVENT_NH_ADD: /* fall through */
4175 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004176 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4177 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004178 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4179 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004180 }
Ido Schimmel30572242016-12-03 16:45:01 +01004181 rtnl_unlock();
4182 kfree(fib_work);
4183}
4184
Ido Schimmel66a57632017-08-03 13:28:26 +02004185static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4186{
Ido Schimmel583419f2017-08-03 13:28:27 +02004187 struct mlxsw_sp_fib_event_work *fib_work =
4188 container_of(work, struct mlxsw_sp_fib_event_work, work);
4189 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4190 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004191 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004192 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004193
4194 rtnl_lock();
4195 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004196 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004197 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004198 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004199 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004200 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004201 if (err)
4202 mlxsw_sp_router_fib_abort(mlxsw_sp);
4203 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4204 break;
4205 case FIB_EVENT_ENTRY_DEL:
4206 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4207 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4208 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004209 case FIB_EVENT_RULE_ADD: /* fall through */
4210 case FIB_EVENT_RULE_DEL:
4211 rule = fib_work->fr_info.rule;
4212 if (!fib6_rule_default(rule) && !rule->l3mdev)
4213 mlxsw_sp_router_fib_abort(mlxsw_sp);
4214 fib_rule_put(rule);
4215 break;
4216 }
4217 rtnl_unlock();
4218 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004219}
4220
4221static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4222 struct fib_notifier_info *info)
4223{
4224 switch (fib_work->event) {
4225 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4226 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4227 case FIB_EVENT_ENTRY_ADD: /* fall through */
4228 case FIB_EVENT_ENTRY_DEL:
4229 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4230 /* Take referece on fib_info to prevent it from being
4231 * freed while work is queued. Release it afterwards.
4232 */
4233 fib_info_hold(fib_work->fen_info.fi);
4234 break;
4235 case FIB_EVENT_RULE_ADD: /* fall through */
4236 case FIB_EVENT_RULE_DEL:
4237 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4238 fib_rule_get(fib_work->fr_info.rule);
4239 break;
4240 case FIB_EVENT_NH_ADD: /* fall through */
4241 case FIB_EVENT_NH_DEL:
4242 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4243 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4244 break;
4245 }
4246}
4247
4248static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4249 struct fib_notifier_info *info)
4250{
Ido Schimmel583419f2017-08-03 13:28:27 +02004251 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004252 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004253 case FIB_EVENT_ENTRY_ADD: /* fall through */
4254 case FIB_EVENT_ENTRY_DEL:
4255 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4256 rt6_hold(fib_work->fen6_info.rt);
4257 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004258 case FIB_EVENT_RULE_ADD: /* fall through */
4259 case FIB_EVENT_RULE_DEL:
4260 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4261 fib_rule_get(fib_work->fr_info.rule);
4262 break;
4263 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004264}
4265
Ido Schimmel30572242016-12-03 16:45:01 +01004266/* Called with rcu_read_lock() */
4267static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4268 unsigned long event, void *ptr)
4269{
Ido Schimmel30572242016-12-03 16:45:01 +01004270 struct mlxsw_sp_fib_event_work *fib_work;
4271 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004272 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004273
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004274 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004275 return NOTIFY_DONE;
4276
4277 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4278 if (WARN_ON(!fib_work))
4279 return NOTIFY_BAD;
4280
Ido Schimmel7e39d112017-05-16 19:38:28 +02004281 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4282 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004283 fib_work->event = event;
4284
Ido Schimmel66a57632017-08-03 13:28:26 +02004285 switch (info->family) {
4286 case AF_INET:
4287 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4288 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004289 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004290 case AF_INET6:
4291 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4292 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004293 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004294 }
4295
Ido Schimmela0e47612017-02-06 16:20:10 +01004296 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004297
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004298 return NOTIFY_DONE;
4299}
4300
Ido Schimmel4724ba562017-03-10 08:53:39 +01004301static struct mlxsw_sp_rif *
4302mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4303 const struct net_device *dev)
4304{
4305 int i;
4306
4307 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004308 if (mlxsw_sp->router->rifs[i] &&
4309 mlxsw_sp->router->rifs[i]->dev == dev)
4310 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004311
4312 return NULL;
4313}
4314
4315static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4316{
4317 char ritr_pl[MLXSW_REG_RITR_LEN];
4318 int err;
4319
4320 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4321 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4322 if (WARN_ON_ONCE(err))
4323 return err;
4324
4325 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4326 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4327}
4328
4329static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004330 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004331{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004332 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4333 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4334 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004335}
4336
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004337static bool
4338mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4339 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004340{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004341 struct inet6_dev *inet6_dev;
4342 bool addr_list_empty = true;
4343 struct in_device *idev;
4344
Ido Schimmel4724ba562017-03-10 08:53:39 +01004345 switch (event) {
4346 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004347 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004348 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004349 idev = __in_dev_get_rtnl(dev);
4350 if (idev && idev->ifa_list)
4351 addr_list_empty = false;
4352
4353 inet6_dev = __in6_dev_get(dev);
4354 if (addr_list_empty && inet6_dev &&
4355 !list_empty(&inet6_dev->addr_list))
4356 addr_list_empty = false;
4357
4358 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004359 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004360 return true;
4361 /* It is possible we already removed the RIF ourselves
4362 * if it was assigned to a netdev that is now a bridge
4363 * or LAG slave.
4364 */
4365 return false;
4366 }
4367
4368 return false;
4369}
4370
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004371static enum mlxsw_sp_rif_type
4372mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4373 const struct net_device *dev)
4374{
4375 enum mlxsw_sp_fid_type type;
4376
4377 /* RIF type is derived from the type of the underlying FID */
4378 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4379 type = MLXSW_SP_FID_TYPE_8021Q;
4380 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4381 type = MLXSW_SP_FID_TYPE_8021Q;
4382 else if (netif_is_bridge_master(dev))
4383 type = MLXSW_SP_FID_TYPE_8021D;
4384 else
4385 type = MLXSW_SP_FID_TYPE_RFID;
4386
4387 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4388}
4389
Ido Schimmelde5ed992017-06-04 16:53:40 +02004390static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004391{
4392 int i;
4393
Ido Schimmelde5ed992017-06-04 16:53:40 +02004394 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4395 if (!mlxsw_sp->router->rifs[i]) {
4396 *p_rif_index = i;
4397 return 0;
4398 }
4399 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004400
Ido Schimmelde5ed992017-06-04 16:53:40 +02004401 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004402}
4403
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004404static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4405 u16 vr_id,
4406 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004407{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004408 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004409
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004410 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004411 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004412 return NULL;
4413
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004414 INIT_LIST_HEAD(&rif->nexthop_list);
4415 INIT_LIST_HEAD(&rif->neigh_list);
4416 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4417 rif->mtu = l3_dev->mtu;
4418 rif->vr_id = vr_id;
4419 rif->dev = l3_dev;
4420 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004421
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004422 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004423}
4424
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004425struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4426 u16 rif_index)
4427{
4428 return mlxsw_sp->router->rifs[rif_index];
4429}
4430
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004431u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4432{
4433 return rif->rif_index;
4434}
4435
4436int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4437{
4438 return rif->dev->ifindex;
4439}
4440
Ido Schimmel4724ba562017-03-10 08:53:39 +01004441static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004442mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4443 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004444{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004445 u32 tb_id = l3mdev_fib_table(params->dev);
4446 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02004447 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004448 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004449 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004450 struct mlxsw_sp_vr *vr;
4451 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004452 int err;
4453
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004454 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4455 ops = mlxsw_sp->router->rif_ops_arr[type];
4456
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004457 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4458 if (IS_ERR(vr))
4459 return ERR_CAST(vr);
4460
Ido Schimmelde5ed992017-06-04 16:53:40 +02004461 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4462 if (err)
4463 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004464
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004465 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004466 if (!rif) {
4467 err = -ENOMEM;
4468 goto err_rif_alloc;
4469 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004470 rif->mlxsw_sp = mlxsw_sp;
4471 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004472
Petr Machata010cadf2017-09-02 23:49:18 +02004473 if (ops->fid_get) {
4474 fid = ops->fid_get(rif);
4475 if (IS_ERR(fid)) {
4476 err = PTR_ERR(fid);
4477 goto err_fid_get;
4478 }
4479 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004480 }
4481
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 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004490 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004491 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004492
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004493 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004494
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004495err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02004496 if (fid)
4497 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02004498err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004499 kfree(rif);
4500err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004501err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004502 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004503 return ERR_PTR(err);
4504}
4505
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004506void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004507{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004508 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4509 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004510 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004511 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004512
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004513 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004514 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004515
Ido Schimmel69132292017-03-10 08:53:42 +01004516 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004517 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004518 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004519 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02004520 if (fid)
4521 /* Loopback RIFs are not associated with a FID. */
4522 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004523 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004524 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004525}
4526
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004527static void
4528mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4529 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4530{
4531 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4532
4533 params->vid = mlxsw_sp_port_vlan->vid;
4534 params->lag = mlxsw_sp_port->lagged;
4535 if (params->lag)
4536 params->lag_id = mlxsw_sp_port->lag_id;
4537 else
4538 params->system_port = mlxsw_sp_port->local_port;
4539}
4540
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004541static int
Ido Schimmela1107482017-05-26 08:37:39 +02004542mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004543 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004544{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004545 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004546 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004547 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004548 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004549 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004550 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004551
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004552 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004553 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004554 struct mlxsw_sp_rif_params params = {
4555 .dev = l3_dev,
4556 };
4557
4558 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4559 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004560 if (IS_ERR(rif))
4561 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004562 }
4563
Ido Schimmela1107482017-05-26 08:37:39 +02004564 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004565 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004566 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4567 if (err)
4568 goto err_fid_port_vid_map;
4569
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004570 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004571 if (err)
4572 goto err_port_vid_learning_set;
4573
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004574 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004575 BR_STATE_FORWARDING);
4576 if (err)
4577 goto err_port_vid_stp_set;
4578
Ido Schimmela1107482017-05-26 08:37:39 +02004579 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004580
Ido Schimmel4724ba562017-03-10 08:53:39 +01004581 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004582
4583err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004584 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004585err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004586 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4587err_fid_port_vid_map:
4588 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004589 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004590}
4591
Ido Schimmela1107482017-05-26 08:37:39 +02004592void
4593mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004594{
Ido Schimmelce95e152017-05-26 08:37:27 +02004595 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004596 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004597 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004598
Ido Schimmela1107482017-05-26 08:37:39 +02004599 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4600 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004601
Ido Schimmela1107482017-05-26 08:37:39 +02004602 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004603 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4604 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004605 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4606 /* If router port holds the last reference on the rFID, then the
4607 * associated Sub-port RIF will be destroyed.
4608 */
4609 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004610}
4611
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004612static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4613 struct net_device *port_dev,
4614 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004615{
4616 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004617 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004618
Ido Schimmelce95e152017-05-26 08:37:27 +02004619 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004620 if (WARN_ON(!mlxsw_sp_port_vlan))
4621 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004622
4623 switch (event) {
4624 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004625 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004626 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004627 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004628 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004629 break;
4630 }
4631
4632 return 0;
4633}
4634
4635static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4636 unsigned long event)
4637{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004638 if (netif_is_bridge_port(port_dev) ||
4639 netif_is_lag_port(port_dev) ||
4640 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004641 return 0;
4642
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004643 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004644}
4645
4646static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4647 struct net_device *lag_dev,
4648 unsigned long event, u16 vid)
4649{
4650 struct net_device *port_dev;
4651 struct list_head *iter;
4652 int err;
4653
4654 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4655 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004656 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4657 port_dev,
4658 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004659 if (err)
4660 return err;
4661 }
4662 }
4663
4664 return 0;
4665}
4666
4667static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4668 unsigned long event)
4669{
4670 if (netif_is_bridge_port(lag_dev))
4671 return 0;
4672
4673 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4674}
4675
Ido Schimmel4724ba562017-03-10 08:53:39 +01004676static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004677 unsigned long event)
4678{
4679 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004680 struct mlxsw_sp_rif_params params = {
4681 .dev = l3_dev,
4682 };
Ido Schimmela1107482017-05-26 08:37:39 +02004683 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004684
4685 switch (event) {
4686 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004687 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4688 if (IS_ERR(rif))
4689 return PTR_ERR(rif);
4690 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004691 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004692 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004693 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004694 break;
4695 }
4696
4697 return 0;
4698}
4699
4700static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4701 unsigned long event)
4702{
4703 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004704 u16 vid = vlan_dev_vlan_id(vlan_dev);
4705
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004706 if (netif_is_bridge_port(vlan_dev))
4707 return 0;
4708
Ido Schimmel4724ba562017-03-10 08:53:39 +01004709 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004710 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4711 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004712 else if (netif_is_lag_master(real_dev))
4713 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4714 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004715 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004716 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004717
4718 return 0;
4719}
4720
Ido Schimmelb1e45522017-04-30 19:47:14 +03004721static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4722 unsigned long event)
4723{
4724 if (mlxsw_sp_port_dev_check(dev))
4725 return mlxsw_sp_inetaddr_port_event(dev, event);
4726 else if (netif_is_lag_master(dev))
4727 return mlxsw_sp_inetaddr_lag_event(dev, event);
4728 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004729 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004730 else if (is_vlan_dev(dev))
4731 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4732 else
4733 return 0;
4734}
4735
Ido Schimmel4724ba562017-03-10 08:53:39 +01004736int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4737 unsigned long event, void *ptr)
4738{
4739 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4740 struct net_device *dev = ifa->ifa_dev->dev;
4741 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004742 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004743 int err = 0;
4744
4745 mlxsw_sp = mlxsw_sp_lower_get(dev);
4746 if (!mlxsw_sp)
4747 goto out;
4748
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004749 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004750 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004751 goto out;
4752
Ido Schimmelb1e45522017-04-30 19:47:14 +03004753 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004754out:
4755 return notifier_from_errno(err);
4756}
4757
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004758struct mlxsw_sp_inet6addr_event_work {
4759 struct work_struct work;
4760 struct net_device *dev;
4761 unsigned long event;
4762};
4763
4764static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4765{
4766 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4767 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4768 struct net_device *dev = inet6addr_work->dev;
4769 unsigned long event = inet6addr_work->event;
4770 struct mlxsw_sp *mlxsw_sp;
4771 struct mlxsw_sp_rif *rif;
4772
4773 rtnl_lock();
4774 mlxsw_sp = mlxsw_sp_lower_get(dev);
4775 if (!mlxsw_sp)
4776 goto out;
4777
4778 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4779 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4780 goto out;
4781
4782 __mlxsw_sp_inetaddr_event(dev, event);
4783out:
4784 rtnl_unlock();
4785 dev_put(dev);
4786 kfree(inet6addr_work);
4787}
4788
4789/* Called with rcu_read_lock() */
4790int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4791 unsigned long event, void *ptr)
4792{
4793 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4794 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4795 struct net_device *dev = if6->idev->dev;
4796
4797 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4798 return NOTIFY_DONE;
4799
4800 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4801 if (!inet6addr_work)
4802 return NOTIFY_BAD;
4803
4804 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4805 inet6addr_work->dev = dev;
4806 inet6addr_work->event = event;
4807 dev_hold(dev);
4808 mlxsw_core_schedule_work(&inet6addr_work->work);
4809
4810 return NOTIFY_DONE;
4811}
4812
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004813static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004814 const char *mac, int mtu)
4815{
4816 char ritr_pl[MLXSW_REG_RITR_LEN];
4817 int err;
4818
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004819 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004820 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4821 if (err)
4822 return err;
4823
4824 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4825 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4826 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4827 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4828}
4829
4830int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4831{
4832 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004833 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004834 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004835 int err;
4836
4837 mlxsw_sp = mlxsw_sp_lower_get(dev);
4838 if (!mlxsw_sp)
4839 return 0;
4840
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004841 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4842 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004843 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004844 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004845
Ido Schimmela1107482017-05-26 08:37:39 +02004846 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004847 if (err)
4848 return err;
4849
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004850 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4851 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004852 if (err)
4853 goto err_rif_edit;
4854
Ido Schimmela1107482017-05-26 08:37:39 +02004855 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004856 if (err)
4857 goto err_rif_fdb_op;
4858
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004859 ether_addr_copy(rif->addr, dev->dev_addr);
4860 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004861
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004862 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004863
4864 return 0;
4865
4866err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004867 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004868err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004869 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004870 return err;
4871}
4872
Ido Schimmelb1e45522017-04-30 19:47:14 +03004873static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4874 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004875{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004876 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004877
Ido Schimmelb1e45522017-04-30 19:47:14 +03004878 /* If netdev is already associated with a RIF, then we need to
4879 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004880 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004881 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4882 if (rif)
4883 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004884
Ido Schimmelb1e45522017-04-30 19:47:14 +03004885 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004886}
4887
Ido Schimmelb1e45522017-04-30 19:47:14 +03004888static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4889 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004890{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004891 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004892
Ido Schimmelb1e45522017-04-30 19:47:14 +03004893 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4894 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004895 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004896 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004897}
4898
Ido Schimmelb1e45522017-04-30 19:47:14 +03004899int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4900 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004901{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004902 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4903 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004904
Ido Schimmelb1e45522017-04-30 19:47:14 +03004905 if (!mlxsw_sp)
4906 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004907
Ido Schimmelb1e45522017-04-30 19:47:14 +03004908 switch (event) {
4909 case NETDEV_PRECHANGEUPPER:
4910 return 0;
4911 case NETDEV_CHANGEUPPER:
4912 if (info->linking)
4913 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4914 else
4915 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4916 break;
4917 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004918
Ido Schimmelb1e45522017-04-30 19:47:14 +03004919 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004920}
4921
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004922static struct mlxsw_sp_rif_subport *
4923mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004924{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004925 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004926}
4927
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004928static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4929 const struct mlxsw_sp_rif_params *params)
4930{
4931 struct mlxsw_sp_rif_subport *rif_subport;
4932
4933 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4934 rif_subport->vid = params->vid;
4935 rif_subport->lag = params->lag;
4936 if (params->lag)
4937 rif_subport->lag_id = params->lag_id;
4938 else
4939 rif_subport->system_port = params->system_port;
4940}
4941
4942static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4943{
4944 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4945 struct mlxsw_sp_rif_subport *rif_subport;
4946 char ritr_pl[MLXSW_REG_RITR_LEN];
4947
4948 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4949 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02004950 rif->rif_index, rif->vr_id, rif->dev->mtu);
4951 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004952 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4953 rif_subport->lag ? rif_subport->lag_id :
4954 rif_subport->system_port,
4955 rif_subport->vid);
4956
4957 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4958}
4959
4960static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4961{
Petr Machata010cadf2017-09-02 23:49:18 +02004962 int err;
4963
4964 err = mlxsw_sp_rif_subport_op(rif, true);
4965 if (err)
4966 return err;
4967
4968 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
4969 mlxsw_sp_fid_index(rif->fid), true);
4970 if (err)
4971 goto err_rif_fdb_op;
4972
4973 mlxsw_sp_fid_rif_set(rif->fid, rif);
4974 return 0;
4975
4976err_rif_fdb_op:
4977 mlxsw_sp_rif_subport_op(rif, false);
4978 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004979}
4980
4981static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4982{
Petr Machata010cadf2017-09-02 23:49:18 +02004983 struct mlxsw_sp_fid *fid = rif->fid;
4984
4985 mlxsw_sp_fid_rif_set(fid, NULL);
4986 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
4987 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004988 mlxsw_sp_rif_subport_op(rif, false);
4989}
4990
4991static struct mlxsw_sp_fid *
4992mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4993{
4994 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4995}
4996
4997static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4998 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4999 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5000 .setup = mlxsw_sp_rif_subport_setup,
5001 .configure = mlxsw_sp_rif_subport_configure,
5002 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5003 .fid_get = mlxsw_sp_rif_subport_fid_get,
5004};
5005
5006static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5007 enum mlxsw_reg_ritr_if_type type,
5008 u16 vid_fid, bool enable)
5009{
5010 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5011 char ritr_pl[MLXSW_REG_RITR_LEN];
5012
5013 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005014 rif->dev->mtu);
5015 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005016 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5017
5018 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5019}
5020
5021static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5022{
5023 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5024}
5025
5026static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5027{
5028 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5029 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5030 int err;
5031
5032 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5033 if (err)
5034 return err;
5035
Ido Schimmel0d284812017-07-18 10:10:12 +02005036 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5037 mlxsw_sp_router_port(mlxsw_sp), true);
5038 if (err)
5039 goto err_fid_mc_flood_set;
5040
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005041 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5042 mlxsw_sp_router_port(mlxsw_sp), true);
5043 if (err)
5044 goto err_fid_bc_flood_set;
5045
Petr Machata010cadf2017-09-02 23:49:18 +02005046 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5047 mlxsw_sp_fid_index(rif->fid), true);
5048 if (err)
5049 goto err_rif_fdb_op;
5050
5051 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005052 return 0;
5053
Petr Machata010cadf2017-09-02 23:49:18 +02005054err_rif_fdb_op:
5055 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5056 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005057err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005058 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5059 mlxsw_sp_router_port(mlxsw_sp), false);
5060err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005061 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5062 return err;
5063}
5064
5065static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5066{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005067 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005068 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5069 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005070
Petr Machata010cadf2017-09-02 23:49:18 +02005071 mlxsw_sp_fid_rif_set(fid, NULL);
5072 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5073 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005074 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5075 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005076 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5077 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005078 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5079}
5080
5081static struct mlxsw_sp_fid *
5082mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5083{
5084 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5085
5086 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5087}
5088
5089static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5090 .type = MLXSW_SP_RIF_TYPE_VLAN,
5091 .rif_size = sizeof(struct mlxsw_sp_rif),
5092 .configure = mlxsw_sp_rif_vlan_configure,
5093 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5094 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5095};
5096
5097static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5098{
5099 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5100 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5101 int err;
5102
5103 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5104 true);
5105 if (err)
5106 return err;
5107
Ido Schimmel0d284812017-07-18 10:10:12 +02005108 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5109 mlxsw_sp_router_port(mlxsw_sp), true);
5110 if (err)
5111 goto err_fid_mc_flood_set;
5112
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005113 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5114 mlxsw_sp_router_port(mlxsw_sp), true);
5115 if (err)
5116 goto err_fid_bc_flood_set;
5117
Petr Machata010cadf2017-09-02 23:49:18 +02005118 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5119 mlxsw_sp_fid_index(rif->fid), true);
5120 if (err)
5121 goto err_rif_fdb_op;
5122
5123 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005124 return 0;
5125
Petr Machata010cadf2017-09-02 23:49:18 +02005126err_rif_fdb_op:
5127 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5128 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005129err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005130 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5131 mlxsw_sp_router_port(mlxsw_sp), false);
5132err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005133 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5134 return err;
5135}
5136
5137static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5138{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005139 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005140 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5141 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005142
Petr Machata010cadf2017-09-02 23:49:18 +02005143 mlxsw_sp_fid_rif_set(fid, NULL);
5144 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5145 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005146 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5147 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005148 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5149 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005150 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5151}
5152
5153static struct mlxsw_sp_fid *
5154mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5155{
5156 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5157}
5158
5159static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5160 .type = MLXSW_SP_RIF_TYPE_FID,
5161 .rif_size = sizeof(struct mlxsw_sp_rif),
5162 .configure = mlxsw_sp_rif_fid_configure,
5163 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5164 .fid_get = mlxsw_sp_rif_fid_fid_get,
5165};
5166
5167static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5168 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5169 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5170 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
5171};
5172
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005173static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5174{
5175 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5176
5177 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5178 sizeof(struct mlxsw_sp_rif *),
5179 GFP_KERNEL);
5180 if (!mlxsw_sp->router->rifs)
5181 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005182
5183 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5184
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005185 return 0;
5186}
5187
5188static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5189{
5190 int i;
5191
5192 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5193 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5194
5195 kfree(mlxsw_sp->router->rifs);
5196}
5197
Petr Machata38ebc0f2017-09-02 23:49:17 +02005198static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5199{
5200 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
5201 return 0;
5202}
5203
5204static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5205{
5206}
5207
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005208static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5209{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005210 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005211
5212 /* Flush pending FIB notifications and then flush the device's
5213 * table before requesting another dump. The FIB notification
5214 * block is unregistered, so no need to take RTNL.
5215 */
5216 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005217 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5218 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005219}
5220
Ido Schimmel4724ba562017-03-10 08:53:39 +01005221static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5222{
5223 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5224 u64 max_rifs;
5225 int err;
5226
5227 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5228 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005229 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005230
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005231 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005232 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5233 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5234 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005235 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005236 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005237}
5238
5239static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5240{
5241 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005242
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005243 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005244 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005245}
5246
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005247int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5248{
Ido Schimmel9011b672017-05-16 19:38:25 +02005249 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005250 int err;
5251
Ido Schimmel9011b672017-05-16 19:38:25 +02005252 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5253 if (!router)
5254 return -ENOMEM;
5255 mlxsw_sp->router = router;
5256 router->mlxsw_sp = mlxsw_sp;
5257
5258 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005259 err = __mlxsw_sp_router_init(mlxsw_sp);
5260 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005261 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005262
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005263 err = mlxsw_sp_rifs_init(mlxsw_sp);
5264 if (err)
5265 goto err_rifs_init;
5266
Petr Machata38ebc0f2017-09-02 23:49:17 +02005267 err = mlxsw_sp_ipips_init(mlxsw_sp);
5268 if (err)
5269 goto err_ipips_init;
5270
Ido Schimmel9011b672017-05-16 19:38:25 +02005271 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005272 &mlxsw_sp_nexthop_ht_params);
5273 if (err)
5274 goto err_nexthop_ht_init;
5275
Ido Schimmel9011b672017-05-16 19:38:25 +02005276 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005277 &mlxsw_sp_nexthop_group_ht_params);
5278 if (err)
5279 goto err_nexthop_group_ht_init;
5280
Ido Schimmel8494ab02017-03-24 08:02:47 +01005281 err = mlxsw_sp_lpm_init(mlxsw_sp);
5282 if (err)
5283 goto err_lpm_init;
5284
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005285 err = mlxsw_sp_vrs_init(mlxsw_sp);
5286 if (err)
5287 goto err_vrs_init;
5288
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005289 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005290 if (err)
5291 goto err_neigh_init;
5292
Ido Schimmel7e39d112017-05-16 19:38:28 +02005293 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5294 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005295 mlxsw_sp_router_fib_dump_flush);
5296 if (err)
5297 goto err_register_fib_notifier;
5298
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005299 return 0;
5300
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005301err_register_fib_notifier:
5302 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005303err_neigh_init:
5304 mlxsw_sp_vrs_fini(mlxsw_sp);
5305err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005306 mlxsw_sp_lpm_fini(mlxsw_sp);
5307err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005308 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005309err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005310 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005311err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02005312 mlxsw_sp_ipips_fini(mlxsw_sp);
5313err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005314 mlxsw_sp_rifs_fini(mlxsw_sp);
5315err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005316 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005317err_router_init:
5318 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005319 return err;
5320}
5321
5322void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5323{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005324 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005325 mlxsw_sp_neigh_fini(mlxsw_sp);
5326 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005327 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005328 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5329 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005330 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005331 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005332 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005333 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005334}