blob: 2345c00ef7062e2d7c08cc5d76d59d7d92963226 [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
Yotam Gigic723c7352016-07-05 11:27:43 +02006 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020039#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020042#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010043#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010044#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020045#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020046#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020047#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020048#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020049#include <net/neighbour.h>
50#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020051#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020052#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010053#include <net/fib_rules.h>
Ido Schimmel57837882017-03-16 09:08:16 +010054#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020055#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020056#include <net/ndisc.h>
57#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020058#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020059
60#include "spectrum.h"
61#include "core.h"
62#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020063#include "spectrum_cnt.h"
64#include "spectrum_dpipe.h"
65#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020066
Ido Schimmel9011b672017-05-16 19:38:25 +020067struct mlxsw_sp_vr;
68struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020069struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020070
71struct mlxsw_sp_router {
72 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020073 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020074 struct mlxsw_sp_vr *vrs;
75 struct rhashtable neigh_ht;
76 struct rhashtable nexthop_group_ht;
77 struct rhashtable nexthop_ht;
78 struct {
79 struct mlxsw_sp_lpm_tree *trees;
80 unsigned int tree_count;
81 } lpm;
82 struct {
83 struct delayed_work dw;
84 unsigned long interval; /* ms */
85 } neighs_update;
86 struct delayed_work nexthop_probe_dw;
87#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
88 struct list_head nexthop_neighs_list;
89 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020090 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020091 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020092};
93
Ido Schimmel4724ba562017-03-10 08:53:39 +010094struct mlxsw_sp_rif {
95 struct list_head nexthop_list;
96 struct list_head neigh_list;
97 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +020098 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +010099 unsigned char addr[ETH_ALEN];
100 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100101 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100102 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200103 const struct mlxsw_sp_rif_ops *ops;
104 struct mlxsw_sp *mlxsw_sp;
105
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200106 unsigned int counter_ingress;
107 bool counter_ingress_valid;
108 unsigned int counter_egress;
109 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100110};
111
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200112struct mlxsw_sp_rif_params {
113 struct net_device *dev;
114 union {
115 u16 system_port;
116 u16 lag_id;
117 };
118 u16 vid;
119 bool lag;
120};
121
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200122struct mlxsw_sp_rif_subport {
123 struct mlxsw_sp_rif common;
124 union {
125 u16 system_port;
126 u16 lag_id;
127 };
128 u16 vid;
129 bool lag;
130};
131
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200132struct mlxsw_sp_rif_ops {
133 enum mlxsw_sp_rif_type type;
134 size_t rif_size;
135
136 void (*setup)(struct mlxsw_sp_rif *rif,
137 const struct mlxsw_sp_rif_params *params);
138 int (*configure)(struct mlxsw_sp_rif *rif);
139 void (*deconfigure)(struct mlxsw_sp_rif *rif);
140 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
141};
142
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200143static unsigned int *
144mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
145 enum mlxsw_sp_rif_counter_dir dir)
146{
147 switch (dir) {
148 case MLXSW_SP_RIF_COUNTER_EGRESS:
149 return &rif->counter_egress;
150 case MLXSW_SP_RIF_COUNTER_INGRESS:
151 return &rif->counter_ingress;
152 }
153 return NULL;
154}
155
156static bool
157mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
158 enum mlxsw_sp_rif_counter_dir dir)
159{
160 switch (dir) {
161 case MLXSW_SP_RIF_COUNTER_EGRESS:
162 return rif->counter_egress_valid;
163 case MLXSW_SP_RIF_COUNTER_INGRESS:
164 return rif->counter_ingress_valid;
165 }
166 return false;
167}
168
169static void
170mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
171 enum mlxsw_sp_rif_counter_dir dir,
172 bool valid)
173{
174 switch (dir) {
175 case MLXSW_SP_RIF_COUNTER_EGRESS:
176 rif->counter_egress_valid = valid;
177 break;
178 case MLXSW_SP_RIF_COUNTER_INGRESS:
179 rif->counter_ingress_valid = valid;
180 break;
181 }
182}
183
184static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
185 unsigned int counter_index, bool enable,
186 enum mlxsw_sp_rif_counter_dir dir)
187{
188 char ritr_pl[MLXSW_REG_RITR_LEN];
189 bool is_egress = false;
190 int err;
191
192 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
193 is_egress = true;
194 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
195 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
196 if (err)
197 return err;
198
199 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
200 is_egress);
201 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
202}
203
204int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
205 struct mlxsw_sp_rif *rif,
206 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
207{
208 char ricnt_pl[MLXSW_REG_RICNT_LEN];
209 unsigned int *p_counter_index;
210 bool valid;
211 int err;
212
213 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
214 if (!valid)
215 return -EINVAL;
216
217 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
218 if (!p_counter_index)
219 return -EINVAL;
220 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
221 MLXSW_REG_RICNT_OPCODE_NOP);
222 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
223 if (err)
224 return err;
225 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
226 return 0;
227}
228
229static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
230 unsigned int counter_index)
231{
232 char ricnt_pl[MLXSW_REG_RICNT_LEN];
233
234 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
235 MLXSW_REG_RICNT_OPCODE_CLEAR);
236 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
237}
238
239int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
240 struct mlxsw_sp_rif *rif,
241 enum mlxsw_sp_rif_counter_dir dir)
242{
243 unsigned int *p_counter_index;
244 int err;
245
246 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
247 if (!p_counter_index)
248 return -EINVAL;
249 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
250 p_counter_index);
251 if (err)
252 return err;
253
254 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
255 if (err)
256 goto err_counter_clear;
257
258 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
259 *p_counter_index, true, dir);
260 if (err)
261 goto err_counter_edit;
262 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
263 return 0;
264
265err_counter_edit:
266err_counter_clear:
267 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
268 *p_counter_index);
269 return err;
270}
271
272void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
273 struct mlxsw_sp_rif *rif,
274 enum mlxsw_sp_rif_counter_dir dir)
275{
276 unsigned int *p_counter_index;
277
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200278 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
279 return;
280
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200281 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
282 if (WARN_ON(!p_counter_index))
283 return;
284 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
285 *p_counter_index, false, dir);
286 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
287 *p_counter_index);
288 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
289}
290
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200291static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
292{
293 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
294 struct devlink *devlink;
295
296 devlink = priv_to_devlink(mlxsw_sp->core);
297 if (!devlink_dpipe_table_counter_enabled(devlink,
298 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
299 return;
300 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
301}
302
303static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
304{
305 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
306
307 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
308}
309
Ido Schimmel4724ba562017-03-10 08:53:39 +0100310static struct mlxsw_sp_rif *
311mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
312 const struct net_device *dev);
313
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200314#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200315
316struct mlxsw_sp_prefix_usage {
317 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
318};
319
Jiri Pirko53342022016-07-04 08:23:08 +0200320#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
321 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
322
323static bool
Jiri Pirko6b75c482016-07-04 08:23:09 +0200324mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
325 struct mlxsw_sp_prefix_usage *prefix_usage2)
326{
327 unsigned char prefix;
328
329 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
330 if (!test_bit(prefix, prefix_usage2->b))
331 return false;
332 }
333 return true;
334}
335
336static bool
Jiri Pirko53342022016-07-04 08:23:08 +0200337mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
338 struct mlxsw_sp_prefix_usage *prefix_usage2)
339{
340 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
341}
342
Jiri Pirko6b75c482016-07-04 08:23:09 +0200343static bool
344mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
345{
346 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
347
348 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
349}
350
351static void
352mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
353 struct mlxsw_sp_prefix_usage *prefix_usage2)
354{
355 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
356}
357
358static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200359mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
360 unsigned char prefix_len)
361{
362 set_bit(prefix_len, prefix_usage->b);
363}
364
365static void
366mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
367 unsigned char prefix_len)
368{
369 clear_bit(prefix_len, prefix_usage->b);
370}
371
372struct mlxsw_sp_fib_key {
373 unsigned char addr[sizeof(struct in6_addr)];
374 unsigned char prefix_len;
375};
376
Jiri Pirko61c503f2016-07-04 08:23:11 +0200377enum mlxsw_sp_fib_entry_type {
378 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
379 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
380 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
381};
382
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200383struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200384struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200385
Ido Schimmel9aecce12017-02-09 10:28:42 +0100386struct mlxsw_sp_fib_node {
387 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200388 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100389 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100390 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100391 struct mlxsw_sp_fib_key key;
392};
393
Ido Schimmel9aecce12017-02-09 10:28:42 +0100394struct mlxsw_sp_fib_entry {
395 struct list_head list;
396 struct mlxsw_sp_fib_node *fib_node;
397 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200398 struct list_head nexthop_group_node;
399 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200400};
401
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200402struct mlxsw_sp_fib4_entry {
403 struct mlxsw_sp_fib_entry common;
404 u32 tb_id;
405 u32 prio;
406 u8 tos;
407 u8 type;
408};
409
Ido Schimmel428b8512017-08-03 13:28:28 +0200410struct mlxsw_sp_fib6_entry {
411 struct mlxsw_sp_fib_entry common;
412 struct list_head rt6_list;
413 unsigned int nrt6;
414};
415
416struct mlxsw_sp_rt6 {
417 struct list_head list;
418 struct rt6_info *rt;
419};
420
Ido Schimmel9011b672017-05-16 19:38:25 +0200421enum mlxsw_sp_l3proto {
422 MLXSW_SP_L3_PROTO_IPV4,
423 MLXSW_SP_L3_PROTO_IPV6,
424};
425
426struct mlxsw_sp_lpm_tree {
427 u8 id; /* tree ID */
428 unsigned int ref_count;
429 enum mlxsw_sp_l3proto proto;
430 struct mlxsw_sp_prefix_usage prefix_usage;
431};
432
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200433struct mlxsw_sp_fib {
434 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100435 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100436 struct mlxsw_sp_vr *vr;
437 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200438 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
439 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100440 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200441};
442
Ido Schimmel9011b672017-05-16 19:38:25 +0200443struct mlxsw_sp_vr {
444 u16 id; /* virtual router ID */
445 u32 tb_id; /* kernel fib table id */
446 unsigned int rif_count;
447 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200448 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200449};
450
Ido Schimmel9aecce12017-02-09 10:28:42 +0100451static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200452
Ido Schimmel76610eb2017-03-10 08:53:41 +0100453static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
454 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200455{
456 struct mlxsw_sp_fib *fib;
457 int err;
458
459 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
460 if (!fib)
461 return ERR_PTR(-ENOMEM);
462 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
463 if (err)
464 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100465 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100466 fib->proto = proto;
467 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200468 return fib;
469
470err_rhashtable_init:
471 kfree(fib);
472 return ERR_PTR(err);
473}
474
475static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
476{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100477 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100478 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200479 rhashtable_destroy(&fib->ht);
480 kfree(fib);
481}
482
Jiri Pirko53342022016-07-04 08:23:08 +0200483static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100484mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200485{
486 static struct mlxsw_sp_lpm_tree *lpm_tree;
487 int i;
488
Ido Schimmel9011b672017-05-16 19:38:25 +0200489 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
490 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100491 if (lpm_tree->ref_count == 0)
492 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200493 }
494 return NULL;
495}
496
497static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
498 struct mlxsw_sp_lpm_tree *lpm_tree)
499{
500 char ralta_pl[MLXSW_REG_RALTA_LEN];
501
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200502 mlxsw_reg_ralta_pack(ralta_pl, true,
503 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
504 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200505 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
506}
507
508static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
509 struct mlxsw_sp_lpm_tree *lpm_tree)
510{
511 char ralta_pl[MLXSW_REG_RALTA_LEN];
512
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200513 mlxsw_reg_ralta_pack(ralta_pl, false,
514 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
515 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200516 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
517}
518
519static int
520mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
521 struct mlxsw_sp_prefix_usage *prefix_usage,
522 struct mlxsw_sp_lpm_tree *lpm_tree)
523{
524 char ralst_pl[MLXSW_REG_RALST_LEN];
525 u8 root_bin = 0;
526 u8 prefix;
527 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
528
529 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
530 root_bin = prefix;
531
532 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
533 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
534 if (prefix == 0)
535 continue;
536 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
537 MLXSW_REG_RALST_BIN_NO_CHILD);
538 last_prefix = prefix;
539 }
540 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
541}
542
543static struct mlxsw_sp_lpm_tree *
544mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
545 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100546 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200547{
548 struct mlxsw_sp_lpm_tree *lpm_tree;
549 int err;
550
Ido Schimmel382dbb42017-03-10 08:53:40 +0100551 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200552 if (!lpm_tree)
553 return ERR_PTR(-EBUSY);
554 lpm_tree->proto = proto;
555 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
556 if (err)
557 return ERR_PTR(err);
558
559 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
560 lpm_tree);
561 if (err)
562 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200563 memcpy(&lpm_tree->prefix_usage, prefix_usage,
564 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200565 return lpm_tree;
566
567err_left_struct_set:
568 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
569 return ERR_PTR(err);
570}
571
572static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
573 struct mlxsw_sp_lpm_tree *lpm_tree)
574{
575 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
576}
577
578static struct mlxsw_sp_lpm_tree *
579mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
580 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100581 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200582{
583 struct mlxsw_sp_lpm_tree *lpm_tree;
584 int i;
585
Ido Schimmel9011b672017-05-16 19:38:25 +0200586 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
587 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200588 if (lpm_tree->ref_count != 0 &&
589 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200590 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
591 prefix_usage))
592 goto inc_ref_count;
593 }
594 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100595 proto);
Jiri Pirko53342022016-07-04 08:23:08 +0200596 if (IS_ERR(lpm_tree))
597 return lpm_tree;
598
599inc_ref_count:
600 lpm_tree->ref_count++;
601 return lpm_tree;
602}
603
604static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
605 struct mlxsw_sp_lpm_tree *lpm_tree)
606{
607 if (--lpm_tree->ref_count == 0)
608 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
609 return 0;
610}
611
Ido Schimmeld7a60302017-06-08 08:47:43 +0200612#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100613
614static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200615{
616 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100617 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200618 int i;
619
Ido Schimmel8494ab02017-03-24 08:02:47 +0100620 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
621 return -EIO;
622
623 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200624 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
625 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100626 sizeof(struct mlxsw_sp_lpm_tree),
627 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200628 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100629 return -ENOMEM;
630
Ido Schimmel9011b672017-05-16 19:38:25 +0200631 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
632 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200633 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
634 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100635
636 return 0;
637}
638
639static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
640{
Ido Schimmel9011b672017-05-16 19:38:25 +0200641 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200642}
643
Ido Schimmel76610eb2017-03-10 08:53:41 +0100644static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
645{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200646 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100647}
648
Jiri Pirko6b75c482016-07-04 08:23:09 +0200649static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
650{
651 struct mlxsw_sp_vr *vr;
652 int i;
653
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200654 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200655 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100656 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200657 return vr;
658 }
659 return NULL;
660}
661
662static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100663 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200664{
665 char raltb_pl[MLXSW_REG_RALTB_LEN];
666
Ido Schimmel76610eb2017-03-10 08:53:41 +0100667 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
668 (enum mlxsw_reg_ralxx_protocol) fib->proto,
669 fib->lpm_tree->id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200670 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
671}
672
673static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100674 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200675{
676 char raltb_pl[MLXSW_REG_RALTB_LEN];
677
678 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100679 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
680 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200681 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
682}
683
684static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
685{
686 /* For our purpose, squash main and local table into one */
687 if (tb_id == RT_TABLE_LOCAL)
688 tb_id = RT_TABLE_MAIN;
689 return tb_id;
690}
691
692static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100693 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200694{
695 struct mlxsw_sp_vr *vr;
696 int i;
697
698 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200699
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200700 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200701 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100702 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200703 return vr;
704 }
705 return NULL;
706}
707
Ido Schimmel76610eb2017-03-10 08:53:41 +0100708static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
709 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200710{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100711 switch (proto) {
712 case MLXSW_SP_L3_PROTO_IPV4:
713 return vr->fib4;
714 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200715 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100716 }
717 return NULL;
718}
719
720static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
721 u32 tb_id)
722{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200723 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200724 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200725
726 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
727 if (!vr)
728 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100729 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
730 if (IS_ERR(vr->fib4))
731 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200732 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
733 if (IS_ERR(vr->fib6)) {
734 err = PTR_ERR(vr->fib6);
735 goto err_fib6_create;
736 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200737 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200738 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200739
740err_fib6_create:
741 mlxsw_sp_fib_destroy(vr->fib4);
742 vr->fib4 = NULL;
743 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200744}
745
Ido Schimmel76610eb2017-03-10 08:53:41 +0100746static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200748 mlxsw_sp_fib_destroy(vr->fib6);
749 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100750 mlxsw_sp_fib_destroy(vr->fib4);
751 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200752}
753
754static int
Ido Schimmel76610eb2017-03-10 08:53:41 +0100755mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib,
Jiri Pirko6b75c482016-07-04 08:23:09 +0200756 struct mlxsw_sp_prefix_usage *req_prefix_usage)
757{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100758 struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100759 struct mlxsw_sp_lpm_tree *new_tree;
760 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200761
Ido Schimmelf7df4922017-02-28 08:55:40 +0100762 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200763 return 0;
764
Ido Schimmelf7df4922017-02-28 08:55:40 +0100765 new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100766 fib->proto);
Ido Schimmelf7df4922017-02-28 08:55:40 +0100767 if (IS_ERR(new_tree)) {
Jiri Pirko6b75c482016-07-04 08:23:09 +0200768 /* We failed to get a tree according to the required
769 * prefix usage. However, the current tree might be still good
770 * for us if our requirement is subset of the prefixes used
771 * in the tree.
772 */
773 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
Ido Schimmelf7df4922017-02-28 08:55:40 +0100774 &lpm_tree->prefix_usage))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200775 return 0;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100776 return PTR_ERR(new_tree);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200777 }
778
Ido Schimmelf7df4922017-02-28 08:55:40 +0100779 /* Prevent packet loss by overwriting existing binding */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100780 fib->lpm_tree = new_tree;
781 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
Ido Schimmelf7df4922017-02-28 08:55:40 +0100782 if (err)
783 goto err_tree_bind;
784 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
785
786 return 0;
787
788err_tree_bind:
Ido Schimmel76610eb2017-03-10 08:53:41 +0100789 fib->lpm_tree = lpm_tree;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100790 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
791 return err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200792}
793
Ido Schimmel76610eb2017-03-10 08:53:41 +0100794static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200795{
796 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200797
798 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100799 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
800 if (!vr)
801 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200802 return vr;
803}
804
Ido Schimmel76610eb2017-03-10 08:53:41 +0100805static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200806{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200807 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
808 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100809 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200810}
811
Nogah Frankel9497c042016-09-20 11:16:54 +0200812static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200813{
814 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200815 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200816 int i;
817
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200818 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200819 return -EIO;
820
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200821 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200822 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
823 GFP_KERNEL);
824 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200825 return -ENOMEM;
826
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200827 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200828 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200829 vr->id = i;
830 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200831
832 return 0;
833}
834
Ido Schimmelac571de2016-11-14 11:26:32 +0100835static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
836
Nogah Frankel9497c042016-09-20 11:16:54 +0200837static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
838{
Ido Schimmel30572242016-12-03 16:45:01 +0100839 /* At this stage we're guaranteed not to have new incoming
840 * FIB notifications and the work queue is free from FIBs
841 * sitting on top of mlxsw netdevs. However, we can still
842 * have other FIBs queued. Flush the queue before flushing
843 * the device's tables. No need for locks, as we're the only
844 * writer.
845 */
846 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100847 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200848 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200849}
850
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200851struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100852 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200853};
854
855struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100856 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200857 struct rhash_head ht_node;
858 struct mlxsw_sp_neigh_key key;
859 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100860 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200861 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200862 struct list_head nexthop_list; /* list of nexthops using
863 * this neigh entry
864 */
Yotam Gigib2157142016-07-05 11:27:51 +0200865 struct list_head nexthop_neighs_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200866};
867
868static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
869 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
870 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
871 .key_len = sizeof(struct mlxsw_sp_neigh_key),
872};
873
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100874static struct mlxsw_sp_neigh_entry *
875mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
876 u16 rif)
877{
878 struct mlxsw_sp_neigh_entry *neigh_entry;
879
880 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
881 if (!neigh_entry)
882 return NULL;
883
884 neigh_entry->key.n = n;
885 neigh_entry->rif = rif;
886 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
887
888 return neigh_entry;
889}
890
891static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
892{
893 kfree(neigh_entry);
894}
895
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200896static int
897mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
898 struct mlxsw_sp_neigh_entry *neigh_entry)
899{
Ido Schimmel9011b672017-05-16 19:38:25 +0200900 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200901 &neigh_entry->ht_node,
902 mlxsw_sp_neigh_ht_params);
903}
904
905static void
906mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
907 struct mlxsw_sp_neigh_entry *neigh_entry)
908{
Ido Schimmel9011b672017-05-16 19:38:25 +0200909 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200910 &neigh_entry->ht_node,
911 mlxsw_sp_neigh_ht_params);
912}
913
914static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100915mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200916{
917 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100918 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100919 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200920
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100921 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
922 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100923 return ERR_PTR(-EINVAL);
924
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100925 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200926 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100927 return ERR_PTR(-ENOMEM);
928
929 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
930 if (err)
931 goto err_neigh_entry_insert;
932
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100933 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +0100934
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200935 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100936
937err_neigh_entry_insert:
938 mlxsw_sp_neigh_entry_free(neigh_entry);
939 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200940}
941
942static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100943mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
944 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200945{
Ido Schimmel9665b742017-02-08 11:16:42 +0100946 list_del(&neigh_entry->rif_list_node);
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100947 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
948 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200949}
950
951static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +0100952mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200953{
Jiri Pirko33b13412016-11-10 12:31:04 +0100954 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200955
Jiri Pirko33b13412016-11-10 12:31:04 +0100956 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +0200957 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200958 &key, mlxsw_sp_neigh_ht_params);
959}
960
Yotam Gigic723c7352016-07-05 11:27:43 +0200961static void
962mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
963{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +0200964 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +0200965
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +0200966#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +0200967 interval = min_t(unsigned long,
968 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
969 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +0200970#else
971 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
972#endif
Ido Schimmel9011b672017-05-16 19:38:25 +0200973 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +0200974}
975
976static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
977 char *rauhtd_pl,
978 int ent_index)
979{
980 struct net_device *dev;
981 struct neighbour *n;
982 __be32 dipn;
983 u32 dip;
984 u16 rif;
985
986 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
987
Ido Schimmel5f9efff2017-05-16 19:38:27 +0200988 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +0200989 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
990 return;
991 }
992
993 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +0200994 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +0200995 n = neigh_lookup(&arp_tbl, &dipn, dev);
996 if (!n) {
997 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
998 &dip);
999 return;
1000 }
1001
1002 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1003 neigh_event_send(n, NULL);
1004 neigh_release(n);
1005}
1006
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001007#if IS_ENABLED(IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001008static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1009 char *rauhtd_pl,
1010 int rec_index)
1011{
1012 struct net_device *dev;
1013 struct neighbour *n;
1014 struct in6_addr dip;
1015 u16 rif;
1016
1017 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1018 (char *) &dip);
1019
1020 if (!mlxsw_sp->router->rifs[rif]) {
1021 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1022 return;
1023 }
1024
1025 dev = mlxsw_sp->router->rifs[rif]->dev;
1026 n = neigh_lookup(&nd_tbl, &dip, dev);
1027 if (!n) {
1028 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1029 &dip);
1030 return;
1031 }
1032
1033 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1034 neigh_event_send(n, NULL);
1035 neigh_release(n);
1036}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001037#else
1038static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1039 char *rauhtd_pl,
1040 int rec_index)
1041{
1042}
1043#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001044
Yotam Gigic723c7352016-07-05 11:27:43 +02001045static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1046 char *rauhtd_pl,
1047 int rec_index)
1048{
1049 u8 num_entries;
1050 int i;
1051
1052 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1053 rec_index);
1054 /* Hardware starts counting at 0, so add 1. */
1055 num_entries++;
1056
1057 /* Each record consists of several neighbour entries. */
1058 for (i = 0; i < num_entries; i++) {
1059 int ent_index;
1060
1061 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1062 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1063 ent_index);
1064 }
1065
1066}
1067
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001068static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1069 char *rauhtd_pl,
1070 int rec_index)
1071{
1072 /* One record contains one entry. */
1073 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1074 rec_index);
1075}
1076
Yotam Gigic723c7352016-07-05 11:27:43 +02001077static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1078 char *rauhtd_pl, int rec_index)
1079{
1080 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1081 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1082 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1083 rec_index);
1084 break;
1085 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001086 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1087 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001088 break;
1089 }
1090}
1091
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001092static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1093{
1094 u8 num_rec, last_rec_index, num_entries;
1095
1096 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1097 last_rec_index = num_rec - 1;
1098
1099 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1100 return false;
1101 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1102 MLXSW_REG_RAUHTD_TYPE_IPV6)
1103 return true;
1104
1105 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1106 last_rec_index);
1107 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1108 return true;
1109 return false;
1110}
1111
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001112static int
1113__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1114 char *rauhtd_pl,
1115 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001116{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001117 int i, num_rec;
1118 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001119
1120 /* Make sure the neighbour's netdev isn't removed in the
1121 * process.
1122 */
1123 rtnl_lock();
1124 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001125 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001126 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1127 rauhtd_pl);
1128 if (err) {
1129 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1130 break;
1131 }
1132 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1133 for (i = 0; i < num_rec; i++)
1134 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1135 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001136 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001137 rtnl_unlock();
1138
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001139 return err;
1140}
1141
1142static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1143{
1144 enum mlxsw_reg_rauhtd_type type;
1145 char *rauhtd_pl;
1146 int err;
1147
1148 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1149 if (!rauhtd_pl)
1150 return -ENOMEM;
1151
1152 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1153 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1154 if (err)
1155 goto out;
1156
1157 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1158 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1159out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001160 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001161 return err;
1162}
1163
1164static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1165{
1166 struct mlxsw_sp_neigh_entry *neigh_entry;
1167
1168 /* Take RTNL mutex here to prevent lists from changes */
1169 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001170 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001171 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001172 /* If this neigh have nexthops, make the kernel think this neigh
1173 * is active regardless of the traffic.
1174 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001175 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001176 rtnl_unlock();
1177}
1178
1179static void
1180mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1181{
Ido Schimmel9011b672017-05-16 19:38:25 +02001182 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001183
Ido Schimmel9011b672017-05-16 19:38:25 +02001184 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001185 msecs_to_jiffies(interval));
1186}
1187
1188static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1189{
Ido Schimmel9011b672017-05-16 19:38:25 +02001190 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001191 int err;
1192
Ido Schimmel9011b672017-05-16 19:38:25 +02001193 router = container_of(work, struct mlxsw_sp_router,
1194 neighs_update.dw.work);
1195 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001196 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001197 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001198
Ido Schimmel9011b672017-05-16 19:38:25 +02001199 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001200
Ido Schimmel9011b672017-05-16 19:38:25 +02001201 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001202}
1203
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001204static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1205{
1206 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001207 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001208
Ido Schimmel9011b672017-05-16 19:38:25 +02001209 router = container_of(work, struct mlxsw_sp_router,
1210 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001211 /* Iterate over nexthop neighbours, find those who are unresolved and
1212 * send arp on them. This solves the chicken-egg problem when
1213 * the nexthop wouldn't get offloaded until the neighbor is resolved
1214 * but it wouldn't get resolved ever in case traffic is flowing in HW
1215 * using different nexthop.
1216 *
1217 * Take RTNL mutex here to prevent lists from changes.
1218 */
1219 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001220 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001221 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001222 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001223 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001224 rtnl_unlock();
1225
Ido Schimmel9011b672017-05-16 19:38:25 +02001226 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001227 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1228}
1229
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001230static void
1231mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1232 struct mlxsw_sp_neigh_entry *neigh_entry,
1233 bool removing);
1234
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001235static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001236{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001237 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1238 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1239}
1240
1241static void
1242mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1243 struct mlxsw_sp_neigh_entry *neigh_entry,
1244 enum mlxsw_reg_rauht_op op)
1245{
Jiri Pirko33b13412016-11-10 12:31:04 +01001246 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001247 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001248 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001249
1250 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1251 dip);
1252 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1253}
1254
1255static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001256mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1257 struct mlxsw_sp_neigh_entry *neigh_entry,
1258 enum mlxsw_reg_rauht_op op)
1259{
1260 struct neighbour *n = neigh_entry->key.n;
1261 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1262 const char *dip = n->primary_key;
1263
1264 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1265 dip);
1266 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1267}
1268
1269static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1270{
1271 /* Packets with a link-local destination address are trapped
1272 * after LPM lookup and never reach the neighbour table, so
1273 * there is no need to program such neighbours to the device.
1274 */
1275 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1276 IPV6_ADDR_LINKLOCAL)
1277 return true;
1278 return false;
1279}
1280
1281static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001282mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1283 struct mlxsw_sp_neigh_entry *neigh_entry,
1284 bool adding)
1285{
1286 if (!adding && !neigh_entry->connected)
1287 return;
1288 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001289 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001290 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1291 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001292 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001293 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1294 return;
1295 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1296 mlxsw_sp_rauht_op(adding));
1297 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001298 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001299 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001300}
1301
1302struct mlxsw_sp_neigh_event_work {
1303 struct work_struct work;
1304 struct mlxsw_sp *mlxsw_sp;
1305 struct neighbour *n;
1306};
1307
1308static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1309{
1310 struct mlxsw_sp_neigh_event_work *neigh_work =
1311 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1312 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1313 struct mlxsw_sp_neigh_entry *neigh_entry;
1314 struct neighbour *n = neigh_work->n;
1315 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001316 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001317 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001318
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001319 /* If these parameters are changed after we release the lock,
1320 * then we are guaranteed to receive another event letting us
1321 * know about it.
1322 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001323 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001324 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001325 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001326 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001327 read_unlock_bh(&n->lock);
1328
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001329 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001330 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001331 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1332 if (!entry_connected && !neigh_entry)
1333 goto out;
1334 if (!neigh_entry) {
1335 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1336 if (IS_ERR(neigh_entry))
1337 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001338 }
1339
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001340 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1341 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1342 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1343
1344 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1345 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1346
1347out:
1348 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001349 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001350 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001351}
1352
Jiri Pirkoe7322632016-09-01 10:37:43 +02001353int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1354 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001355{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001356 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001357 struct mlxsw_sp_port *mlxsw_sp_port;
1358 struct mlxsw_sp *mlxsw_sp;
1359 unsigned long interval;
1360 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001361 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001362
1363 switch (event) {
1364 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1365 p = ptr;
1366
1367 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001368 if (!p->dev || (p->tbl->family != AF_INET &&
1369 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001370 return NOTIFY_DONE;
1371
1372 /* We are in atomic context and can't take RTNL mutex,
1373 * so use RCU variant to walk the device chain.
1374 */
1375 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1376 if (!mlxsw_sp_port)
1377 return NOTIFY_DONE;
1378
1379 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1380 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001381 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001382
1383 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1384 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001385 case NETEVENT_NEIGH_UPDATE:
1386 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001387
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001388 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001389 return NOTIFY_DONE;
1390
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001391 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001392 if (!mlxsw_sp_port)
1393 return NOTIFY_DONE;
1394
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001395 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1396 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001397 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001398 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001399 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001400
1401 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1402 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1403 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001404
1405 /* Take a reference to ensure the neighbour won't be
1406 * destructed until we drop the reference in delayed
1407 * work.
1408 */
1409 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001410 mlxsw_core_schedule_work(&neigh_work->work);
1411 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001412 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001413 }
1414
1415 return NOTIFY_DONE;
1416}
1417
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001418static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1419{
Yotam Gigic723c7352016-07-05 11:27:43 +02001420 int err;
1421
Ido Schimmel9011b672017-05-16 19:38:25 +02001422 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001423 &mlxsw_sp_neigh_ht_params);
1424 if (err)
1425 return err;
1426
1427 /* Initialize the polling interval according to the default
1428 * table.
1429 */
1430 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1431
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001432 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001433 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001434 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001435 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001436 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001437 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1438 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001439 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001440}
1441
1442static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1443{
Ido Schimmel9011b672017-05-16 19:38:25 +02001444 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1445 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1446 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001447}
1448
Ido Schimmel9665b742017-02-08 11:16:42 +01001449static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001450 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001451{
1452 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1453
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001454 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001455 rif_list_node) {
1456 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001457 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001458 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001459}
1460
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001461struct mlxsw_sp_nexthop_key {
1462 struct fib_nh *fib_nh;
1463};
1464
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001465struct mlxsw_sp_nexthop {
1466 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001467 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001468 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1469 * this belongs to
1470 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001471 struct rhash_head ht_node;
1472 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001473 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001474 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001475 u8 should_offload:1, /* set indicates this neigh is connected and
1476 * should be put to KVD linear area of this group.
1477 */
1478 offloaded:1, /* set in case the neigh is actually put into
1479 * KVD linear area of this group.
1480 */
1481 update:1; /* set indicates that MAC of this neigh should be
1482 * updated in HW
1483 */
1484 struct mlxsw_sp_neigh_entry *neigh_entry;
1485};
1486
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001487struct mlxsw_sp_nexthop_group_key {
1488 struct fib_info *fi;
1489};
1490
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001491struct mlxsw_sp_nexthop_group {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001492 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001493 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001494 struct neigh_table *neigh_tbl;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001495 struct mlxsw_sp_nexthop_group_key key;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001496 u8 adj_index_valid:1,
1497 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001498 u32 adj_index;
1499 u16 ecmp_size;
1500 u16 count;
1501 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001502#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001503};
1504
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001505static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1506 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1507 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1508 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1509};
1510
1511static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1512 struct mlxsw_sp_nexthop_group *nh_grp)
1513{
Ido Schimmel9011b672017-05-16 19:38:25 +02001514 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001515 &nh_grp->ht_node,
1516 mlxsw_sp_nexthop_group_ht_params);
1517}
1518
1519static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1520 struct mlxsw_sp_nexthop_group *nh_grp)
1521{
Ido Schimmel9011b672017-05-16 19:38:25 +02001522 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001523 &nh_grp->ht_node,
1524 mlxsw_sp_nexthop_group_ht_params);
1525}
1526
1527static struct mlxsw_sp_nexthop_group *
1528mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1529 struct mlxsw_sp_nexthop_group_key key)
1530{
Ido Schimmel9011b672017-05-16 19:38:25 +02001531 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001532 mlxsw_sp_nexthop_group_ht_params);
1533}
1534
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001535static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1536 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1537 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1538 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1539};
1540
1541static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1542 struct mlxsw_sp_nexthop *nh)
1543{
Ido Schimmel9011b672017-05-16 19:38:25 +02001544 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001545 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1546}
1547
1548static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1549 struct mlxsw_sp_nexthop *nh)
1550{
Ido Schimmel9011b672017-05-16 19:38:25 +02001551 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001552 mlxsw_sp_nexthop_ht_params);
1553}
1554
Ido Schimmelad178c82017-02-08 11:16:40 +01001555static struct mlxsw_sp_nexthop *
1556mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1557 struct mlxsw_sp_nexthop_key key)
1558{
Ido Schimmel9011b672017-05-16 19:38:25 +02001559 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001560 mlxsw_sp_nexthop_ht_params);
1561}
1562
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001563static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001564 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001565 u32 adj_index, u16 ecmp_size,
1566 u32 new_adj_index,
1567 u16 new_ecmp_size)
1568{
1569 char raleu_pl[MLXSW_REG_RALEU_LEN];
1570
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001571 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001572 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1573 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001574 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001575 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1576}
1577
1578static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1579 struct mlxsw_sp_nexthop_group *nh_grp,
1580 u32 old_adj_index, u16 old_ecmp_size)
1581{
1582 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001583 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001584 int err;
1585
1586 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001587 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001588 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001589 fib = fib_entry->fib_node->fib;
1590 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001591 old_adj_index,
1592 old_ecmp_size,
1593 nh_grp->adj_index,
1594 nh_grp->ecmp_size);
1595 if (err)
1596 return err;
1597 }
1598 return 0;
1599}
1600
1601static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1602 struct mlxsw_sp_nexthop *nh)
1603{
1604 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1605 char ratr_pl[MLXSW_REG_RATR_LEN];
1606
1607 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1608 true, adj_index, neigh_entry->rif);
1609 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1610 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1611}
1612
1613static int
1614mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001615 struct mlxsw_sp_nexthop_group *nh_grp,
1616 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001617{
1618 u32 adj_index = nh_grp->adj_index; /* base */
1619 struct mlxsw_sp_nexthop *nh;
1620 int i;
1621 int err;
1622
1623 for (i = 0; i < nh_grp->count; i++) {
1624 nh = &nh_grp->nexthops[i];
1625
1626 if (!nh->should_offload) {
1627 nh->offloaded = 0;
1628 continue;
1629 }
1630
Ido Schimmela59b7e02017-01-23 11:11:42 +01001631 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001632 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1633 adj_index, nh);
1634 if (err)
1635 return err;
1636 nh->update = 0;
1637 nh->offloaded = 1;
1638 }
1639 adj_index++;
1640 }
1641 return 0;
1642}
1643
1644static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1645 struct mlxsw_sp_fib_entry *fib_entry);
1646
Ido Schimmel1819ae32017-07-21 18:04:28 +02001647static bool
1648mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1649 const struct mlxsw_sp_fib_entry *fib_entry);
1650
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001651static int
1652mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1653 struct mlxsw_sp_nexthop_group *nh_grp)
1654{
1655 struct mlxsw_sp_fib_entry *fib_entry;
1656 int err;
1657
1658 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001659 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1660 fib_entry))
1661 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001662 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1663 if (err)
1664 return err;
1665 }
1666 return 0;
1667}
1668
1669static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001670mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1671 enum mlxsw_reg_ralue_op op, int err);
1672
1673static void
1674mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1675{
1676 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1677 struct mlxsw_sp_fib_entry *fib_entry;
1678
1679 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1680 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1681 fib_entry))
1682 continue;
1683 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1684 }
1685}
1686
1687static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001688mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1689 struct mlxsw_sp_nexthop_group *nh_grp)
1690{
1691 struct mlxsw_sp_nexthop *nh;
1692 bool offload_change = false;
1693 u32 adj_index;
1694 u16 ecmp_size = 0;
1695 bool old_adj_index_valid;
1696 u32 old_adj_index;
1697 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001698 int i;
1699 int err;
1700
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001701 if (!nh_grp->gateway) {
1702 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1703 return;
1704 }
1705
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001706 for (i = 0; i < nh_grp->count; i++) {
1707 nh = &nh_grp->nexthops[i];
1708
Petr Machata56b8a9e2017-07-31 09:27:29 +02001709 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001710 offload_change = true;
1711 if (nh->should_offload)
1712 nh->update = 1;
1713 }
1714 if (nh->should_offload)
1715 ecmp_size++;
1716 }
1717 if (!offload_change) {
1718 /* Nothing was added or removed, so no need to reallocate. Just
1719 * update MAC on existing adjacency indexes.
1720 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001721 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1722 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001723 if (err) {
1724 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1725 goto set_trap;
1726 }
1727 return;
1728 }
1729 if (!ecmp_size)
1730 /* No neigh of this group is connected so we just set
1731 * the trap and let everthing flow through kernel.
1732 */
1733 goto set_trap;
1734
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001735 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1736 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001737 /* We ran out of KVD linear space, just set the
1738 * trap and let everything flow through kernel.
1739 */
1740 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1741 goto set_trap;
1742 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001743 old_adj_index_valid = nh_grp->adj_index_valid;
1744 old_adj_index = nh_grp->adj_index;
1745 old_ecmp_size = nh_grp->ecmp_size;
1746 nh_grp->adj_index_valid = 1;
1747 nh_grp->adj_index = adj_index;
1748 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001749 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001750 if (err) {
1751 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1752 goto set_trap;
1753 }
1754
1755 if (!old_adj_index_valid) {
1756 /* The trap was set for fib entries, so we have to call
1757 * fib entry update to unset it and use adjacency index.
1758 */
1759 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1760 if (err) {
1761 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1762 goto set_trap;
1763 }
1764 return;
1765 }
1766
1767 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1768 old_adj_index, old_ecmp_size);
1769 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1770 if (err) {
1771 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1772 goto set_trap;
1773 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02001774
1775 /* Offload state within the group changed, so update the flags. */
1776 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
1777
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001778 return;
1779
1780set_trap:
1781 old_adj_index_valid = nh_grp->adj_index_valid;
1782 nh_grp->adj_index_valid = 0;
1783 for (i = 0; i < nh_grp->count; i++) {
1784 nh = &nh_grp->nexthops[i];
1785 nh->offloaded = 0;
1786 }
1787 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1788 if (err)
1789 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1790 if (old_adj_index_valid)
1791 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1792}
1793
1794static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1795 bool removing)
1796{
Petr Machata213666a2017-07-31 09:27:30 +02001797 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001798 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02001799 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001800 nh->should_offload = 0;
1801 nh->update = 1;
1802}
1803
1804static void
1805mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1806 struct mlxsw_sp_neigh_entry *neigh_entry,
1807 bool removing)
1808{
1809 struct mlxsw_sp_nexthop *nh;
1810
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001811 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1812 neigh_list_node) {
1813 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1814 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1815 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001816}
1817
Ido Schimmel9665b742017-02-08 11:16:42 +01001818static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001819 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001820{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001821 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001822 return;
1823
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001824 nh->rif = rif;
1825 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001826}
1827
1828static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1829{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001830 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001831 return;
1832
1833 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001834 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01001835}
1836
Ido Schimmela8c97012017-02-08 11:16:35 +01001837static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1838 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001839{
1840 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001841 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001842 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001843 int err;
1844
Ido Schimmelad178c82017-02-08 11:16:40 +01001845 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01001846 return 0;
1847
Jiri Pirko33b13412016-11-10 12:31:04 +01001848 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02001849 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01001850 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01001851 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01001852 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001853 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01001854 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001855 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1856 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01001857 if (IS_ERR(n))
1858 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001859 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01001860 }
1861 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1862 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001863 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1864 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001865 err = -EINVAL;
1866 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001867 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001868 }
Yotam Gigib2157142016-07-05 11:27:51 +02001869
1870 /* If that is the first nexthop connected to that neigh, add to
1871 * nexthop_neighs_list
1872 */
1873 if (list_empty(&neigh_entry->nexthop_list))
1874 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02001875 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02001876
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001877 nh->neigh_entry = neigh_entry;
1878 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1879 read_lock_bh(&n->lock);
1880 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001881 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001882 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01001883 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001884
1885 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001886
1887err_neigh_entry_create:
1888 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001889 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001890}
1891
Ido Schimmela8c97012017-02-08 11:16:35 +01001892static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1893 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001894{
1895 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01001896 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001897
Ido Schimmelb8399a12017-02-08 11:16:33 +01001898 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01001899 return;
1900 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01001901
Ido Schimmel58312122016-12-23 09:32:50 +01001902 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001903 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01001904 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02001905
1906 /* If that is the last nexthop connected to that neigh, remove from
1907 * nexthop_neighs_list
1908 */
Ido Schimmele58be792017-02-08 11:16:28 +01001909 if (list_empty(&neigh_entry->nexthop_list))
1910 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02001911
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001912 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1913 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1914
1915 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01001916}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001917
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001918static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1919 struct mlxsw_sp_nexthop_group *nh_grp,
1920 struct mlxsw_sp_nexthop *nh,
1921 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001922{
1923 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001924 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001925 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01001926 int err;
1927
1928 nh->nh_grp = nh_grp;
1929 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001930 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01001931 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1932 if (err)
1933 return err;
1934
Ido Schimmel97989ee2017-03-10 08:53:38 +01001935 if (!dev)
1936 return 0;
1937
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001938 in_dev = __in_dev_get_rtnl(dev);
1939 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1940 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1941 return 0;
1942
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001943 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1944 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01001945 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001946 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01001947
1948 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1949 if (err)
1950 goto err_nexthop_neigh_init;
1951
1952 return 0;
1953
1954err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02001955 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01001956 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1957 return err;
1958}
1959
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001960static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
1961 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001962{
1963 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001964 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001965 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001966}
1967
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001968static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
1969 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01001970{
1971 struct mlxsw_sp_nexthop_key key;
1972 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001973 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01001974
Ido Schimmel9011b672017-05-16 19:38:25 +02001975 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01001976 return;
1977
1978 key.fib_nh = fib_nh;
1979 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1980 if (WARN_ON_ONCE(!nh))
1981 return;
1982
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001983 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1984 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01001985 return;
1986
1987 switch (event) {
1988 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001989 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01001990 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1991 break;
1992 case FIB_EVENT_NH_DEL:
1993 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001994 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01001995 break;
1996 }
1997
1998 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1999}
2000
Ido Schimmel9665b742017-02-08 11:16:42 +01002001static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002002 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002003{
2004 struct mlxsw_sp_nexthop *nh, *tmp;
2005
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002006 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002007 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2008 mlxsw_sp_nexthop_rif_fini(nh);
2009 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2010 }
2011}
2012
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002013static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002014mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002015{
2016 struct mlxsw_sp_nexthop_group *nh_grp;
2017 struct mlxsw_sp_nexthop *nh;
2018 struct fib_nh *fib_nh;
2019 size_t alloc_size;
2020 int i;
2021 int err;
2022
2023 alloc_size = sizeof(*nh_grp) +
2024 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2025 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2026 if (!nh_grp)
2027 return ERR_PTR(-ENOMEM);
2028 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002029 nh_grp->neigh_tbl = &arp_tbl;
2030
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002031 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002032 nh_grp->count = fi->fib_nhs;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002033 nh_grp->key.fi = fi;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002034 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002035 for (i = 0; i < nh_grp->count; i++) {
2036 nh = &nh_grp->nexthops[i];
2037 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002038 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002039 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002040 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002041 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002042 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2043 if (err)
2044 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002045 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2046 return nh_grp;
2047
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002048err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002049err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002050 for (i--; i >= 0; i--) {
2051 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002052 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002053 }
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002054 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002055 kfree(nh_grp);
2056 return ERR_PTR(err);
2057}
2058
2059static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002060mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2061 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002062{
2063 struct mlxsw_sp_nexthop *nh;
2064 int i;
2065
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002066 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002067 for (i = 0; i < nh_grp->count; i++) {
2068 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002069 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002070 }
Ido Schimmel58312122016-12-23 09:32:50 +01002071 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2072 WARN_ON_ONCE(nh_grp->adj_index_valid);
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002073 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002074 kfree(nh_grp);
2075}
2076
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002077static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2078 struct mlxsw_sp_fib_entry *fib_entry,
2079 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002080{
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002081 struct mlxsw_sp_nexthop_group_key key;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002082 struct mlxsw_sp_nexthop_group *nh_grp;
2083
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002084 key.fi = fi;
2085 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002086 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002087 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002088 if (IS_ERR(nh_grp))
2089 return PTR_ERR(nh_grp);
2090 }
2091 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2092 fib_entry->nh_group = nh_grp;
2093 return 0;
2094}
2095
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002096static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2097 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002098{
2099 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2100
2101 list_del(&fib_entry->nexthop_group_node);
2102 if (!list_empty(&nh_grp->fib_list))
2103 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002104 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002105}
2106
Ido Schimmel013b20f2017-02-08 11:16:36 +01002107static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002108mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2109{
2110 struct mlxsw_sp_fib4_entry *fib4_entry;
2111
2112 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2113 common);
2114 return !fib4_entry->tos;
2115}
2116
2117static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002118mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2119{
2120 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2121
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002122 switch (fib_entry->fib_node->fib->proto) {
2123 case MLXSW_SP_L3_PROTO_IPV4:
2124 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2125 return false;
2126 break;
2127 case MLXSW_SP_L3_PROTO_IPV6:
2128 break;
2129 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002130
Ido Schimmel013b20f2017-02-08 11:16:36 +01002131 switch (fib_entry->type) {
2132 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2133 return !!nh_group->adj_index_valid;
2134 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002135 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002136 default:
2137 return false;
2138 }
2139}
2140
Ido Schimmel428b8512017-08-03 13:28:28 +02002141static struct mlxsw_sp_nexthop *
2142mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2143 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2144{
2145 int i;
2146
2147 for (i = 0; i < nh_grp->count; i++) {
2148 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2149 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2150
2151 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2152 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2153 &rt->rt6i_gateway))
2154 return nh;
2155 continue;
2156 }
2157
2158 return NULL;
2159}
2160
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002161static void
2162mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2163{
2164 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2165 int i;
2166
2167 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2168 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2169 return;
2170 }
2171
2172 for (i = 0; i < nh_grp->count; i++) {
2173 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2174
2175 if (nh->offloaded)
2176 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2177 else
2178 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2179 }
2180}
2181
2182static void
2183mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2184{
2185 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2186 int i;
2187
2188 for (i = 0; i < nh_grp->count; i++) {
2189 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2190
2191 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2192 }
2193}
2194
Ido Schimmel428b8512017-08-03 13:28:28 +02002195static void
2196mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2197{
2198 struct mlxsw_sp_fib6_entry *fib6_entry;
2199 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2200
2201 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2202 common);
2203
2204 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2205 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
2206 list)->rt->rt6i_flags |= RTF_OFFLOAD;
2207 return;
2208 }
2209
2210 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2211 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2212 struct mlxsw_sp_nexthop *nh;
2213
2214 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2215 if (nh && nh->offloaded)
2216 mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD;
2217 else
2218 mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD;
2219 }
2220}
2221
2222static void
2223mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2224{
2225 struct mlxsw_sp_fib6_entry *fib6_entry;
2226 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2227
2228 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2229 common);
2230 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2231 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2232
2233 rt->rt6i_flags &= ~RTF_OFFLOAD;
2234 }
2235}
2236
Ido Schimmel013b20f2017-02-08 11:16:36 +01002237static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2238{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002239 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002240 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002241 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002242 break;
2243 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002244 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2245 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002246 }
2247}
2248
2249static void
2250mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2251{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002252 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002253 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002254 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002255 break;
2256 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002257 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2258 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002259 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002260}
2261
2262static void
2263mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2264 enum mlxsw_reg_ralue_op op, int err)
2265{
2266 switch (op) {
2267 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002268 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2269 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2270 if (err)
2271 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002272 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002273 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002274 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002275 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2276 return;
2277 default:
2278 return;
2279 }
2280}
2281
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002282static void
2283mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2284 const struct mlxsw_sp_fib_entry *fib_entry,
2285 enum mlxsw_reg_ralue_op op)
2286{
2287 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2288 enum mlxsw_reg_ralxx_protocol proto;
2289 u32 *p_dip;
2290
2291 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2292
2293 switch (fib->proto) {
2294 case MLXSW_SP_L3_PROTO_IPV4:
2295 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2296 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2297 fib_entry->fib_node->key.prefix_len,
2298 *p_dip);
2299 break;
2300 case MLXSW_SP_L3_PROTO_IPV6:
2301 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2302 fib_entry->fib_node->key.prefix_len,
2303 fib_entry->fib_node->key.addr);
2304 break;
2305 }
2306}
2307
2308static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2309 struct mlxsw_sp_fib_entry *fib_entry,
2310 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002311{
2312 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002313 enum mlxsw_reg_ralue_trap_action trap_action;
2314 u16 trap_id = 0;
2315 u32 adjacency_index = 0;
2316 u16 ecmp_size = 0;
2317
2318 /* In case the nexthop group adjacency index is valid, use it
2319 * with provided ECMP size. Otherwise, setup trap and pass
2320 * traffic to kernel.
2321 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002322 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002323 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2324 adjacency_index = fib_entry->nh_group->adj_index;
2325 ecmp_size = fib_entry->nh_group->ecmp_size;
2326 } else {
2327 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2328 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2329 }
2330
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002331 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002332 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2333 adjacency_index, ecmp_size);
2334 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2335}
2336
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002337static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2338 struct mlxsw_sp_fib_entry *fib_entry,
2339 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002340{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002341 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002342 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002343 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002344 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002345 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002346
2347 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2348 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002349 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002350 } else {
2351 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2352 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2353 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002354
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002355 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002356 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2357 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002358 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2359}
2360
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002361static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2362 struct mlxsw_sp_fib_entry *fib_entry,
2363 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002364{
2365 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002366
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002367 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002368 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2369 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2370}
2371
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002372static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2373 struct mlxsw_sp_fib_entry *fib_entry,
2374 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002375{
2376 switch (fib_entry->type) {
2377 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002378 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002379 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002380 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002381 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002382 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002383 }
2384 return -EINVAL;
2385}
2386
2387static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2388 struct mlxsw_sp_fib_entry *fib_entry,
2389 enum mlxsw_reg_ralue_op op)
2390{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002391 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002392
Ido Schimmel013b20f2017-02-08 11:16:36 +01002393 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002394
Ido Schimmel013b20f2017-02-08 11:16:36 +01002395 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002396}
2397
2398static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2399 struct mlxsw_sp_fib_entry *fib_entry)
2400{
Jiri Pirko7146da32016-09-01 10:37:41 +02002401 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2402 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002403}
2404
2405static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2406 struct mlxsw_sp_fib_entry *fib_entry)
2407{
2408 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2409 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2410}
2411
Jiri Pirko61c503f2016-07-04 08:23:11 +02002412static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002413mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2414 const struct fib_entry_notifier_info *fen_info,
2415 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002416{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002417 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002418
Ido Schimmel97989ee2017-03-10 08:53:38 +01002419 switch (fen_info->type) {
2420 case RTN_BROADCAST: /* fall through */
2421 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002422 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2423 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002424 case RTN_UNREACHABLE: /* fall through */
2425 case RTN_BLACKHOLE: /* fall through */
2426 case RTN_PROHIBIT:
2427 /* Packets hitting these routes need to be trapped, but
2428 * can do so with a lower priority than packets directed
2429 * at the host, so use action type local instead of trap.
2430 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002431 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002432 return 0;
2433 case RTN_UNICAST:
2434 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2435 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2436 else
2437 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2438 return 0;
2439 default:
2440 return -EINVAL;
2441 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002442}
2443
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002444static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002445mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2446 struct mlxsw_sp_fib_node *fib_node,
2447 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002448{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002449 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002450 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002451 int err;
2452
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002453 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2454 if (!fib4_entry)
2455 return ERR_PTR(-ENOMEM);
2456 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002457
2458 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2459 if (err)
2460 goto err_fib4_entry_type_set;
2461
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002462 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002463 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002464 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002465
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002466 fib4_entry->prio = fen_info->fi->fib_priority;
2467 fib4_entry->tb_id = fen_info->tb_id;
2468 fib4_entry->type = fen_info->type;
2469 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002470
2471 fib_entry->fib_node = fib_node;
2472
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002473 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002474
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002475err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002476err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002477 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002478 return ERR_PTR(err);
2479}
2480
2481static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002482 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002483{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002484 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002485 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002486}
2487
2488static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002489mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2490 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002491
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002492static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002493mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2494 const struct fib_entry_notifier_info *fen_info)
2495{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002496 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002497 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002498 struct mlxsw_sp_fib *fib;
2499 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002500
Ido Schimmel160e22a2017-07-18 10:10:20 +02002501 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2502 if (!vr)
2503 return NULL;
2504 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2505
2506 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2507 sizeof(fen_info->dst),
2508 fen_info->dst_len);
2509 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002510 return NULL;
2511
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002512 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2513 if (fib4_entry->tb_id == fen_info->tb_id &&
2514 fib4_entry->tos == fen_info->tos &&
2515 fib4_entry->type == fen_info->type &&
2516 fib4_entry->common.nh_group->key.fi == fen_info->fi) {
2517 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002518 }
2519 }
2520
2521 return NULL;
2522}
2523
2524static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2525 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2526 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2527 .key_len = sizeof(struct mlxsw_sp_fib_key),
2528 .automatic_shrinking = true,
2529};
2530
2531static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2532 struct mlxsw_sp_fib_node *fib_node)
2533{
2534 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2535 mlxsw_sp_fib_ht_params);
2536}
2537
2538static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2539 struct mlxsw_sp_fib_node *fib_node)
2540{
2541 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2542 mlxsw_sp_fib_ht_params);
2543}
2544
2545static struct mlxsw_sp_fib_node *
2546mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2547 size_t addr_len, unsigned char prefix_len)
2548{
2549 struct mlxsw_sp_fib_key key;
2550
2551 memset(&key, 0, sizeof(key));
2552 memcpy(key.addr, addr, addr_len);
2553 key.prefix_len = prefix_len;
2554 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2555}
2556
2557static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002558mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002559 size_t addr_len, unsigned char prefix_len)
2560{
2561 struct mlxsw_sp_fib_node *fib_node;
2562
2563 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2564 if (!fib_node)
2565 return NULL;
2566
2567 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002568 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002569 memcpy(fib_node->key.addr, addr, addr_len);
2570 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002571
2572 return fib_node;
2573}
2574
2575static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2576{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002577 list_del(&fib_node->list);
2578 WARN_ON(!list_empty(&fib_node->entry_list));
2579 kfree(fib_node);
2580}
2581
2582static bool
2583mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2584 const struct mlxsw_sp_fib_entry *fib_entry)
2585{
2586 return list_first_entry(&fib_node->entry_list,
2587 struct mlxsw_sp_fib_entry, list) == fib_entry;
2588}
2589
2590static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2591{
2592 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002593 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002594
2595 if (fib->prefix_ref_count[prefix_len]++ == 0)
2596 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2597}
2598
2599static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2600{
2601 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002602 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002603
2604 if (--fib->prefix_ref_count[prefix_len] == 0)
2605 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2606}
2607
Ido Schimmel76610eb2017-03-10 08:53:41 +01002608static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2609 struct mlxsw_sp_fib_node *fib_node,
2610 struct mlxsw_sp_fib *fib)
2611{
2612 struct mlxsw_sp_prefix_usage req_prefix_usage;
2613 struct mlxsw_sp_lpm_tree *lpm_tree;
2614 int err;
2615
2616 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2617 if (err)
2618 return err;
2619 fib_node->fib = fib;
2620
2621 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
2622 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2623
2624 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2625 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
2626 &req_prefix_usage);
2627 if (err)
2628 goto err_tree_check;
2629 } else {
2630 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2631 fib->proto);
2632 if (IS_ERR(lpm_tree))
2633 return PTR_ERR(lpm_tree);
2634 fib->lpm_tree = lpm_tree;
2635 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
2636 if (err)
2637 goto err_tree_bind;
2638 }
2639
2640 mlxsw_sp_fib_node_prefix_inc(fib_node);
2641
2642 return 0;
2643
2644err_tree_bind:
2645 fib->lpm_tree = NULL;
2646 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2647err_tree_check:
2648 fib_node->fib = NULL;
2649 mlxsw_sp_fib_node_remove(fib, fib_node);
2650 return err;
2651}
2652
2653static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2654 struct mlxsw_sp_fib_node *fib_node)
2655{
2656 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
2657 struct mlxsw_sp_fib *fib = fib_node->fib;
2658
2659 mlxsw_sp_fib_node_prefix_dec(fib_node);
2660
2661 if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2662 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2663 fib->lpm_tree = NULL;
2664 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2665 } else {
2666 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
2667 }
2668
2669 fib_node->fib = NULL;
2670 mlxsw_sp_fib_node_remove(fib, fib_node);
2671}
2672
Ido Schimmel9aecce12017-02-09 10:28:42 +01002673static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002674mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2675 size_t addr_len, unsigned char prefix_len,
2676 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002677{
2678 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002679 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002680 struct mlxsw_sp_vr *vr;
2681 int err;
2682
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002683 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002684 if (IS_ERR(vr))
2685 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002686 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002687
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002688 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002689 if (fib_node)
2690 return fib_node;
2691
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002692 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002693 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002694 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002695 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002696 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002697
Ido Schimmel76610eb2017-03-10 08:53:41 +01002698 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2699 if (err)
2700 goto err_fib_node_init;
2701
Ido Schimmel9aecce12017-02-09 10:28:42 +01002702 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002703
Ido Schimmel76610eb2017-03-10 08:53:41 +01002704err_fib_node_init:
2705 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002706err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002707 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002708 return ERR_PTR(err);
2709}
2710
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002711static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2712 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002713{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002714 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002715
Ido Schimmel9aecce12017-02-09 10:28:42 +01002716 if (!list_empty(&fib_node->entry_list))
2717 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002718 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002719 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002720 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002721}
2722
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002723static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002724mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002725 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002726{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002727 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002728
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002729 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2730 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002731 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002732 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002733 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002734 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002735 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002736 if (fib4_entry->prio >= new4_entry->prio ||
2737 fib4_entry->tos < new4_entry->tos)
2738 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002739 }
2740
2741 return NULL;
2742}
2743
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002744static int
2745mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2746 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002747{
2748 struct mlxsw_sp_fib_node *fib_node;
2749
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002750 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01002751 return -EINVAL;
2752
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002753 fib_node = fib4_entry->common.fib_node;
2754 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2755 common.list) {
2756 if (fib4_entry->tb_id != new4_entry->tb_id ||
2757 fib4_entry->tos != new4_entry->tos ||
2758 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002759 break;
2760 }
2761
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002762 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01002763 return 0;
2764}
2765
Ido Schimmel9aecce12017-02-09 10:28:42 +01002766static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02002767mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002768 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002769{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002770 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002771 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002772
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002773 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002774
Ido Schimmel4283bce2017-02-09 10:28:43 +01002775 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002776 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2777 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002778 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01002779
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002780 /* Insert new entry before replaced one, so that we can later
2781 * remove the second.
2782 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002783 if (fib4_entry) {
2784 list_add_tail(&new4_entry->common.list,
2785 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002786 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002787 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002788
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002789 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2790 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002791 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002792 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002793 }
2794
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002795 if (fib4_entry)
2796 list_add(&new4_entry->common.list,
2797 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002798 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002799 list_add(&new4_entry->common.list,
2800 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002801 }
2802
2803 return 0;
2804}
2805
2806static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002807mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002808{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002809 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002810}
2811
Ido Schimmel80c238f2017-07-18 10:10:29 +02002812static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2813 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002814{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002815 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2816
Ido Schimmel9aecce12017-02-09 10:28:42 +01002817 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2818 return 0;
2819
2820 /* To prevent packet loss, overwrite the previously offloaded
2821 * entry.
2822 */
2823 if (!list_is_singular(&fib_node->entry_list)) {
2824 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2825 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2826
2827 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2828 }
2829
2830 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2831}
2832
Ido Schimmel80c238f2017-07-18 10:10:29 +02002833static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2834 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002835{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002836 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2837
Ido Schimmel9aecce12017-02-09 10:28:42 +01002838 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2839 return;
2840
2841 /* Promote the next entry by overwriting the deleted entry */
2842 if (!list_is_singular(&fib_node->entry_list)) {
2843 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2844 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2845
2846 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2847 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2848 return;
2849 }
2850
2851 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2852}
2853
2854static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002855 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002856 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002857{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002858 int err;
2859
Ido Schimmel9efbee62017-07-18 10:10:28 +02002860 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002861 if (err)
2862 return err;
2863
Ido Schimmel80c238f2017-07-18 10:10:29 +02002864 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002865 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02002866 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002867
Ido Schimmel9aecce12017-02-09 10:28:42 +01002868 return 0;
2869
Ido Schimmel80c238f2017-07-18 10:10:29 +02002870err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002871 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002872 return err;
2873}
2874
2875static void
2876mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002877 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002878{
Ido Schimmel80c238f2017-07-18 10:10:29 +02002879 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002880 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002881}
2882
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002883static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002884 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002885 bool replace)
2886{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002887 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2888 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002889
2890 if (!replace)
2891 return;
2892
2893 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002894 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002895
2896 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2897 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002898 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002899}
2900
Ido Schimmel9aecce12017-02-09 10:28:42 +01002901static int
2902mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01002903 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002904 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002905{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002906 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002907 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002908 int err;
2909
Ido Schimmel9011b672017-05-16 19:38:25 +02002910 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002911 return 0;
2912
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002913 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
2914 &fen_info->dst, sizeof(fen_info->dst),
2915 fen_info->dst_len,
2916 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002917 if (IS_ERR(fib_node)) {
2918 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2919 return PTR_ERR(fib_node);
2920 }
2921
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002922 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2923 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002924 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002925 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002926 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002927 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002928
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002929 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002930 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002931 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002932 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2933 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002934 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002935
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002936 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002937
Jiri Pirko61c503f2016-07-04 08:23:11 +02002938 return 0;
2939
Ido Schimmel9aecce12017-02-09 10:28:42 +01002940err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002941 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002942err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002943 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002944 return err;
2945}
2946
Jiri Pirko37956d72016-10-20 16:05:43 +02002947static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2948 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002949{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002950 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002951 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002952
Ido Schimmel9011b672017-05-16 19:38:25 +02002953 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02002954 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002955
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002956 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2957 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02002958 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002959 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002960
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002961 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2962 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002963 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002964}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002965
Ido Schimmel428b8512017-08-03 13:28:28 +02002966static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
2967{
2968 /* Packets with link-local destination IP arriving to the router
2969 * are trapped to the CPU, so no need to program specific routes
2970 * for them.
2971 */
2972 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
2973 return true;
2974
2975 /* Multicast routes aren't supported, so ignore them. Neighbour
2976 * Discovery packets are specifically trapped.
2977 */
2978 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
2979 return true;
2980
2981 /* Cloned routes are irrelevant in the forwarding path. */
2982 if (rt->rt6i_flags & RTF_CACHE)
2983 return true;
2984
2985 return false;
2986}
2987
2988static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
2989{
2990 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2991
2992 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
2993 if (!mlxsw_sp_rt6)
2994 return ERR_PTR(-ENOMEM);
2995
2996 /* In case of route replace, replaced route is deleted with
2997 * no notification. Take reference to prevent accessing freed
2998 * memory.
2999 */
3000 mlxsw_sp_rt6->rt = rt;
3001 rt6_hold(rt);
3002
3003 return mlxsw_sp_rt6;
3004}
3005
3006#if IS_ENABLED(CONFIG_IPV6)
3007static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3008{
3009 rt6_release(rt);
3010}
3011#else
3012static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3013{
3014}
3015#endif
3016
3017static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3018{
3019 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3020 kfree(mlxsw_sp_rt6);
3021}
3022
3023static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3024{
3025 /* RTF_CACHE routes are ignored */
3026 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3027}
3028
3029static struct rt6_info *
3030mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3031{
3032 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3033 list)->rt;
3034}
3035
3036static struct mlxsw_sp_fib6_entry *
3037mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
3038 const struct rt6_info *nrt)
3039{
3040 struct mlxsw_sp_fib6_entry *fib6_entry;
3041
3042 if (!mlxsw_sp_fib6_rt_can_mp(nrt))
3043 return NULL;
3044
3045 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3046 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3047
3048 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3049 * virtual router.
3050 */
3051 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3052 continue;
3053 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3054 break;
3055 if (rt->rt6i_metric < nrt->rt6i_metric)
3056 continue;
3057 if (rt->rt6i_metric == nrt->rt6i_metric &&
3058 mlxsw_sp_fib6_rt_can_mp(rt))
3059 return fib6_entry;
3060 if (rt->rt6i_metric > nrt->rt6i_metric)
3061 break;
3062 }
3063
3064 return NULL;
3065}
3066
3067static struct mlxsw_sp_rt6 *
3068mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3069 const struct rt6_info *rt)
3070{
3071 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3072
3073 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3074 if (mlxsw_sp_rt6->rt == rt)
3075 return mlxsw_sp_rt6;
3076 }
3077
3078 return NULL;
3079}
3080
3081static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3082 struct mlxsw_sp_nexthop_group *nh_grp,
3083 struct mlxsw_sp_nexthop *nh,
3084 const struct rt6_info *rt)
3085{
3086 struct net_device *dev = rt->dst.dev;
3087 struct mlxsw_sp_rif *rif;
3088 int err;
3089
3090 nh->nh_grp = nh_grp;
3091 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3092
3093 if (!dev)
3094 return 0;
3095
3096 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3097 if (!rif)
3098 return 0;
3099 mlxsw_sp_nexthop_rif_init(nh, rif);
3100
3101 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3102 if (err)
3103 goto err_nexthop_neigh_init;
3104
3105 return 0;
3106
3107err_nexthop_neigh_init:
3108 mlxsw_sp_nexthop_rif_fini(nh);
3109 return err;
3110}
3111
3112static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3113 struct mlxsw_sp_nexthop *nh)
3114{
3115 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3116 mlxsw_sp_nexthop_rif_fini(nh);
3117}
3118
3119static struct mlxsw_sp_nexthop_group *
3120mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3121 struct mlxsw_sp_fib6_entry *fib6_entry)
3122{
3123 struct mlxsw_sp_nexthop_group *nh_grp;
3124 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3125 struct mlxsw_sp_nexthop *nh;
3126 size_t alloc_size;
3127 int i = 0;
3128 int err;
3129
3130 alloc_size = sizeof(*nh_grp) +
3131 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3132 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3133 if (!nh_grp)
3134 return ERR_PTR(-ENOMEM);
3135 INIT_LIST_HEAD(&nh_grp->fib_list);
3136#if IS_ENABLED(CONFIG_IPV6)
3137 nh_grp->neigh_tbl = &nd_tbl;
3138#endif
3139 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3140 struct mlxsw_sp_rt6, list);
3141 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3142 nh_grp->count = fib6_entry->nrt6;
3143 for (i = 0; i < nh_grp->count; i++) {
3144 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3145
3146 nh = &nh_grp->nexthops[i];
3147 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3148 if (err)
3149 goto err_nexthop6_init;
3150 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3151 }
3152 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3153 return nh_grp;
3154
3155err_nexthop6_init:
3156 for (i--; i >= 0; i--) {
3157 nh = &nh_grp->nexthops[i];
3158 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3159 }
3160 kfree(nh_grp);
3161 return ERR_PTR(err);
3162}
3163
3164static void
3165mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3166 struct mlxsw_sp_nexthop_group *nh_grp)
3167{
3168 struct mlxsw_sp_nexthop *nh;
3169 int i = nh_grp->count;
3170
3171 for (i--; i >= 0; i--) {
3172 nh = &nh_grp->nexthops[i];
3173 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3174 }
3175 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3176 WARN_ON(nh_grp->adj_index_valid);
3177 kfree(nh_grp);
3178}
3179
3180static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3181 struct mlxsw_sp_fib6_entry *fib6_entry)
3182{
3183 struct mlxsw_sp_nexthop_group *nh_grp;
3184
3185 /* For now, don't consolidate nexthop groups */
3186 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3187 if (IS_ERR(nh_grp))
3188 return PTR_ERR(nh_grp);
3189
3190 list_add_tail(&fib6_entry->common.nexthop_group_node,
3191 &nh_grp->fib_list);
3192 fib6_entry->common.nh_group = nh_grp;
3193
3194 return 0;
3195}
3196
3197static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3198 struct mlxsw_sp_fib_entry *fib_entry)
3199{
3200 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3201
3202 list_del(&fib_entry->nexthop_group_node);
3203 if (!list_empty(&nh_grp->fib_list))
3204 return;
3205 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3206}
3207
3208static int
3209mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3210 struct mlxsw_sp_fib6_entry *fib6_entry)
3211{
3212 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3213 int err;
3214
3215 fib6_entry->common.nh_group = NULL;
3216 list_del(&fib6_entry->common.nexthop_group_node);
3217
3218 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3219 if (err)
3220 goto err_nexthop6_group_get;
3221
3222 /* In case this entry is offloaded, then the adjacency index
3223 * currently associated with it in the device's table is that
3224 * of the old group. Start using the new one instead.
3225 */
3226 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3227 if (err)
3228 goto err_fib_node_entry_add;
3229
3230 if (list_empty(&old_nh_grp->fib_list))
3231 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3232
3233 return 0;
3234
3235err_fib_node_entry_add:
3236 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3237err_nexthop6_group_get:
3238 list_add_tail(&fib6_entry->common.nexthop_group_node,
3239 &old_nh_grp->fib_list);
3240 fib6_entry->common.nh_group = old_nh_grp;
3241 return err;
3242}
3243
3244static int
3245mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3246 struct mlxsw_sp_fib6_entry *fib6_entry,
3247 struct rt6_info *rt)
3248{
3249 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3250 int err;
3251
3252 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3253 if (IS_ERR(mlxsw_sp_rt6))
3254 return PTR_ERR(mlxsw_sp_rt6);
3255
3256 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3257 fib6_entry->nrt6++;
3258
3259 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3260 if (err)
3261 goto err_nexthop6_group_update;
3262
3263 return 0;
3264
3265err_nexthop6_group_update:
3266 fib6_entry->nrt6--;
3267 list_del(&mlxsw_sp_rt6->list);
3268 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3269 return err;
3270}
3271
3272static void
3273mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3274 struct mlxsw_sp_fib6_entry *fib6_entry,
3275 struct rt6_info *rt)
3276{
3277 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3278
3279 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3280 if (WARN_ON(!mlxsw_sp_rt6))
3281 return;
3282
3283 fib6_entry->nrt6--;
3284 list_del(&mlxsw_sp_rt6->list);
3285 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3286 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3287}
3288
3289static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3290 const struct rt6_info *rt)
3291{
3292 /* Packets hitting RTF_REJECT routes need to be discarded by the
3293 * stack. We can rely on their destination device not having a
3294 * RIF (it's the loopback device) and can thus use action type
3295 * local, which will cause them to be trapped with a lower
3296 * priority than packets that need to be locally received.
3297 */
3298 if (rt->rt6i_flags & RTF_LOCAL)
3299 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3300 else if (rt->rt6i_flags & RTF_REJECT)
3301 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3302 else if (rt->rt6i_flags & RTF_GATEWAY)
3303 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3304 else
3305 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3306}
3307
3308static void
3309mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3310{
3311 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3312
3313 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3314 list) {
3315 fib6_entry->nrt6--;
3316 list_del(&mlxsw_sp_rt6->list);
3317 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3318 }
3319}
3320
3321static struct mlxsw_sp_fib6_entry *
3322mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3323 struct mlxsw_sp_fib_node *fib_node,
3324 struct rt6_info *rt)
3325{
3326 struct mlxsw_sp_fib6_entry *fib6_entry;
3327 struct mlxsw_sp_fib_entry *fib_entry;
3328 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3329 int err;
3330
3331 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3332 if (!fib6_entry)
3333 return ERR_PTR(-ENOMEM);
3334 fib_entry = &fib6_entry->common;
3335
3336 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3337 if (IS_ERR(mlxsw_sp_rt6)) {
3338 err = PTR_ERR(mlxsw_sp_rt6);
3339 goto err_rt6_create;
3340 }
3341
3342 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3343
3344 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3345 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3346 fib6_entry->nrt6 = 1;
3347 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3348 if (err)
3349 goto err_nexthop6_group_get;
3350
3351 fib_entry->fib_node = fib_node;
3352
3353 return fib6_entry;
3354
3355err_nexthop6_group_get:
3356 list_del(&mlxsw_sp_rt6->list);
3357 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3358err_rt6_create:
3359 kfree(fib6_entry);
3360 return ERR_PTR(err);
3361}
3362
3363static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3364 struct mlxsw_sp_fib6_entry *fib6_entry)
3365{
3366 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3367 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3368 WARN_ON(fib6_entry->nrt6);
3369 kfree(fib6_entry);
3370}
3371
3372static struct mlxsw_sp_fib6_entry *
3373mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
3374 const struct rt6_info *nrt)
3375{
3376 struct mlxsw_sp_fib6_entry *fib6_entry;
3377
3378 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3379 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3380
3381 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3382 continue;
3383 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3384 break;
3385 if (rt->rt6i_metric > nrt->rt6i_metric)
3386 return fib6_entry;
3387 }
3388
3389 return NULL;
3390}
3391
3392static int
3393mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry)
3394{
3395 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3396 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3397 struct mlxsw_sp_fib6_entry *fib6_entry;
3398
3399 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt);
3400
3401 if (fib6_entry) {
3402 list_add_tail(&new6_entry->common.list,
3403 &fib6_entry->common.list);
3404 } else {
3405 struct mlxsw_sp_fib6_entry *last;
3406
3407 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3408 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3409
3410 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3411 break;
3412 fib6_entry = last;
3413 }
3414
3415 if (fib6_entry)
3416 list_add(&new6_entry->common.list,
3417 &fib6_entry->common.list);
3418 else
3419 list_add(&new6_entry->common.list,
3420 &fib_node->entry_list);
3421 }
3422
3423 return 0;
3424}
3425
3426static void
3427mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3428{
3429 list_del(&fib6_entry->common.list);
3430}
3431
3432static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
3433 struct mlxsw_sp_fib6_entry *fib6_entry)
3434{
3435 int err;
3436
3437 err = mlxsw_sp_fib6_node_list_insert(fib6_entry);
3438 if (err)
3439 return err;
3440
3441 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3442 if (err)
3443 goto err_fib_node_entry_add;
3444
3445 return 0;
3446
3447err_fib_node_entry_add:
3448 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3449 return err;
3450}
3451
3452static void
3453mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3454 struct mlxsw_sp_fib6_entry *fib6_entry)
3455{
3456 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3457 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3458}
3459
3460static struct mlxsw_sp_fib6_entry *
3461mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3462 const struct rt6_info *rt)
3463{
3464 struct mlxsw_sp_fib6_entry *fib6_entry;
3465 struct mlxsw_sp_fib_node *fib_node;
3466 struct mlxsw_sp_fib *fib;
3467 struct mlxsw_sp_vr *vr;
3468
3469 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3470 if (!vr)
3471 return NULL;
3472 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3473
3474 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3475 sizeof(rt->rt6i_dst.addr),
3476 rt->rt6i_dst.plen);
3477 if (!fib_node)
3478 return NULL;
3479
3480 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3481 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3482
3483 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3484 rt->rt6i_metric == iter_rt->rt6i_metric &&
3485 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3486 return fib6_entry;
3487 }
3488
3489 return NULL;
3490}
3491
3492static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
3493 struct rt6_info *rt)
3494{
3495 struct mlxsw_sp_fib6_entry *fib6_entry;
3496 struct mlxsw_sp_fib_node *fib_node;
3497 int err;
3498
3499 if (mlxsw_sp->router->aborted)
3500 return 0;
3501
3502 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3503 return 0;
3504
3505 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3506 &rt->rt6i_dst.addr,
3507 sizeof(rt->rt6i_dst.addr),
3508 rt->rt6i_dst.plen,
3509 MLXSW_SP_L3_PROTO_IPV6);
3510 if (IS_ERR(fib_node))
3511 return PTR_ERR(fib_node);
3512
3513 /* Before creating a new entry, try to append route to an existing
3514 * multipath entry.
3515 */
3516 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt);
3517 if (fib6_entry) {
3518 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3519 if (err)
3520 goto err_fib6_entry_nexthop_add;
3521 return 0;
3522 }
3523
3524 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3525 if (IS_ERR(fib6_entry)) {
3526 err = PTR_ERR(fib6_entry);
3527 goto err_fib6_entry_create;
3528 }
3529
3530 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry);
3531 if (err)
3532 goto err_fib6_node_entry_link;
3533
3534 return 0;
3535
3536err_fib6_node_entry_link:
3537 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3538err_fib6_entry_create:
3539err_fib6_entry_nexthop_add:
3540 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3541 return err;
3542}
3543
3544static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3545 struct rt6_info *rt)
3546{
3547 struct mlxsw_sp_fib6_entry *fib6_entry;
3548 struct mlxsw_sp_fib_node *fib_node;
3549
3550 if (mlxsw_sp->router->aborted)
3551 return;
3552
3553 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3554 return;
3555
3556 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3557 if (WARN_ON(!fib6_entry))
3558 return;
3559
3560 /* If route is part of a multipath entry, but not the last one
3561 * removed, then only reduce its nexthop group.
3562 */
3563 if (!list_is_singular(&fib6_entry->rt6_list)) {
3564 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3565 return;
3566 }
3567
3568 fib_node = fib6_entry->common.fib_node;
3569
3570 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3571 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3572 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3573}
3574
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003575static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3576 enum mlxsw_reg_ralxx_protocol proto,
3577 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003578{
3579 char ralta_pl[MLXSW_REG_RALTA_LEN];
3580 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003581 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003582
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003583 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003584 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3585 if (err)
3586 return err;
3587
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003588 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003589 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3590 if (err)
3591 return err;
3592
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003593 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003594 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003595 char raltb_pl[MLXSW_REG_RALTB_LEN];
3596 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003597
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003598 if (!mlxsw_sp_vr_is_used(vr))
3599 continue;
3600
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003601 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003602 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3603 raltb_pl);
3604 if (err)
3605 return err;
3606
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003607 mlxsw_reg_ralue_pack(ralue_pl, proto,
3608 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003609 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3610 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3611 ralue_pl);
3612 if (err)
3613 return err;
3614 }
3615
3616 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003617}
3618
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003619static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3620{
3621 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3622 int err;
3623
3624 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3625 MLXSW_SP_LPM_TREE_MIN);
3626 if (err)
3627 return err;
3628
3629 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3630 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3631 MLXSW_SP_LPM_TREE_MIN + 1);
3632}
3633
Ido Schimmel9aecce12017-02-09 10:28:42 +01003634static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3635 struct mlxsw_sp_fib_node *fib_node)
3636{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003637 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003638
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003639 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
3640 common.list) {
3641 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003642
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003643 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3644 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003645 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003646 /* Break when entry list is empty and node was freed.
3647 * Otherwise, we'll access freed memory in the next
3648 * iteration.
3649 */
3650 if (do_break)
3651 break;
3652 }
3653}
3654
Ido Schimmel428b8512017-08-03 13:28:28 +02003655static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
3656 struct mlxsw_sp_fib_node *fib_node)
3657{
3658 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
3659
3660 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
3661 common.list) {
3662 bool do_break = &tmp->common.list == &fib_node->entry_list;
3663
3664 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3665 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3666 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3667 if (do_break)
3668 break;
3669 }
3670}
3671
Ido Schimmel9aecce12017-02-09 10:28:42 +01003672static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
3673 struct mlxsw_sp_fib_node *fib_node)
3674{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003675 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003676 case MLXSW_SP_L3_PROTO_IPV4:
3677 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
3678 break;
3679 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003680 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003681 break;
3682 }
3683}
3684
Ido Schimmel76610eb2017-03-10 08:53:41 +01003685static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
3686 struct mlxsw_sp_vr *vr,
3687 enum mlxsw_sp_l3proto proto)
3688{
3689 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
3690 struct mlxsw_sp_fib_node *fib_node, *tmp;
3691
3692 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
3693 bool do_break = &tmp->list == &fib->node_list;
3694
3695 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
3696 if (do_break)
3697 break;
3698 }
3699}
3700
Ido Schimmelac571de2016-11-14 11:26:32 +01003701static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003702{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003703 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003704
Jiri Pirkoc1a38312016-10-21 16:07:23 +02003705 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003706 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01003707
Ido Schimmel76610eb2017-03-10 08:53:41 +01003708 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003709 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003710 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02003711
3712 /* If virtual router was only used for IPv4, then it's no
3713 * longer used.
3714 */
3715 if (!mlxsw_sp_vr_is_used(vr))
3716 continue;
3717 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003718 }
Ido Schimmelac571de2016-11-14 11:26:32 +01003719}
3720
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003721static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01003722{
3723 int err;
3724
Ido Schimmel9011b672017-05-16 19:38:25 +02003725 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003726 return;
3727 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 +01003728 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003729 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003730 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3731 if (err)
3732 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3733}
3734
Ido Schimmel30572242016-12-03 16:45:01 +01003735struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003736 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003737 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02003738 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003739 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003740 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003741 struct fib_nh_notifier_info fnh_info;
3742 };
Ido Schimmel30572242016-12-03 16:45:01 +01003743 struct mlxsw_sp *mlxsw_sp;
3744 unsigned long event;
3745};
3746
Ido Schimmel66a57632017-08-03 13:28:26 +02003747static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003748{
Ido Schimmel30572242016-12-03 16:45:01 +01003749 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003750 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003751 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003752 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003753 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003754 int err;
3755
Ido Schimmel30572242016-12-03 16:45:01 +01003756 /* Protect internal structures from changes */
3757 rtnl_lock();
3758 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003759 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003760 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003761 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003762 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003763 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3764 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003765 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003766 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003767 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003768 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003769 break;
3770 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003771 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3772 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003773 break;
3774 case FIB_EVENT_RULE_ADD: /* fall through */
3775 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003776 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003777 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003778 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003779 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003780 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003781 case FIB_EVENT_NH_ADD: /* fall through */
3782 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003783 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3784 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003785 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3786 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003787 }
Ido Schimmel30572242016-12-03 16:45:01 +01003788 rtnl_unlock();
3789 kfree(fib_work);
3790}
3791
Ido Schimmel66a57632017-08-03 13:28:26 +02003792static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
3793{
Ido Schimmel583419f2017-08-03 13:28:27 +02003794 struct mlxsw_sp_fib_event_work *fib_work =
3795 container_of(work, struct mlxsw_sp_fib_event_work, work);
3796 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
3797 struct fib_rule *rule;
Ido Schimmel428b8512017-08-03 13:28:28 +02003798 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02003799
3800 rtnl_lock();
3801 switch (fib_work->event) {
Ido Schimmel428b8512017-08-03 13:28:28 +02003802 case FIB_EVENT_ENTRY_ADD:
3803 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
3804 fib_work->fen6_info.rt);
3805 if (err)
3806 mlxsw_sp_router_fib_abort(mlxsw_sp);
3807 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3808 break;
3809 case FIB_EVENT_ENTRY_DEL:
3810 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
3811 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3812 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003813 case FIB_EVENT_RULE_ADD: /* fall through */
3814 case FIB_EVENT_RULE_DEL:
3815 rule = fib_work->fr_info.rule;
3816 if (!fib6_rule_default(rule) && !rule->l3mdev)
3817 mlxsw_sp_router_fib_abort(mlxsw_sp);
3818 fib_rule_put(rule);
3819 break;
3820 }
3821 rtnl_unlock();
3822 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02003823}
3824
3825static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
3826 struct fib_notifier_info *info)
3827{
3828 switch (fib_work->event) {
3829 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
3830 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3831 case FIB_EVENT_ENTRY_ADD: /* fall through */
3832 case FIB_EVENT_ENTRY_DEL:
3833 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
3834 /* Take referece on fib_info to prevent it from being
3835 * freed while work is queued. Release it afterwards.
3836 */
3837 fib_info_hold(fib_work->fen_info.fi);
3838 break;
3839 case FIB_EVENT_RULE_ADD: /* fall through */
3840 case FIB_EVENT_RULE_DEL:
3841 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3842 fib_rule_get(fib_work->fr_info.rule);
3843 break;
3844 case FIB_EVENT_NH_ADD: /* fall through */
3845 case FIB_EVENT_NH_DEL:
3846 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
3847 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3848 break;
3849 }
3850}
3851
3852static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
3853 struct fib_notifier_info *info)
3854{
Ido Schimmel583419f2017-08-03 13:28:27 +02003855 switch (fib_work->event) {
Ido Schimmel428b8512017-08-03 13:28:28 +02003856 case FIB_EVENT_ENTRY_ADD: /* fall through */
3857 case FIB_EVENT_ENTRY_DEL:
3858 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
3859 rt6_hold(fib_work->fen6_info.rt);
3860 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003861 case FIB_EVENT_RULE_ADD: /* fall through */
3862 case FIB_EVENT_RULE_DEL:
3863 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3864 fib_rule_get(fib_work->fr_info.rule);
3865 break;
3866 }
Ido Schimmel66a57632017-08-03 13:28:26 +02003867}
3868
Ido Schimmel30572242016-12-03 16:45:01 +01003869/* Called with rcu_read_lock() */
3870static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3871 unsigned long event, void *ptr)
3872{
Ido Schimmel30572242016-12-03 16:45:01 +01003873 struct mlxsw_sp_fib_event_work *fib_work;
3874 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02003875 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01003876
Ido Schimmel64e5e822017-08-03 13:28:12 +02003877 if (!net_eq(info->net, &init_net) || info->family != AF_INET)
Ido Schimmel30572242016-12-03 16:45:01 +01003878 return NOTIFY_DONE;
3879
3880 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3881 if (WARN_ON(!fib_work))
3882 return NOTIFY_BAD;
3883
Ido Schimmel7e39d112017-05-16 19:38:28 +02003884 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3885 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01003886 fib_work->event = event;
3887
Ido Schimmel66a57632017-08-03 13:28:26 +02003888 switch (info->family) {
3889 case AF_INET:
3890 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
3891 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01003892 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02003893 case AF_INET6:
3894 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
3895 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01003896 break;
Ido Schimmel30572242016-12-03 16:45:01 +01003897 }
3898
Ido Schimmela0e47612017-02-06 16:20:10 +01003899 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01003900
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003901 return NOTIFY_DONE;
3902}
3903
Ido Schimmel4724ba562017-03-10 08:53:39 +01003904static struct mlxsw_sp_rif *
3905mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
3906 const struct net_device *dev)
3907{
3908 int i;
3909
3910 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003911 if (mlxsw_sp->router->rifs[i] &&
3912 mlxsw_sp->router->rifs[i]->dev == dev)
3913 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01003914
3915 return NULL;
3916}
3917
3918static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
3919{
3920 char ritr_pl[MLXSW_REG_RITR_LEN];
3921 int err;
3922
3923 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3924 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3925 if (WARN_ON_ONCE(err))
3926 return err;
3927
3928 mlxsw_reg_ritr_enable_set(ritr_pl, false);
3929 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3930}
3931
3932static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003933 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003934{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003935 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
3936 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
3937 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003938}
3939
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003940static bool
3941mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
3942 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003943{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003944 struct inet6_dev *inet6_dev;
3945 bool addr_list_empty = true;
3946 struct in_device *idev;
3947
Ido Schimmel4724ba562017-03-10 08:53:39 +01003948 switch (event) {
3949 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02003950 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003951 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003952 idev = __in_dev_get_rtnl(dev);
3953 if (idev && idev->ifa_list)
3954 addr_list_empty = false;
3955
3956 inet6_dev = __in6_dev_get(dev);
3957 if (addr_list_empty && inet6_dev &&
3958 !list_empty(&inet6_dev->addr_list))
3959 addr_list_empty = false;
3960
3961 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003962 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003963 return true;
3964 /* It is possible we already removed the RIF ourselves
3965 * if it was assigned to a netdev that is now a bridge
3966 * or LAG slave.
3967 */
3968 return false;
3969 }
3970
3971 return false;
3972}
3973
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003974static enum mlxsw_sp_rif_type
3975mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
3976 const struct net_device *dev)
3977{
3978 enum mlxsw_sp_fid_type type;
3979
3980 /* RIF type is derived from the type of the underlying FID */
3981 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
3982 type = MLXSW_SP_FID_TYPE_8021Q;
3983 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
3984 type = MLXSW_SP_FID_TYPE_8021Q;
3985 else if (netif_is_bridge_master(dev))
3986 type = MLXSW_SP_FID_TYPE_8021D;
3987 else
3988 type = MLXSW_SP_FID_TYPE_RFID;
3989
3990 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
3991}
3992
Ido Schimmelde5ed992017-06-04 16:53:40 +02003993static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003994{
3995 int i;
3996
Ido Schimmelde5ed992017-06-04 16:53:40 +02003997 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
3998 if (!mlxsw_sp->router->rifs[i]) {
3999 *p_rif_index = i;
4000 return 0;
4001 }
4002 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004003
Ido Schimmelde5ed992017-06-04 16:53:40 +02004004 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004005}
4006
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004007static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4008 u16 vr_id,
4009 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004010{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004011 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004012
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004013 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004014 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004015 return NULL;
4016
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004017 INIT_LIST_HEAD(&rif->nexthop_list);
4018 INIT_LIST_HEAD(&rif->neigh_list);
4019 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4020 rif->mtu = l3_dev->mtu;
4021 rif->vr_id = vr_id;
4022 rif->dev = l3_dev;
4023 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004024
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004025 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004026}
4027
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004028struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4029 u16 rif_index)
4030{
4031 return mlxsw_sp->router->rifs[rif_index];
4032}
4033
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004034u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4035{
4036 return rif->rif_index;
4037}
4038
4039int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4040{
4041 return rif->dev->ifindex;
4042}
4043
Ido Schimmel4724ba562017-03-10 08:53:39 +01004044static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004045mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4046 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004047{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004048 u32 tb_id = l3mdev_fib_table(params->dev);
4049 const struct mlxsw_sp_rif_ops *ops;
4050 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004051 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004052 struct mlxsw_sp_fid *fid;
4053 struct mlxsw_sp_vr *vr;
4054 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004055 int err;
4056
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004057 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4058 ops = mlxsw_sp->router->rif_ops_arr[type];
4059
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004060 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4061 if (IS_ERR(vr))
4062 return ERR_CAST(vr);
4063
Ido Schimmelde5ed992017-06-04 16:53:40 +02004064 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4065 if (err)
4066 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004067
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004068 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004069 if (!rif) {
4070 err = -ENOMEM;
4071 goto err_rif_alloc;
4072 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004073 rif->mlxsw_sp = mlxsw_sp;
4074 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004075
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004076 fid = ops->fid_get(rif);
4077 if (IS_ERR(fid)) {
4078 err = PTR_ERR(fid);
4079 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004080 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004081 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004082
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004083 if (ops->setup)
4084 ops->setup(rif, params);
4085
4086 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004087 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004088 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004089
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004090 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004091 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004092 if (err)
4093 goto err_rif_fdb_op;
4094
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004095 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004096 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004097 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004098 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004099
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004100 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004101
Ido Schimmel4724ba562017-03-10 08:53:39 +01004102err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004103 ops->deconfigure(rif);
4104err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004105 mlxsw_sp_fid_put(fid);
4106err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004107 kfree(rif);
4108err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004109err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004110 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004111 return ERR_PTR(err);
4112}
4113
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004114void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004115{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004116 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4117 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004118 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004119 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004120
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004121 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004122 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004123
Ido Schimmel69132292017-03-10 08:53:42 +01004124 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004125 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004126 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004127 mlxsw_sp_rif_counters_free(rif);
4128 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4129 mlxsw_sp_fid_index(fid), false);
4130 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004131 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004132 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004133 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004134}
4135
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004136static void
4137mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4138 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4139{
4140 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4141
4142 params->vid = mlxsw_sp_port_vlan->vid;
4143 params->lag = mlxsw_sp_port->lagged;
4144 if (params->lag)
4145 params->lag_id = mlxsw_sp_port->lag_id;
4146 else
4147 params->system_port = mlxsw_sp_port->local_port;
4148}
4149
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004150static int
Ido Schimmela1107482017-05-26 08:37:39 +02004151mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004152 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004153{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004154 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004155 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004156 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004157 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004158 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004159 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004160
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004161 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004162 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004163 struct mlxsw_sp_rif_params params = {
4164 .dev = l3_dev,
4165 };
4166
4167 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4168 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004169 if (IS_ERR(rif))
4170 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004171 }
4172
Ido Schimmela1107482017-05-26 08:37:39 +02004173 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004174 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004175 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4176 if (err)
4177 goto err_fid_port_vid_map;
4178
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004179 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004180 if (err)
4181 goto err_port_vid_learning_set;
4182
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004183 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004184 BR_STATE_FORWARDING);
4185 if (err)
4186 goto err_port_vid_stp_set;
4187
Ido Schimmela1107482017-05-26 08:37:39 +02004188 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004189
Ido Schimmel4724ba562017-03-10 08:53:39 +01004190 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004191
4192err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004193 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004194err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004195 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4196err_fid_port_vid_map:
4197 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004198 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004199}
4200
Ido Schimmela1107482017-05-26 08:37:39 +02004201void
4202mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004203{
Ido Schimmelce95e152017-05-26 08:37:27 +02004204 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004205 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004206 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004207
Ido Schimmela1107482017-05-26 08:37:39 +02004208 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4209 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004210
Ido Schimmela1107482017-05-26 08:37:39 +02004211 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004212 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4213 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004214 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4215 /* If router port holds the last reference on the rFID, then the
4216 * associated Sub-port RIF will be destroyed.
4217 */
4218 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004219}
4220
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004221static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4222 struct net_device *port_dev,
4223 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004224{
4225 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004226 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004227
Ido Schimmelce95e152017-05-26 08:37:27 +02004228 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004229 if (WARN_ON(!mlxsw_sp_port_vlan))
4230 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004231
4232 switch (event) {
4233 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004234 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004235 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004236 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004237 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004238 break;
4239 }
4240
4241 return 0;
4242}
4243
4244static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4245 unsigned long event)
4246{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004247 if (netif_is_bridge_port(port_dev) ||
4248 netif_is_lag_port(port_dev) ||
4249 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004250 return 0;
4251
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004252 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004253}
4254
4255static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4256 struct net_device *lag_dev,
4257 unsigned long event, u16 vid)
4258{
4259 struct net_device *port_dev;
4260 struct list_head *iter;
4261 int err;
4262
4263 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4264 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004265 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4266 port_dev,
4267 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004268 if (err)
4269 return err;
4270 }
4271 }
4272
4273 return 0;
4274}
4275
4276static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4277 unsigned long event)
4278{
4279 if (netif_is_bridge_port(lag_dev))
4280 return 0;
4281
4282 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4283}
4284
Ido Schimmel4724ba562017-03-10 08:53:39 +01004285static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004286 unsigned long event)
4287{
4288 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004289 struct mlxsw_sp_rif_params params = {
4290 .dev = l3_dev,
4291 };
Ido Schimmela1107482017-05-26 08:37:39 +02004292 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004293
4294 switch (event) {
4295 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004296 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4297 if (IS_ERR(rif))
4298 return PTR_ERR(rif);
4299 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004300 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004301 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004302 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004303 break;
4304 }
4305
4306 return 0;
4307}
4308
4309static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4310 unsigned long event)
4311{
4312 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004313 u16 vid = vlan_dev_vlan_id(vlan_dev);
4314
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004315 if (netif_is_bridge_port(vlan_dev))
4316 return 0;
4317
Ido Schimmel4724ba562017-03-10 08:53:39 +01004318 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004319 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4320 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004321 else if (netif_is_lag_master(real_dev))
4322 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4323 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004324 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004325 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004326
4327 return 0;
4328}
4329
Ido Schimmelb1e45522017-04-30 19:47:14 +03004330static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4331 unsigned long event)
4332{
4333 if (mlxsw_sp_port_dev_check(dev))
4334 return mlxsw_sp_inetaddr_port_event(dev, event);
4335 else if (netif_is_lag_master(dev))
4336 return mlxsw_sp_inetaddr_lag_event(dev, event);
4337 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004338 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004339 else if (is_vlan_dev(dev))
4340 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4341 else
4342 return 0;
4343}
4344
Ido Schimmel4724ba562017-03-10 08:53:39 +01004345int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4346 unsigned long event, void *ptr)
4347{
4348 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4349 struct net_device *dev = ifa->ifa_dev->dev;
4350 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004351 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004352 int err = 0;
4353
4354 mlxsw_sp = mlxsw_sp_lower_get(dev);
4355 if (!mlxsw_sp)
4356 goto out;
4357
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004358 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004359 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004360 goto out;
4361
Ido Schimmelb1e45522017-04-30 19:47:14 +03004362 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004363out:
4364 return notifier_from_errno(err);
4365}
4366
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004367struct mlxsw_sp_inet6addr_event_work {
4368 struct work_struct work;
4369 struct net_device *dev;
4370 unsigned long event;
4371};
4372
4373static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4374{
4375 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4376 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4377 struct net_device *dev = inet6addr_work->dev;
4378 unsigned long event = inet6addr_work->event;
4379 struct mlxsw_sp *mlxsw_sp;
4380 struct mlxsw_sp_rif *rif;
4381
4382 rtnl_lock();
4383 mlxsw_sp = mlxsw_sp_lower_get(dev);
4384 if (!mlxsw_sp)
4385 goto out;
4386
4387 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4388 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4389 goto out;
4390
4391 __mlxsw_sp_inetaddr_event(dev, event);
4392out:
4393 rtnl_unlock();
4394 dev_put(dev);
4395 kfree(inet6addr_work);
4396}
4397
4398/* Called with rcu_read_lock() */
4399int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4400 unsigned long event, void *ptr)
4401{
4402 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4403 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4404 struct net_device *dev = if6->idev->dev;
4405
4406 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4407 return NOTIFY_DONE;
4408
4409 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4410 if (!inet6addr_work)
4411 return NOTIFY_BAD;
4412
4413 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4414 inet6addr_work->dev = dev;
4415 inet6addr_work->event = event;
4416 dev_hold(dev);
4417 mlxsw_core_schedule_work(&inet6addr_work->work);
4418
4419 return NOTIFY_DONE;
4420}
4421
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004422static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004423 const char *mac, int mtu)
4424{
4425 char ritr_pl[MLXSW_REG_RITR_LEN];
4426 int err;
4427
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004428 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004429 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4430 if (err)
4431 return err;
4432
4433 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4434 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4435 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4436 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4437}
4438
4439int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4440{
4441 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004442 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004443 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004444 int err;
4445
4446 mlxsw_sp = mlxsw_sp_lower_get(dev);
4447 if (!mlxsw_sp)
4448 return 0;
4449
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004450 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4451 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004452 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004453 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004454
Ido Schimmela1107482017-05-26 08:37:39 +02004455 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004456 if (err)
4457 return err;
4458
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004459 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4460 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004461 if (err)
4462 goto err_rif_edit;
4463
Ido Schimmela1107482017-05-26 08:37:39 +02004464 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004465 if (err)
4466 goto err_rif_fdb_op;
4467
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004468 ether_addr_copy(rif->addr, dev->dev_addr);
4469 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004470
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004471 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004472
4473 return 0;
4474
4475err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004476 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004477err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004478 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004479 return err;
4480}
4481
Ido Schimmelb1e45522017-04-30 19:47:14 +03004482static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4483 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004484{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004485 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004486
Ido Schimmelb1e45522017-04-30 19:47:14 +03004487 /* If netdev is already associated with a RIF, then we need to
4488 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004489 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004490 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4491 if (rif)
4492 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004493
Ido Schimmelb1e45522017-04-30 19:47:14 +03004494 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004495}
4496
Ido Schimmelb1e45522017-04-30 19:47:14 +03004497static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4498 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004499{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004500 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004501
Ido Schimmelb1e45522017-04-30 19:47:14 +03004502 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4503 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004504 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004505 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004506}
4507
Ido Schimmelb1e45522017-04-30 19:47:14 +03004508int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4509 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004510{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004511 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4512 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004513
Ido Schimmelb1e45522017-04-30 19:47:14 +03004514 if (!mlxsw_sp)
4515 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004516
Ido Schimmelb1e45522017-04-30 19:47:14 +03004517 switch (event) {
4518 case NETDEV_PRECHANGEUPPER:
4519 return 0;
4520 case NETDEV_CHANGEUPPER:
4521 if (info->linking)
4522 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4523 else
4524 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4525 break;
4526 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004527
Ido Schimmelb1e45522017-04-30 19:47:14 +03004528 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004529}
4530
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004531static struct mlxsw_sp_rif_subport *
4532mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004533{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004534 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004535}
4536
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004537static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4538 const struct mlxsw_sp_rif_params *params)
4539{
4540 struct mlxsw_sp_rif_subport *rif_subport;
4541
4542 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4543 rif_subport->vid = params->vid;
4544 rif_subport->lag = params->lag;
4545 if (params->lag)
4546 rif_subport->lag_id = params->lag_id;
4547 else
4548 rif_subport->system_port = params->system_port;
4549}
4550
4551static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4552{
4553 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4554 struct mlxsw_sp_rif_subport *rif_subport;
4555 char ritr_pl[MLXSW_REG_RITR_LEN];
4556
4557 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4558 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4559 rif->rif_index, rif->vr_id, rif->dev->mtu,
4560 rif->dev->dev_addr);
4561 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4562 rif_subport->lag ? rif_subport->lag_id :
4563 rif_subport->system_port,
4564 rif_subport->vid);
4565
4566 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4567}
4568
4569static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4570{
4571 return mlxsw_sp_rif_subport_op(rif, true);
4572}
4573
4574static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4575{
4576 mlxsw_sp_rif_subport_op(rif, false);
4577}
4578
4579static struct mlxsw_sp_fid *
4580mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4581{
4582 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4583}
4584
4585static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4586 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4587 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4588 .setup = mlxsw_sp_rif_subport_setup,
4589 .configure = mlxsw_sp_rif_subport_configure,
4590 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4591 .fid_get = mlxsw_sp_rif_subport_fid_get,
4592};
4593
4594static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4595 enum mlxsw_reg_ritr_if_type type,
4596 u16 vid_fid, bool enable)
4597{
4598 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4599 char ritr_pl[MLXSW_REG_RITR_LEN];
4600
4601 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4602 rif->dev->mtu, rif->dev->dev_addr);
4603 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4604
4605 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4606}
4607
4608static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4609{
4610 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4611}
4612
4613static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4614{
4615 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4616 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4617 int err;
4618
4619 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4620 if (err)
4621 return err;
4622
Ido Schimmel0d284812017-07-18 10:10:12 +02004623 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4624 mlxsw_sp_router_port(mlxsw_sp), true);
4625 if (err)
4626 goto err_fid_mc_flood_set;
4627
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004628 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4629 mlxsw_sp_router_port(mlxsw_sp), true);
4630 if (err)
4631 goto err_fid_bc_flood_set;
4632
4633 return 0;
4634
4635err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004636 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4637 mlxsw_sp_router_port(mlxsw_sp), false);
4638err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004639 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4640 return err;
4641}
4642
4643static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
4644{
4645 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4646 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4647
4648 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4649 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004650 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4651 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004652 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4653}
4654
4655static struct mlxsw_sp_fid *
4656mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
4657{
4658 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
4659
4660 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
4661}
4662
4663static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
4664 .type = MLXSW_SP_RIF_TYPE_VLAN,
4665 .rif_size = sizeof(struct mlxsw_sp_rif),
4666 .configure = mlxsw_sp_rif_vlan_configure,
4667 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
4668 .fid_get = mlxsw_sp_rif_vlan_fid_get,
4669};
4670
4671static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
4672{
4673 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4674 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4675 int err;
4676
4677 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
4678 true);
4679 if (err)
4680 return err;
4681
Ido Schimmel0d284812017-07-18 10:10:12 +02004682 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4683 mlxsw_sp_router_port(mlxsw_sp), true);
4684 if (err)
4685 goto err_fid_mc_flood_set;
4686
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004687 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4688 mlxsw_sp_router_port(mlxsw_sp), true);
4689 if (err)
4690 goto err_fid_bc_flood_set;
4691
4692 return 0;
4693
4694err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004695 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4696 mlxsw_sp_router_port(mlxsw_sp), false);
4697err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004698 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4699 return err;
4700}
4701
4702static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
4703{
4704 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4705 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4706
4707 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4708 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004709 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4710 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004711 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4712}
4713
4714static struct mlxsw_sp_fid *
4715mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
4716{
4717 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
4718}
4719
4720static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
4721 .type = MLXSW_SP_RIF_TYPE_FID,
4722 .rif_size = sizeof(struct mlxsw_sp_rif),
4723 .configure = mlxsw_sp_rif_fid_configure,
4724 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
4725 .fid_get = mlxsw_sp_rif_fid_fid_get,
4726};
4727
4728static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
4729 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
4730 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
4731 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
4732};
4733
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004734static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
4735{
4736 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4737
4738 mlxsw_sp->router->rifs = kcalloc(max_rifs,
4739 sizeof(struct mlxsw_sp_rif *),
4740 GFP_KERNEL);
4741 if (!mlxsw_sp->router->rifs)
4742 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004743
4744 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
4745
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004746 return 0;
4747}
4748
4749static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
4750{
4751 int i;
4752
4753 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
4754 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
4755
4756 kfree(mlxsw_sp->router->rifs);
4757}
4758
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004759static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
4760{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004761 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004762
4763 /* Flush pending FIB notifications and then flush the device's
4764 * table before requesting another dump. The FIB notification
4765 * block is unregistered, so no need to take RTNL.
4766 */
4767 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02004768 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4769 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004770}
4771
Ido Schimmel4724ba562017-03-10 08:53:39 +01004772static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4773{
4774 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4775 u64 max_rifs;
4776 int err;
4777
4778 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
4779 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004780 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004781
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004782 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004783 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
4784 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4785 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004786 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004787 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004788}
4789
4790static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4791{
4792 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004793
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004794 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004795 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004796}
4797
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004798int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4799{
Ido Schimmel9011b672017-05-16 19:38:25 +02004800 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004801 int err;
4802
Ido Schimmel9011b672017-05-16 19:38:25 +02004803 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4804 if (!router)
4805 return -ENOMEM;
4806 mlxsw_sp->router = router;
4807 router->mlxsw_sp = mlxsw_sp;
4808
4809 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004810 err = __mlxsw_sp_router_init(mlxsw_sp);
4811 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004812 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004813
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004814 err = mlxsw_sp_rifs_init(mlxsw_sp);
4815 if (err)
4816 goto err_rifs_init;
4817
Ido Schimmel9011b672017-05-16 19:38:25 +02004818 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004819 &mlxsw_sp_nexthop_ht_params);
4820 if (err)
4821 goto err_nexthop_ht_init;
4822
Ido Schimmel9011b672017-05-16 19:38:25 +02004823 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004824 &mlxsw_sp_nexthop_group_ht_params);
4825 if (err)
4826 goto err_nexthop_group_ht_init;
4827
Ido Schimmel8494ab02017-03-24 08:02:47 +01004828 err = mlxsw_sp_lpm_init(mlxsw_sp);
4829 if (err)
4830 goto err_lpm_init;
4831
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004832 err = mlxsw_sp_vrs_init(mlxsw_sp);
4833 if (err)
4834 goto err_vrs_init;
4835
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004836 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004837 if (err)
4838 goto err_neigh_init;
4839
Ido Schimmel7e39d112017-05-16 19:38:28 +02004840 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4841 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004842 mlxsw_sp_router_fib_dump_flush);
4843 if (err)
4844 goto err_register_fib_notifier;
4845
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004846 return 0;
4847
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004848err_register_fib_notifier:
4849 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004850err_neigh_init:
4851 mlxsw_sp_vrs_fini(mlxsw_sp);
4852err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004853 mlxsw_sp_lpm_fini(mlxsw_sp);
4854err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004855 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004856err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004857 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004858err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004859 mlxsw_sp_rifs_fini(mlxsw_sp);
4860err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004861 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004862err_router_init:
4863 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004864 return err;
4865}
4866
4867void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4868{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004869 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004870 mlxsw_sp_neigh_fini(mlxsw_sp);
4871 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01004872 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004873 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4874 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004875 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004876 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004877 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004878}