blob: 2f03c7e71584c17b07c39e299b092ec445e525e5 [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>
Yotam Gigic723c7352016-07-05 11:27:43 +020047#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020048#include <net/neighbour.h>
49#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020050#include <net/ip_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010051#include <net/fib_rules.h>
Ido Schimmel57837882017-03-16 09:08:16 +010052#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020053#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020054#include <net/ndisc.h>
55#include <net/ipv6.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020056
57#include "spectrum.h"
58#include "core.h"
59#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020060#include "spectrum_cnt.h"
61#include "spectrum_dpipe.h"
62#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020063
Ido Schimmel9011b672017-05-16 19:38:25 +020064struct mlxsw_sp_vr;
65struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020066struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020067
68struct mlxsw_sp_router {
69 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020070 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020071 struct mlxsw_sp_vr *vrs;
72 struct rhashtable neigh_ht;
73 struct rhashtable nexthop_group_ht;
74 struct rhashtable nexthop_ht;
75 struct {
76 struct mlxsw_sp_lpm_tree *trees;
77 unsigned int tree_count;
78 } lpm;
79 struct {
80 struct delayed_work dw;
81 unsigned long interval; /* ms */
82 } neighs_update;
83 struct delayed_work nexthop_probe_dw;
84#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
85 struct list_head nexthop_neighs_list;
86 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020087 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020088 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020089};
90
Ido Schimmel4724ba562017-03-10 08:53:39 +010091struct mlxsw_sp_rif {
92 struct list_head nexthop_list;
93 struct list_head neigh_list;
94 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +020095 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +010096 unsigned char addr[ETH_ALEN];
97 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +010098 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +010099 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200100 const struct mlxsw_sp_rif_ops *ops;
101 struct mlxsw_sp *mlxsw_sp;
102
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200103 unsigned int counter_ingress;
104 bool counter_ingress_valid;
105 unsigned int counter_egress;
106 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100107};
108
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200109struct mlxsw_sp_rif_params {
110 struct net_device *dev;
111 union {
112 u16 system_port;
113 u16 lag_id;
114 };
115 u16 vid;
116 bool lag;
117};
118
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200119struct mlxsw_sp_rif_subport {
120 struct mlxsw_sp_rif common;
121 union {
122 u16 system_port;
123 u16 lag_id;
124 };
125 u16 vid;
126 bool lag;
127};
128
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200129struct mlxsw_sp_rif_ops {
130 enum mlxsw_sp_rif_type type;
131 size_t rif_size;
132
133 void (*setup)(struct mlxsw_sp_rif *rif,
134 const struct mlxsw_sp_rif_params *params);
135 int (*configure)(struct mlxsw_sp_rif *rif);
136 void (*deconfigure)(struct mlxsw_sp_rif *rif);
137 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
138};
139
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200140static unsigned int *
141mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
142 enum mlxsw_sp_rif_counter_dir dir)
143{
144 switch (dir) {
145 case MLXSW_SP_RIF_COUNTER_EGRESS:
146 return &rif->counter_egress;
147 case MLXSW_SP_RIF_COUNTER_INGRESS:
148 return &rif->counter_ingress;
149 }
150 return NULL;
151}
152
153static bool
154mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
155 enum mlxsw_sp_rif_counter_dir dir)
156{
157 switch (dir) {
158 case MLXSW_SP_RIF_COUNTER_EGRESS:
159 return rif->counter_egress_valid;
160 case MLXSW_SP_RIF_COUNTER_INGRESS:
161 return rif->counter_ingress_valid;
162 }
163 return false;
164}
165
166static void
167mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
168 enum mlxsw_sp_rif_counter_dir dir,
169 bool valid)
170{
171 switch (dir) {
172 case MLXSW_SP_RIF_COUNTER_EGRESS:
173 rif->counter_egress_valid = valid;
174 break;
175 case MLXSW_SP_RIF_COUNTER_INGRESS:
176 rif->counter_ingress_valid = valid;
177 break;
178 }
179}
180
181static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
182 unsigned int counter_index, bool enable,
183 enum mlxsw_sp_rif_counter_dir dir)
184{
185 char ritr_pl[MLXSW_REG_RITR_LEN];
186 bool is_egress = false;
187 int err;
188
189 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
190 is_egress = true;
191 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
192 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
193 if (err)
194 return err;
195
196 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
197 is_egress);
198 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
199}
200
201int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
202 struct mlxsw_sp_rif *rif,
203 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
204{
205 char ricnt_pl[MLXSW_REG_RICNT_LEN];
206 unsigned int *p_counter_index;
207 bool valid;
208 int err;
209
210 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
211 if (!valid)
212 return -EINVAL;
213
214 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
215 if (!p_counter_index)
216 return -EINVAL;
217 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
218 MLXSW_REG_RICNT_OPCODE_NOP);
219 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
220 if (err)
221 return err;
222 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
223 return 0;
224}
225
226static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
227 unsigned int counter_index)
228{
229 char ricnt_pl[MLXSW_REG_RICNT_LEN];
230
231 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
232 MLXSW_REG_RICNT_OPCODE_CLEAR);
233 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
234}
235
236int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
237 struct mlxsw_sp_rif *rif,
238 enum mlxsw_sp_rif_counter_dir dir)
239{
240 unsigned int *p_counter_index;
241 int err;
242
243 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
244 if (!p_counter_index)
245 return -EINVAL;
246 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
247 p_counter_index);
248 if (err)
249 return err;
250
251 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
252 if (err)
253 goto err_counter_clear;
254
255 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
256 *p_counter_index, true, dir);
257 if (err)
258 goto err_counter_edit;
259 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
260 return 0;
261
262err_counter_edit:
263err_counter_clear:
264 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
265 *p_counter_index);
266 return err;
267}
268
269void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
270 struct mlxsw_sp_rif *rif,
271 enum mlxsw_sp_rif_counter_dir dir)
272{
273 unsigned int *p_counter_index;
274
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200275 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
276 return;
277
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200278 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
279 if (WARN_ON(!p_counter_index))
280 return;
281 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
282 *p_counter_index, false, dir);
283 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
284 *p_counter_index);
285 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
286}
287
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200288static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
289{
290 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
291 struct devlink *devlink;
292
293 devlink = priv_to_devlink(mlxsw_sp->core);
294 if (!devlink_dpipe_table_counter_enabled(devlink,
295 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
296 return;
297 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
298}
299
300static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
301{
302 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
303
304 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
305}
306
Ido Schimmel4724ba562017-03-10 08:53:39 +0100307static struct mlxsw_sp_rif *
308mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
309 const struct net_device *dev);
310
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200311#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200312
313struct mlxsw_sp_prefix_usage {
314 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
315};
316
Jiri Pirko53342022016-07-04 08:23:08 +0200317#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
318 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
319
320static bool
Jiri Pirko6b75c482016-07-04 08:23:09 +0200321mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
322 struct mlxsw_sp_prefix_usage *prefix_usage2)
323{
324 unsigned char prefix;
325
326 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
327 if (!test_bit(prefix, prefix_usage2->b))
328 return false;
329 }
330 return true;
331}
332
333static bool
Jiri Pirko53342022016-07-04 08:23:08 +0200334mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
335 struct mlxsw_sp_prefix_usage *prefix_usage2)
336{
337 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
338}
339
Jiri Pirko6b75c482016-07-04 08:23:09 +0200340static bool
341mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
342{
343 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
344
345 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
346}
347
348static void
349mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
350 struct mlxsw_sp_prefix_usage *prefix_usage2)
351{
352 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
353}
354
355static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200356mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
357 unsigned char prefix_len)
358{
359 set_bit(prefix_len, prefix_usage->b);
360}
361
362static void
363mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
364 unsigned char prefix_len)
365{
366 clear_bit(prefix_len, prefix_usage->b);
367}
368
369struct mlxsw_sp_fib_key {
370 unsigned char addr[sizeof(struct in6_addr)];
371 unsigned char prefix_len;
372};
373
Jiri Pirko61c503f2016-07-04 08:23:11 +0200374enum mlxsw_sp_fib_entry_type {
375 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
376 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
377 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
378};
379
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200380struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200381struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200382
Ido Schimmel9aecce12017-02-09 10:28:42 +0100383struct mlxsw_sp_fib_node {
384 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200385 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100386 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100387 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100388 struct mlxsw_sp_fib_key key;
389};
390
Ido Schimmel9aecce12017-02-09 10:28:42 +0100391struct mlxsw_sp_fib_entry {
392 struct list_head list;
393 struct mlxsw_sp_fib_node *fib_node;
394 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200395 struct list_head nexthop_group_node;
396 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200397};
398
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200399struct mlxsw_sp_fib4_entry {
400 struct mlxsw_sp_fib_entry common;
401 u32 tb_id;
402 u32 prio;
403 u8 tos;
404 u8 type;
405};
406
Ido Schimmel9011b672017-05-16 19:38:25 +0200407enum mlxsw_sp_l3proto {
408 MLXSW_SP_L3_PROTO_IPV4,
409 MLXSW_SP_L3_PROTO_IPV6,
410};
411
412struct mlxsw_sp_lpm_tree {
413 u8 id; /* tree ID */
414 unsigned int ref_count;
415 enum mlxsw_sp_l3proto proto;
416 struct mlxsw_sp_prefix_usage prefix_usage;
417};
418
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200419struct mlxsw_sp_fib {
420 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100421 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100422 struct mlxsw_sp_vr *vr;
423 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200424 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
425 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100426 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200427};
428
Ido Schimmel9011b672017-05-16 19:38:25 +0200429struct mlxsw_sp_vr {
430 u16 id; /* virtual router ID */
431 u32 tb_id; /* kernel fib table id */
432 unsigned int rif_count;
433 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200434 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200435};
436
Ido Schimmel9aecce12017-02-09 10:28:42 +0100437static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200438
Ido Schimmel76610eb2017-03-10 08:53:41 +0100439static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
440 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200441{
442 struct mlxsw_sp_fib *fib;
443 int err;
444
445 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
446 if (!fib)
447 return ERR_PTR(-ENOMEM);
448 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
449 if (err)
450 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100451 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100452 fib->proto = proto;
453 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200454 return fib;
455
456err_rhashtable_init:
457 kfree(fib);
458 return ERR_PTR(err);
459}
460
461static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
462{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100463 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100464 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200465 rhashtable_destroy(&fib->ht);
466 kfree(fib);
467}
468
Jiri Pirko53342022016-07-04 08:23:08 +0200469static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100470mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200471{
472 static struct mlxsw_sp_lpm_tree *lpm_tree;
473 int i;
474
Ido Schimmel9011b672017-05-16 19:38:25 +0200475 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
476 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100477 if (lpm_tree->ref_count == 0)
478 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200479 }
480 return NULL;
481}
482
483static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
484 struct mlxsw_sp_lpm_tree *lpm_tree)
485{
486 char ralta_pl[MLXSW_REG_RALTA_LEN];
487
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200488 mlxsw_reg_ralta_pack(ralta_pl, true,
489 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
490 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200491 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
492}
493
494static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
495 struct mlxsw_sp_lpm_tree *lpm_tree)
496{
497 char ralta_pl[MLXSW_REG_RALTA_LEN];
498
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200499 mlxsw_reg_ralta_pack(ralta_pl, false,
500 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
501 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200502 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
503}
504
505static int
506mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
507 struct mlxsw_sp_prefix_usage *prefix_usage,
508 struct mlxsw_sp_lpm_tree *lpm_tree)
509{
510 char ralst_pl[MLXSW_REG_RALST_LEN];
511 u8 root_bin = 0;
512 u8 prefix;
513 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
514
515 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
516 root_bin = prefix;
517
518 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
519 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
520 if (prefix == 0)
521 continue;
522 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
523 MLXSW_REG_RALST_BIN_NO_CHILD);
524 last_prefix = prefix;
525 }
526 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
527}
528
529static struct mlxsw_sp_lpm_tree *
530mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
531 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100532 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200533{
534 struct mlxsw_sp_lpm_tree *lpm_tree;
535 int err;
536
Ido Schimmel382dbb42017-03-10 08:53:40 +0100537 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200538 if (!lpm_tree)
539 return ERR_PTR(-EBUSY);
540 lpm_tree->proto = proto;
541 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
542 if (err)
543 return ERR_PTR(err);
544
545 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
546 lpm_tree);
547 if (err)
548 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200549 memcpy(&lpm_tree->prefix_usage, prefix_usage,
550 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200551 return lpm_tree;
552
553err_left_struct_set:
554 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
555 return ERR_PTR(err);
556}
557
558static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
559 struct mlxsw_sp_lpm_tree *lpm_tree)
560{
561 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
562}
563
564static struct mlxsw_sp_lpm_tree *
565mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
566 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100567 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200568{
569 struct mlxsw_sp_lpm_tree *lpm_tree;
570 int i;
571
Ido Schimmel9011b672017-05-16 19:38:25 +0200572 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
573 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200574 if (lpm_tree->ref_count != 0 &&
575 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200576 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
577 prefix_usage))
578 goto inc_ref_count;
579 }
580 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100581 proto);
Jiri Pirko53342022016-07-04 08:23:08 +0200582 if (IS_ERR(lpm_tree))
583 return lpm_tree;
584
585inc_ref_count:
586 lpm_tree->ref_count++;
587 return lpm_tree;
588}
589
590static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
591 struct mlxsw_sp_lpm_tree *lpm_tree)
592{
593 if (--lpm_tree->ref_count == 0)
594 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
595 return 0;
596}
597
Ido Schimmeld7a60302017-06-08 08:47:43 +0200598#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100599
600static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200601{
602 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100603 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200604 int i;
605
Ido Schimmel8494ab02017-03-24 08:02:47 +0100606 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
607 return -EIO;
608
609 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200610 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
611 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100612 sizeof(struct mlxsw_sp_lpm_tree),
613 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200614 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100615 return -ENOMEM;
616
Ido Schimmel9011b672017-05-16 19:38:25 +0200617 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
618 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200619 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
620 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100621
622 return 0;
623}
624
625static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
626{
Ido Schimmel9011b672017-05-16 19:38:25 +0200627 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200628}
629
Ido Schimmel76610eb2017-03-10 08:53:41 +0100630static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
631{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200632 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100633}
634
Jiri Pirko6b75c482016-07-04 08:23:09 +0200635static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
636{
637 struct mlxsw_sp_vr *vr;
638 int i;
639
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200640 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200641 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100642 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200643 return vr;
644 }
645 return NULL;
646}
647
648static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100649 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200650{
651 char raltb_pl[MLXSW_REG_RALTB_LEN];
652
Ido Schimmel76610eb2017-03-10 08:53:41 +0100653 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
654 (enum mlxsw_reg_ralxx_protocol) fib->proto,
655 fib->lpm_tree->id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200656 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
657}
658
659static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100660 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200661{
662 char raltb_pl[MLXSW_REG_RALTB_LEN];
663
664 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100665 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
666 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200667 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
668}
669
670static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
671{
672 /* For our purpose, squash main and local table into one */
673 if (tb_id == RT_TABLE_LOCAL)
674 tb_id = RT_TABLE_MAIN;
675 return tb_id;
676}
677
678static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100679 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200680{
681 struct mlxsw_sp_vr *vr;
682 int i;
683
684 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200685
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200686 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200687 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100688 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200689 return vr;
690 }
691 return NULL;
692}
693
Ido Schimmel76610eb2017-03-10 08:53:41 +0100694static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
695 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200696{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100697 switch (proto) {
698 case MLXSW_SP_L3_PROTO_IPV4:
699 return vr->fib4;
700 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200701 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100702 }
703 return NULL;
704}
705
706static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
707 u32 tb_id)
708{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200709 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200710 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200711
712 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
713 if (!vr)
714 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100715 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
716 if (IS_ERR(vr->fib4))
717 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200718 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
719 if (IS_ERR(vr->fib6)) {
720 err = PTR_ERR(vr->fib6);
721 goto err_fib6_create;
722 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200723 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200724 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200725
726err_fib6_create:
727 mlxsw_sp_fib_destroy(vr->fib4);
728 vr->fib4 = NULL;
729 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200730}
731
Ido Schimmel76610eb2017-03-10 08:53:41 +0100732static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200733{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200734 mlxsw_sp_fib_destroy(vr->fib6);
735 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100736 mlxsw_sp_fib_destroy(vr->fib4);
737 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200738}
739
740static int
Ido Schimmel76610eb2017-03-10 08:53:41 +0100741mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib,
Jiri Pirko6b75c482016-07-04 08:23:09 +0200742 struct mlxsw_sp_prefix_usage *req_prefix_usage)
743{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100744 struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100745 struct mlxsw_sp_lpm_tree *new_tree;
746 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747
Ido Schimmelf7df4922017-02-28 08:55:40 +0100748 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200749 return 0;
750
Ido Schimmelf7df4922017-02-28 08:55:40 +0100751 new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100752 fib->proto);
Ido Schimmelf7df4922017-02-28 08:55:40 +0100753 if (IS_ERR(new_tree)) {
Jiri Pirko6b75c482016-07-04 08:23:09 +0200754 /* We failed to get a tree according to the required
755 * prefix usage. However, the current tree might be still good
756 * for us if our requirement is subset of the prefixes used
757 * in the tree.
758 */
759 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
Ido Schimmelf7df4922017-02-28 08:55:40 +0100760 &lpm_tree->prefix_usage))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200761 return 0;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100762 return PTR_ERR(new_tree);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200763 }
764
Ido Schimmelf7df4922017-02-28 08:55:40 +0100765 /* Prevent packet loss by overwriting existing binding */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100766 fib->lpm_tree = new_tree;
767 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
Ido Schimmelf7df4922017-02-28 08:55:40 +0100768 if (err)
769 goto err_tree_bind;
770 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
771
772 return 0;
773
774err_tree_bind:
Ido Schimmel76610eb2017-03-10 08:53:41 +0100775 fib->lpm_tree = lpm_tree;
Ido Schimmelf7df4922017-02-28 08:55:40 +0100776 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
777 return err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200778}
779
Ido Schimmel76610eb2017-03-10 08:53:41 +0100780static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200781{
782 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200783
784 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100785 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
786 if (!vr)
787 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200788 return vr;
789}
790
Ido Schimmel76610eb2017-03-10 08:53:41 +0100791static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200792{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200793 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
794 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100795 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200796}
797
Nogah Frankel9497c042016-09-20 11:16:54 +0200798static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200799{
800 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200801 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200802 int i;
803
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200804 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200805 return -EIO;
806
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200807 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200808 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
809 GFP_KERNEL);
810 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200811 return -ENOMEM;
812
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200813 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200814 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200815 vr->id = i;
816 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200817
818 return 0;
819}
820
Ido Schimmelac571de2016-11-14 11:26:32 +0100821static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
822
Nogah Frankel9497c042016-09-20 11:16:54 +0200823static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
824{
Ido Schimmel30572242016-12-03 16:45:01 +0100825 /* At this stage we're guaranteed not to have new incoming
826 * FIB notifications and the work queue is free from FIBs
827 * sitting on top of mlxsw netdevs. However, we can still
828 * have other FIBs queued. Flush the queue before flushing
829 * the device's tables. No need for locks, as we're the only
830 * writer.
831 */
832 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100833 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200834 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200835}
836
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200837struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100838 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200839};
840
841struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100842 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200843 struct rhash_head ht_node;
844 struct mlxsw_sp_neigh_key key;
845 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100846 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200847 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200848 struct list_head nexthop_list; /* list of nexthops using
849 * this neigh entry
850 */
Yotam Gigib2157142016-07-05 11:27:51 +0200851 struct list_head nexthop_neighs_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200852};
853
854static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
855 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
856 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
857 .key_len = sizeof(struct mlxsw_sp_neigh_key),
858};
859
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100860static struct mlxsw_sp_neigh_entry *
861mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
862 u16 rif)
863{
864 struct mlxsw_sp_neigh_entry *neigh_entry;
865
866 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
867 if (!neigh_entry)
868 return NULL;
869
870 neigh_entry->key.n = n;
871 neigh_entry->rif = rif;
872 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
873
874 return neigh_entry;
875}
876
877static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
878{
879 kfree(neigh_entry);
880}
881
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200882static int
883mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
884 struct mlxsw_sp_neigh_entry *neigh_entry)
885{
Ido Schimmel9011b672017-05-16 19:38:25 +0200886 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200887 &neigh_entry->ht_node,
888 mlxsw_sp_neigh_ht_params);
889}
890
891static void
892mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
893 struct mlxsw_sp_neigh_entry *neigh_entry)
894{
Ido Schimmel9011b672017-05-16 19:38:25 +0200895 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200896 &neigh_entry->ht_node,
897 mlxsw_sp_neigh_ht_params);
898}
899
900static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100901mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200902{
903 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100904 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100905 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200906
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100907 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
908 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100909 return ERR_PTR(-EINVAL);
910
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100911 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200912 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100913 return ERR_PTR(-ENOMEM);
914
915 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
916 if (err)
917 goto err_neigh_entry_insert;
918
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100919 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +0100920
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200921 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100922
923err_neigh_entry_insert:
924 mlxsw_sp_neigh_entry_free(neigh_entry);
925 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200926}
927
928static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100929mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
930 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200931{
Ido Schimmel9665b742017-02-08 11:16:42 +0100932 list_del(&neigh_entry->rif_list_node);
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100933 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
934 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200935}
936
937static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +0100938mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200939{
Jiri Pirko33b13412016-11-10 12:31:04 +0100940 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200941
Jiri Pirko33b13412016-11-10 12:31:04 +0100942 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +0200943 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200944 &key, mlxsw_sp_neigh_ht_params);
945}
946
Yotam Gigic723c7352016-07-05 11:27:43 +0200947static void
948mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
949{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +0200950 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +0200951
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +0200952#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +0200953 interval = min_t(unsigned long,
954 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
955 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +0200956#else
957 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
958#endif
Ido Schimmel9011b672017-05-16 19:38:25 +0200959 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +0200960}
961
962static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
963 char *rauhtd_pl,
964 int ent_index)
965{
966 struct net_device *dev;
967 struct neighbour *n;
968 __be32 dipn;
969 u32 dip;
970 u16 rif;
971
972 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
973
Ido Schimmel5f9efff2017-05-16 19:38:27 +0200974 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +0200975 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
976 return;
977 }
978
979 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +0200980 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +0200981 n = neigh_lookup(&arp_tbl, &dipn, dev);
982 if (!n) {
983 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
984 &dip);
985 return;
986 }
987
988 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
989 neigh_event_send(n, NULL);
990 neigh_release(n);
991}
992
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +0200993#if IS_ENABLED(IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +0200994static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
995 char *rauhtd_pl,
996 int rec_index)
997{
998 struct net_device *dev;
999 struct neighbour *n;
1000 struct in6_addr dip;
1001 u16 rif;
1002
1003 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1004 (char *) &dip);
1005
1006 if (!mlxsw_sp->router->rifs[rif]) {
1007 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1008 return;
1009 }
1010
1011 dev = mlxsw_sp->router->rifs[rif]->dev;
1012 n = neigh_lookup(&nd_tbl, &dip, dev);
1013 if (!n) {
1014 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1015 &dip);
1016 return;
1017 }
1018
1019 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1020 neigh_event_send(n, NULL);
1021 neigh_release(n);
1022}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001023#else
1024static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1025 char *rauhtd_pl,
1026 int rec_index)
1027{
1028}
1029#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001030
Yotam Gigic723c7352016-07-05 11:27:43 +02001031static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1032 char *rauhtd_pl,
1033 int rec_index)
1034{
1035 u8 num_entries;
1036 int i;
1037
1038 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1039 rec_index);
1040 /* Hardware starts counting at 0, so add 1. */
1041 num_entries++;
1042
1043 /* Each record consists of several neighbour entries. */
1044 for (i = 0; i < num_entries; i++) {
1045 int ent_index;
1046
1047 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1048 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1049 ent_index);
1050 }
1051
1052}
1053
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001054static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1055 char *rauhtd_pl,
1056 int rec_index)
1057{
1058 /* One record contains one entry. */
1059 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1060 rec_index);
1061}
1062
Yotam Gigic723c7352016-07-05 11:27:43 +02001063static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1064 char *rauhtd_pl, int rec_index)
1065{
1066 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1067 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1068 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1069 rec_index);
1070 break;
1071 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001072 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1073 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001074 break;
1075 }
1076}
1077
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001078static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1079{
1080 u8 num_rec, last_rec_index, num_entries;
1081
1082 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1083 last_rec_index = num_rec - 1;
1084
1085 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1086 return false;
1087 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1088 MLXSW_REG_RAUHTD_TYPE_IPV6)
1089 return true;
1090
1091 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1092 last_rec_index);
1093 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1094 return true;
1095 return false;
1096}
1097
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001098static int
1099__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1100 char *rauhtd_pl,
1101 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001102{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001103 int i, num_rec;
1104 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001105
1106 /* Make sure the neighbour's netdev isn't removed in the
1107 * process.
1108 */
1109 rtnl_lock();
1110 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001111 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001112 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1113 rauhtd_pl);
1114 if (err) {
1115 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1116 break;
1117 }
1118 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1119 for (i = 0; i < num_rec; i++)
1120 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1121 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001122 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001123 rtnl_unlock();
1124
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001125 return err;
1126}
1127
1128static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1129{
1130 enum mlxsw_reg_rauhtd_type type;
1131 char *rauhtd_pl;
1132 int err;
1133
1134 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1135 if (!rauhtd_pl)
1136 return -ENOMEM;
1137
1138 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1139 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1140 if (err)
1141 goto out;
1142
1143 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1144 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1145out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001146 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001147 return err;
1148}
1149
1150static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1151{
1152 struct mlxsw_sp_neigh_entry *neigh_entry;
1153
1154 /* Take RTNL mutex here to prevent lists from changes */
1155 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001156 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001157 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001158 /* If this neigh have nexthops, make the kernel think this neigh
1159 * is active regardless of the traffic.
1160 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001161 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001162 rtnl_unlock();
1163}
1164
1165static void
1166mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1167{
Ido Schimmel9011b672017-05-16 19:38:25 +02001168 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001169
Ido Schimmel9011b672017-05-16 19:38:25 +02001170 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001171 msecs_to_jiffies(interval));
1172}
1173
1174static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1175{
Ido Schimmel9011b672017-05-16 19:38:25 +02001176 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001177 int err;
1178
Ido Schimmel9011b672017-05-16 19:38:25 +02001179 router = container_of(work, struct mlxsw_sp_router,
1180 neighs_update.dw.work);
1181 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001182 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001183 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001184
Ido Schimmel9011b672017-05-16 19:38:25 +02001185 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001186
Ido Schimmel9011b672017-05-16 19:38:25 +02001187 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001188}
1189
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001190static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1191{
1192 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001193 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001194
Ido Schimmel9011b672017-05-16 19:38:25 +02001195 router = container_of(work, struct mlxsw_sp_router,
1196 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001197 /* Iterate over nexthop neighbours, find those who are unresolved and
1198 * send arp on them. This solves the chicken-egg problem when
1199 * the nexthop wouldn't get offloaded until the neighbor is resolved
1200 * but it wouldn't get resolved ever in case traffic is flowing in HW
1201 * using different nexthop.
1202 *
1203 * Take RTNL mutex here to prevent lists from changes.
1204 */
1205 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001206 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001207 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001208 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001209 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001210 rtnl_unlock();
1211
Ido Schimmel9011b672017-05-16 19:38:25 +02001212 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001213 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1214}
1215
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001216static void
1217mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1218 struct mlxsw_sp_neigh_entry *neigh_entry,
1219 bool removing);
1220
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001221static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001222{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001223 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1224 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1225}
1226
1227static void
1228mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1229 struct mlxsw_sp_neigh_entry *neigh_entry,
1230 enum mlxsw_reg_rauht_op op)
1231{
Jiri Pirko33b13412016-11-10 12:31:04 +01001232 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001233 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001234 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001235
1236 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1237 dip);
1238 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1239}
1240
1241static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001242mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1243 struct mlxsw_sp_neigh_entry *neigh_entry,
1244 enum mlxsw_reg_rauht_op op)
1245{
1246 struct neighbour *n = neigh_entry->key.n;
1247 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1248 const char *dip = n->primary_key;
1249
1250 mlxsw_reg_rauht_pack6(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 bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1256{
1257 /* Packets with a link-local destination address are trapped
1258 * after LPM lookup and never reach the neighbour table, so
1259 * there is no need to program such neighbours to the device.
1260 */
1261 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1262 IPV6_ADDR_LINKLOCAL)
1263 return true;
1264 return false;
1265}
1266
1267static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001268mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1269 struct mlxsw_sp_neigh_entry *neigh_entry,
1270 bool adding)
1271{
1272 if (!adding && !neigh_entry->connected)
1273 return;
1274 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001275 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001276 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1277 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001278 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001279 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1280 return;
1281 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1282 mlxsw_sp_rauht_op(adding));
1283 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001284 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001285 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001286}
1287
1288struct mlxsw_sp_neigh_event_work {
1289 struct work_struct work;
1290 struct mlxsw_sp *mlxsw_sp;
1291 struct neighbour *n;
1292};
1293
1294static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1295{
1296 struct mlxsw_sp_neigh_event_work *neigh_work =
1297 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1298 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1299 struct mlxsw_sp_neigh_entry *neigh_entry;
1300 struct neighbour *n = neigh_work->n;
1301 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001302 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001303 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001304
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001305 /* If these parameters are changed after we release the lock,
1306 * then we are guaranteed to receive another event letting us
1307 * know about it.
1308 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001309 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001310 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001311 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001312 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001313 read_unlock_bh(&n->lock);
1314
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001315 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001316 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001317 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1318 if (!entry_connected && !neigh_entry)
1319 goto out;
1320 if (!neigh_entry) {
1321 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1322 if (IS_ERR(neigh_entry))
1323 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001324 }
1325
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001326 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1327 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1328 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1329
1330 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1331 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1332
1333out:
1334 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001335 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001336 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001337}
1338
Jiri Pirkoe7322632016-09-01 10:37:43 +02001339int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1340 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001341{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001342 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001343 struct mlxsw_sp_port *mlxsw_sp_port;
1344 struct mlxsw_sp *mlxsw_sp;
1345 unsigned long interval;
1346 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001347 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001348
1349 switch (event) {
1350 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1351 p = ptr;
1352
1353 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001354 if (!p->dev || (p->tbl->family != AF_INET &&
1355 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001356 return NOTIFY_DONE;
1357
1358 /* We are in atomic context and can't take RTNL mutex,
1359 * so use RCU variant to walk the device chain.
1360 */
1361 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1362 if (!mlxsw_sp_port)
1363 return NOTIFY_DONE;
1364
1365 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1366 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001367 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001368
1369 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1370 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001371 case NETEVENT_NEIGH_UPDATE:
1372 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001373
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001374 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001375 return NOTIFY_DONE;
1376
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001377 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001378 if (!mlxsw_sp_port)
1379 return NOTIFY_DONE;
1380
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001381 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1382 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001383 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001384 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001385 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001386
1387 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1388 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1389 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001390
1391 /* Take a reference to ensure the neighbour won't be
1392 * destructed until we drop the reference in delayed
1393 * work.
1394 */
1395 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001396 mlxsw_core_schedule_work(&neigh_work->work);
1397 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001398 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001399 }
1400
1401 return NOTIFY_DONE;
1402}
1403
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001404static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1405{
Yotam Gigic723c7352016-07-05 11:27:43 +02001406 int err;
1407
Ido Schimmel9011b672017-05-16 19:38:25 +02001408 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001409 &mlxsw_sp_neigh_ht_params);
1410 if (err)
1411 return err;
1412
1413 /* Initialize the polling interval according to the default
1414 * table.
1415 */
1416 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1417
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001418 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001419 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001420 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001421 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001422 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001423 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1424 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001425 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001426}
1427
1428static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1429{
Ido Schimmel9011b672017-05-16 19:38:25 +02001430 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1431 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1432 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001433}
1434
Ido Schimmel9665b742017-02-08 11:16:42 +01001435static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001436 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001437{
1438 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1439
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001440 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001441 rif_list_node) {
1442 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001443 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001444 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001445}
1446
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001447struct mlxsw_sp_nexthop_key {
1448 struct fib_nh *fib_nh;
1449};
1450
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001451struct mlxsw_sp_nexthop {
1452 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001453 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001454 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1455 * this belongs to
1456 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001457 struct rhash_head ht_node;
1458 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001459 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001460 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001461 u8 should_offload:1, /* set indicates this neigh is connected and
1462 * should be put to KVD linear area of this group.
1463 */
1464 offloaded:1, /* set in case the neigh is actually put into
1465 * KVD linear area of this group.
1466 */
1467 update:1; /* set indicates that MAC of this neigh should be
1468 * updated in HW
1469 */
1470 struct mlxsw_sp_neigh_entry *neigh_entry;
1471};
1472
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001473struct mlxsw_sp_nexthop_group_key {
1474 struct fib_info *fi;
1475};
1476
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001477struct mlxsw_sp_nexthop_group {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001478 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001479 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001480 struct neigh_table *neigh_tbl;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001481 struct mlxsw_sp_nexthop_group_key key;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001482 u8 adj_index_valid:1,
1483 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001484 u32 adj_index;
1485 u16 ecmp_size;
1486 u16 count;
1487 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001488#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001489};
1490
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001491static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1492 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1493 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1494 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1495};
1496
1497static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1498 struct mlxsw_sp_nexthop_group *nh_grp)
1499{
Ido Schimmel9011b672017-05-16 19:38:25 +02001500 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001501 &nh_grp->ht_node,
1502 mlxsw_sp_nexthop_group_ht_params);
1503}
1504
1505static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1506 struct mlxsw_sp_nexthop_group *nh_grp)
1507{
Ido Schimmel9011b672017-05-16 19:38:25 +02001508 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001509 &nh_grp->ht_node,
1510 mlxsw_sp_nexthop_group_ht_params);
1511}
1512
1513static struct mlxsw_sp_nexthop_group *
1514mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1515 struct mlxsw_sp_nexthop_group_key key)
1516{
Ido Schimmel9011b672017-05-16 19:38:25 +02001517 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001518 mlxsw_sp_nexthop_group_ht_params);
1519}
1520
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001521static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1522 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1523 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1524 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1525};
1526
1527static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1528 struct mlxsw_sp_nexthop *nh)
1529{
Ido Schimmel9011b672017-05-16 19:38:25 +02001530 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001531 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1532}
1533
1534static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1535 struct mlxsw_sp_nexthop *nh)
1536{
Ido Schimmel9011b672017-05-16 19:38:25 +02001537 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001538 mlxsw_sp_nexthop_ht_params);
1539}
1540
Ido Schimmelad178c82017-02-08 11:16:40 +01001541static struct mlxsw_sp_nexthop *
1542mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1543 struct mlxsw_sp_nexthop_key key)
1544{
Ido Schimmel9011b672017-05-16 19:38:25 +02001545 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001546 mlxsw_sp_nexthop_ht_params);
1547}
1548
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001549static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001550 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001551 u32 adj_index, u16 ecmp_size,
1552 u32 new_adj_index,
1553 u16 new_ecmp_size)
1554{
1555 char raleu_pl[MLXSW_REG_RALEU_LEN];
1556
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001557 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001558 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1559 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001560 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001561 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1562}
1563
1564static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1565 struct mlxsw_sp_nexthop_group *nh_grp,
1566 u32 old_adj_index, u16 old_ecmp_size)
1567{
1568 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001569 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001570 int err;
1571
1572 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001573 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001574 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001575 fib = fib_entry->fib_node->fib;
1576 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001577 old_adj_index,
1578 old_ecmp_size,
1579 nh_grp->adj_index,
1580 nh_grp->ecmp_size);
1581 if (err)
1582 return err;
1583 }
1584 return 0;
1585}
1586
1587static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1588 struct mlxsw_sp_nexthop *nh)
1589{
1590 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1591 char ratr_pl[MLXSW_REG_RATR_LEN];
1592
1593 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1594 true, adj_index, neigh_entry->rif);
1595 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1596 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1597}
1598
1599static int
1600mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001601 struct mlxsw_sp_nexthop_group *nh_grp,
1602 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001603{
1604 u32 adj_index = nh_grp->adj_index; /* base */
1605 struct mlxsw_sp_nexthop *nh;
1606 int i;
1607 int err;
1608
1609 for (i = 0; i < nh_grp->count; i++) {
1610 nh = &nh_grp->nexthops[i];
1611
1612 if (!nh->should_offload) {
1613 nh->offloaded = 0;
1614 continue;
1615 }
1616
Ido Schimmela59b7e02017-01-23 11:11:42 +01001617 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001618 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1619 adj_index, nh);
1620 if (err)
1621 return err;
1622 nh->update = 0;
1623 nh->offloaded = 1;
1624 }
1625 adj_index++;
1626 }
1627 return 0;
1628}
1629
1630static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1631 struct mlxsw_sp_fib_entry *fib_entry);
1632
Ido Schimmel1819ae32017-07-21 18:04:28 +02001633static bool
1634mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1635 const struct mlxsw_sp_fib_entry *fib_entry);
1636
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001637static int
1638mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1639 struct mlxsw_sp_nexthop_group *nh_grp)
1640{
1641 struct mlxsw_sp_fib_entry *fib_entry;
1642 int err;
1643
1644 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02001645 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1646 fib_entry))
1647 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001648 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1649 if (err)
1650 return err;
1651 }
1652 return 0;
1653}
1654
1655static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02001656mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1657 enum mlxsw_reg_ralue_op op, int err);
1658
1659static void
1660mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
1661{
1662 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
1663 struct mlxsw_sp_fib_entry *fib_entry;
1664
1665 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1666 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1667 fib_entry))
1668 continue;
1669 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
1670 }
1671}
1672
1673static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001674mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1675 struct mlxsw_sp_nexthop_group *nh_grp)
1676{
1677 struct mlxsw_sp_nexthop *nh;
1678 bool offload_change = false;
1679 u32 adj_index;
1680 u16 ecmp_size = 0;
1681 bool old_adj_index_valid;
1682 u32 old_adj_index;
1683 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001684 int i;
1685 int err;
1686
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001687 if (!nh_grp->gateway) {
1688 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1689 return;
1690 }
1691
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001692 for (i = 0; i < nh_grp->count; i++) {
1693 nh = &nh_grp->nexthops[i];
1694
Petr Machata56b8a9e2017-07-31 09:27:29 +02001695 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001696 offload_change = true;
1697 if (nh->should_offload)
1698 nh->update = 1;
1699 }
1700 if (nh->should_offload)
1701 ecmp_size++;
1702 }
1703 if (!offload_change) {
1704 /* Nothing was added or removed, so no need to reallocate. Just
1705 * update MAC on existing adjacency indexes.
1706 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001707 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1708 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001709 if (err) {
1710 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1711 goto set_trap;
1712 }
1713 return;
1714 }
1715 if (!ecmp_size)
1716 /* No neigh of this group is connected so we just set
1717 * the trap and let everthing flow through kernel.
1718 */
1719 goto set_trap;
1720
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001721 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1722 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001723 /* We ran out of KVD linear space, just set the
1724 * trap and let everything flow through kernel.
1725 */
1726 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1727 goto set_trap;
1728 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001729 old_adj_index_valid = nh_grp->adj_index_valid;
1730 old_adj_index = nh_grp->adj_index;
1731 old_ecmp_size = nh_grp->ecmp_size;
1732 nh_grp->adj_index_valid = 1;
1733 nh_grp->adj_index = adj_index;
1734 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001735 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001736 if (err) {
1737 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1738 goto set_trap;
1739 }
1740
1741 if (!old_adj_index_valid) {
1742 /* The trap was set for fib entries, so we have to call
1743 * fib entry update to unset it and use adjacency index.
1744 */
1745 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1746 if (err) {
1747 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1748 goto set_trap;
1749 }
1750 return;
1751 }
1752
1753 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1754 old_adj_index, old_ecmp_size);
1755 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1756 if (err) {
1757 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1758 goto set_trap;
1759 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02001760
1761 /* Offload state within the group changed, so update the flags. */
1762 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
1763
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001764 return;
1765
1766set_trap:
1767 old_adj_index_valid = nh_grp->adj_index_valid;
1768 nh_grp->adj_index_valid = 0;
1769 for (i = 0; i < nh_grp->count; i++) {
1770 nh = &nh_grp->nexthops[i];
1771 nh->offloaded = 0;
1772 }
1773 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1774 if (err)
1775 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1776 if (old_adj_index_valid)
1777 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1778}
1779
1780static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1781 bool removing)
1782{
Petr Machata213666a2017-07-31 09:27:30 +02001783 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001784 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02001785 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001786 nh->should_offload = 0;
1787 nh->update = 1;
1788}
1789
1790static void
1791mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1792 struct mlxsw_sp_neigh_entry *neigh_entry,
1793 bool removing)
1794{
1795 struct mlxsw_sp_nexthop *nh;
1796
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001797 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1798 neigh_list_node) {
1799 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1800 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1801 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001802}
1803
Ido Schimmel9665b742017-02-08 11:16:42 +01001804static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001805 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001806{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001807 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001808 return;
1809
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001810 nh->rif = rif;
1811 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001812}
1813
1814static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1815{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001816 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001817 return;
1818
1819 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001820 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01001821}
1822
Ido Schimmela8c97012017-02-08 11:16:35 +01001823static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1824 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001825{
1826 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001827 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001828 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001829 int err;
1830
Ido Schimmelad178c82017-02-08 11:16:40 +01001831 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01001832 return 0;
1833
Jiri Pirko33b13412016-11-10 12:31:04 +01001834 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02001835 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01001836 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01001837 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01001838 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001839 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01001840 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001841 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1842 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01001843 if (IS_ERR(n))
1844 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001845 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01001846 }
1847 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1848 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001849 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1850 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001851 err = -EINVAL;
1852 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001853 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001854 }
Yotam Gigib2157142016-07-05 11:27:51 +02001855
1856 /* If that is the first nexthop connected to that neigh, add to
1857 * nexthop_neighs_list
1858 */
1859 if (list_empty(&neigh_entry->nexthop_list))
1860 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02001861 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02001862
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001863 nh->neigh_entry = neigh_entry;
1864 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1865 read_lock_bh(&n->lock);
1866 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001867 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001868 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01001869 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001870
1871 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001872
1873err_neigh_entry_create:
1874 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001875 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001876}
1877
Ido Schimmela8c97012017-02-08 11:16:35 +01001878static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1879 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001880{
1881 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01001882 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001883
Ido Schimmelb8399a12017-02-08 11:16:33 +01001884 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01001885 return;
1886 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01001887
Ido Schimmel58312122016-12-23 09:32:50 +01001888 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001889 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01001890 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02001891
1892 /* If that is the last nexthop connected to that neigh, remove from
1893 * nexthop_neighs_list
1894 */
Ido Schimmele58be792017-02-08 11:16:28 +01001895 if (list_empty(&neigh_entry->nexthop_list))
1896 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02001897
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001898 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1899 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1900
1901 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01001902}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001903
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001904static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1905 struct mlxsw_sp_nexthop_group *nh_grp,
1906 struct mlxsw_sp_nexthop *nh,
1907 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001908{
1909 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001910 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001911 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01001912 int err;
1913
1914 nh->nh_grp = nh_grp;
1915 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001916 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01001917 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1918 if (err)
1919 return err;
1920
Ido Schimmel97989ee2017-03-10 08:53:38 +01001921 if (!dev)
1922 return 0;
1923
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001924 in_dev = __in_dev_get_rtnl(dev);
1925 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1926 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1927 return 0;
1928
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001929 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1930 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01001931 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001932 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01001933
1934 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1935 if (err)
1936 goto err_nexthop_neigh_init;
1937
1938 return 0;
1939
1940err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02001941 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01001942 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1943 return err;
1944}
1945
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001946static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
1947 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001948{
1949 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001950 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001951 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001952}
1953
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001954static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
1955 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01001956{
1957 struct mlxsw_sp_nexthop_key key;
1958 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001959 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01001960
Ido Schimmel9011b672017-05-16 19:38:25 +02001961 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01001962 return;
1963
1964 key.fib_nh = fib_nh;
1965 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1966 if (WARN_ON_ONCE(!nh))
1967 return;
1968
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001969 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1970 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01001971 return;
1972
1973 switch (event) {
1974 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001975 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01001976 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1977 break;
1978 case FIB_EVENT_NH_DEL:
1979 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001980 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01001981 break;
1982 }
1983
1984 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1985}
1986
Ido Schimmel9665b742017-02-08 11:16:42 +01001987static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001988 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001989{
1990 struct mlxsw_sp_nexthop *nh, *tmp;
1991
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001992 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01001993 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
1994 mlxsw_sp_nexthop_rif_fini(nh);
1995 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1996 }
1997}
1998
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001999static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002000mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002001{
2002 struct mlxsw_sp_nexthop_group *nh_grp;
2003 struct mlxsw_sp_nexthop *nh;
2004 struct fib_nh *fib_nh;
2005 size_t alloc_size;
2006 int i;
2007 int err;
2008
2009 alloc_size = sizeof(*nh_grp) +
2010 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2011 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2012 if (!nh_grp)
2013 return ERR_PTR(-ENOMEM);
2014 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002015 nh_grp->neigh_tbl = &arp_tbl;
2016
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002017 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002018 nh_grp->count = fi->fib_nhs;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002019 nh_grp->key.fi = fi;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002020 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002021 for (i = 0; i < nh_grp->count; i++) {
2022 nh = &nh_grp->nexthops[i];
2023 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002024 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002025 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002026 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002027 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002028 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2029 if (err)
2030 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2032 return nh_grp;
2033
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002034err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002035err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002036 for (i--; i >= 0; i--) {
2037 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002038 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002039 }
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002040 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002041 kfree(nh_grp);
2042 return ERR_PTR(err);
2043}
2044
2045static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002046mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2047 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002048{
2049 struct mlxsw_sp_nexthop *nh;
2050 int i;
2051
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002052 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002053 for (i = 0; i < nh_grp->count; i++) {
2054 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002055 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002056 }
Ido Schimmel58312122016-12-23 09:32:50 +01002057 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2058 WARN_ON_ONCE(nh_grp->adj_index_valid);
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002059 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002060 kfree(nh_grp);
2061}
2062
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002063static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2064 struct mlxsw_sp_fib_entry *fib_entry,
2065 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002066{
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002067 struct mlxsw_sp_nexthop_group_key key;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002068 struct mlxsw_sp_nexthop_group *nh_grp;
2069
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002070 key.fi = fi;
2071 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002072 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002073 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002074 if (IS_ERR(nh_grp))
2075 return PTR_ERR(nh_grp);
2076 }
2077 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2078 fib_entry->nh_group = nh_grp;
2079 return 0;
2080}
2081
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002082static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2083 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002084{
2085 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2086
2087 list_del(&fib_entry->nexthop_group_node);
2088 if (!list_empty(&nh_grp->fib_list))
2089 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002090 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002091}
2092
Ido Schimmel013b20f2017-02-08 11:16:36 +01002093static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002094mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2095{
2096 struct mlxsw_sp_fib4_entry *fib4_entry;
2097
2098 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2099 common);
2100 return !fib4_entry->tos;
2101}
2102
2103static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002104mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2105{
2106 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2107
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002108 switch (fib_entry->fib_node->fib->proto) {
2109 case MLXSW_SP_L3_PROTO_IPV4:
2110 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2111 return false;
2112 break;
2113 case MLXSW_SP_L3_PROTO_IPV6:
2114 break;
2115 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002116
Ido Schimmel013b20f2017-02-08 11:16:36 +01002117 switch (fib_entry->type) {
2118 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2119 return !!nh_group->adj_index_valid;
2120 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002121 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002122 default:
2123 return false;
2124 }
2125}
2126
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002127static void
2128mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2129{
2130 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2131 int i;
2132
2133 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2134 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2135 return;
2136 }
2137
2138 for (i = 0; i < nh_grp->count; i++) {
2139 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2140
2141 if (nh->offloaded)
2142 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2143 else
2144 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2145 }
2146}
2147
2148static void
2149mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2150{
2151 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2152 int i;
2153
2154 for (i = 0; i < nh_grp->count; i++) {
2155 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2156
2157 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2158 }
2159}
2160
Ido Schimmel013b20f2017-02-08 11:16:36 +01002161static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2162{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002163 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002164 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002165 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002166 break;
2167 case MLXSW_SP_L3_PROTO_IPV6:
2168 WARN_ON_ONCE(1);
2169 }
2170}
2171
2172static void
2173mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2174{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002175 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002176 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002177 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002178 break;
2179 case MLXSW_SP_L3_PROTO_IPV6:
2180 WARN_ON_ONCE(1);
2181 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002182}
2183
2184static void
2185mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2186 enum mlxsw_reg_ralue_op op, int err)
2187{
2188 switch (op) {
2189 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002190 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2191 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2192 if (err)
2193 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002194 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002195 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002196 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002197 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2198 return;
2199 default:
2200 return;
2201 }
2202}
2203
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002204static void
2205mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2206 const struct mlxsw_sp_fib_entry *fib_entry,
2207 enum mlxsw_reg_ralue_op op)
2208{
2209 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2210 enum mlxsw_reg_ralxx_protocol proto;
2211 u32 *p_dip;
2212
2213 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2214
2215 switch (fib->proto) {
2216 case MLXSW_SP_L3_PROTO_IPV4:
2217 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2218 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2219 fib_entry->fib_node->key.prefix_len,
2220 *p_dip);
2221 break;
2222 case MLXSW_SP_L3_PROTO_IPV6:
2223 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2224 fib_entry->fib_node->key.prefix_len,
2225 fib_entry->fib_node->key.addr);
2226 break;
2227 }
2228}
2229
2230static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2231 struct mlxsw_sp_fib_entry *fib_entry,
2232 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002233{
2234 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002235 enum mlxsw_reg_ralue_trap_action trap_action;
2236 u16 trap_id = 0;
2237 u32 adjacency_index = 0;
2238 u16 ecmp_size = 0;
2239
2240 /* In case the nexthop group adjacency index is valid, use it
2241 * with provided ECMP size. Otherwise, setup trap and pass
2242 * traffic to kernel.
2243 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002244 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002245 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2246 adjacency_index = fib_entry->nh_group->adj_index;
2247 ecmp_size = fib_entry->nh_group->ecmp_size;
2248 } else {
2249 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2250 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2251 }
2252
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002253 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002254 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2255 adjacency_index, ecmp_size);
2256 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2257}
2258
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002259static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2260 struct mlxsw_sp_fib_entry *fib_entry,
2261 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002262{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002263 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002264 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002265 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002266 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002267 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002268
2269 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2270 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002271 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002272 } else {
2273 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2274 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2275 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002276
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002277 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002278 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2279 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002280 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2281}
2282
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002283static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2284 struct mlxsw_sp_fib_entry *fib_entry,
2285 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002286{
2287 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002288
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002289 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002290 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2291 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2292}
2293
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002294static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2295 struct mlxsw_sp_fib_entry *fib_entry,
2296 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002297{
2298 switch (fib_entry->type) {
2299 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002300 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002301 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002302 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002303 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002304 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002305 }
2306 return -EINVAL;
2307}
2308
2309static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2310 struct mlxsw_sp_fib_entry *fib_entry,
2311 enum mlxsw_reg_ralue_op op)
2312{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002313 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002314
Ido Schimmel013b20f2017-02-08 11:16:36 +01002315 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002316
Ido Schimmel013b20f2017-02-08 11:16:36 +01002317 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002318}
2319
2320static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2321 struct mlxsw_sp_fib_entry *fib_entry)
2322{
Jiri Pirko7146da32016-09-01 10:37:41 +02002323 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2324 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002325}
2326
2327static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2328 struct mlxsw_sp_fib_entry *fib_entry)
2329{
2330 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2331 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2332}
2333
Jiri Pirko61c503f2016-07-04 08:23:11 +02002334static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002335mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2336 const struct fib_entry_notifier_info *fen_info,
2337 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002338{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002339 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002340
Ido Schimmel97989ee2017-03-10 08:53:38 +01002341 switch (fen_info->type) {
2342 case RTN_BROADCAST: /* fall through */
2343 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002344 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2345 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002346 case RTN_UNREACHABLE: /* fall through */
2347 case RTN_BLACKHOLE: /* fall through */
2348 case RTN_PROHIBIT:
2349 /* Packets hitting these routes need to be trapped, but
2350 * can do so with a lower priority than packets directed
2351 * at the host, so use action type local instead of trap.
2352 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002353 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002354 return 0;
2355 case RTN_UNICAST:
2356 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2357 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2358 else
2359 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2360 return 0;
2361 default:
2362 return -EINVAL;
2363 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002364}
2365
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002366static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002367mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2368 struct mlxsw_sp_fib_node *fib_node,
2369 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002370{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002371 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002372 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002373 int err;
2374
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002375 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2376 if (!fib4_entry)
2377 return ERR_PTR(-ENOMEM);
2378 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002379
2380 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2381 if (err)
2382 goto err_fib4_entry_type_set;
2383
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002384 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002385 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002386 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002387
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002388 fib4_entry->prio = fen_info->fi->fib_priority;
2389 fib4_entry->tb_id = fen_info->tb_id;
2390 fib4_entry->type = fen_info->type;
2391 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002392
2393 fib_entry->fib_node = fib_node;
2394
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002395 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002396
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002397err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002398err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002399 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002400 return ERR_PTR(err);
2401}
2402
2403static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002404 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002405{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002406 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002407 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002408}
2409
2410static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002411mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2412 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002413
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002414static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002415mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2416 const struct fib_entry_notifier_info *fen_info)
2417{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002418 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002419 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002420 struct mlxsw_sp_fib *fib;
2421 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002422
Ido Schimmel160e22a2017-07-18 10:10:20 +02002423 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2424 if (!vr)
2425 return NULL;
2426 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2427
2428 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2429 sizeof(fen_info->dst),
2430 fen_info->dst_len);
2431 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002432 return NULL;
2433
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002434 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2435 if (fib4_entry->tb_id == fen_info->tb_id &&
2436 fib4_entry->tos == fen_info->tos &&
2437 fib4_entry->type == fen_info->type &&
2438 fib4_entry->common.nh_group->key.fi == fen_info->fi) {
2439 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002440 }
2441 }
2442
2443 return NULL;
2444}
2445
2446static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2447 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2448 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2449 .key_len = sizeof(struct mlxsw_sp_fib_key),
2450 .automatic_shrinking = true,
2451};
2452
2453static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2454 struct mlxsw_sp_fib_node *fib_node)
2455{
2456 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2457 mlxsw_sp_fib_ht_params);
2458}
2459
2460static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2461 struct mlxsw_sp_fib_node *fib_node)
2462{
2463 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2464 mlxsw_sp_fib_ht_params);
2465}
2466
2467static struct mlxsw_sp_fib_node *
2468mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2469 size_t addr_len, unsigned char prefix_len)
2470{
2471 struct mlxsw_sp_fib_key key;
2472
2473 memset(&key, 0, sizeof(key));
2474 memcpy(key.addr, addr, addr_len);
2475 key.prefix_len = prefix_len;
2476 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2477}
2478
2479static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002480mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002481 size_t addr_len, unsigned char prefix_len)
2482{
2483 struct mlxsw_sp_fib_node *fib_node;
2484
2485 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2486 if (!fib_node)
2487 return NULL;
2488
2489 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002490 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002491 memcpy(fib_node->key.addr, addr, addr_len);
2492 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002493
2494 return fib_node;
2495}
2496
2497static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2498{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002499 list_del(&fib_node->list);
2500 WARN_ON(!list_empty(&fib_node->entry_list));
2501 kfree(fib_node);
2502}
2503
2504static bool
2505mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2506 const struct mlxsw_sp_fib_entry *fib_entry)
2507{
2508 return list_first_entry(&fib_node->entry_list,
2509 struct mlxsw_sp_fib_entry, list) == fib_entry;
2510}
2511
2512static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2513{
2514 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002515 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002516
2517 if (fib->prefix_ref_count[prefix_len]++ == 0)
2518 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2519}
2520
2521static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2522{
2523 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002524 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002525
2526 if (--fib->prefix_ref_count[prefix_len] == 0)
2527 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2528}
2529
Ido Schimmel76610eb2017-03-10 08:53:41 +01002530static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2531 struct mlxsw_sp_fib_node *fib_node,
2532 struct mlxsw_sp_fib *fib)
2533{
2534 struct mlxsw_sp_prefix_usage req_prefix_usage;
2535 struct mlxsw_sp_lpm_tree *lpm_tree;
2536 int err;
2537
2538 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2539 if (err)
2540 return err;
2541 fib_node->fib = fib;
2542
2543 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
2544 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2545
2546 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2547 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
2548 &req_prefix_usage);
2549 if (err)
2550 goto err_tree_check;
2551 } else {
2552 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2553 fib->proto);
2554 if (IS_ERR(lpm_tree))
2555 return PTR_ERR(lpm_tree);
2556 fib->lpm_tree = lpm_tree;
2557 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
2558 if (err)
2559 goto err_tree_bind;
2560 }
2561
2562 mlxsw_sp_fib_node_prefix_inc(fib_node);
2563
2564 return 0;
2565
2566err_tree_bind:
2567 fib->lpm_tree = NULL;
2568 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2569err_tree_check:
2570 fib_node->fib = NULL;
2571 mlxsw_sp_fib_node_remove(fib, fib_node);
2572 return err;
2573}
2574
2575static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2576 struct mlxsw_sp_fib_node *fib_node)
2577{
2578 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
2579 struct mlxsw_sp_fib *fib = fib_node->fib;
2580
2581 mlxsw_sp_fib_node_prefix_dec(fib_node);
2582
2583 if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2584 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2585 fib->lpm_tree = NULL;
2586 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2587 } else {
2588 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
2589 }
2590
2591 fib_node->fib = NULL;
2592 mlxsw_sp_fib_node_remove(fib, fib_node);
2593}
2594
Ido Schimmel9aecce12017-02-09 10:28:42 +01002595static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002596mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2597 size_t addr_len, unsigned char prefix_len,
2598 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002599{
2600 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002601 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002602 struct mlxsw_sp_vr *vr;
2603 int err;
2604
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002605 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002606 if (IS_ERR(vr))
2607 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002608 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002609
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002610 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002611 if (fib_node)
2612 return fib_node;
2613
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002614 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002615 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002616 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002617 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002618 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002619
Ido Schimmel76610eb2017-03-10 08:53:41 +01002620 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2621 if (err)
2622 goto err_fib_node_init;
2623
Ido Schimmel9aecce12017-02-09 10:28:42 +01002624 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002625
Ido Schimmel76610eb2017-03-10 08:53:41 +01002626err_fib_node_init:
2627 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002628err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002629 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002630 return ERR_PTR(err);
2631}
2632
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002633static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2634 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002635{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002636 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002637
Ido Schimmel9aecce12017-02-09 10:28:42 +01002638 if (!list_empty(&fib_node->entry_list))
2639 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002640 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002641 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002642 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002643}
2644
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002645static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002646mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002647 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002648{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002649 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002650
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002651 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2652 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002653 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002654 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002655 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002656 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002657 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002658 if (fib4_entry->prio >= new4_entry->prio ||
2659 fib4_entry->tos < new4_entry->tos)
2660 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002661 }
2662
2663 return NULL;
2664}
2665
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002666static int
2667mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2668 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002669{
2670 struct mlxsw_sp_fib_node *fib_node;
2671
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002672 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01002673 return -EINVAL;
2674
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002675 fib_node = fib4_entry->common.fib_node;
2676 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2677 common.list) {
2678 if (fib4_entry->tb_id != new4_entry->tb_id ||
2679 fib4_entry->tos != new4_entry->tos ||
2680 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002681 break;
2682 }
2683
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002684 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01002685 return 0;
2686}
2687
Ido Schimmel9aecce12017-02-09 10:28:42 +01002688static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02002689mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002690 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002691{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002692 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002693 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002694
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002695 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002696
Ido Schimmel4283bce2017-02-09 10:28:43 +01002697 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002698 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2699 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002700 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01002701
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002702 /* Insert new entry before replaced one, so that we can later
2703 * remove the second.
2704 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002705 if (fib4_entry) {
2706 list_add_tail(&new4_entry->common.list,
2707 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002708 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002709 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002710
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002711 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2712 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002713 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002714 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002715 }
2716
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002717 if (fib4_entry)
2718 list_add(&new4_entry->common.list,
2719 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002720 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002721 list_add(&new4_entry->common.list,
2722 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002723 }
2724
2725 return 0;
2726}
2727
2728static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002729mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002730{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002731 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002732}
2733
Ido Schimmel80c238f2017-07-18 10:10:29 +02002734static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2735 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002736{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002737 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2738
Ido Schimmel9aecce12017-02-09 10:28:42 +01002739 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2740 return 0;
2741
2742 /* To prevent packet loss, overwrite the previously offloaded
2743 * entry.
2744 */
2745 if (!list_is_singular(&fib_node->entry_list)) {
2746 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2747 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2748
2749 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2750 }
2751
2752 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2753}
2754
Ido Schimmel80c238f2017-07-18 10:10:29 +02002755static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2756 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002757{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002758 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2759
Ido Schimmel9aecce12017-02-09 10:28:42 +01002760 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2761 return;
2762
2763 /* Promote the next entry by overwriting the deleted entry */
2764 if (!list_is_singular(&fib_node->entry_list)) {
2765 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2766 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2767
2768 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2769 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2770 return;
2771 }
2772
2773 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2774}
2775
2776static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002777 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002778 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002779{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002780 int err;
2781
Ido Schimmel9efbee62017-07-18 10:10:28 +02002782 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002783 if (err)
2784 return err;
2785
Ido Schimmel80c238f2017-07-18 10:10:29 +02002786 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002787 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02002788 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002789
Ido Schimmel9aecce12017-02-09 10:28:42 +01002790 return 0;
2791
Ido Schimmel80c238f2017-07-18 10:10:29 +02002792err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002793 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002794 return err;
2795}
2796
2797static void
2798mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002799 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002800{
Ido Schimmel80c238f2017-07-18 10:10:29 +02002801 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002802 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002803}
2804
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002805static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002806 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002807 bool replace)
2808{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002809 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2810 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002811
2812 if (!replace)
2813 return;
2814
2815 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002816 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002817
2818 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2819 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002820 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002821}
2822
Ido Schimmel9aecce12017-02-09 10:28:42 +01002823static int
2824mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01002825 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002826 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002827{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002828 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002829 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002830 int err;
2831
Ido Schimmel9011b672017-05-16 19:38:25 +02002832 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002833 return 0;
2834
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002835 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
2836 &fen_info->dst, sizeof(fen_info->dst),
2837 fen_info->dst_len,
2838 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002839 if (IS_ERR(fib_node)) {
2840 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2841 return PTR_ERR(fib_node);
2842 }
2843
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002844 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2845 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002846 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002847 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002848 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002849 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002850
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002851 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002852 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002853 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002854 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2855 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002856 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002857
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002858 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002859
Jiri Pirko61c503f2016-07-04 08:23:11 +02002860 return 0;
2861
Ido Schimmel9aecce12017-02-09 10:28:42 +01002862err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002863 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002864err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002865 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002866 return err;
2867}
2868
Jiri Pirko37956d72016-10-20 16:05:43 +02002869static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2870 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002871{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002872 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002873 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002874
Ido Schimmel9011b672017-05-16 19:38:25 +02002875 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02002876 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002877
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002878 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2879 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02002880 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002881 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002882
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002883 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2884 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002885 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002886}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002887
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002888static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
2889 enum mlxsw_reg_ralxx_protocol proto,
2890 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002891{
2892 char ralta_pl[MLXSW_REG_RALTA_LEN];
2893 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002894 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002895
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002896 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002897 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
2898 if (err)
2899 return err;
2900
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002901 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002902 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
2903 if (err)
2904 return err;
2905
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002906 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02002907 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002908 char raltb_pl[MLXSW_REG_RALTB_LEN];
2909 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002910
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002911 if (!mlxsw_sp_vr_is_used(vr))
2912 continue;
2913
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002914 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002915 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
2916 raltb_pl);
2917 if (err)
2918 return err;
2919
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002920 mlxsw_reg_ralue_pack(ralue_pl, proto,
2921 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002922 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2923 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
2924 ralue_pl);
2925 if (err)
2926 return err;
2927 }
2928
2929 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002930}
2931
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002932static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
2933{
2934 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
2935 int err;
2936
2937 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2938 MLXSW_SP_LPM_TREE_MIN);
2939 if (err)
2940 return err;
2941
2942 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
2943 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2944 MLXSW_SP_LPM_TREE_MIN + 1);
2945}
2946
Ido Schimmel9aecce12017-02-09 10:28:42 +01002947static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
2948 struct mlxsw_sp_fib_node *fib_node)
2949{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002950 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002951
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002952 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
2953 common.list) {
2954 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002955
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002956 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2957 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002958 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002959 /* Break when entry list is empty and node was freed.
2960 * Otherwise, we'll access freed memory in the next
2961 * iteration.
2962 */
2963 if (do_break)
2964 break;
2965 }
2966}
2967
2968static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
2969 struct mlxsw_sp_fib_node *fib_node)
2970{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002971 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002972 case MLXSW_SP_L3_PROTO_IPV4:
2973 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
2974 break;
2975 case MLXSW_SP_L3_PROTO_IPV6:
2976 WARN_ON_ONCE(1);
2977 break;
2978 }
2979}
2980
Ido Schimmel76610eb2017-03-10 08:53:41 +01002981static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
2982 struct mlxsw_sp_vr *vr,
2983 enum mlxsw_sp_l3proto proto)
2984{
2985 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
2986 struct mlxsw_sp_fib_node *fib_node, *tmp;
2987
2988 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
2989 bool do_break = &tmp->list == &fib->node_list;
2990
2991 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
2992 if (do_break)
2993 break;
2994 }
2995}
2996
Ido Schimmelac571de2016-11-14 11:26:32 +01002997static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002998{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002999 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003000
Jiri Pirkoc1a38312016-10-21 16:07:23 +02003001 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02003002 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01003003
Ido Schimmel76610eb2017-03-10 08:53:41 +01003004 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003005 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003006 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02003007
3008 /* If virtual router was only used for IPv4, then it's no
3009 * longer used.
3010 */
3011 if (!mlxsw_sp_vr_is_used(vr))
3012 continue;
3013 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003014 }
Ido Schimmelac571de2016-11-14 11:26:32 +01003015}
3016
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003017static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01003018{
3019 int err;
3020
Ido Schimmel9011b672017-05-16 19:38:25 +02003021 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003022 return;
3023 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 +01003024 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003025 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003026 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3027 if (err)
3028 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3029}
3030
Ido Schimmel30572242016-12-03 16:45:01 +01003031struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003032 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003033 union {
3034 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003035 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003036 struct fib_nh_notifier_info fnh_info;
3037 };
Ido Schimmel30572242016-12-03 16:45:01 +01003038 struct mlxsw_sp *mlxsw_sp;
3039 unsigned long event;
3040};
3041
3042static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003043{
Ido Schimmel30572242016-12-03 16:45:01 +01003044 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003045 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003046 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003047 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003048 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003049 int err;
3050
Ido Schimmel30572242016-12-03 16:45:01 +01003051 /* Protect internal structures from changes */
3052 rtnl_lock();
3053 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003054 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003055 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003056 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003057 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003058 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3059 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003060 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003061 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003062 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003063 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003064 break;
3065 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003066 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3067 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003068 break;
3069 case FIB_EVENT_RULE_ADD: /* fall through */
3070 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003071 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003072 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003073 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003074 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003075 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003076 case FIB_EVENT_NH_ADD: /* fall through */
3077 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003078 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3079 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003080 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3081 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003082 }
Ido Schimmel30572242016-12-03 16:45:01 +01003083 rtnl_unlock();
3084 kfree(fib_work);
3085}
3086
3087/* Called with rcu_read_lock() */
3088static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3089 unsigned long event, void *ptr)
3090{
Ido Schimmel30572242016-12-03 16:45:01 +01003091 struct mlxsw_sp_fib_event_work *fib_work;
3092 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02003093 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01003094
3095 if (!net_eq(info->net, &init_net))
3096 return NOTIFY_DONE;
3097
3098 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3099 if (WARN_ON(!fib_work))
3100 return NOTIFY_BAD;
3101
Ido Schimmela0e47612017-02-06 16:20:10 +01003102 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
Ido Schimmel7e39d112017-05-16 19:38:28 +02003103 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3104 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01003105 fib_work->event = event;
3106
3107 switch (event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003108 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003109 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Ido Schimmel30572242016-12-03 16:45:01 +01003110 case FIB_EVENT_ENTRY_ADD: /* fall through */
3111 case FIB_EVENT_ENTRY_DEL:
3112 memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
3113 /* Take referece on fib_info to prevent it from being
3114 * freed while work is queued. Release it afterwards.
3115 */
3116 fib_info_hold(fib_work->fen_info.fi);
3117 break;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003118 case FIB_EVENT_RULE_ADD: /* fall through */
3119 case FIB_EVENT_RULE_DEL:
3120 memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info));
3121 fib_rule_get(fib_work->fr_info.rule);
3122 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003123 case FIB_EVENT_NH_ADD: /* fall through */
3124 case FIB_EVENT_NH_DEL:
3125 memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
3126 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3127 break;
Ido Schimmel30572242016-12-03 16:45:01 +01003128 }
3129
Ido Schimmela0e47612017-02-06 16:20:10 +01003130 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01003131
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003132 return NOTIFY_DONE;
3133}
3134
Ido Schimmel4724ba562017-03-10 08:53:39 +01003135static struct mlxsw_sp_rif *
3136mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
3137 const struct net_device *dev)
3138{
3139 int i;
3140
3141 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003142 if (mlxsw_sp->router->rifs[i] &&
3143 mlxsw_sp->router->rifs[i]->dev == dev)
3144 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01003145
3146 return NULL;
3147}
3148
3149static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
3150{
3151 char ritr_pl[MLXSW_REG_RITR_LEN];
3152 int err;
3153
3154 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3155 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3156 if (WARN_ON_ONCE(err))
3157 return err;
3158
3159 mlxsw_reg_ritr_enable_set(ritr_pl, false);
3160 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3161}
3162
3163static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003164 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003165{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003166 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
3167 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
3168 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003169}
3170
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003171static bool
3172mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
3173 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003174{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003175 struct inet6_dev *inet6_dev;
3176 bool addr_list_empty = true;
3177 struct in_device *idev;
3178
Ido Schimmel4724ba562017-03-10 08:53:39 +01003179 switch (event) {
3180 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02003181 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003182 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003183 idev = __in_dev_get_rtnl(dev);
3184 if (idev && idev->ifa_list)
3185 addr_list_empty = false;
3186
3187 inet6_dev = __in6_dev_get(dev);
3188 if (addr_list_empty && inet6_dev &&
3189 !list_empty(&inet6_dev->addr_list))
3190 addr_list_empty = false;
3191
3192 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003193 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003194 return true;
3195 /* It is possible we already removed the RIF ourselves
3196 * if it was assigned to a netdev that is now a bridge
3197 * or LAG slave.
3198 */
3199 return false;
3200 }
3201
3202 return false;
3203}
3204
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003205static enum mlxsw_sp_rif_type
3206mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
3207 const struct net_device *dev)
3208{
3209 enum mlxsw_sp_fid_type type;
3210
3211 /* RIF type is derived from the type of the underlying FID */
3212 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
3213 type = MLXSW_SP_FID_TYPE_8021Q;
3214 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
3215 type = MLXSW_SP_FID_TYPE_8021Q;
3216 else if (netif_is_bridge_master(dev))
3217 type = MLXSW_SP_FID_TYPE_8021D;
3218 else
3219 type = MLXSW_SP_FID_TYPE_RFID;
3220
3221 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
3222}
3223
Ido Schimmelde5ed992017-06-04 16:53:40 +02003224static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003225{
3226 int i;
3227
Ido Schimmelde5ed992017-06-04 16:53:40 +02003228 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
3229 if (!mlxsw_sp->router->rifs[i]) {
3230 *p_rif_index = i;
3231 return 0;
3232 }
3233 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01003234
Ido Schimmelde5ed992017-06-04 16:53:40 +02003235 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003236}
3237
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003238static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
3239 u16 vr_id,
3240 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003241{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003242 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003243
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003244 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003245 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003246 return NULL;
3247
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003248 INIT_LIST_HEAD(&rif->nexthop_list);
3249 INIT_LIST_HEAD(&rif->neigh_list);
3250 ether_addr_copy(rif->addr, l3_dev->dev_addr);
3251 rif->mtu = l3_dev->mtu;
3252 rif->vr_id = vr_id;
3253 rif->dev = l3_dev;
3254 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003255
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003256 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003257}
3258
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003259struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
3260 u16 rif_index)
3261{
3262 return mlxsw_sp->router->rifs[rif_index];
3263}
3264
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02003265u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
3266{
3267 return rif->rif_index;
3268}
3269
3270int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
3271{
3272 return rif->dev->ifindex;
3273}
3274
Ido Schimmel4724ba562017-03-10 08:53:39 +01003275static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003276mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
3277 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003278{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003279 u32 tb_id = l3mdev_fib_table(params->dev);
3280 const struct mlxsw_sp_rif_ops *ops;
3281 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003282 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003283 struct mlxsw_sp_fid *fid;
3284 struct mlxsw_sp_vr *vr;
3285 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003286 int err;
3287
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003288 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
3289 ops = mlxsw_sp->router->rif_ops_arr[type];
3290
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003291 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
3292 if (IS_ERR(vr))
3293 return ERR_CAST(vr);
3294
Ido Schimmelde5ed992017-06-04 16:53:40 +02003295 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
3296 if (err)
3297 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003298
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003299 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02003300 if (!rif) {
3301 err = -ENOMEM;
3302 goto err_rif_alloc;
3303 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003304 rif->mlxsw_sp = mlxsw_sp;
3305 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02003306
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003307 fid = ops->fid_get(rif);
3308 if (IS_ERR(fid)) {
3309 err = PTR_ERR(fid);
3310 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02003311 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003312 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02003313
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003314 if (ops->setup)
3315 ops->setup(rif, params);
3316
3317 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003318 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003319 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003320
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003321 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02003322 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003323 if (err)
3324 goto err_rif_fdb_op;
3325
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003326 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003327 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003328 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01003329 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003330
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003331 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003332
Ido Schimmel4724ba562017-03-10 08:53:39 +01003333err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003334 ops->deconfigure(rif);
3335err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02003336 mlxsw_sp_fid_put(fid);
3337err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003338 kfree(rif);
3339err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02003340err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003341 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003342 return ERR_PTR(err);
3343}
3344
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003345void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003346{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003347 const struct mlxsw_sp_rif_ops *ops = rif->ops;
3348 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02003349 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003350 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003351
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003352 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003353 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02003354
Ido Schimmel69132292017-03-10 08:53:42 +01003355 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003356 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02003357 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003358 mlxsw_sp_rif_counters_free(rif);
3359 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
3360 mlxsw_sp_fid_index(fid), false);
3361 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003362 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003363 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003364 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003365}
3366
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003367static void
3368mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
3369 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
3370{
3371 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
3372
3373 params->vid = mlxsw_sp_port_vlan->vid;
3374 params->lag = mlxsw_sp_port->lagged;
3375 if (params->lag)
3376 params->lag_id = mlxsw_sp_port->lag_id;
3377 else
3378 params->system_port = mlxsw_sp_port->local_port;
3379}
3380
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003381static int
Ido Schimmela1107482017-05-26 08:37:39 +02003382mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003383 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003384{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003385 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02003386 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003387 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003388 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003389 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003390 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003391
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02003392 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003393 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003394 struct mlxsw_sp_rif_params params = {
3395 .dev = l3_dev,
3396 };
3397
3398 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
3399 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003400 if (IS_ERR(rif))
3401 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003402 }
3403
Ido Schimmela1107482017-05-26 08:37:39 +02003404 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003405 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003406 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
3407 if (err)
3408 goto err_fid_port_vid_map;
3409
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003410 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003411 if (err)
3412 goto err_port_vid_learning_set;
3413
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003414 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003415 BR_STATE_FORWARDING);
3416 if (err)
3417 goto err_port_vid_stp_set;
3418
Ido Schimmela1107482017-05-26 08:37:39 +02003419 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003420
Ido Schimmel4724ba562017-03-10 08:53:39 +01003421 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003422
3423err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003424 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003425err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02003426 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3427err_fid_port_vid_map:
3428 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003429 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003430}
3431
Ido Schimmela1107482017-05-26 08:37:39 +02003432void
3433mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003434{
Ido Schimmelce95e152017-05-26 08:37:27 +02003435 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003436 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02003437 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02003438
Ido Schimmela1107482017-05-26 08:37:39 +02003439 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
3440 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02003441
Ido Schimmela1107482017-05-26 08:37:39 +02003442 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003443 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
3444 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02003445 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3446 /* If router port holds the last reference on the rFID, then the
3447 * associated Sub-port RIF will be destroyed.
3448 */
3449 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003450}
3451
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003452static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
3453 struct net_device *port_dev,
3454 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003455{
3456 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02003457 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003458
Ido Schimmelce95e152017-05-26 08:37:27 +02003459 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003460 if (WARN_ON(!mlxsw_sp_port_vlan))
3461 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003462
3463 switch (event) {
3464 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02003465 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003466 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003467 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02003468 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003469 break;
3470 }
3471
3472 return 0;
3473}
3474
3475static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
3476 unsigned long event)
3477{
Jiri Pirko2b94e582017-04-18 16:55:37 +02003478 if (netif_is_bridge_port(port_dev) ||
3479 netif_is_lag_port(port_dev) ||
3480 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003481 return 0;
3482
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003483 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003484}
3485
3486static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
3487 struct net_device *lag_dev,
3488 unsigned long event, u16 vid)
3489{
3490 struct net_device *port_dev;
3491 struct list_head *iter;
3492 int err;
3493
3494 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
3495 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003496 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
3497 port_dev,
3498 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003499 if (err)
3500 return err;
3501 }
3502 }
3503
3504 return 0;
3505}
3506
3507static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
3508 unsigned long event)
3509{
3510 if (netif_is_bridge_port(lag_dev))
3511 return 0;
3512
3513 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
3514}
3515
Ido Schimmel4724ba562017-03-10 08:53:39 +01003516static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01003517 unsigned long event)
3518{
3519 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003520 struct mlxsw_sp_rif_params params = {
3521 .dev = l3_dev,
3522 };
Ido Schimmela1107482017-05-26 08:37:39 +02003523 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003524
3525 switch (event) {
3526 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003527 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
3528 if (IS_ERR(rif))
3529 return PTR_ERR(rif);
3530 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003531 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02003532 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003533 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003534 break;
3535 }
3536
3537 return 0;
3538}
3539
3540static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
3541 unsigned long event)
3542{
3543 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003544 u16 vid = vlan_dev_vlan_id(vlan_dev);
3545
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03003546 if (netif_is_bridge_port(vlan_dev))
3547 return 0;
3548
Ido Schimmel4724ba562017-03-10 08:53:39 +01003549 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003550 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
3551 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003552 else if (netif_is_lag_master(real_dev))
3553 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
3554 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02003555 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02003556 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003557
3558 return 0;
3559}
3560
Ido Schimmelb1e45522017-04-30 19:47:14 +03003561static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
3562 unsigned long event)
3563{
3564 if (mlxsw_sp_port_dev_check(dev))
3565 return mlxsw_sp_inetaddr_port_event(dev, event);
3566 else if (netif_is_lag_master(dev))
3567 return mlxsw_sp_inetaddr_lag_event(dev, event);
3568 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02003569 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03003570 else if (is_vlan_dev(dev))
3571 return mlxsw_sp_inetaddr_vlan_event(dev, event);
3572 else
3573 return 0;
3574}
3575
Ido Schimmel4724ba562017-03-10 08:53:39 +01003576int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
3577 unsigned long event, void *ptr)
3578{
3579 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
3580 struct net_device *dev = ifa->ifa_dev->dev;
3581 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003582 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003583 int err = 0;
3584
3585 mlxsw_sp = mlxsw_sp_lower_get(dev);
3586 if (!mlxsw_sp)
3587 goto out;
3588
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003589 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003590 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003591 goto out;
3592
Ido Schimmelb1e45522017-04-30 19:47:14 +03003593 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003594out:
3595 return notifier_from_errno(err);
3596}
3597
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003598struct mlxsw_sp_inet6addr_event_work {
3599 struct work_struct work;
3600 struct net_device *dev;
3601 unsigned long event;
3602};
3603
3604static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
3605{
3606 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
3607 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
3608 struct net_device *dev = inet6addr_work->dev;
3609 unsigned long event = inet6addr_work->event;
3610 struct mlxsw_sp *mlxsw_sp;
3611 struct mlxsw_sp_rif *rif;
3612
3613 rtnl_lock();
3614 mlxsw_sp = mlxsw_sp_lower_get(dev);
3615 if (!mlxsw_sp)
3616 goto out;
3617
3618 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3619 if (!mlxsw_sp_rif_should_config(rif, dev, event))
3620 goto out;
3621
3622 __mlxsw_sp_inetaddr_event(dev, event);
3623out:
3624 rtnl_unlock();
3625 dev_put(dev);
3626 kfree(inet6addr_work);
3627}
3628
3629/* Called with rcu_read_lock() */
3630int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
3631 unsigned long event, void *ptr)
3632{
3633 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
3634 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
3635 struct net_device *dev = if6->idev->dev;
3636
3637 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
3638 return NOTIFY_DONE;
3639
3640 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
3641 if (!inet6addr_work)
3642 return NOTIFY_BAD;
3643
3644 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
3645 inet6addr_work->dev = dev;
3646 inet6addr_work->event = event;
3647 dev_hold(dev);
3648 mlxsw_core_schedule_work(&inet6addr_work->work);
3649
3650 return NOTIFY_DONE;
3651}
3652
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003653static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01003654 const char *mac, int mtu)
3655{
3656 char ritr_pl[MLXSW_REG_RITR_LEN];
3657 int err;
3658
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003659 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003660 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3661 if (err)
3662 return err;
3663
3664 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
3665 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
3666 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
3667 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3668}
3669
3670int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
3671{
3672 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003673 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003674 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003675 int err;
3676
3677 mlxsw_sp = mlxsw_sp_lower_get(dev);
3678 if (!mlxsw_sp)
3679 return 0;
3680
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003681 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3682 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003683 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02003684 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003685
Ido Schimmela1107482017-05-26 08:37:39 +02003686 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003687 if (err)
3688 return err;
3689
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003690 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
3691 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003692 if (err)
3693 goto err_rif_edit;
3694
Ido Schimmela1107482017-05-26 08:37:39 +02003695 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003696 if (err)
3697 goto err_rif_fdb_op;
3698
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003699 ether_addr_copy(rif->addr, dev->dev_addr);
3700 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003701
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003702 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003703
3704 return 0;
3705
3706err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003707 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003708err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02003709 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003710 return err;
3711}
3712
Ido Schimmelb1e45522017-04-30 19:47:14 +03003713static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
3714 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003715{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003716 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01003717
Ido Schimmelb1e45522017-04-30 19:47:14 +03003718 /* If netdev is already associated with a RIF, then we need to
3719 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01003720 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03003721 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3722 if (rif)
3723 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003724
Ido Schimmelb1e45522017-04-30 19:47:14 +03003725 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003726}
3727
Ido Schimmelb1e45522017-04-30 19:47:14 +03003728static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
3729 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003730{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003731 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01003732
Ido Schimmelb1e45522017-04-30 19:47:14 +03003733 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3734 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003735 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03003736 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003737}
3738
Ido Schimmelb1e45522017-04-30 19:47:14 +03003739int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
3740 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003741{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003742 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
3743 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003744
Ido Schimmelb1e45522017-04-30 19:47:14 +03003745 if (!mlxsw_sp)
3746 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003747
Ido Schimmelb1e45522017-04-30 19:47:14 +03003748 switch (event) {
3749 case NETDEV_PRECHANGEUPPER:
3750 return 0;
3751 case NETDEV_CHANGEUPPER:
3752 if (info->linking)
3753 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
3754 else
3755 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
3756 break;
3757 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003758
Ido Schimmelb1e45522017-04-30 19:47:14 +03003759 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003760}
3761
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003762static struct mlxsw_sp_rif_subport *
3763mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02003764{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003765 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02003766}
3767
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003768static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
3769 const struct mlxsw_sp_rif_params *params)
3770{
3771 struct mlxsw_sp_rif_subport *rif_subport;
3772
3773 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3774 rif_subport->vid = params->vid;
3775 rif_subport->lag = params->lag;
3776 if (params->lag)
3777 rif_subport->lag_id = params->lag_id;
3778 else
3779 rif_subport->system_port = params->system_port;
3780}
3781
3782static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
3783{
3784 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3785 struct mlxsw_sp_rif_subport *rif_subport;
3786 char ritr_pl[MLXSW_REG_RITR_LEN];
3787
3788 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3789 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
3790 rif->rif_index, rif->vr_id, rif->dev->mtu,
3791 rif->dev->dev_addr);
3792 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
3793 rif_subport->lag ? rif_subport->lag_id :
3794 rif_subport->system_port,
3795 rif_subport->vid);
3796
3797 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3798}
3799
3800static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
3801{
3802 return mlxsw_sp_rif_subport_op(rif, true);
3803}
3804
3805static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
3806{
3807 mlxsw_sp_rif_subport_op(rif, false);
3808}
3809
3810static struct mlxsw_sp_fid *
3811mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
3812{
3813 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
3814}
3815
3816static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
3817 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
3818 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
3819 .setup = mlxsw_sp_rif_subport_setup,
3820 .configure = mlxsw_sp_rif_subport_configure,
3821 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
3822 .fid_get = mlxsw_sp_rif_subport_fid_get,
3823};
3824
3825static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
3826 enum mlxsw_reg_ritr_if_type type,
3827 u16 vid_fid, bool enable)
3828{
3829 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3830 char ritr_pl[MLXSW_REG_RITR_LEN];
3831
3832 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
3833 rif->dev->mtu, rif->dev->dev_addr);
3834 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
3835
3836 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3837}
3838
3839static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
3840{
3841 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
3842}
3843
3844static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
3845{
3846 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3847 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3848 int err;
3849
3850 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
3851 if (err)
3852 return err;
3853
Ido Schimmel0d284812017-07-18 10:10:12 +02003854 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3855 mlxsw_sp_router_port(mlxsw_sp), true);
3856 if (err)
3857 goto err_fid_mc_flood_set;
3858
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003859 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3860 mlxsw_sp_router_port(mlxsw_sp), true);
3861 if (err)
3862 goto err_fid_bc_flood_set;
3863
3864 return 0;
3865
3866err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02003867 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3868 mlxsw_sp_router_port(mlxsw_sp), false);
3869err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003870 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3871 return err;
3872}
3873
3874static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
3875{
3876 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3877 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3878
3879 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3880 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02003881 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3882 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003883 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3884}
3885
3886static struct mlxsw_sp_fid *
3887mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
3888{
3889 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
3890
3891 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
3892}
3893
3894static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
3895 .type = MLXSW_SP_RIF_TYPE_VLAN,
3896 .rif_size = sizeof(struct mlxsw_sp_rif),
3897 .configure = mlxsw_sp_rif_vlan_configure,
3898 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
3899 .fid_get = mlxsw_sp_rif_vlan_fid_get,
3900};
3901
3902static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
3903{
3904 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3905 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3906 int err;
3907
3908 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
3909 true);
3910 if (err)
3911 return err;
3912
Ido Schimmel0d284812017-07-18 10:10:12 +02003913 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3914 mlxsw_sp_router_port(mlxsw_sp), true);
3915 if (err)
3916 goto err_fid_mc_flood_set;
3917
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003918 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3919 mlxsw_sp_router_port(mlxsw_sp), true);
3920 if (err)
3921 goto err_fid_bc_flood_set;
3922
3923 return 0;
3924
3925err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02003926 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3927 mlxsw_sp_router_port(mlxsw_sp), false);
3928err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003929 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3930 return err;
3931}
3932
3933static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
3934{
3935 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3936 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3937
3938 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3939 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02003940 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3941 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003942 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3943}
3944
3945static struct mlxsw_sp_fid *
3946mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
3947{
3948 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
3949}
3950
3951static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
3952 .type = MLXSW_SP_RIF_TYPE_FID,
3953 .rif_size = sizeof(struct mlxsw_sp_rif),
3954 .configure = mlxsw_sp_rif_fid_configure,
3955 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
3956 .fid_get = mlxsw_sp_rif_fid_fid_get,
3957};
3958
3959static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
3960 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
3961 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
3962 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
3963};
3964
Ido Schimmel348b8fc2017-05-16 19:38:29 +02003965static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
3966{
3967 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
3968
3969 mlxsw_sp->router->rifs = kcalloc(max_rifs,
3970 sizeof(struct mlxsw_sp_rif *),
3971 GFP_KERNEL);
3972 if (!mlxsw_sp->router->rifs)
3973 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003974
3975 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
3976
Ido Schimmel348b8fc2017-05-16 19:38:29 +02003977 return 0;
3978}
3979
3980static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
3981{
3982 int i;
3983
3984 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
3985 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
3986
3987 kfree(mlxsw_sp->router->rifs);
3988}
3989
Ido Schimmelc3852ef2016-12-03 16:45:07 +01003990static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
3991{
Ido Schimmel7e39d112017-05-16 19:38:28 +02003992 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01003993
3994 /* Flush pending FIB notifications and then flush the device's
3995 * table before requesting another dump. The FIB notification
3996 * block is unregistered, so no need to take RTNL.
3997 */
3998 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02003999 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4000 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004001}
4002
Ido Schimmel4724ba562017-03-10 08:53:39 +01004003static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4004{
4005 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4006 u64 max_rifs;
4007 int err;
4008
4009 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
4010 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004011 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004012
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004013 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004014 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
4015 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4016 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004017 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004018 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004019}
4020
4021static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4022{
4023 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004024
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004025 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004026 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004027}
4028
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004029int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4030{
Ido Schimmel9011b672017-05-16 19:38:25 +02004031 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004032 int err;
4033
Ido Schimmel9011b672017-05-16 19:38:25 +02004034 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4035 if (!router)
4036 return -ENOMEM;
4037 mlxsw_sp->router = router;
4038 router->mlxsw_sp = mlxsw_sp;
4039
4040 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004041 err = __mlxsw_sp_router_init(mlxsw_sp);
4042 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004043 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004044
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004045 err = mlxsw_sp_rifs_init(mlxsw_sp);
4046 if (err)
4047 goto err_rifs_init;
4048
Ido Schimmel9011b672017-05-16 19:38:25 +02004049 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004050 &mlxsw_sp_nexthop_ht_params);
4051 if (err)
4052 goto err_nexthop_ht_init;
4053
Ido Schimmel9011b672017-05-16 19:38:25 +02004054 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004055 &mlxsw_sp_nexthop_group_ht_params);
4056 if (err)
4057 goto err_nexthop_group_ht_init;
4058
Ido Schimmel8494ab02017-03-24 08:02:47 +01004059 err = mlxsw_sp_lpm_init(mlxsw_sp);
4060 if (err)
4061 goto err_lpm_init;
4062
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004063 err = mlxsw_sp_vrs_init(mlxsw_sp);
4064 if (err)
4065 goto err_vrs_init;
4066
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004067 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004068 if (err)
4069 goto err_neigh_init;
4070
Ido Schimmel7e39d112017-05-16 19:38:28 +02004071 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4072 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004073 mlxsw_sp_router_fib_dump_flush);
4074 if (err)
4075 goto err_register_fib_notifier;
4076
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004077 return 0;
4078
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004079err_register_fib_notifier:
4080 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004081err_neigh_init:
4082 mlxsw_sp_vrs_fini(mlxsw_sp);
4083err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004084 mlxsw_sp_lpm_fini(mlxsw_sp);
4085err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004086 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004087err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004088 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004089err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004090 mlxsw_sp_rifs_fini(mlxsw_sp);
4091err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004092 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004093err_router_init:
4094 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004095 return err;
4096}
4097
4098void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4099{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004100 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004101 mlxsw_sp_neigh_fini(mlxsw_sp);
4102 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01004103 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004104 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4105 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004106 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004107 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004108 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004109}