blob: 40aecbc116ac7ee200728d52e1d949ac1006985c [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
1656mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1657 struct mlxsw_sp_nexthop_group *nh_grp)
1658{
1659 struct mlxsw_sp_nexthop *nh;
1660 bool offload_change = false;
1661 u32 adj_index;
1662 u16 ecmp_size = 0;
1663 bool old_adj_index_valid;
1664 u32 old_adj_index;
1665 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001666 int i;
1667 int err;
1668
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001669 if (!nh_grp->gateway) {
1670 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1671 return;
1672 }
1673
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001674 for (i = 0; i < nh_grp->count; i++) {
1675 nh = &nh_grp->nexthops[i];
1676
Petr Machata56b8a9e2017-07-31 09:27:29 +02001677 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001678 offload_change = true;
1679 if (nh->should_offload)
1680 nh->update = 1;
1681 }
1682 if (nh->should_offload)
1683 ecmp_size++;
1684 }
1685 if (!offload_change) {
1686 /* Nothing was added or removed, so no need to reallocate. Just
1687 * update MAC on existing adjacency indexes.
1688 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01001689 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1690 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001691 if (err) {
1692 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1693 goto set_trap;
1694 }
1695 return;
1696 }
1697 if (!ecmp_size)
1698 /* No neigh of this group is connected so we just set
1699 * the trap and let everthing flow through kernel.
1700 */
1701 goto set_trap;
1702
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01001703 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1704 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001705 /* We ran out of KVD linear space, just set the
1706 * trap and let everything flow through kernel.
1707 */
1708 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1709 goto set_trap;
1710 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001711 old_adj_index_valid = nh_grp->adj_index_valid;
1712 old_adj_index = nh_grp->adj_index;
1713 old_ecmp_size = nh_grp->ecmp_size;
1714 nh_grp->adj_index_valid = 1;
1715 nh_grp->adj_index = adj_index;
1716 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01001717 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001718 if (err) {
1719 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1720 goto set_trap;
1721 }
1722
1723 if (!old_adj_index_valid) {
1724 /* The trap was set for fib entries, so we have to call
1725 * fib entry update to unset it and use adjacency index.
1726 */
1727 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1728 if (err) {
1729 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1730 goto set_trap;
1731 }
1732 return;
1733 }
1734
1735 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1736 old_adj_index, old_ecmp_size);
1737 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1738 if (err) {
1739 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1740 goto set_trap;
1741 }
1742 return;
1743
1744set_trap:
1745 old_adj_index_valid = nh_grp->adj_index_valid;
1746 nh_grp->adj_index_valid = 0;
1747 for (i = 0; i < nh_grp->count; i++) {
1748 nh = &nh_grp->nexthops[i];
1749 nh->offloaded = 0;
1750 }
1751 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1752 if (err)
1753 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1754 if (old_adj_index_valid)
1755 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1756}
1757
1758static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1759 bool removing)
1760{
Petr Machata213666a2017-07-31 09:27:30 +02001761 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001762 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02001763 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001764 nh->should_offload = 0;
1765 nh->update = 1;
1766}
1767
1768static void
1769mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1770 struct mlxsw_sp_neigh_entry *neigh_entry,
1771 bool removing)
1772{
1773 struct mlxsw_sp_nexthop *nh;
1774
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001775 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1776 neigh_list_node) {
1777 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1778 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1779 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001780}
1781
Ido Schimmel9665b742017-02-08 11:16:42 +01001782static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001783 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001784{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001785 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001786 return;
1787
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001788 nh->rif = rif;
1789 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001790}
1791
1792static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1793{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001794 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001795 return;
1796
1797 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001798 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01001799}
1800
Ido Schimmela8c97012017-02-08 11:16:35 +01001801static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1802 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001803{
1804 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001805 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001806 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001807 int err;
1808
Ido Schimmelad178c82017-02-08 11:16:40 +01001809 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01001810 return 0;
1811
Jiri Pirko33b13412016-11-10 12:31:04 +01001812 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02001813 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01001814 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01001815 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01001816 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001817 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01001818 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001819 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1820 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01001821 if (IS_ERR(n))
1822 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001823 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01001824 }
1825 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1826 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001827 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1828 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001829 err = -EINVAL;
1830 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001831 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001832 }
Yotam Gigib2157142016-07-05 11:27:51 +02001833
1834 /* If that is the first nexthop connected to that neigh, add to
1835 * nexthop_neighs_list
1836 */
1837 if (list_empty(&neigh_entry->nexthop_list))
1838 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02001839 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02001840
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001841 nh->neigh_entry = neigh_entry;
1842 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1843 read_lock_bh(&n->lock);
1844 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001845 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001846 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01001847 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001848
1849 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001850
1851err_neigh_entry_create:
1852 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001853 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001854}
1855
Ido Schimmela8c97012017-02-08 11:16:35 +01001856static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1857 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001858{
1859 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01001860 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001861
Ido Schimmelb8399a12017-02-08 11:16:33 +01001862 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01001863 return;
1864 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01001865
Ido Schimmel58312122016-12-23 09:32:50 +01001866 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001867 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01001868 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02001869
1870 /* If that is the last nexthop connected to that neigh, remove from
1871 * nexthop_neighs_list
1872 */
Ido Schimmele58be792017-02-08 11:16:28 +01001873 if (list_empty(&neigh_entry->nexthop_list))
1874 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02001875
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001876 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1877 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1878
1879 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01001880}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001881
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001882static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1883 struct mlxsw_sp_nexthop_group *nh_grp,
1884 struct mlxsw_sp_nexthop *nh,
1885 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001886{
1887 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001888 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001889 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01001890 int err;
1891
1892 nh->nh_grp = nh_grp;
1893 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001894 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01001895 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1896 if (err)
1897 return err;
1898
Ido Schimmel97989ee2017-03-10 08:53:38 +01001899 if (!dev)
1900 return 0;
1901
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01001902 in_dev = __in_dev_get_rtnl(dev);
1903 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1904 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1905 return 0;
1906
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001907 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1908 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01001909 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001910 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01001911
1912 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1913 if (err)
1914 goto err_nexthop_neigh_init;
1915
1916 return 0;
1917
1918err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02001919 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01001920 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1921 return err;
1922}
1923
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001924static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
1925 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01001926{
1927 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001928 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001929 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001930}
1931
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001932static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
1933 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01001934{
1935 struct mlxsw_sp_nexthop_key key;
1936 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001937 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01001938
Ido Schimmel9011b672017-05-16 19:38:25 +02001939 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01001940 return;
1941
1942 key.fib_nh = fib_nh;
1943 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1944 if (WARN_ON_ONCE(!nh))
1945 return;
1946
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001947 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1948 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01001949 return;
1950
1951 switch (event) {
1952 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001953 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01001954 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1955 break;
1956 case FIB_EVENT_NH_DEL:
1957 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01001958 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01001959 break;
1960 }
1961
1962 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1963}
1964
Ido Schimmel9665b742017-02-08 11:16:42 +01001965static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001966 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001967{
1968 struct mlxsw_sp_nexthop *nh, *tmp;
1969
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001970 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01001971 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
1972 mlxsw_sp_nexthop_rif_fini(nh);
1973 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1974 }
1975}
1976
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001977static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02001978mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001979{
1980 struct mlxsw_sp_nexthop_group *nh_grp;
1981 struct mlxsw_sp_nexthop *nh;
1982 struct fib_nh *fib_nh;
1983 size_t alloc_size;
1984 int i;
1985 int err;
1986
1987 alloc_size = sizeof(*nh_grp) +
1988 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
1989 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
1990 if (!nh_grp)
1991 return ERR_PTR(-ENOMEM);
1992 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001993 nh_grp->neigh_tbl = &arp_tbl;
1994
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001995 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001996 nh_grp->count = fi->fib_nhs;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001997 nh_grp->key.fi = fi;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02001998 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001999 for (i = 0; i < nh_grp->count; i++) {
2000 nh = &nh_grp->nexthops[i];
2001 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002002 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002003 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002004 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002005 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002006 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2007 if (err)
2008 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002009 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2010 return nh_grp;
2011
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002012err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002013err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002014 for (i--; i >= 0; i--) {
2015 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002016 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002017 }
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002018 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002019 kfree(nh_grp);
2020 return ERR_PTR(err);
2021}
2022
2023static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002024mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2025 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002026{
2027 struct mlxsw_sp_nexthop *nh;
2028 int i;
2029
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002030 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 for (i = 0; i < nh_grp->count; i++) {
2032 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002033 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002034 }
Ido Schimmel58312122016-12-23 09:32:50 +01002035 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2036 WARN_ON_ONCE(nh_grp->adj_index_valid);
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002037 fib_info_put(nh_grp->key.fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002038 kfree(nh_grp);
2039}
2040
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002041static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2042 struct mlxsw_sp_fib_entry *fib_entry,
2043 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002044{
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002045 struct mlxsw_sp_nexthop_group_key key;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002046 struct mlxsw_sp_nexthop_group *nh_grp;
2047
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002048 key.fi = fi;
2049 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002050 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002051 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002052 if (IS_ERR(nh_grp))
2053 return PTR_ERR(nh_grp);
2054 }
2055 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2056 fib_entry->nh_group = nh_grp;
2057 return 0;
2058}
2059
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002060static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2061 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002062{
2063 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2064
2065 list_del(&fib_entry->nexthop_group_node);
2066 if (!list_empty(&nh_grp->fib_list))
2067 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002068 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002069}
2070
Ido Schimmel013b20f2017-02-08 11:16:36 +01002071static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002072mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2073{
2074 struct mlxsw_sp_fib4_entry *fib4_entry;
2075
2076 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2077 common);
2078 return !fib4_entry->tos;
2079}
2080
2081static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002082mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2083{
2084 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2085
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002086 switch (fib_entry->fib_node->fib->proto) {
2087 case MLXSW_SP_L3_PROTO_IPV4:
2088 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2089 return false;
2090 break;
2091 case MLXSW_SP_L3_PROTO_IPV6:
2092 break;
2093 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002094
Ido Schimmel013b20f2017-02-08 11:16:36 +01002095 switch (fib_entry->type) {
2096 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2097 return !!nh_group->adj_index_valid;
2098 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002099 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002100 default:
2101 return false;
2102 }
2103}
2104
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002105static void
2106mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2107{
2108 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2109 int i;
2110
2111 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2112 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2113 return;
2114 }
2115
2116 for (i = 0; i < nh_grp->count; i++) {
2117 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2118
2119 if (nh->offloaded)
2120 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2121 else
2122 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2123 }
2124}
2125
2126static void
2127mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2128{
2129 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2130 int i;
2131
2132 for (i = 0; i < nh_grp->count; i++) {
2133 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2134
2135 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2136 }
2137}
2138
Ido Schimmel013b20f2017-02-08 11:16:36 +01002139static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2140{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002141 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002142 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002143 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002144 break;
2145 case MLXSW_SP_L3_PROTO_IPV6:
2146 WARN_ON_ONCE(1);
2147 }
2148}
2149
2150static void
2151mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2152{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002153 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002154 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002155 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002156 break;
2157 case MLXSW_SP_L3_PROTO_IPV6:
2158 WARN_ON_ONCE(1);
2159 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002160}
2161
2162static void
2163mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2164 enum mlxsw_reg_ralue_op op, int err)
2165{
2166 switch (op) {
2167 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002168 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2169 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2170 if (err)
2171 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002172 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002173 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002174 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002175 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2176 return;
2177 default:
2178 return;
2179 }
2180}
2181
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002182static void
2183mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2184 const struct mlxsw_sp_fib_entry *fib_entry,
2185 enum mlxsw_reg_ralue_op op)
2186{
2187 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2188 enum mlxsw_reg_ralxx_protocol proto;
2189 u32 *p_dip;
2190
2191 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2192
2193 switch (fib->proto) {
2194 case MLXSW_SP_L3_PROTO_IPV4:
2195 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2196 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2197 fib_entry->fib_node->key.prefix_len,
2198 *p_dip);
2199 break;
2200 case MLXSW_SP_L3_PROTO_IPV6:
2201 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2202 fib_entry->fib_node->key.prefix_len,
2203 fib_entry->fib_node->key.addr);
2204 break;
2205 }
2206}
2207
2208static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2209 struct mlxsw_sp_fib_entry *fib_entry,
2210 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002211{
2212 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002213 enum mlxsw_reg_ralue_trap_action trap_action;
2214 u16 trap_id = 0;
2215 u32 adjacency_index = 0;
2216 u16 ecmp_size = 0;
2217
2218 /* In case the nexthop group adjacency index is valid, use it
2219 * with provided ECMP size. Otherwise, setup trap and pass
2220 * traffic to kernel.
2221 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002222 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002223 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2224 adjacency_index = fib_entry->nh_group->adj_index;
2225 ecmp_size = fib_entry->nh_group->ecmp_size;
2226 } else {
2227 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2228 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2229 }
2230
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002231 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002232 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2233 adjacency_index, ecmp_size);
2234 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2235}
2236
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002237static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2238 struct mlxsw_sp_fib_entry *fib_entry,
2239 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002240{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002241 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002242 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002243 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002244 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002245 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002246
2247 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2248 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002249 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002250 } else {
2251 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2252 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2253 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002254
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002255 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002256 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2257 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002258 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2259}
2260
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002261static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2262 struct mlxsw_sp_fib_entry *fib_entry,
2263 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002264{
2265 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002266
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002267 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002268 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2269 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2270}
2271
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002272static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2273 struct mlxsw_sp_fib_entry *fib_entry,
2274 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002275{
2276 switch (fib_entry->type) {
2277 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002278 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002279 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002280 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002281 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002282 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002283 }
2284 return -EINVAL;
2285}
2286
2287static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2288 struct mlxsw_sp_fib_entry *fib_entry,
2289 enum mlxsw_reg_ralue_op op)
2290{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002291 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002292
Ido Schimmel013b20f2017-02-08 11:16:36 +01002293 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002294
Ido Schimmel013b20f2017-02-08 11:16:36 +01002295 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002296}
2297
2298static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2299 struct mlxsw_sp_fib_entry *fib_entry)
2300{
Jiri Pirko7146da32016-09-01 10:37:41 +02002301 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2302 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002303}
2304
2305static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2306 struct mlxsw_sp_fib_entry *fib_entry)
2307{
2308 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2309 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2310}
2311
Jiri Pirko61c503f2016-07-04 08:23:11 +02002312static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002313mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2314 const struct fib_entry_notifier_info *fen_info,
2315 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002316{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002317 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002318
Ido Schimmel97989ee2017-03-10 08:53:38 +01002319 switch (fen_info->type) {
2320 case RTN_BROADCAST: /* fall through */
2321 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002322 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2323 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002324 case RTN_UNREACHABLE: /* fall through */
2325 case RTN_BLACKHOLE: /* fall through */
2326 case RTN_PROHIBIT:
2327 /* Packets hitting these routes need to be trapped, but
2328 * can do so with a lower priority than packets directed
2329 * at the host, so use action type local instead of trap.
2330 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002331 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002332 return 0;
2333 case RTN_UNICAST:
2334 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2335 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2336 else
2337 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2338 return 0;
2339 default:
2340 return -EINVAL;
2341 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002342}
2343
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002344static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002345mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2346 struct mlxsw_sp_fib_node *fib_node,
2347 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002348{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002349 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002350 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002351 int err;
2352
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002353 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2354 if (!fib4_entry)
2355 return ERR_PTR(-ENOMEM);
2356 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002357
2358 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2359 if (err)
2360 goto err_fib4_entry_type_set;
2361
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002362 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002363 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002364 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002365
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002366 fib4_entry->prio = fen_info->fi->fib_priority;
2367 fib4_entry->tb_id = fen_info->tb_id;
2368 fib4_entry->type = fen_info->type;
2369 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002370
2371 fib_entry->fib_node = fib_node;
2372
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002373 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002374
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002375err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002376err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002377 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002378 return ERR_PTR(err);
2379}
2380
2381static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002382 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002383{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002384 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002385 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002386}
2387
2388static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002389mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2390 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002391
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002392static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002393mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2394 const struct fib_entry_notifier_info *fen_info)
2395{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002396 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002397 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002398 struct mlxsw_sp_fib *fib;
2399 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002400
Ido Schimmel160e22a2017-07-18 10:10:20 +02002401 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2402 if (!vr)
2403 return NULL;
2404 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2405
2406 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2407 sizeof(fen_info->dst),
2408 fen_info->dst_len);
2409 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002410 return NULL;
2411
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002412 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2413 if (fib4_entry->tb_id == fen_info->tb_id &&
2414 fib4_entry->tos == fen_info->tos &&
2415 fib4_entry->type == fen_info->type &&
2416 fib4_entry->common.nh_group->key.fi == fen_info->fi) {
2417 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002418 }
2419 }
2420
2421 return NULL;
2422}
2423
2424static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2425 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2426 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2427 .key_len = sizeof(struct mlxsw_sp_fib_key),
2428 .automatic_shrinking = true,
2429};
2430
2431static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2432 struct mlxsw_sp_fib_node *fib_node)
2433{
2434 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2435 mlxsw_sp_fib_ht_params);
2436}
2437
2438static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2439 struct mlxsw_sp_fib_node *fib_node)
2440{
2441 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2442 mlxsw_sp_fib_ht_params);
2443}
2444
2445static struct mlxsw_sp_fib_node *
2446mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2447 size_t addr_len, unsigned char prefix_len)
2448{
2449 struct mlxsw_sp_fib_key key;
2450
2451 memset(&key, 0, sizeof(key));
2452 memcpy(key.addr, addr, addr_len);
2453 key.prefix_len = prefix_len;
2454 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2455}
2456
2457static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002458mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002459 size_t addr_len, unsigned char prefix_len)
2460{
2461 struct mlxsw_sp_fib_node *fib_node;
2462
2463 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2464 if (!fib_node)
2465 return NULL;
2466
2467 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002468 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002469 memcpy(fib_node->key.addr, addr, addr_len);
2470 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002471
2472 return fib_node;
2473}
2474
2475static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2476{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002477 list_del(&fib_node->list);
2478 WARN_ON(!list_empty(&fib_node->entry_list));
2479 kfree(fib_node);
2480}
2481
2482static bool
2483mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2484 const struct mlxsw_sp_fib_entry *fib_entry)
2485{
2486 return list_first_entry(&fib_node->entry_list,
2487 struct mlxsw_sp_fib_entry, list) == fib_entry;
2488}
2489
2490static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2491{
2492 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002493 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002494
2495 if (fib->prefix_ref_count[prefix_len]++ == 0)
2496 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2497}
2498
2499static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2500{
2501 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002502 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002503
2504 if (--fib->prefix_ref_count[prefix_len] == 0)
2505 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
2506}
2507
Ido Schimmel76610eb2017-03-10 08:53:41 +01002508static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2509 struct mlxsw_sp_fib_node *fib_node,
2510 struct mlxsw_sp_fib *fib)
2511{
2512 struct mlxsw_sp_prefix_usage req_prefix_usage;
2513 struct mlxsw_sp_lpm_tree *lpm_tree;
2514 int err;
2515
2516 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2517 if (err)
2518 return err;
2519 fib_node->fib = fib;
2520
2521 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
2522 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2523
2524 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2525 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
2526 &req_prefix_usage);
2527 if (err)
2528 goto err_tree_check;
2529 } else {
2530 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2531 fib->proto);
2532 if (IS_ERR(lpm_tree))
2533 return PTR_ERR(lpm_tree);
2534 fib->lpm_tree = lpm_tree;
2535 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
2536 if (err)
2537 goto err_tree_bind;
2538 }
2539
2540 mlxsw_sp_fib_node_prefix_inc(fib_node);
2541
2542 return 0;
2543
2544err_tree_bind:
2545 fib->lpm_tree = NULL;
2546 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2547err_tree_check:
2548 fib_node->fib = NULL;
2549 mlxsw_sp_fib_node_remove(fib, fib_node);
2550 return err;
2551}
2552
2553static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2554 struct mlxsw_sp_fib_node *fib_node)
2555{
2556 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
2557 struct mlxsw_sp_fib *fib = fib_node->fib;
2558
2559 mlxsw_sp_fib_node_prefix_dec(fib_node);
2560
2561 if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2562 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2563 fib->lpm_tree = NULL;
2564 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2565 } else {
2566 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
2567 }
2568
2569 fib_node->fib = NULL;
2570 mlxsw_sp_fib_node_remove(fib, fib_node);
2571}
2572
Ido Schimmel9aecce12017-02-09 10:28:42 +01002573static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002574mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2575 size_t addr_len, unsigned char prefix_len,
2576 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002577{
2578 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002579 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02002580 struct mlxsw_sp_vr *vr;
2581 int err;
2582
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002583 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02002584 if (IS_ERR(vr))
2585 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002586 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02002587
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002588 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002589 if (fib_node)
2590 return fib_node;
2591
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002592 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002593 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02002594 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002595 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02002596 }
Jiri Pirko5b004412016-09-01 10:37:40 +02002597
Ido Schimmel76610eb2017-03-10 08:53:41 +01002598 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2599 if (err)
2600 goto err_fib_node_init;
2601
Ido Schimmel9aecce12017-02-09 10:28:42 +01002602 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002603
Ido Schimmel76610eb2017-03-10 08:53:41 +01002604err_fib_node_init:
2605 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002606err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01002607 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002608 return ERR_PTR(err);
2609}
2610
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002611static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2612 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02002613{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002614 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02002615
Ido Schimmel9aecce12017-02-09 10:28:42 +01002616 if (!list_empty(&fib_node->entry_list))
2617 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002618 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002619 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002620 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02002621}
2622
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002623static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002624mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002625 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002626{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002627 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002628
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002629 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2630 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002631 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002632 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002633 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002634 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002635 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002636 if (fib4_entry->prio >= new4_entry->prio ||
2637 fib4_entry->tos < new4_entry->tos)
2638 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002639 }
2640
2641 return NULL;
2642}
2643
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002644static int
2645mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2646 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002647{
2648 struct mlxsw_sp_fib_node *fib_node;
2649
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002650 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01002651 return -EINVAL;
2652
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002653 fib_node = fib4_entry->common.fib_node;
2654 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2655 common.list) {
2656 if (fib4_entry->tb_id != new4_entry->tb_id ||
2657 fib4_entry->tos != new4_entry->tos ||
2658 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01002659 break;
2660 }
2661
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002662 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01002663 return 0;
2664}
2665
Ido Schimmel9aecce12017-02-09 10:28:42 +01002666static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02002667mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002668 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002669{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002670 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002671 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002672
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002673 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002674
Ido Schimmel4283bce2017-02-09 10:28:43 +01002675 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002676 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2677 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002678 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01002679
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002680 /* Insert new entry before replaced one, so that we can later
2681 * remove the second.
2682 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002683 if (fib4_entry) {
2684 list_add_tail(&new4_entry->common.list,
2685 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002686 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002687 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002688
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002689 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2690 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002691 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002692 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002693 }
2694
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002695 if (fib4_entry)
2696 list_add(&new4_entry->common.list,
2697 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002698 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002699 list_add(&new4_entry->common.list,
2700 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002701 }
2702
2703 return 0;
2704}
2705
2706static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002707mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002708{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002709 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002710}
2711
Ido Schimmel80c238f2017-07-18 10:10:29 +02002712static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2713 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002714{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002715 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2716
Ido Schimmel9aecce12017-02-09 10:28:42 +01002717 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2718 return 0;
2719
2720 /* To prevent packet loss, overwrite the previously offloaded
2721 * entry.
2722 */
2723 if (!list_is_singular(&fib_node->entry_list)) {
2724 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2725 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2726
2727 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2728 }
2729
2730 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2731}
2732
Ido Schimmel80c238f2017-07-18 10:10:29 +02002733static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2734 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002735{
Ido Schimmel9efbee62017-07-18 10:10:28 +02002736 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2737
Ido Schimmel9aecce12017-02-09 10:28:42 +01002738 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2739 return;
2740
2741 /* Promote the next entry by overwriting the deleted entry */
2742 if (!list_is_singular(&fib_node->entry_list)) {
2743 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2744 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2745
2746 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2747 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2748 return;
2749 }
2750
2751 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2752}
2753
2754static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002755 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002756 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002757{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002758 int err;
2759
Ido Schimmel9efbee62017-07-18 10:10:28 +02002760 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002761 if (err)
2762 return err;
2763
Ido Schimmel80c238f2017-07-18 10:10:29 +02002764 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002765 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02002766 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002767
Ido Schimmel9aecce12017-02-09 10:28:42 +01002768 return 0;
2769
Ido Schimmel80c238f2017-07-18 10:10:29 +02002770err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002771 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002772 return err;
2773}
2774
2775static void
2776mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002777 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002778{
Ido Schimmel80c238f2017-07-18 10:10:29 +02002779 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002780 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002781}
2782
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002783static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002784 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002785 bool replace)
2786{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002787 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2788 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002789
2790 if (!replace)
2791 return;
2792
2793 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002794 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002795
2796 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2797 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002798 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002799}
2800
Ido Schimmel9aecce12017-02-09 10:28:42 +01002801static int
2802mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01002803 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002804 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002805{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002806 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002807 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002808 int err;
2809
Ido Schimmel9011b672017-05-16 19:38:25 +02002810 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002811 return 0;
2812
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002813 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
2814 &fen_info->dst, sizeof(fen_info->dst),
2815 fen_info->dst_len,
2816 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002817 if (IS_ERR(fib_node)) {
2818 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2819 return PTR_ERR(fib_node);
2820 }
2821
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002822 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2823 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002824 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002825 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002826 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002827 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002828
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002829 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002830 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002831 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002832 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2833 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002834 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002835
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002836 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01002837
Jiri Pirko61c503f2016-07-04 08:23:11 +02002838 return 0;
2839
Ido Schimmel9aecce12017-02-09 10:28:42 +01002840err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002841 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002842err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002843 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002844 return err;
2845}
2846
Jiri Pirko37956d72016-10-20 16:05:43 +02002847static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2848 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002849{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002850 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002851 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002852
Ido Schimmel9011b672017-05-16 19:38:25 +02002853 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02002854 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002855
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002856 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2857 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02002858 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002859 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02002860
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002861 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2862 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002863 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002864}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002865
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002866static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
2867 enum mlxsw_reg_ralxx_protocol proto,
2868 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002869{
2870 char ralta_pl[MLXSW_REG_RALTA_LEN];
2871 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002872 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002873
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002874 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002875 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
2876 if (err)
2877 return err;
2878
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002879 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002880 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
2881 if (err)
2882 return err;
2883
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002884 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02002885 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002886 char raltb_pl[MLXSW_REG_RALTB_LEN];
2887 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002888
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002889 if (!mlxsw_sp_vr_is_used(vr))
2890 continue;
2891
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002892 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002893 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
2894 raltb_pl);
2895 if (err)
2896 return err;
2897
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002898 mlxsw_reg_ralue_pack(ralue_pl, proto,
2899 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01002900 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2901 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
2902 ralue_pl);
2903 if (err)
2904 return err;
2905 }
2906
2907 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002908}
2909
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002910static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
2911{
2912 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
2913 int err;
2914
2915 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2916 MLXSW_SP_LPM_TREE_MIN);
2917 if (err)
2918 return err;
2919
2920 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
2921 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2922 MLXSW_SP_LPM_TREE_MIN + 1);
2923}
2924
Ido Schimmel9aecce12017-02-09 10:28:42 +01002925static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
2926 struct mlxsw_sp_fib_node *fib_node)
2927{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002928 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002929
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002930 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
2931 common.list) {
2932 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002933
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002934 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2935 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02002936 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002937 /* Break when entry list is empty and node was freed.
2938 * Otherwise, we'll access freed memory in the next
2939 * iteration.
2940 */
2941 if (do_break)
2942 break;
2943 }
2944}
2945
2946static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
2947 struct mlxsw_sp_fib_node *fib_node)
2948{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002949 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01002950 case MLXSW_SP_L3_PROTO_IPV4:
2951 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
2952 break;
2953 case MLXSW_SP_L3_PROTO_IPV6:
2954 WARN_ON_ONCE(1);
2955 break;
2956 }
2957}
2958
Ido Schimmel76610eb2017-03-10 08:53:41 +01002959static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
2960 struct mlxsw_sp_vr *vr,
2961 enum mlxsw_sp_l3proto proto)
2962{
2963 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
2964 struct mlxsw_sp_fib_node *fib_node, *tmp;
2965
2966 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
2967 bool do_break = &tmp->list == &fib->node_list;
2968
2969 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
2970 if (do_break)
2971 break;
2972 }
2973}
2974
Ido Schimmelac571de2016-11-14 11:26:32 +01002975static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002976{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002977 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002978
Jiri Pirkoc1a38312016-10-21 16:07:23 +02002979 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02002980 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01002981
Ido Schimmel76610eb2017-03-10 08:53:41 +01002982 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002983 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002984 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02002985
2986 /* If virtual router was only used for IPv4, then it's no
2987 * longer used.
2988 */
2989 if (!mlxsw_sp_vr_is_used(vr))
2990 continue;
2991 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002992 }
Ido Schimmelac571de2016-11-14 11:26:32 +01002993}
2994
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02002995static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01002996{
2997 int err;
2998
Ido Schimmel9011b672017-05-16 19:38:25 +02002999 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01003000 return;
3001 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 +01003002 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02003003 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003004 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3005 if (err)
3006 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3007}
3008
Ido Schimmel30572242016-12-03 16:45:01 +01003009struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01003010 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01003011 union {
3012 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003013 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01003014 struct fib_nh_notifier_info fnh_info;
3015 };
Ido Schimmel30572242016-12-03 16:45:01 +01003016 struct mlxsw_sp *mlxsw_sp;
3017 unsigned long event;
3018};
3019
3020static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003021{
Ido Schimmel30572242016-12-03 16:45:01 +01003022 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01003023 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01003024 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003025 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003026 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003027 int err;
3028
Ido Schimmel30572242016-12-03 16:45:01 +01003029 /* Protect internal structures from changes */
3030 rtnl_lock();
3031 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003032 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003033 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003034 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003035 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003036 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3037 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003038 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003039 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003040 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01003041 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003042 break;
3043 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01003044 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3045 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003046 break;
3047 case FIB_EVENT_RULE_ADD: /* fall through */
3048 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003049 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01003050 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02003051 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003052 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003053 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003054 case FIB_EVENT_NH_ADD: /* fall through */
3055 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003056 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3057 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01003058 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3059 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003060 }
Ido Schimmel30572242016-12-03 16:45:01 +01003061 rtnl_unlock();
3062 kfree(fib_work);
3063}
3064
3065/* Called with rcu_read_lock() */
3066static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3067 unsigned long event, void *ptr)
3068{
Ido Schimmel30572242016-12-03 16:45:01 +01003069 struct mlxsw_sp_fib_event_work *fib_work;
3070 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02003071 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01003072
3073 if (!net_eq(info->net, &init_net))
3074 return NOTIFY_DONE;
3075
3076 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3077 if (WARN_ON(!fib_work))
3078 return NOTIFY_BAD;
3079
Ido Schimmela0e47612017-02-06 16:20:10 +01003080 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
Ido Schimmel7e39d112017-05-16 19:38:28 +02003081 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3082 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01003083 fib_work->event = event;
3084
3085 switch (event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003086 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01003087 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Ido Schimmel30572242016-12-03 16:45:01 +01003088 case FIB_EVENT_ENTRY_ADD: /* fall through */
3089 case FIB_EVENT_ENTRY_DEL:
3090 memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
3091 /* Take referece on fib_info to prevent it from being
3092 * freed while work is queued. Release it afterwards.
3093 */
3094 fib_info_hold(fib_work->fen_info.fi);
3095 break;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01003096 case FIB_EVENT_RULE_ADD: /* fall through */
3097 case FIB_EVENT_RULE_DEL:
3098 memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info));
3099 fib_rule_get(fib_work->fr_info.rule);
3100 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01003101 case FIB_EVENT_NH_ADD: /* fall through */
3102 case FIB_EVENT_NH_DEL:
3103 memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
3104 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3105 break;
Ido Schimmel30572242016-12-03 16:45:01 +01003106 }
3107
Ido Schimmela0e47612017-02-06 16:20:10 +01003108 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01003109
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003110 return NOTIFY_DONE;
3111}
3112
Ido Schimmel4724ba562017-03-10 08:53:39 +01003113static struct mlxsw_sp_rif *
3114mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
3115 const struct net_device *dev)
3116{
3117 int i;
3118
3119 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003120 if (mlxsw_sp->router->rifs[i] &&
3121 mlxsw_sp->router->rifs[i]->dev == dev)
3122 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01003123
3124 return NULL;
3125}
3126
3127static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
3128{
3129 char ritr_pl[MLXSW_REG_RITR_LEN];
3130 int err;
3131
3132 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3133 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3134 if (WARN_ON_ONCE(err))
3135 return err;
3136
3137 mlxsw_reg_ritr_enable_set(ritr_pl, false);
3138 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3139}
3140
3141static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003142 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003143{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003144 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
3145 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
3146 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003147}
3148
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003149static bool
3150mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
3151 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003152{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003153 struct inet6_dev *inet6_dev;
3154 bool addr_list_empty = true;
3155 struct in_device *idev;
3156
Ido Schimmel4724ba562017-03-10 08:53:39 +01003157 switch (event) {
3158 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02003159 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003160 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003161 idev = __in_dev_get_rtnl(dev);
3162 if (idev && idev->ifa_list)
3163 addr_list_empty = false;
3164
3165 inet6_dev = __in6_dev_get(dev);
3166 if (addr_list_empty && inet6_dev &&
3167 !list_empty(&inet6_dev->addr_list))
3168 addr_list_empty = false;
3169
3170 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003171 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003172 return true;
3173 /* It is possible we already removed the RIF ourselves
3174 * if it was assigned to a netdev that is now a bridge
3175 * or LAG slave.
3176 */
3177 return false;
3178 }
3179
3180 return false;
3181}
3182
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003183static enum mlxsw_sp_rif_type
3184mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
3185 const struct net_device *dev)
3186{
3187 enum mlxsw_sp_fid_type type;
3188
3189 /* RIF type is derived from the type of the underlying FID */
3190 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
3191 type = MLXSW_SP_FID_TYPE_8021Q;
3192 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
3193 type = MLXSW_SP_FID_TYPE_8021Q;
3194 else if (netif_is_bridge_master(dev))
3195 type = MLXSW_SP_FID_TYPE_8021D;
3196 else
3197 type = MLXSW_SP_FID_TYPE_RFID;
3198
3199 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
3200}
3201
Ido Schimmelde5ed992017-06-04 16:53:40 +02003202static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003203{
3204 int i;
3205
Ido Schimmelde5ed992017-06-04 16:53:40 +02003206 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
3207 if (!mlxsw_sp->router->rifs[i]) {
3208 *p_rif_index = i;
3209 return 0;
3210 }
3211 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01003212
Ido Schimmelde5ed992017-06-04 16:53:40 +02003213 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003214}
3215
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003216static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
3217 u16 vr_id,
3218 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003219{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003220 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003221
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003222 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003223 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003224 return NULL;
3225
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003226 INIT_LIST_HEAD(&rif->nexthop_list);
3227 INIT_LIST_HEAD(&rif->neigh_list);
3228 ether_addr_copy(rif->addr, l3_dev->dev_addr);
3229 rif->mtu = l3_dev->mtu;
3230 rif->vr_id = vr_id;
3231 rif->dev = l3_dev;
3232 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003233
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003234 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003235}
3236
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003237struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
3238 u16 rif_index)
3239{
3240 return mlxsw_sp->router->rifs[rif_index];
3241}
3242
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02003243u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
3244{
3245 return rif->rif_index;
3246}
3247
3248int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
3249{
3250 return rif->dev->ifindex;
3251}
3252
Ido Schimmel4724ba562017-03-10 08:53:39 +01003253static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003254mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
3255 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003256{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003257 u32 tb_id = l3mdev_fib_table(params->dev);
3258 const struct mlxsw_sp_rif_ops *ops;
3259 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003260 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003261 struct mlxsw_sp_fid *fid;
3262 struct mlxsw_sp_vr *vr;
3263 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003264 int err;
3265
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003266 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
3267 ops = mlxsw_sp->router->rif_ops_arr[type];
3268
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003269 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
3270 if (IS_ERR(vr))
3271 return ERR_CAST(vr);
3272
Ido Schimmelde5ed992017-06-04 16:53:40 +02003273 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
3274 if (err)
3275 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003276
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003277 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02003278 if (!rif) {
3279 err = -ENOMEM;
3280 goto err_rif_alloc;
3281 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003282 rif->mlxsw_sp = mlxsw_sp;
3283 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02003284
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003285 fid = ops->fid_get(rif);
3286 if (IS_ERR(fid)) {
3287 err = PTR_ERR(fid);
3288 goto err_fid_get;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02003289 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003290 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02003291
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003292 if (ops->setup)
3293 ops->setup(rif, params);
3294
3295 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003296 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003297 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003298
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003299 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
Ido Schimmela1107482017-05-26 08:37:39 +02003300 mlxsw_sp_fid_index(fid), true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003301 if (err)
3302 goto err_rif_fdb_op;
3303
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003304 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003305 mlxsw_sp_fid_rif_set(fid, rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02003306 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01003307 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003308
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003309 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003310
Ido Schimmel4724ba562017-03-10 08:53:39 +01003311err_rif_fdb_op:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003312 ops->deconfigure(rif);
3313err_configure:
Ido Schimmela1107482017-05-26 08:37:39 +02003314 mlxsw_sp_fid_put(fid);
3315err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003316 kfree(rif);
3317err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02003318err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003319 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003320 return ERR_PTR(err);
3321}
3322
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003323void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003324{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003325 const struct mlxsw_sp_rif_ops *ops = rif->ops;
3326 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02003327 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003328 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003329
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003330 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003331 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02003332
Ido Schimmel69132292017-03-10 08:53:42 +01003333 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003334 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmela1107482017-05-26 08:37:39 +02003335 mlxsw_sp_fid_rif_set(fid, NULL);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003336 mlxsw_sp_rif_counters_free(rif);
3337 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
3338 mlxsw_sp_fid_index(fid), false);
3339 ops->deconfigure(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003340 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003341 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02003342 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003343}
3344
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003345static void
3346mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
3347 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
3348{
3349 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
3350
3351 params->vid = mlxsw_sp_port_vlan->vid;
3352 params->lag = mlxsw_sp_port->lagged;
3353 if (params->lag)
3354 params->lag_id = mlxsw_sp_port->lag_id;
3355 else
3356 params->system_port = mlxsw_sp_port->local_port;
3357}
3358
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003359static int
Ido Schimmela1107482017-05-26 08:37:39 +02003360mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003361 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003362{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003363 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02003364 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003365 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003366 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003367 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003368 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003369
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02003370 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003371 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003372 struct mlxsw_sp_rif_params params = {
3373 .dev = l3_dev,
3374 };
3375
3376 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
3377 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003378 if (IS_ERR(rif))
3379 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003380 }
3381
Ido Schimmela1107482017-05-26 08:37:39 +02003382 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003383 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02003384 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
3385 if (err)
3386 goto err_fid_port_vid_map;
3387
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003388 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003389 if (err)
3390 goto err_port_vid_learning_set;
3391
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003392 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003393 BR_STATE_FORWARDING);
3394 if (err)
3395 goto err_port_vid_stp_set;
3396
Ido Schimmela1107482017-05-26 08:37:39 +02003397 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003398
Ido Schimmel4724ba562017-03-10 08:53:39 +01003399 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003400
3401err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003402 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003403err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02003404 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3405err_fid_port_vid_map:
3406 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02003407 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003408}
3409
Ido Schimmela1107482017-05-26 08:37:39 +02003410void
3411mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003412{
Ido Schimmelce95e152017-05-26 08:37:27 +02003413 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003414 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02003415 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02003416
Ido Schimmela1107482017-05-26 08:37:39 +02003417 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
3418 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02003419
Ido Schimmela1107482017-05-26 08:37:39 +02003420 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003421 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
3422 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02003423 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3424 /* If router port holds the last reference on the rFID, then the
3425 * associated Sub-port RIF will be destroyed.
3426 */
3427 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003428}
3429
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003430static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
3431 struct net_device *port_dev,
3432 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003433{
3434 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02003435 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003436
Ido Schimmelce95e152017-05-26 08:37:27 +02003437 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003438 if (WARN_ON(!mlxsw_sp_port_vlan))
3439 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003440
3441 switch (event) {
3442 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02003443 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003444 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003445 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02003446 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003447 break;
3448 }
3449
3450 return 0;
3451}
3452
3453static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
3454 unsigned long event)
3455{
Jiri Pirko2b94e582017-04-18 16:55:37 +02003456 if (netif_is_bridge_port(port_dev) ||
3457 netif_is_lag_port(port_dev) ||
3458 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003459 return 0;
3460
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003461 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003462}
3463
3464static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
3465 struct net_device *lag_dev,
3466 unsigned long event, u16 vid)
3467{
3468 struct net_device *port_dev;
3469 struct list_head *iter;
3470 int err;
3471
3472 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
3473 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003474 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
3475 port_dev,
3476 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003477 if (err)
3478 return err;
3479 }
3480 }
3481
3482 return 0;
3483}
3484
3485static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
3486 unsigned long event)
3487{
3488 if (netif_is_bridge_port(lag_dev))
3489 return 0;
3490
3491 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
3492}
3493
Ido Schimmel4724ba562017-03-10 08:53:39 +01003494static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01003495 unsigned long event)
3496{
3497 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003498 struct mlxsw_sp_rif_params params = {
3499 .dev = l3_dev,
3500 };
Ido Schimmela1107482017-05-26 08:37:39 +02003501 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003502
3503 switch (event) {
3504 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003505 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
3506 if (IS_ERR(rif))
3507 return PTR_ERR(rif);
3508 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003509 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02003510 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003511 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003512 break;
3513 }
3514
3515 return 0;
3516}
3517
3518static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
3519 unsigned long event)
3520{
3521 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003522 u16 vid = vlan_dev_vlan_id(vlan_dev);
3523
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03003524 if (netif_is_bridge_port(vlan_dev))
3525 return 0;
3526
Ido Schimmel4724ba562017-03-10 08:53:39 +01003527 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02003528 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
3529 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003530 else if (netif_is_lag_master(real_dev))
3531 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
3532 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02003533 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02003534 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003535
3536 return 0;
3537}
3538
Ido Schimmelb1e45522017-04-30 19:47:14 +03003539static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
3540 unsigned long event)
3541{
3542 if (mlxsw_sp_port_dev_check(dev))
3543 return mlxsw_sp_inetaddr_port_event(dev, event);
3544 else if (netif_is_lag_master(dev))
3545 return mlxsw_sp_inetaddr_lag_event(dev, event);
3546 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02003547 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03003548 else if (is_vlan_dev(dev))
3549 return mlxsw_sp_inetaddr_vlan_event(dev, event);
3550 else
3551 return 0;
3552}
3553
Ido Schimmel4724ba562017-03-10 08:53:39 +01003554int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
3555 unsigned long event, void *ptr)
3556{
3557 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
3558 struct net_device *dev = ifa->ifa_dev->dev;
3559 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003560 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003561 int err = 0;
3562
3563 mlxsw_sp = mlxsw_sp_lower_get(dev);
3564 if (!mlxsw_sp)
3565 goto out;
3566
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003567 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003568 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01003569 goto out;
3570
Ido Schimmelb1e45522017-04-30 19:47:14 +03003571 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003572out:
3573 return notifier_from_errno(err);
3574}
3575
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02003576struct mlxsw_sp_inet6addr_event_work {
3577 struct work_struct work;
3578 struct net_device *dev;
3579 unsigned long event;
3580};
3581
3582static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
3583{
3584 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
3585 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
3586 struct net_device *dev = inet6addr_work->dev;
3587 unsigned long event = inet6addr_work->event;
3588 struct mlxsw_sp *mlxsw_sp;
3589 struct mlxsw_sp_rif *rif;
3590
3591 rtnl_lock();
3592 mlxsw_sp = mlxsw_sp_lower_get(dev);
3593 if (!mlxsw_sp)
3594 goto out;
3595
3596 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3597 if (!mlxsw_sp_rif_should_config(rif, dev, event))
3598 goto out;
3599
3600 __mlxsw_sp_inetaddr_event(dev, event);
3601out:
3602 rtnl_unlock();
3603 dev_put(dev);
3604 kfree(inet6addr_work);
3605}
3606
3607/* Called with rcu_read_lock() */
3608int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
3609 unsigned long event, void *ptr)
3610{
3611 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
3612 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
3613 struct net_device *dev = if6->idev->dev;
3614
3615 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
3616 return NOTIFY_DONE;
3617
3618 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
3619 if (!inet6addr_work)
3620 return NOTIFY_BAD;
3621
3622 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
3623 inet6addr_work->dev = dev;
3624 inet6addr_work->event = event;
3625 dev_hold(dev);
3626 mlxsw_core_schedule_work(&inet6addr_work->work);
3627
3628 return NOTIFY_DONE;
3629}
3630
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003631static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01003632 const char *mac, int mtu)
3633{
3634 char ritr_pl[MLXSW_REG_RITR_LEN];
3635 int err;
3636
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003637 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003638 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3639 if (err)
3640 return err;
3641
3642 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
3643 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
3644 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
3645 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3646}
3647
3648int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
3649{
3650 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003651 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02003652 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003653 int err;
3654
3655 mlxsw_sp = mlxsw_sp_lower_get(dev);
3656 if (!mlxsw_sp)
3657 return 0;
3658
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003659 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3660 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01003661 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02003662 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003663
Ido Schimmela1107482017-05-26 08:37:39 +02003664 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003665 if (err)
3666 return err;
3667
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003668 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
3669 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003670 if (err)
3671 goto err_rif_edit;
3672
Ido Schimmela1107482017-05-26 08:37:39 +02003673 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003674 if (err)
3675 goto err_rif_fdb_op;
3676
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003677 ether_addr_copy(rif->addr, dev->dev_addr);
3678 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003679
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003680 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003681
3682 return 0;
3683
3684err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003685 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003686err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02003687 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003688 return err;
3689}
3690
Ido Schimmelb1e45522017-04-30 19:47:14 +03003691static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
3692 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003693{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003694 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01003695
Ido Schimmelb1e45522017-04-30 19:47:14 +03003696 /* If netdev is already associated with a RIF, then we need to
3697 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01003698 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03003699 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3700 if (rif)
3701 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003702
Ido Schimmelb1e45522017-04-30 19:47:14 +03003703 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003704}
3705
Ido Schimmelb1e45522017-04-30 19:47:14 +03003706static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
3707 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003708{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003709 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01003710
Ido Schimmelb1e45522017-04-30 19:47:14 +03003711 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3712 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01003713 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03003714 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01003715}
3716
Ido Schimmelb1e45522017-04-30 19:47:14 +03003717int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
3718 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003719{
Ido Schimmelb1e45522017-04-30 19:47:14 +03003720 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
3721 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003722
Ido Schimmelb1e45522017-04-30 19:47:14 +03003723 if (!mlxsw_sp)
3724 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003725
Ido Schimmelb1e45522017-04-30 19:47:14 +03003726 switch (event) {
3727 case NETDEV_PRECHANGEUPPER:
3728 return 0;
3729 case NETDEV_CHANGEUPPER:
3730 if (info->linking)
3731 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
3732 else
3733 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
3734 break;
3735 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003736
Ido Schimmelb1e45522017-04-30 19:47:14 +03003737 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01003738}
3739
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003740static struct mlxsw_sp_rif_subport *
3741mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02003742{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003743 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02003744}
3745
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003746static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
3747 const struct mlxsw_sp_rif_params *params)
3748{
3749 struct mlxsw_sp_rif_subport *rif_subport;
3750
3751 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3752 rif_subport->vid = params->vid;
3753 rif_subport->lag = params->lag;
3754 if (params->lag)
3755 rif_subport->lag_id = params->lag_id;
3756 else
3757 rif_subport->system_port = params->system_port;
3758}
3759
3760static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
3761{
3762 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3763 struct mlxsw_sp_rif_subport *rif_subport;
3764 char ritr_pl[MLXSW_REG_RITR_LEN];
3765
3766 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3767 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
3768 rif->rif_index, rif->vr_id, rif->dev->mtu,
3769 rif->dev->dev_addr);
3770 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
3771 rif_subport->lag ? rif_subport->lag_id :
3772 rif_subport->system_port,
3773 rif_subport->vid);
3774
3775 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3776}
3777
3778static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
3779{
3780 return mlxsw_sp_rif_subport_op(rif, true);
3781}
3782
3783static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
3784{
3785 mlxsw_sp_rif_subport_op(rif, false);
3786}
3787
3788static struct mlxsw_sp_fid *
3789mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
3790{
3791 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
3792}
3793
3794static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
3795 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
3796 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
3797 .setup = mlxsw_sp_rif_subport_setup,
3798 .configure = mlxsw_sp_rif_subport_configure,
3799 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
3800 .fid_get = mlxsw_sp_rif_subport_fid_get,
3801};
3802
3803static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
3804 enum mlxsw_reg_ritr_if_type type,
3805 u16 vid_fid, bool enable)
3806{
3807 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3808 char ritr_pl[MLXSW_REG_RITR_LEN];
3809
3810 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
3811 rif->dev->mtu, rif->dev->dev_addr);
3812 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
3813
3814 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3815}
3816
3817static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
3818{
3819 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
3820}
3821
3822static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
3823{
3824 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3825 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3826 int err;
3827
3828 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
3829 if (err)
3830 return err;
3831
Ido Schimmel0d284812017-07-18 10:10:12 +02003832 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3833 mlxsw_sp_router_port(mlxsw_sp), true);
3834 if (err)
3835 goto err_fid_mc_flood_set;
3836
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003837 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3838 mlxsw_sp_router_port(mlxsw_sp), true);
3839 if (err)
3840 goto err_fid_bc_flood_set;
3841
3842 return 0;
3843
3844err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02003845 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3846 mlxsw_sp_router_port(mlxsw_sp), false);
3847err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003848 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3849 return err;
3850}
3851
3852static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
3853{
3854 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3855 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3856
3857 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3858 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02003859 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3860 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003861 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3862}
3863
3864static struct mlxsw_sp_fid *
3865mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
3866{
3867 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
3868
3869 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
3870}
3871
3872static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
3873 .type = MLXSW_SP_RIF_TYPE_VLAN,
3874 .rif_size = sizeof(struct mlxsw_sp_rif),
3875 .configure = mlxsw_sp_rif_vlan_configure,
3876 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
3877 .fid_get = mlxsw_sp_rif_vlan_fid_get,
3878};
3879
3880static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
3881{
3882 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3883 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3884 int err;
3885
3886 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
3887 true);
3888 if (err)
3889 return err;
3890
Ido Schimmel0d284812017-07-18 10:10:12 +02003891 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3892 mlxsw_sp_router_port(mlxsw_sp), true);
3893 if (err)
3894 goto err_fid_mc_flood_set;
3895
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003896 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3897 mlxsw_sp_router_port(mlxsw_sp), true);
3898 if (err)
3899 goto err_fid_bc_flood_set;
3900
3901 return 0;
3902
3903err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02003904 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3905 mlxsw_sp_router_port(mlxsw_sp), false);
3906err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003907 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3908 return err;
3909}
3910
3911static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
3912{
3913 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3914 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3915
3916 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3917 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02003918 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3919 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003920 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3921}
3922
3923static struct mlxsw_sp_fid *
3924mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
3925{
3926 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
3927}
3928
3929static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
3930 .type = MLXSW_SP_RIF_TYPE_FID,
3931 .rif_size = sizeof(struct mlxsw_sp_rif),
3932 .configure = mlxsw_sp_rif_fid_configure,
3933 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
3934 .fid_get = mlxsw_sp_rif_fid_fid_get,
3935};
3936
3937static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
3938 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
3939 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
3940 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
3941};
3942
Ido Schimmel348b8fc2017-05-16 19:38:29 +02003943static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
3944{
3945 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
3946
3947 mlxsw_sp->router->rifs = kcalloc(max_rifs,
3948 sizeof(struct mlxsw_sp_rif *),
3949 GFP_KERNEL);
3950 if (!mlxsw_sp->router->rifs)
3951 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02003952
3953 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
3954
Ido Schimmel348b8fc2017-05-16 19:38:29 +02003955 return 0;
3956}
3957
3958static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
3959{
3960 int i;
3961
3962 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
3963 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
3964
3965 kfree(mlxsw_sp->router->rifs);
3966}
3967
Ido Schimmelc3852ef2016-12-03 16:45:07 +01003968static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
3969{
Ido Schimmel7e39d112017-05-16 19:38:28 +02003970 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01003971
3972 /* Flush pending FIB notifications and then flush the device's
3973 * table before requesting another dump. The FIB notification
3974 * block is unregistered, so no need to take RTNL.
3975 */
3976 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02003977 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3978 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01003979}
3980
Ido Schimmel4724ba562017-03-10 08:53:39 +01003981static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
3982{
3983 char rgcr_pl[MLXSW_REG_RGCR_LEN];
3984 u64 max_rifs;
3985 int err;
3986
3987 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
3988 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003989 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003990
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02003991 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01003992 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
3993 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
3994 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02003995 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003996 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01003997}
3998
3999static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4000{
4001 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004002
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02004003 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004004 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004005}
4006
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004007int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4008{
Ido Schimmel9011b672017-05-16 19:38:25 +02004009 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004010 int err;
4011
Ido Schimmel9011b672017-05-16 19:38:25 +02004012 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4013 if (!router)
4014 return -ENOMEM;
4015 mlxsw_sp->router = router;
4016 router->mlxsw_sp = mlxsw_sp;
4017
4018 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004019 err = __mlxsw_sp_router_init(mlxsw_sp);
4020 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02004021 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004022
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004023 err = mlxsw_sp_rifs_init(mlxsw_sp);
4024 if (err)
4025 goto err_rifs_init;
4026
Ido Schimmel9011b672017-05-16 19:38:25 +02004027 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004028 &mlxsw_sp_nexthop_ht_params);
4029 if (err)
4030 goto err_nexthop_ht_init;
4031
Ido Schimmel9011b672017-05-16 19:38:25 +02004032 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004033 &mlxsw_sp_nexthop_group_ht_params);
4034 if (err)
4035 goto err_nexthop_group_ht_init;
4036
Ido Schimmel8494ab02017-03-24 08:02:47 +01004037 err = mlxsw_sp_lpm_init(mlxsw_sp);
4038 if (err)
4039 goto err_lpm_init;
4040
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004041 err = mlxsw_sp_vrs_init(mlxsw_sp);
4042 if (err)
4043 goto err_vrs_init;
4044
Ido Schimmel8c9583a2016-10-27 15:12:57 +02004045 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004046 if (err)
4047 goto err_neigh_init;
4048
Ido Schimmel7e39d112017-05-16 19:38:28 +02004049 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4050 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004051 mlxsw_sp_router_fib_dump_flush);
4052 if (err)
4053 goto err_register_fib_notifier;
4054
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004055 return 0;
4056
Ido Schimmelc3852ef2016-12-03 16:45:07 +01004057err_register_fib_notifier:
4058 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004059err_neigh_init:
4060 mlxsw_sp_vrs_fini(mlxsw_sp);
4061err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01004062 mlxsw_sp_lpm_fini(mlxsw_sp);
4063err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004064 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01004065err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02004066 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01004067err_nexthop_ht_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004068 mlxsw_sp_rifs_fini(mlxsw_sp);
4069err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004070 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004071err_router_init:
4072 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004073 return err;
4074}
4075
4076void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4077{
Ido Schimmel7e39d112017-05-16 19:38:28 +02004078 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004079 mlxsw_sp_neigh_fini(mlxsw_sp);
4080 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01004081 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004082 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4083 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02004084 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004085 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004086 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004087}