blob: cded8e8039bd838cdc06aaee030f28a4cf8c3416 [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,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003038 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003039{
3040 struct mlxsw_sp_fib6_entry *fib6_entry;
3041
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003042 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003043 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,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003374 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003375{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003376 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003377
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;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003385 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3386 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3387 mlxsw_sp_fib6_rt_can_mp(nrt))
3388 return fib6_entry;
3389 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3390 fallback = fallback ?: fib6_entry;
3391 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003392 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003393 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003394 }
3395
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003396 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003397}
3398
3399static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003400mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3401 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003402{
3403 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3404 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3405 struct mlxsw_sp_fib6_entry *fib6_entry;
3406
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003407 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3408
3409 if (replace && WARN_ON(!fib6_entry))
3410 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003411
3412 if (fib6_entry) {
3413 list_add_tail(&new6_entry->common.list,
3414 &fib6_entry->common.list);
3415 } else {
3416 struct mlxsw_sp_fib6_entry *last;
3417
3418 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3419 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3420
3421 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3422 break;
3423 fib6_entry = last;
3424 }
3425
3426 if (fib6_entry)
3427 list_add(&new6_entry->common.list,
3428 &fib6_entry->common.list);
3429 else
3430 list_add(&new6_entry->common.list,
3431 &fib_node->entry_list);
3432 }
3433
3434 return 0;
3435}
3436
3437static void
3438mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3439{
3440 list_del(&fib6_entry->common.list);
3441}
3442
3443static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003444 struct mlxsw_sp_fib6_entry *fib6_entry,
3445 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003446{
3447 int err;
3448
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003449 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003450 if (err)
3451 return err;
3452
3453 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3454 if (err)
3455 goto err_fib_node_entry_add;
3456
3457 return 0;
3458
3459err_fib_node_entry_add:
3460 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3461 return err;
3462}
3463
3464static void
3465mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3466 struct mlxsw_sp_fib6_entry *fib6_entry)
3467{
3468 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3469 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3470}
3471
3472static struct mlxsw_sp_fib6_entry *
3473mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3474 const struct rt6_info *rt)
3475{
3476 struct mlxsw_sp_fib6_entry *fib6_entry;
3477 struct mlxsw_sp_fib_node *fib_node;
3478 struct mlxsw_sp_fib *fib;
3479 struct mlxsw_sp_vr *vr;
3480
3481 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3482 if (!vr)
3483 return NULL;
3484 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3485
3486 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3487 sizeof(rt->rt6i_dst.addr),
3488 rt->rt6i_dst.plen);
3489 if (!fib_node)
3490 return NULL;
3491
3492 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3493 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3494
3495 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3496 rt->rt6i_metric == iter_rt->rt6i_metric &&
3497 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3498 return fib6_entry;
3499 }
3500
3501 return NULL;
3502}
3503
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003504static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3505 struct mlxsw_sp_fib6_entry *fib6_entry,
3506 bool replace)
3507{
3508 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3509 struct mlxsw_sp_fib6_entry *replaced;
3510
3511 if (!replace)
3512 return;
3513
3514 replaced = list_next_entry(fib6_entry, common.list);
3515
3516 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3517 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3518 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3519}
3520
Ido Schimmel428b8512017-08-03 13:28:28 +02003521static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003522 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003523{
3524 struct mlxsw_sp_fib6_entry *fib6_entry;
3525 struct mlxsw_sp_fib_node *fib_node;
3526 int err;
3527
3528 if (mlxsw_sp->router->aborted)
3529 return 0;
3530
3531 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3532 return 0;
3533
3534 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3535 &rt->rt6i_dst.addr,
3536 sizeof(rt->rt6i_dst.addr),
3537 rt->rt6i_dst.plen,
3538 MLXSW_SP_L3_PROTO_IPV6);
3539 if (IS_ERR(fib_node))
3540 return PTR_ERR(fib_node);
3541
3542 /* Before creating a new entry, try to append route to an existing
3543 * multipath entry.
3544 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003545 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003546 if (fib6_entry) {
3547 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3548 if (err)
3549 goto err_fib6_entry_nexthop_add;
3550 return 0;
3551 }
3552
3553 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3554 if (IS_ERR(fib6_entry)) {
3555 err = PTR_ERR(fib6_entry);
3556 goto err_fib6_entry_create;
3557 }
3558
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003559 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003560 if (err)
3561 goto err_fib6_node_entry_link;
3562
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003563 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3564
Ido Schimmel428b8512017-08-03 13:28:28 +02003565 return 0;
3566
3567err_fib6_node_entry_link:
3568 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3569err_fib6_entry_create:
3570err_fib6_entry_nexthop_add:
3571 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3572 return err;
3573}
3574
3575static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3576 struct rt6_info *rt)
3577{
3578 struct mlxsw_sp_fib6_entry *fib6_entry;
3579 struct mlxsw_sp_fib_node *fib_node;
3580
3581 if (mlxsw_sp->router->aborted)
3582 return;
3583
3584 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3585 return;
3586
3587 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
3588 if (WARN_ON(!fib6_entry))
3589 return;
3590
3591 /* If route is part of a multipath entry, but not the last one
3592 * removed, then only reduce its nexthop group.
3593 */
3594 if (!list_is_singular(&fib6_entry->rt6_list)) {
3595 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
3596 return;
3597 }
3598
3599 fib_node = fib6_entry->common.fib_node;
3600
3601 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3602 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3603 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3604}
3605
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003606static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
3607 enum mlxsw_reg_ralxx_protocol proto,
3608 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003609{
3610 char ralta_pl[MLXSW_REG_RALTA_LEN];
3611 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003612 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003613
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003614 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003615 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
3616 if (err)
3617 return err;
3618
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003619 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003620 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
3621 if (err)
3622 return err;
3623
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003624 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003625 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003626 char raltb_pl[MLXSW_REG_RALTB_LEN];
3627 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003628
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003629 if (!mlxsw_sp_vr_is_used(vr))
3630 continue;
3631
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003632 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003633 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
3634 raltb_pl);
3635 if (err)
3636 return err;
3637
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003638 mlxsw_reg_ralue_pack(ralue_pl, proto,
3639 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01003640 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3641 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
3642 ralue_pl);
3643 if (err)
3644 return err;
3645 }
3646
3647 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003648}
3649
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003650static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
3651{
3652 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
3653 int err;
3654
3655 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3656 MLXSW_SP_LPM_TREE_MIN);
3657 if (err)
3658 return err;
3659
3660 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
3661 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
3662 MLXSW_SP_LPM_TREE_MIN + 1);
3663}
3664
Ido Schimmel9aecce12017-02-09 10:28:42 +01003665static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
3666 struct mlxsw_sp_fib_node *fib_node)
3667{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003668 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003669
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003670 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
3671 common.list) {
3672 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003673
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003674 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3675 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003676 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003677 /* Break when entry list is empty and node was freed.
3678 * Otherwise, we'll access freed memory in the next
3679 * iteration.
3680 */
3681 if (do_break)
3682 break;
3683 }
3684}
3685
Ido Schimmel428b8512017-08-03 13:28:28 +02003686static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
3687 struct mlxsw_sp_fib_node *fib_node)
3688{
3689 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
3690
3691 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
3692 common.list) {
3693 bool do_break = &tmp->common.list == &fib_node->entry_list;
3694
3695 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
3696 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3697 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3698 if (do_break)
3699 break;
3700 }
3701}
3702
Ido Schimmel9aecce12017-02-09 10:28:42 +01003703static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
3704 struct mlxsw_sp_fib_node *fib_node)
3705{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003706 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003707 case MLXSW_SP_L3_PROTO_IPV4:
3708 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
3709 break;
3710 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003711 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003712 break;
3713 }
3714}
3715
Ido Schimmel76610eb2017-03-10 08:53:41 +01003716static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
3717 struct mlxsw_sp_vr *vr,
3718 enum mlxsw_sp_l3proto proto)
3719{
3720 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
3721 struct mlxsw_sp_fib_node *fib_node, *tmp;
3722
3723 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
3724 bool do_break = &tmp->list == &fib->node_list;
3725
3726 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
3727 if (do_break)
3728 break;
3729 }
3730}
3731
Ido Schimmelac571de2016-11-14 11:26:32 +01003732static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003733{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003734 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003735
Jiri Pirkoc1a38312016-10-21 16:07:23 +02003736 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003737 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01003738
Ido Schimmel76610eb2017-03-10 08:53:41 +01003739 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003740 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003741 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02003742
3743 /* If virtual router was only used for IPv4, then it's no
3744 * longer used.
3745 */
3746 if (!mlxsw_sp_vr_is_used(vr))
3747 continue;
3748 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003749 }
Ido Schimmelac571de2016-11-14 11:26:32 +01003750}
3751
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003752static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01003753{
3754 int err;
3755
Ido Schimmel9011b672017-05-16 19:38:25 +02003756 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003757 return;
3758 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 +01003759 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003760 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003761 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3762 if (err)
3763 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3764}
3765
Ido Schimmel30572242016-12-03 16:45:01 +01003766struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003767 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003768 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02003769 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003770 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003771 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003772 struct fib_nh_notifier_info fnh_info;
3773 };
Ido Schimmel30572242016-12-03 16:45:01 +01003774 struct mlxsw_sp *mlxsw_sp;
3775 unsigned long event;
3776};
3777
Ido Schimmel66a57632017-08-03 13:28:26 +02003778static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003779{
Ido Schimmel30572242016-12-03 16:45:01 +01003780 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003781 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003782 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003783 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003784 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003785 int err;
3786
Ido Schimmel30572242016-12-03 16:45:01 +01003787 /* Protect internal structures from changes */
3788 rtnl_lock();
3789 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003790 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003791 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003792 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003793 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003794 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3795 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003796 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003797 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003798 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003799 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003800 break;
3801 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003802 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3803 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003804 break;
3805 case FIB_EVENT_RULE_ADD: /* fall through */
3806 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003807 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003808 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003809 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003810 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003811 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003812 case FIB_EVENT_NH_ADD: /* fall through */
3813 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003814 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3815 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003816 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3817 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003818 }
Ido Schimmel30572242016-12-03 16:45:01 +01003819 rtnl_unlock();
3820 kfree(fib_work);
3821}
3822
Ido Schimmel66a57632017-08-03 13:28:26 +02003823static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
3824{
Ido Schimmel583419f2017-08-03 13:28:27 +02003825 struct mlxsw_sp_fib_event_work *fib_work =
3826 container_of(work, struct mlxsw_sp_fib_event_work, work);
3827 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
3828 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003829 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02003830 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02003831
3832 rtnl_lock();
3833 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003834 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003835 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003836 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02003837 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003838 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003839 if (err)
3840 mlxsw_sp_router_fib_abort(mlxsw_sp);
3841 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3842 break;
3843 case FIB_EVENT_ENTRY_DEL:
3844 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
3845 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
3846 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003847 case FIB_EVENT_RULE_ADD: /* fall through */
3848 case FIB_EVENT_RULE_DEL:
3849 rule = fib_work->fr_info.rule;
3850 if (!fib6_rule_default(rule) && !rule->l3mdev)
3851 mlxsw_sp_router_fib_abort(mlxsw_sp);
3852 fib_rule_put(rule);
3853 break;
3854 }
3855 rtnl_unlock();
3856 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02003857}
3858
3859static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
3860 struct fib_notifier_info *info)
3861{
3862 switch (fib_work->event) {
3863 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
3864 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3865 case FIB_EVENT_ENTRY_ADD: /* fall through */
3866 case FIB_EVENT_ENTRY_DEL:
3867 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
3868 /* Take referece on fib_info to prevent it from being
3869 * freed while work is queued. Release it afterwards.
3870 */
3871 fib_info_hold(fib_work->fen_info.fi);
3872 break;
3873 case FIB_EVENT_RULE_ADD: /* fall through */
3874 case FIB_EVENT_RULE_DEL:
3875 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3876 fib_rule_get(fib_work->fr_info.rule);
3877 break;
3878 case FIB_EVENT_NH_ADD: /* fall through */
3879 case FIB_EVENT_NH_DEL:
3880 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
3881 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3882 break;
3883 }
3884}
3885
3886static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
3887 struct fib_notifier_info *info)
3888{
Ido Schimmel583419f2017-08-03 13:28:27 +02003889 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003890 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02003891 case FIB_EVENT_ENTRY_ADD: /* fall through */
3892 case FIB_EVENT_ENTRY_DEL:
3893 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
3894 rt6_hold(fib_work->fen6_info.rt);
3895 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02003896 case FIB_EVENT_RULE_ADD: /* fall through */
3897 case FIB_EVENT_RULE_DEL:
3898 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
3899 fib_rule_get(fib_work->fr_info.rule);
3900 break;
3901 }
Ido Schimmel66a57632017-08-03 13:28:26 +02003902}
3903
Ido Schimmel30572242016-12-03 16:45:01 +01003904/* Called with rcu_read_lock() */
3905static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3906 unsigned long event, void *ptr)
3907{
Ido Schimmel30572242016-12-03 16:45:01 +01003908 struct mlxsw_sp_fib_event_work *fib_work;
3909 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02003910 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01003911
Ido Schimmel64e5e822017-08-03 13:28:12 +02003912 if (!net_eq(info->net, &init_net) || info->family != AF_INET)
Ido Schimmel30572242016-12-03 16:45:01 +01003913 return NOTIFY_DONE;
3914
3915 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3916 if (WARN_ON(!fib_work))
3917 return NOTIFY_BAD;
3918
Ido Schimmel7e39d112017-05-16 19:38:28 +02003919 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3920 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01003921 fib_work->event = event;
3922
Ido Schimmel66a57632017-08-03 13:28:26 +02003923 switch (info->family) {
3924 case AF_INET:
3925 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
3926 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01003927 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02003928 case AF_INET6:
3929 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
3930 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01003931 break;
Ido Schimmel30572242016-12-03 16:45:01 +01003932 }
3933
Ido Schimmela0e47612017-02-06 16:20:10 +01003934 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01003935
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003936 return NOTIFY_DONE;
3937}
3938
Ido Schimmel4724ba562017-03-10 08:53:39 +01003939static struct mlxsw_sp_rif *
3940mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
3941 const struct net_device *dev)
3942{
3943 int i;
3944
3945 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003946 if (mlxsw_sp->router->rifs[i] &&
3947 mlxsw_sp->router->rifs[i]->dev == dev)
3948 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01003949
3950 return NULL;
3951}
3952
3953static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
3954{
3955 char ritr_pl[MLXSW_REG_RITR_LEN];
3956 int err;
3957
3958 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3959 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3960 if (WARN_ON_ONCE(err))
3961 return err;
3962
3963 mlxsw_reg_ritr_enable_set(ritr_pl, false);
3964 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3965}
3966
3967static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003968 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003969{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003970 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
3971 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
3972 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003973}
3974
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003975static bool
3976mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
3977 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003978{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003979 struct inet6_dev *inet6_dev;
3980 bool addr_list_empty = true;
3981 struct in_device *idev;
3982
Ido Schimmel4724ba562017-03-10 08:53:39 +01003983 switch (event) {
3984 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02003985 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003986 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003987 idev = __in_dev_get_rtnl(dev);
3988 if (idev && idev->ifa_list)
3989 addr_list_empty = false;
3990
3991 inet6_dev = __in6_dev_get(dev);
3992 if (addr_list_empty && inet6_dev &&
3993 !list_empty(&inet6_dev->addr_list))
3994 addr_list_empty = false;
3995
3996 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003997 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003998 return true;
3999 /* It is possible we already removed the RIF ourselves
4000 * if it was assigned to a netdev that is now a bridge
4001 * or LAG slave.
4002 */
4003 return false;
4004 }
4005
4006 return false;
4007}
4008
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004009static enum mlxsw_sp_rif_type
4010mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4011 const struct net_device *dev)
4012{
4013 enum mlxsw_sp_fid_type type;
4014
4015 /* RIF type is derived from the type of the underlying FID */
4016 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4017 type = MLXSW_SP_FID_TYPE_8021Q;
4018 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4019 type = MLXSW_SP_FID_TYPE_8021Q;
4020 else if (netif_is_bridge_master(dev))
4021 type = MLXSW_SP_FID_TYPE_8021D;
4022 else
4023 type = MLXSW_SP_FID_TYPE_RFID;
4024
4025 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4026}
4027
Ido Schimmelde5ed992017-06-04 16:53:40 +02004028static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004029{
4030 int i;
4031
Ido Schimmelde5ed992017-06-04 16:53:40 +02004032 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4033 if (!mlxsw_sp->router->rifs[i]) {
4034 *p_rif_index = i;
4035 return 0;
4036 }
4037 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004038
Ido Schimmelde5ed992017-06-04 16:53:40 +02004039 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004040}
4041
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004042static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4043 u16 vr_id,
4044 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004045{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004046 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004047
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004048 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004049 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004050 return NULL;
4051
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004052 INIT_LIST_HEAD(&rif->nexthop_list);
4053 INIT_LIST_HEAD(&rif->neigh_list);
4054 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4055 rif->mtu = l3_dev->mtu;
4056 rif->vr_id = vr_id;
4057 rif->dev = l3_dev;
4058 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004059
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004060 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004061}
4062
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004063struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4064 u16 rif_index)
4065{
4066 return mlxsw_sp->router->rifs[rif_index];
4067}
4068
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004069u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4070{
4071 return rif->rif_index;
4072}
4073
4074int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4075{
4076 return rif->dev->ifindex;
4077}
4078
Ido Schimmel4724ba562017-03-10 08:53:39 +01004079static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004080mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4081 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004082{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004083 u32 tb_id = l3mdev_fib_table(params->dev);
4084 const struct mlxsw_sp_rif_ops *ops;
4085 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004086 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004087 struct mlxsw_sp_fid *fid;
4088 struct mlxsw_sp_vr *vr;
4089 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004090 int err;
4091
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004092 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4093 ops = mlxsw_sp->router->rif_ops_arr[type];
4094
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004095 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4096 if (IS_ERR(vr))
4097 return ERR_CAST(vr);
4098
Ido Schimmelde5ed992017-06-04 16:53:40 +02004099 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4100 if (err)
4101 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004102
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004103 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004104 if (!rif) {
4105 err = -ENOMEM;
4106 goto err_rif_alloc;
4107 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004108 rif->mlxsw_sp = mlxsw_sp;
4109 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004110
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004111 fid = ops->fid_get(rif);
4112 if (IS_ERR(fid)) {
4113 err = PTR_ERR(fid);
4114 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004115 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004116 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004117
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004118 if (ops->setup)
4119 ops->setup(rif, params);
4120
4121 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004122 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004123 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004124
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004125 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02004126 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004127 if (err)
4128 goto err_rif_fdb_op;
4129
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004130 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004131 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004132 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004133 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004134
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004135 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004136
Ido Schimmel4724ba562017-03-10 08:53:39 +01004137err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004138 ops->deconfigure(rif);
4139err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02004140 mlxsw_sp_fid_put(fid);
4141err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004142 kfree(rif);
4143err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004144err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004145 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004146 return ERR_PTR(err);
4147}
4148
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004149void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004150{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004151 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4152 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004153 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004154 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004155
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004156 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004157 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004158
Ido Schimmel69132292017-03-10 08:53:42 +01004159 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004160 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02004161 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004162 mlxsw_sp_rif_counters_free(rif);
4163 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
4164 mlxsw_sp_fid_index(fid), false);
4165 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004166 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004167 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004168 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004169}
4170
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004171static void
4172mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4173 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4174{
4175 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4176
4177 params->vid = mlxsw_sp_port_vlan->vid;
4178 params->lag = mlxsw_sp_port->lagged;
4179 if (params->lag)
4180 params->lag_id = mlxsw_sp_port->lag_id;
4181 else
4182 params->system_port = mlxsw_sp_port->local_port;
4183}
4184
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004185static int
Ido Schimmela1107482017-05-26 08:37:39 +02004186mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004187 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004188{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004189 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004190 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004191 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004192 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004193 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004194 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004195
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004196 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004197 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004198 struct mlxsw_sp_rif_params params = {
4199 .dev = l3_dev,
4200 };
4201
4202 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4203 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004204 if (IS_ERR(rif))
4205 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004206 }
4207
Ido Schimmela1107482017-05-26 08:37:39 +02004208 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004209 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004210 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4211 if (err)
4212 goto err_fid_port_vid_map;
4213
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004214 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004215 if (err)
4216 goto err_port_vid_learning_set;
4217
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004218 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004219 BR_STATE_FORWARDING);
4220 if (err)
4221 goto err_port_vid_stp_set;
4222
Ido Schimmela1107482017-05-26 08:37:39 +02004223 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004224
Ido Schimmel4724ba562017-03-10 08:53:39 +01004225 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004226
4227err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004228 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004229err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004230 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4231err_fid_port_vid_map:
4232 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004233 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004234}
4235
Ido Schimmela1107482017-05-26 08:37:39 +02004236void
4237mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004238{
Ido Schimmelce95e152017-05-26 08:37:27 +02004239 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004240 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004241 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004242
Ido Schimmela1107482017-05-26 08:37:39 +02004243 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4244 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004245
Ido Schimmela1107482017-05-26 08:37:39 +02004246 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004247 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4248 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004249 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4250 /* If router port holds the last reference on the rFID, then the
4251 * associated Sub-port RIF will be destroyed.
4252 */
4253 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004254}
4255
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004256static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4257 struct net_device *port_dev,
4258 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004259{
4260 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004261 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004262
Ido Schimmelce95e152017-05-26 08:37:27 +02004263 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004264 if (WARN_ON(!mlxsw_sp_port_vlan))
4265 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004266
4267 switch (event) {
4268 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004269 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004270 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004271 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004272 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004273 break;
4274 }
4275
4276 return 0;
4277}
4278
4279static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4280 unsigned long event)
4281{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004282 if (netif_is_bridge_port(port_dev) ||
4283 netif_is_lag_port(port_dev) ||
4284 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004285 return 0;
4286
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004287 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004288}
4289
4290static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4291 struct net_device *lag_dev,
4292 unsigned long event, u16 vid)
4293{
4294 struct net_device *port_dev;
4295 struct list_head *iter;
4296 int err;
4297
4298 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4299 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004300 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4301 port_dev,
4302 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004303 if (err)
4304 return err;
4305 }
4306 }
4307
4308 return 0;
4309}
4310
4311static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4312 unsigned long event)
4313{
4314 if (netif_is_bridge_port(lag_dev))
4315 return 0;
4316
4317 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4318}
4319
Ido Schimmel4724ba562017-03-10 08:53:39 +01004320static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004321 unsigned long event)
4322{
4323 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004324 struct mlxsw_sp_rif_params params = {
4325 .dev = l3_dev,
4326 };
Ido Schimmela1107482017-05-26 08:37:39 +02004327 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004328
4329 switch (event) {
4330 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004331 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4332 if (IS_ERR(rif))
4333 return PTR_ERR(rif);
4334 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004335 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004336 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004337 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004338 break;
4339 }
4340
4341 return 0;
4342}
4343
4344static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4345 unsigned long event)
4346{
4347 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004348 u16 vid = vlan_dev_vlan_id(vlan_dev);
4349
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004350 if (netif_is_bridge_port(vlan_dev))
4351 return 0;
4352
Ido Schimmel4724ba562017-03-10 08:53:39 +01004353 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004354 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4355 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004356 else if (netif_is_lag_master(real_dev))
4357 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4358 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004359 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004360 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004361
4362 return 0;
4363}
4364
Ido Schimmelb1e45522017-04-30 19:47:14 +03004365static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4366 unsigned long event)
4367{
4368 if (mlxsw_sp_port_dev_check(dev))
4369 return mlxsw_sp_inetaddr_port_event(dev, event);
4370 else if (netif_is_lag_master(dev))
4371 return mlxsw_sp_inetaddr_lag_event(dev, event);
4372 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004373 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004374 else if (is_vlan_dev(dev))
4375 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4376 else
4377 return 0;
4378}
4379
Ido Schimmel4724ba562017-03-10 08:53:39 +01004380int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4381 unsigned long event, void *ptr)
4382{
4383 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4384 struct net_device *dev = ifa->ifa_dev->dev;
4385 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004386 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004387 int err = 0;
4388
4389 mlxsw_sp = mlxsw_sp_lower_get(dev);
4390 if (!mlxsw_sp)
4391 goto out;
4392
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004393 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004394 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004395 goto out;
4396
Ido Schimmelb1e45522017-04-30 19:47:14 +03004397 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004398out:
4399 return notifier_from_errno(err);
4400}
4401
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004402struct mlxsw_sp_inet6addr_event_work {
4403 struct work_struct work;
4404 struct net_device *dev;
4405 unsigned long event;
4406};
4407
4408static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4409{
4410 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4411 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4412 struct net_device *dev = inet6addr_work->dev;
4413 unsigned long event = inet6addr_work->event;
4414 struct mlxsw_sp *mlxsw_sp;
4415 struct mlxsw_sp_rif *rif;
4416
4417 rtnl_lock();
4418 mlxsw_sp = mlxsw_sp_lower_get(dev);
4419 if (!mlxsw_sp)
4420 goto out;
4421
4422 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4423 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4424 goto out;
4425
4426 __mlxsw_sp_inetaddr_event(dev, event);
4427out:
4428 rtnl_unlock();
4429 dev_put(dev);
4430 kfree(inet6addr_work);
4431}
4432
4433/* Called with rcu_read_lock() */
4434int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4435 unsigned long event, void *ptr)
4436{
4437 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4438 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4439 struct net_device *dev = if6->idev->dev;
4440
4441 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4442 return NOTIFY_DONE;
4443
4444 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4445 if (!inet6addr_work)
4446 return NOTIFY_BAD;
4447
4448 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4449 inet6addr_work->dev = dev;
4450 inet6addr_work->event = event;
4451 dev_hold(dev);
4452 mlxsw_core_schedule_work(&inet6addr_work->work);
4453
4454 return NOTIFY_DONE;
4455}
4456
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004457static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004458 const char *mac, int mtu)
4459{
4460 char ritr_pl[MLXSW_REG_RITR_LEN];
4461 int err;
4462
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004463 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004464 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4465 if (err)
4466 return err;
4467
4468 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4469 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4470 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4471 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4472}
4473
4474int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4475{
4476 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004477 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004478 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004479 int err;
4480
4481 mlxsw_sp = mlxsw_sp_lower_get(dev);
4482 if (!mlxsw_sp)
4483 return 0;
4484
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004485 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4486 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004487 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004488 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004489
Ido Schimmela1107482017-05-26 08:37:39 +02004490 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004491 if (err)
4492 return err;
4493
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004494 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4495 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004496 if (err)
4497 goto err_rif_edit;
4498
Ido Schimmela1107482017-05-26 08:37:39 +02004499 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004500 if (err)
4501 goto err_rif_fdb_op;
4502
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004503 ether_addr_copy(rif->addr, dev->dev_addr);
4504 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004505
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004506 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004507
4508 return 0;
4509
4510err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004511 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004512err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004513 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004514 return err;
4515}
4516
Ido Schimmelb1e45522017-04-30 19:47:14 +03004517static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4518 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004519{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004520 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004521
Ido Schimmelb1e45522017-04-30 19:47:14 +03004522 /* If netdev is already associated with a RIF, then we need to
4523 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004524 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004525 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4526 if (rif)
4527 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004528
Ido Schimmelb1e45522017-04-30 19:47:14 +03004529 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004530}
4531
Ido Schimmelb1e45522017-04-30 19:47:14 +03004532static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4533 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004534{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004535 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004536
Ido Schimmelb1e45522017-04-30 19:47:14 +03004537 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4538 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004539 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004540 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004541}
4542
Ido Schimmelb1e45522017-04-30 19:47:14 +03004543int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4544 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004545{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004546 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4547 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004548
Ido Schimmelb1e45522017-04-30 19:47:14 +03004549 if (!mlxsw_sp)
4550 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004551
Ido Schimmelb1e45522017-04-30 19:47:14 +03004552 switch (event) {
4553 case NETDEV_PRECHANGEUPPER:
4554 return 0;
4555 case NETDEV_CHANGEUPPER:
4556 if (info->linking)
4557 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4558 else
4559 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4560 break;
4561 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004562
Ido Schimmelb1e45522017-04-30 19:47:14 +03004563 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004564}
4565
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004566static struct mlxsw_sp_rif_subport *
4567mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004568{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004569 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004570}
4571
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004572static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4573 const struct mlxsw_sp_rif_params *params)
4574{
4575 struct mlxsw_sp_rif_subport *rif_subport;
4576
4577 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4578 rif_subport->vid = params->vid;
4579 rif_subport->lag = params->lag;
4580 if (params->lag)
4581 rif_subport->lag_id = params->lag_id;
4582 else
4583 rif_subport->system_port = params->system_port;
4584}
4585
4586static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4587{
4588 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4589 struct mlxsw_sp_rif_subport *rif_subport;
4590 char ritr_pl[MLXSW_REG_RITR_LEN];
4591
4592 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4593 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
4594 rif->rif_index, rif->vr_id, rif->dev->mtu,
4595 rif->dev->dev_addr);
4596 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
4597 rif_subport->lag ? rif_subport->lag_id :
4598 rif_subport->system_port,
4599 rif_subport->vid);
4600
4601 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4602}
4603
4604static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
4605{
4606 return mlxsw_sp_rif_subport_op(rif, true);
4607}
4608
4609static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
4610{
4611 mlxsw_sp_rif_subport_op(rif, false);
4612}
4613
4614static struct mlxsw_sp_fid *
4615mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
4616{
4617 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
4618}
4619
4620static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
4621 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
4622 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
4623 .setup = mlxsw_sp_rif_subport_setup,
4624 .configure = mlxsw_sp_rif_subport_configure,
4625 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
4626 .fid_get = mlxsw_sp_rif_subport_fid_get,
4627};
4628
4629static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
4630 enum mlxsw_reg_ritr_if_type type,
4631 u16 vid_fid, bool enable)
4632{
4633 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4634 char ritr_pl[MLXSW_REG_RITR_LEN];
4635
4636 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
4637 rif->dev->mtu, rif->dev->dev_addr);
4638 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
4639
4640 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4641}
4642
4643static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
4644{
4645 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
4646}
4647
4648static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
4649{
4650 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4651 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4652 int err;
4653
4654 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
4655 if (err)
4656 return err;
4657
Ido Schimmel0d284812017-07-18 10:10:12 +02004658 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4659 mlxsw_sp_router_port(mlxsw_sp), true);
4660 if (err)
4661 goto err_fid_mc_flood_set;
4662
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004663 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4664 mlxsw_sp_router_port(mlxsw_sp), true);
4665 if (err)
4666 goto err_fid_bc_flood_set;
4667
4668 return 0;
4669
4670err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004671 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4672 mlxsw_sp_router_port(mlxsw_sp), false);
4673err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004674 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4675 return err;
4676}
4677
4678static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
4679{
4680 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4681 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
4682
4683 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4684 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004685 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4686 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004687 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
4688}
4689
4690static struct mlxsw_sp_fid *
4691mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
4692{
4693 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
4694
4695 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
4696}
4697
4698static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
4699 .type = MLXSW_SP_RIF_TYPE_VLAN,
4700 .rif_size = sizeof(struct mlxsw_sp_rif),
4701 .configure = mlxsw_sp_rif_vlan_configure,
4702 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
4703 .fid_get = mlxsw_sp_rif_vlan_fid_get,
4704};
4705
4706static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
4707{
4708 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4709 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4710 int err;
4711
4712 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
4713 true);
4714 if (err)
4715 return err;
4716
Ido Schimmel0d284812017-07-18 10:10:12 +02004717 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4718 mlxsw_sp_router_port(mlxsw_sp), true);
4719 if (err)
4720 goto err_fid_mc_flood_set;
4721
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004722 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4723 mlxsw_sp_router_port(mlxsw_sp), true);
4724 if (err)
4725 goto err_fid_bc_flood_set;
4726
4727 return 0;
4728
4729err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02004730 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4731 mlxsw_sp_router_port(mlxsw_sp), false);
4732err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004733 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4734 return err;
4735}
4736
4737static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
4738{
4739 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4740 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
4741
4742 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
4743 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02004744 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
4745 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004746 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
4747}
4748
4749static struct mlxsw_sp_fid *
4750mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
4751{
4752 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
4753}
4754
4755static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
4756 .type = MLXSW_SP_RIF_TYPE_FID,
4757 .rif_size = sizeof(struct mlxsw_sp_rif),
4758 .configure = mlxsw_sp_rif_fid_configure,
4759 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
4760 .fid_get = mlxsw_sp_rif_fid_fid_get,
4761};
4762
4763static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
4764 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
4765 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
4766 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
4767};
4768
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004769static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
4770{
4771 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4772
4773 mlxsw_sp->router->rifs = kcalloc(max_rifs,
4774 sizeof(struct mlxsw_sp_rif *),
4775 GFP_KERNEL);
4776 if (!mlxsw_sp->router->rifs)
4777 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004778
4779 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
4780
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004781 return 0;
4782}
4783
4784static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
4785{
4786 int i;
4787
4788 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
4789 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
4790
4791 kfree(mlxsw_sp->router->rifs);
4792}
4793
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004794static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
4795{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004796 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004797
4798 /* Flush pending FIB notifications and then flush the device's
4799 * table before requesting another dump. The FIB notification
4800 * block is unregistered, so no need to take RTNL.
4801 */
4802 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02004803 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4804 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004805}
4806
Ido Schimmel4724ba562017-03-10 08:53:39 +01004807static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4808{
4809 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4810 u64 max_rifs;
4811 int err;
4812
4813 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
4814 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004815 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004816
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004817 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004818 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
4819 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4820 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004821 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004822 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004823}
4824
4825static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4826{
4827 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004828
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004829 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004830 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004831}
4832
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004833int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4834{
Ido Schimmel9011b672017-05-16 19:38:25 +02004835 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004836 int err;
4837
Ido Schimmel9011b672017-05-16 19:38:25 +02004838 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4839 if (!router)
4840 return -ENOMEM;
4841 mlxsw_sp->router = router;
4842 router->mlxsw_sp = mlxsw_sp;
4843
4844 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004845 err = __mlxsw_sp_router_init(mlxsw_sp);
4846 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004847 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004848
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004849 err = mlxsw_sp_rifs_init(mlxsw_sp);
4850 if (err)
4851 goto err_rifs_init;
4852
Ido Schimmel9011b672017-05-16 19:38:25 +02004853 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004854 &mlxsw_sp_nexthop_ht_params);
4855 if (err)
4856 goto err_nexthop_ht_init;
4857
Ido Schimmel9011b672017-05-16 19:38:25 +02004858 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004859 &mlxsw_sp_nexthop_group_ht_params);
4860 if (err)
4861 goto err_nexthop_group_ht_init;
4862
Ido Schimmel8494ab02017-03-24 08:02:47 +01004863 err = mlxsw_sp_lpm_init(mlxsw_sp);
4864 if (err)
4865 goto err_lpm_init;
4866
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004867 err = mlxsw_sp_vrs_init(mlxsw_sp);
4868 if (err)
4869 goto err_vrs_init;
4870
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004871 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004872 if (err)
4873 goto err_neigh_init;
4874
Ido Schimmel7e39d112017-05-16 19:38:28 +02004875 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4876 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004877 mlxsw_sp_router_fib_dump_flush);
4878 if (err)
4879 goto err_register_fib_notifier;
4880
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004881 return 0;
4882
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004883err_register_fib_notifier:
4884 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004885err_neigh_init:
4886 mlxsw_sp_vrs_fini(mlxsw_sp);
4887err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004888 mlxsw_sp_lpm_fini(mlxsw_sp);
4889err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004890 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004891err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004892 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004893err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004894 mlxsw_sp_rifs_fini(mlxsw_sp);
4895err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004896 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004897err_router_init:
4898 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004899 return err;
4900}
4901
4902void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4903{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004904 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004905 mlxsw_sp_neigh_fini(mlxsw_sp);
4906 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01004907 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004908 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4909 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004910 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004911 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004912 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004913}