blob: 72e386b647b98d5ee356bc4fdabfba9216e2b8ee [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Petr Machata6ddb7422017-09-02 23:49:19 +02003 * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
Ido Schimmel464dce12016-07-02 11:00:15 +02004 * 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>
Petr Machata6ddb7422017-09-02 23:49:19 +02007 * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02008 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * Alternatively, this software may be distributed under the terms of the
22 * GNU General Public License ("GPL") version 2 as published by the Free
23 * Software Foundation.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/kernel.h>
39#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020040#include <linux/rhashtable.h>
41#include <linux/bitops.h>
42#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020043#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010044#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010045#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020046#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020047#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020048#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020049#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020050#include <net/neighbour.h>
51#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020052#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020053#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010054#include <net/fib_rules.h>
Petr Machata6ddb7422017-09-02 23:49:19 +020055#include <net/ip_tunnels.h>
Ido Schimmel57837882017-03-16 09:08:16 +010056#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020057#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020058#include <net/ndisc.h>
59#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020060#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020061
62#include "spectrum.h"
63#include "core.h"
64#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020065#include "spectrum_cnt.h"
66#include "spectrum_dpipe.h"
Petr Machata38ebc0f2017-09-02 23:49:17 +020067#include "spectrum_ipip.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020068#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020069
Ido Schimmel9011b672017-05-16 19:38:25 +020070struct mlxsw_sp_vr;
71struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020072struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020073
74struct mlxsw_sp_router {
75 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020076 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020077 struct mlxsw_sp_vr *vrs;
78 struct rhashtable neigh_ht;
79 struct rhashtable nexthop_group_ht;
80 struct rhashtable nexthop_ht;
81 struct {
82 struct mlxsw_sp_lpm_tree *trees;
83 unsigned int tree_count;
84 } lpm;
85 struct {
86 struct delayed_work dw;
87 unsigned long interval; /* ms */
88 } neighs_update;
89 struct delayed_work nexthop_probe_dw;
90#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
91 struct list_head nexthop_neighs_list;
92 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020093 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020094 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020095 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020096};
97
Ido Schimmel4724ba562017-03-10 08:53:39 +010098struct mlxsw_sp_rif {
99 struct list_head nexthop_list;
100 struct list_head neigh_list;
101 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200102 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100103 unsigned char addr[ETH_ALEN];
104 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100105 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100106 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200107 const struct mlxsw_sp_rif_ops *ops;
108 struct mlxsw_sp *mlxsw_sp;
109
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200110 unsigned int counter_ingress;
111 bool counter_ingress_valid;
112 unsigned int counter_egress;
113 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100114};
115
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200116struct mlxsw_sp_rif_params {
117 struct net_device *dev;
118 union {
119 u16 system_port;
120 u16 lag_id;
121 };
122 u16 vid;
123 bool lag;
124};
125
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200126struct mlxsw_sp_rif_subport {
127 struct mlxsw_sp_rif common;
128 union {
129 u16 system_port;
130 u16 lag_id;
131 };
132 u16 vid;
133 bool lag;
134};
135
Petr Machata6ddb7422017-09-02 23:49:19 +0200136struct mlxsw_sp_rif_ipip_lb {
137 struct mlxsw_sp_rif common;
138 struct mlxsw_sp_rif_ipip_lb_config lb_config;
139 u16 ul_vr_id; /* Reserved for Spectrum-2. */
140};
141
142struct mlxsw_sp_rif_params_ipip_lb {
143 struct mlxsw_sp_rif_params common;
144 struct mlxsw_sp_rif_ipip_lb_config lb_config;
145};
146
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200147struct mlxsw_sp_rif_ops {
148 enum mlxsw_sp_rif_type type;
149 size_t rif_size;
150
151 void (*setup)(struct mlxsw_sp_rif *rif,
152 const struct mlxsw_sp_rif_params *params);
153 int (*configure)(struct mlxsw_sp_rif *rif);
154 void (*deconfigure)(struct mlxsw_sp_rif *rif);
155 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
156};
157
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200158static unsigned int *
159mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
160 enum mlxsw_sp_rif_counter_dir dir)
161{
162 switch (dir) {
163 case MLXSW_SP_RIF_COUNTER_EGRESS:
164 return &rif->counter_egress;
165 case MLXSW_SP_RIF_COUNTER_INGRESS:
166 return &rif->counter_ingress;
167 }
168 return NULL;
169}
170
171static bool
172mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
173 enum mlxsw_sp_rif_counter_dir dir)
174{
175 switch (dir) {
176 case MLXSW_SP_RIF_COUNTER_EGRESS:
177 return rif->counter_egress_valid;
178 case MLXSW_SP_RIF_COUNTER_INGRESS:
179 return rif->counter_ingress_valid;
180 }
181 return false;
182}
183
184static void
185mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
186 enum mlxsw_sp_rif_counter_dir dir,
187 bool valid)
188{
189 switch (dir) {
190 case MLXSW_SP_RIF_COUNTER_EGRESS:
191 rif->counter_egress_valid = valid;
192 break;
193 case MLXSW_SP_RIF_COUNTER_INGRESS:
194 rif->counter_ingress_valid = valid;
195 break;
196 }
197}
198
199static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
200 unsigned int counter_index, bool enable,
201 enum mlxsw_sp_rif_counter_dir dir)
202{
203 char ritr_pl[MLXSW_REG_RITR_LEN];
204 bool is_egress = false;
205 int err;
206
207 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
208 is_egress = true;
209 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
210 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
211 if (err)
212 return err;
213
214 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
215 is_egress);
216 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
217}
218
219int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
220 struct mlxsw_sp_rif *rif,
221 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
222{
223 char ricnt_pl[MLXSW_REG_RICNT_LEN];
224 unsigned int *p_counter_index;
225 bool valid;
226 int err;
227
228 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
229 if (!valid)
230 return -EINVAL;
231
232 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
233 if (!p_counter_index)
234 return -EINVAL;
235 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
236 MLXSW_REG_RICNT_OPCODE_NOP);
237 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
238 if (err)
239 return err;
240 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
241 return 0;
242}
243
244static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
245 unsigned int counter_index)
246{
247 char ricnt_pl[MLXSW_REG_RICNT_LEN];
248
249 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
250 MLXSW_REG_RICNT_OPCODE_CLEAR);
251 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
252}
253
254int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
255 struct mlxsw_sp_rif *rif,
256 enum mlxsw_sp_rif_counter_dir dir)
257{
258 unsigned int *p_counter_index;
259 int err;
260
261 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
262 if (!p_counter_index)
263 return -EINVAL;
264 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
265 p_counter_index);
266 if (err)
267 return err;
268
269 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
270 if (err)
271 goto err_counter_clear;
272
273 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
274 *p_counter_index, true, dir);
275 if (err)
276 goto err_counter_edit;
277 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
278 return 0;
279
280err_counter_edit:
281err_counter_clear:
282 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
283 *p_counter_index);
284 return err;
285}
286
287void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
288 struct mlxsw_sp_rif *rif,
289 enum mlxsw_sp_rif_counter_dir dir)
290{
291 unsigned int *p_counter_index;
292
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200293 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
294 return;
295
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200296 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
297 if (WARN_ON(!p_counter_index))
298 return;
299 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
300 *p_counter_index, false, dir);
301 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
302 *p_counter_index);
303 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
304}
305
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200306static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
307{
308 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
309 struct devlink *devlink;
310
311 devlink = priv_to_devlink(mlxsw_sp->core);
312 if (!devlink_dpipe_table_counter_enabled(devlink,
313 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
314 return;
315 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
316}
317
318static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
319{
320 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
321
322 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
323}
324
Ido Schimmel4724ba562017-03-10 08:53:39 +0100325static struct mlxsw_sp_rif *
326mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
327 const struct net_device *dev);
328
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200329#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200330
331struct mlxsw_sp_prefix_usage {
332 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
333};
334
Jiri Pirko53342022016-07-04 08:23:08 +0200335#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
336 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
337
338static bool
339mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
340 struct mlxsw_sp_prefix_usage *prefix_usage2)
341{
342 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
343}
344
Jiri Pirko6b75c482016-07-04 08:23:09 +0200345static bool
346mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
347{
348 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
349
350 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
351}
352
353static void
354mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
355 struct mlxsw_sp_prefix_usage *prefix_usage2)
356{
357 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
358}
359
360static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200361mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
362 unsigned char prefix_len)
363{
364 set_bit(prefix_len, prefix_usage->b);
365}
366
367static void
368mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
369 unsigned char prefix_len)
370{
371 clear_bit(prefix_len, prefix_usage->b);
372}
373
374struct mlxsw_sp_fib_key {
375 unsigned char addr[sizeof(struct in6_addr)];
376 unsigned char prefix_len;
377};
378
Jiri Pirko61c503f2016-07-04 08:23:11 +0200379enum mlxsw_sp_fib_entry_type {
380 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
381 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
382 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
383};
384
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200385struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200386struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200387
Ido Schimmel9aecce12017-02-09 10:28:42 +0100388struct mlxsw_sp_fib_node {
389 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200390 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100391 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100392 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100393 struct mlxsw_sp_fib_key key;
394};
395
Ido Schimmel9aecce12017-02-09 10:28:42 +0100396struct mlxsw_sp_fib_entry {
397 struct list_head list;
398 struct mlxsw_sp_fib_node *fib_node;
399 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200400 struct list_head nexthop_group_node;
401 struct mlxsw_sp_nexthop_group *nh_group;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200402};
403
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200404struct mlxsw_sp_fib4_entry {
405 struct mlxsw_sp_fib_entry common;
406 u32 tb_id;
407 u32 prio;
408 u8 tos;
409 u8 type;
410};
411
Ido Schimmel428b8512017-08-03 13:28:28 +0200412struct mlxsw_sp_fib6_entry {
413 struct mlxsw_sp_fib_entry common;
414 struct list_head rt6_list;
415 unsigned int nrt6;
416};
417
418struct mlxsw_sp_rt6 {
419 struct list_head list;
420 struct rt6_info *rt;
421};
422
Ido Schimmel9011b672017-05-16 19:38:25 +0200423struct mlxsw_sp_lpm_tree {
424 u8 id; /* tree ID */
425 unsigned int ref_count;
426 enum mlxsw_sp_l3proto proto;
427 struct mlxsw_sp_prefix_usage prefix_usage;
428};
429
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200430struct mlxsw_sp_fib {
431 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100432 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100433 struct mlxsw_sp_vr *vr;
434 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200435 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
436 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100437 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200438};
439
Ido Schimmel9011b672017-05-16 19:38:25 +0200440struct mlxsw_sp_vr {
441 u16 id; /* virtual router ID */
442 u32 tb_id; /* kernel fib table id */
443 unsigned int rif_count;
444 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200445 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200446};
447
Ido Schimmel9aecce12017-02-09 10:28:42 +0100448static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200449
Ido Schimmel76610eb2017-03-10 08:53:41 +0100450static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
451 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200452{
453 struct mlxsw_sp_fib *fib;
454 int err;
455
456 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
457 if (!fib)
458 return ERR_PTR(-ENOMEM);
459 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
460 if (err)
461 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100462 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100463 fib->proto = proto;
464 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200465 return fib;
466
467err_rhashtable_init:
468 kfree(fib);
469 return ERR_PTR(err);
470}
471
472static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
473{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100474 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100475 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200476 rhashtable_destroy(&fib->ht);
477 kfree(fib);
478}
479
Jiri Pirko53342022016-07-04 08:23:08 +0200480static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100481mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200482{
483 static struct mlxsw_sp_lpm_tree *lpm_tree;
484 int i;
485
Ido Schimmel9011b672017-05-16 19:38:25 +0200486 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
487 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100488 if (lpm_tree->ref_count == 0)
489 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200490 }
491 return NULL;
492}
493
494static int mlxsw_sp_lpm_tree_alloc(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, true,
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
Ido Schimmelcc702672017-08-14 10:54:03 +0200505static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
506 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200507{
508 char ralta_pl[MLXSW_REG_RALTA_LEN];
509
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200510 mlxsw_reg_ralta_pack(ralta_pl, false,
511 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
512 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200513 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200514}
515
516static int
517mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
518 struct mlxsw_sp_prefix_usage *prefix_usage,
519 struct mlxsw_sp_lpm_tree *lpm_tree)
520{
521 char ralst_pl[MLXSW_REG_RALST_LEN];
522 u8 root_bin = 0;
523 u8 prefix;
524 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
525
526 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
527 root_bin = prefix;
528
529 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
530 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
531 if (prefix == 0)
532 continue;
533 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
534 MLXSW_REG_RALST_BIN_NO_CHILD);
535 last_prefix = prefix;
536 }
537 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
538}
539
540static struct mlxsw_sp_lpm_tree *
541mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
542 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100543 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200544{
545 struct mlxsw_sp_lpm_tree *lpm_tree;
546 int err;
547
Ido Schimmel382dbb42017-03-10 08:53:40 +0100548 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200549 if (!lpm_tree)
550 return ERR_PTR(-EBUSY);
551 lpm_tree->proto = proto;
552 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
553 if (err)
554 return ERR_PTR(err);
555
556 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
557 lpm_tree);
558 if (err)
559 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200560 memcpy(&lpm_tree->prefix_usage, prefix_usage,
561 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200562 return lpm_tree;
563
564err_left_struct_set:
565 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
566 return ERR_PTR(err);
567}
568
Ido Schimmelcc702672017-08-14 10:54:03 +0200569static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
570 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200571{
Ido Schimmelcc702672017-08-14 10:54:03 +0200572 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200573}
574
575static struct mlxsw_sp_lpm_tree *
576mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
577 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100578 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200579{
580 struct mlxsw_sp_lpm_tree *lpm_tree;
581 int i;
582
Ido Schimmel9011b672017-05-16 19:38:25 +0200583 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
584 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200585 if (lpm_tree->ref_count != 0 &&
586 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200587 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
588 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200589 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200590 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200591 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
592}
Jiri Pirko53342022016-07-04 08:23:08 +0200593
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200594static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
595{
Jiri Pirko53342022016-07-04 08:23:08 +0200596 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200597}
598
Ido Schimmelcc702672017-08-14 10:54:03 +0200599static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
600 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200601{
602 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200603 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200604}
605
Ido Schimmeld7a60302017-06-08 08:47:43 +0200606#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100607
608static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200609{
610 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100611 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200612 int i;
613
Ido Schimmel8494ab02017-03-24 08:02:47 +0100614 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
615 return -EIO;
616
617 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200618 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
619 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100620 sizeof(struct mlxsw_sp_lpm_tree),
621 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200622 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100623 return -ENOMEM;
624
Ido Schimmel9011b672017-05-16 19:38:25 +0200625 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
626 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200627 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
628 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100629
630 return 0;
631}
632
633static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
634{
Ido Schimmel9011b672017-05-16 19:38:25 +0200635 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200636}
637
Ido Schimmel76610eb2017-03-10 08:53:41 +0100638static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
639{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200640 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100641}
642
Jiri Pirko6b75c482016-07-04 08:23:09 +0200643static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
644{
645 struct mlxsw_sp_vr *vr;
646 int i;
647
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200648 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200649 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100650 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200651 return vr;
652 }
653 return NULL;
654}
655
656static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200657 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200658{
659 char raltb_pl[MLXSW_REG_RALTB_LEN];
660
Ido Schimmel76610eb2017-03-10 08:53:41 +0100661 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
662 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200663 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200664 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
665}
666
667static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100668 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200669{
670 char raltb_pl[MLXSW_REG_RALTB_LEN];
671
672 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100673 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
674 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200675 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
676}
677
678static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
679{
680 /* For our purpose, squash main and local table into one */
681 if (tb_id == RT_TABLE_LOCAL)
682 tb_id = RT_TABLE_MAIN;
683 return tb_id;
684}
685
686static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100687 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200688{
689 struct mlxsw_sp_vr *vr;
690 int i;
691
692 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200693
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200694 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200695 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100696 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200697 return vr;
698 }
699 return NULL;
700}
701
Ido Schimmel76610eb2017-03-10 08:53:41 +0100702static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
703 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200704{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100705 switch (proto) {
706 case MLXSW_SP_L3_PROTO_IPV4:
707 return vr->fib4;
708 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200709 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100710 }
711 return NULL;
712}
713
714static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
715 u32 tb_id)
716{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200717 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200718 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200719
720 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
721 if (!vr)
722 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100723 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
724 if (IS_ERR(vr->fib4))
725 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200726 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
727 if (IS_ERR(vr->fib6)) {
728 err = PTR_ERR(vr->fib6);
729 goto err_fib6_create;
730 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200731 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200732 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200733
734err_fib6_create:
735 mlxsw_sp_fib_destroy(vr->fib4);
736 vr->fib4 = NULL;
737 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200738}
739
Ido Schimmel76610eb2017-03-10 08:53:41 +0100740static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200741{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200742 mlxsw_sp_fib_destroy(vr->fib6);
743 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100744 mlxsw_sp_fib_destroy(vr->fib4);
745 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746}
747
Ido Schimmel76610eb2017-03-10 08:53:41 +0100748static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200749{
750 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200751
752 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100753 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
754 if (!vr)
755 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200756 return vr;
757}
758
Ido Schimmel76610eb2017-03-10 08:53:41 +0100759static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200760{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200761 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
762 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100763 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200764}
765
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200766static bool
767mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
768 enum mlxsw_sp_l3proto proto, u8 tree_id)
769{
770 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
771
772 if (!mlxsw_sp_vr_is_used(vr))
773 return false;
774 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
775 return true;
776 return false;
777}
778
779static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
780 struct mlxsw_sp_fib *fib,
781 struct mlxsw_sp_lpm_tree *new_tree)
782{
783 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
784 int err;
785
786 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
787 if (err)
788 return err;
789 fib->lpm_tree = new_tree;
790 mlxsw_sp_lpm_tree_hold(new_tree);
791 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
792 return 0;
793}
794
795static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
796 struct mlxsw_sp_fib *fib,
797 struct mlxsw_sp_lpm_tree *new_tree)
798{
799 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
800 enum mlxsw_sp_l3proto proto = fib->proto;
801 u8 old_id, new_id = new_tree->id;
802 struct mlxsw_sp_vr *vr;
803 int i, err;
804
805 if (!old_tree)
806 goto no_replace;
807 old_id = old_tree->id;
808
809 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
810 vr = &mlxsw_sp->router->vrs[i];
811 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
812 continue;
813 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
814 mlxsw_sp_vr_fib(vr, proto),
815 new_tree);
816 if (err)
817 goto err_tree_replace;
818 }
819
820 return 0;
821
822err_tree_replace:
823 for (i--; i >= 0; i--) {
824 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
825 continue;
826 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
827 mlxsw_sp_vr_fib(vr, proto),
828 old_tree);
829 }
830 return err;
831
832no_replace:
833 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
834 if (err)
835 return err;
836 fib->lpm_tree = new_tree;
837 mlxsw_sp_lpm_tree_hold(new_tree);
838 return 0;
839}
840
841static void
842mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
843 enum mlxsw_sp_l3proto proto,
844 struct mlxsw_sp_prefix_usage *req_prefix_usage)
845{
846 int i;
847
848 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
849 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
850 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
851 unsigned char prefix;
852
853 if (!mlxsw_sp_vr_is_used(vr))
854 continue;
855 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
856 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
857 }
858}
859
Nogah Frankel9497c042016-09-20 11:16:54 +0200860static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200861{
862 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200863 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200864 int i;
865
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200866 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200867 return -EIO;
868
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200869 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200870 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
871 GFP_KERNEL);
872 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200873 return -ENOMEM;
874
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200875 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200876 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200877 vr->id = i;
878 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200879
880 return 0;
881}
882
Ido Schimmelac571de2016-11-14 11:26:32 +0100883static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
884
Nogah Frankel9497c042016-09-20 11:16:54 +0200885static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
886{
Ido Schimmel30572242016-12-03 16:45:01 +0100887 /* At this stage we're guaranteed not to have new incoming
888 * FIB notifications and the work queue is free from FIBs
889 * sitting on top of mlxsw netdevs. However, we can still
890 * have other FIBs queued. Flush the queue before flushing
891 * the device's tables. No need for locks, as we're the only
892 * writer.
893 */
894 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100895 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200896 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200897}
898
Petr Machata6ddb7422017-09-02 23:49:19 +0200899static struct net_device *
900__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
901{
902 struct ip_tunnel *tun = netdev_priv(ol_dev);
903 struct net *net = dev_net(ol_dev);
904
905 return __dev_get_by_index(net, tun->parms.link);
906}
907
908static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
909{
910 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
911
912 if (d)
913 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
914 else
915 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
916}
917
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200918struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +0100919 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200920};
921
922struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +0100923 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200924 struct rhash_head ht_node;
925 struct mlxsw_sp_neigh_key key;
926 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100927 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +0200928 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200929 struct list_head nexthop_list; /* list of nexthops using
930 * this neigh entry
931 */
Yotam Gigib2157142016-07-05 11:27:51 +0200932 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200933 unsigned int counter_index;
934 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +0200935};
936
937static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
938 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
939 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
940 .key_len = sizeof(struct mlxsw_sp_neigh_key),
941};
942
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +0200943struct mlxsw_sp_neigh_entry *
944mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
945 struct mlxsw_sp_neigh_entry *neigh_entry)
946{
947 if (!neigh_entry) {
948 if (list_empty(&rif->neigh_list))
949 return NULL;
950 else
951 return list_first_entry(&rif->neigh_list,
952 typeof(*neigh_entry),
953 rif_list_node);
954 }
955 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
956 return NULL;
957 return list_next_entry(neigh_entry, rif_list_node);
958}
959
960int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
961{
962 return neigh_entry->key.n->tbl->family;
963}
964
965unsigned char *
966mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
967{
968 return neigh_entry->ha;
969}
970
971u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
972{
973 struct neighbour *n;
974
975 n = neigh_entry->key.n;
976 return ntohl(*((__be32 *) n->primary_key));
977}
978
Arkadi Sharshevsky02507682017-08-31 17:59:15 +0200979struct in6_addr *
980mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
981{
982 struct neighbour *n;
983
984 n = neigh_entry->key.n;
985 return (struct in6_addr *) &n->primary_key;
986}
987
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +0200988int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
989 struct mlxsw_sp_neigh_entry *neigh_entry,
990 u64 *p_counter)
991{
992 if (!neigh_entry->counter_valid)
993 return -EINVAL;
994
995 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
996 p_counter, NULL);
997}
998
Ido Schimmel5c8802f2017-02-06 16:20:13 +0100999static struct mlxsw_sp_neigh_entry *
1000mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1001 u16 rif)
1002{
1003 struct mlxsw_sp_neigh_entry *neigh_entry;
1004
1005 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1006 if (!neigh_entry)
1007 return NULL;
1008
1009 neigh_entry->key.n = n;
1010 neigh_entry->rif = rif;
1011 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1012
1013 return neigh_entry;
1014}
1015
1016static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1017{
1018 kfree(neigh_entry);
1019}
1020
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001021static int
1022mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1023 struct mlxsw_sp_neigh_entry *neigh_entry)
1024{
Ido Schimmel9011b672017-05-16 19:38:25 +02001025 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001026 &neigh_entry->ht_node,
1027 mlxsw_sp_neigh_ht_params);
1028}
1029
1030static void
1031mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1032 struct mlxsw_sp_neigh_entry *neigh_entry)
1033{
Ido Schimmel9011b672017-05-16 19:38:25 +02001034 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001035 &neigh_entry->ht_node,
1036 mlxsw_sp_neigh_ht_params);
1037}
1038
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001039static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001040mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1041 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001042{
1043 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001044 const char *table_name;
1045
1046 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1047 case AF_INET:
1048 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1049 break;
1050 case AF_INET6:
1051 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1052 break;
1053 default:
1054 WARN_ON(1);
1055 return false;
1056 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001057
1058 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001059 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001060}
1061
1062static void
1063mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1064 struct mlxsw_sp_neigh_entry *neigh_entry)
1065{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001066 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001067 return;
1068
1069 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1070 return;
1071
1072 neigh_entry->counter_valid = true;
1073}
1074
1075static void
1076mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1077 struct mlxsw_sp_neigh_entry *neigh_entry)
1078{
1079 if (!neigh_entry->counter_valid)
1080 return;
1081 mlxsw_sp_flow_counter_free(mlxsw_sp,
1082 neigh_entry->counter_index);
1083 neigh_entry->counter_valid = false;
1084}
1085
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001086static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001087mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001088{
1089 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001090 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001091 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001092
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001093 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1094 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001095 return ERR_PTR(-EINVAL);
1096
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001097 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001098 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001099 return ERR_PTR(-ENOMEM);
1100
1101 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1102 if (err)
1103 goto err_neigh_entry_insert;
1104
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001105 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001106 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001107
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001108 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001109
1110err_neigh_entry_insert:
1111 mlxsw_sp_neigh_entry_free(neigh_entry);
1112 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001113}
1114
1115static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001116mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1117 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001118{
Ido Schimmel9665b742017-02-08 11:16:42 +01001119 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001120 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001121 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1122 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001123}
1124
1125static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001126mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001127{
Jiri Pirko33b13412016-11-10 12:31:04 +01001128 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001129
Jiri Pirko33b13412016-11-10 12:31:04 +01001130 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001131 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001132 &key, mlxsw_sp_neigh_ht_params);
1133}
1134
Yotam Gigic723c7352016-07-05 11:27:43 +02001135static void
1136mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1137{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001138 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001139
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001140#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001141 interval = min_t(unsigned long,
1142 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1143 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001144#else
1145 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1146#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001147 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001148}
1149
1150static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1151 char *rauhtd_pl,
1152 int ent_index)
1153{
1154 struct net_device *dev;
1155 struct neighbour *n;
1156 __be32 dipn;
1157 u32 dip;
1158 u16 rif;
1159
1160 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1161
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001162 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001163 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1164 return;
1165 }
1166
1167 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001168 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001169 n = neigh_lookup(&arp_tbl, &dipn, dev);
1170 if (!n) {
1171 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1172 &dip);
1173 return;
1174 }
1175
1176 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1177 neigh_event_send(n, NULL);
1178 neigh_release(n);
1179}
1180
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001181#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001182static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1183 char *rauhtd_pl,
1184 int rec_index)
1185{
1186 struct net_device *dev;
1187 struct neighbour *n;
1188 struct in6_addr dip;
1189 u16 rif;
1190
1191 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1192 (char *) &dip);
1193
1194 if (!mlxsw_sp->router->rifs[rif]) {
1195 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1196 return;
1197 }
1198
1199 dev = mlxsw_sp->router->rifs[rif]->dev;
1200 n = neigh_lookup(&nd_tbl, &dip, dev);
1201 if (!n) {
1202 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1203 &dip);
1204 return;
1205 }
1206
1207 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1208 neigh_event_send(n, NULL);
1209 neigh_release(n);
1210}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001211#else
1212static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1213 char *rauhtd_pl,
1214 int rec_index)
1215{
1216}
1217#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001218
Yotam Gigic723c7352016-07-05 11:27:43 +02001219static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1220 char *rauhtd_pl,
1221 int rec_index)
1222{
1223 u8 num_entries;
1224 int i;
1225
1226 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1227 rec_index);
1228 /* Hardware starts counting at 0, so add 1. */
1229 num_entries++;
1230
1231 /* Each record consists of several neighbour entries. */
1232 for (i = 0; i < num_entries; i++) {
1233 int ent_index;
1234
1235 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1236 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1237 ent_index);
1238 }
1239
1240}
1241
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001242static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1243 char *rauhtd_pl,
1244 int rec_index)
1245{
1246 /* One record contains one entry. */
1247 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1248 rec_index);
1249}
1250
Yotam Gigic723c7352016-07-05 11:27:43 +02001251static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1252 char *rauhtd_pl, int rec_index)
1253{
1254 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1255 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1256 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1257 rec_index);
1258 break;
1259 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001260 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1261 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001262 break;
1263 }
1264}
1265
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001266static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1267{
1268 u8 num_rec, last_rec_index, num_entries;
1269
1270 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1271 last_rec_index = num_rec - 1;
1272
1273 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1274 return false;
1275 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1276 MLXSW_REG_RAUHTD_TYPE_IPV6)
1277 return true;
1278
1279 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1280 last_rec_index);
1281 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1282 return true;
1283 return false;
1284}
1285
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001286static int
1287__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1288 char *rauhtd_pl,
1289 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001290{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001291 int i, num_rec;
1292 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001293
1294 /* Make sure the neighbour's netdev isn't removed in the
1295 * process.
1296 */
1297 rtnl_lock();
1298 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001299 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001300 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1301 rauhtd_pl);
1302 if (err) {
1303 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1304 break;
1305 }
1306 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1307 for (i = 0; i < num_rec; i++)
1308 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1309 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001310 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001311 rtnl_unlock();
1312
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001313 return err;
1314}
1315
1316static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1317{
1318 enum mlxsw_reg_rauhtd_type type;
1319 char *rauhtd_pl;
1320 int err;
1321
1322 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1323 if (!rauhtd_pl)
1324 return -ENOMEM;
1325
1326 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1327 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1328 if (err)
1329 goto out;
1330
1331 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1332 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1333out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001334 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001335 return err;
1336}
1337
1338static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1339{
1340 struct mlxsw_sp_neigh_entry *neigh_entry;
1341
1342 /* Take RTNL mutex here to prevent lists from changes */
1343 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001344 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001345 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001346 /* If this neigh have nexthops, make the kernel think this neigh
1347 * is active regardless of the traffic.
1348 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001349 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001350 rtnl_unlock();
1351}
1352
1353static void
1354mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1355{
Ido Schimmel9011b672017-05-16 19:38:25 +02001356 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001357
Ido Schimmel9011b672017-05-16 19:38:25 +02001358 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001359 msecs_to_jiffies(interval));
1360}
1361
1362static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1363{
Ido Schimmel9011b672017-05-16 19:38:25 +02001364 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001365 int err;
1366
Ido Schimmel9011b672017-05-16 19:38:25 +02001367 router = container_of(work, struct mlxsw_sp_router,
1368 neighs_update.dw.work);
1369 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001370 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001371 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001372
Ido Schimmel9011b672017-05-16 19:38:25 +02001373 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001374
Ido Schimmel9011b672017-05-16 19:38:25 +02001375 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001376}
1377
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001378static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1379{
1380 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001381 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001382
Ido Schimmel9011b672017-05-16 19:38:25 +02001383 router = container_of(work, struct mlxsw_sp_router,
1384 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001385 /* Iterate over nexthop neighbours, find those who are unresolved and
1386 * send arp on them. This solves the chicken-egg problem when
1387 * the nexthop wouldn't get offloaded until the neighbor is resolved
1388 * but it wouldn't get resolved ever in case traffic is flowing in HW
1389 * using different nexthop.
1390 *
1391 * Take RTNL mutex here to prevent lists from changes.
1392 */
1393 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001394 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001395 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001396 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001397 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001398 rtnl_unlock();
1399
Ido Schimmel9011b672017-05-16 19:38:25 +02001400 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001401 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1402}
1403
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001404static void
1405mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1406 struct mlxsw_sp_neigh_entry *neigh_entry,
1407 bool removing);
1408
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001409static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001410{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001411 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1412 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1413}
1414
1415static void
1416mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1417 struct mlxsw_sp_neigh_entry *neigh_entry,
1418 enum mlxsw_reg_rauht_op op)
1419{
Jiri Pirko33b13412016-11-10 12:31:04 +01001420 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001421 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001422 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001423
1424 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1425 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001426 if (neigh_entry->counter_valid)
1427 mlxsw_reg_rauht_pack_counter(rauht_pl,
1428 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001429 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1430}
1431
1432static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001433mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1434 struct mlxsw_sp_neigh_entry *neigh_entry,
1435 enum mlxsw_reg_rauht_op op)
1436{
1437 struct neighbour *n = neigh_entry->key.n;
1438 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1439 const char *dip = n->primary_key;
1440
1441 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1442 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001443 if (neigh_entry->counter_valid)
1444 mlxsw_reg_rauht_pack_counter(rauht_pl,
1445 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001446 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1447}
1448
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001449bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001450{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001451 struct neighbour *n = neigh_entry->key.n;
1452
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001453 /* Packets with a link-local destination address are trapped
1454 * after LPM lookup and never reach the neighbour table, so
1455 * there is no need to program such neighbours to the device.
1456 */
1457 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1458 IPV6_ADDR_LINKLOCAL)
1459 return true;
1460 return false;
1461}
1462
1463static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001464mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1465 struct mlxsw_sp_neigh_entry *neigh_entry,
1466 bool adding)
1467{
1468 if (!adding && !neigh_entry->connected)
1469 return;
1470 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001471 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001472 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1473 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001474 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001475 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001476 return;
1477 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1478 mlxsw_sp_rauht_op(adding));
1479 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001480 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001481 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001482}
1483
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001484void
1485mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1486 struct mlxsw_sp_neigh_entry *neigh_entry,
1487 bool adding)
1488{
1489 if (adding)
1490 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1491 else
1492 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1493 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1494}
1495
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001496struct mlxsw_sp_neigh_event_work {
1497 struct work_struct work;
1498 struct mlxsw_sp *mlxsw_sp;
1499 struct neighbour *n;
1500};
1501
1502static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1503{
1504 struct mlxsw_sp_neigh_event_work *neigh_work =
1505 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1506 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1507 struct mlxsw_sp_neigh_entry *neigh_entry;
1508 struct neighbour *n = neigh_work->n;
1509 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001510 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001511 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001512
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001513 /* If these parameters are changed after we release the lock,
1514 * then we are guaranteed to receive another event letting us
1515 * know about it.
1516 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001517 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001518 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001519 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001520 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001521 read_unlock_bh(&n->lock);
1522
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001523 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001524 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001525 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1526 if (!entry_connected && !neigh_entry)
1527 goto out;
1528 if (!neigh_entry) {
1529 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1530 if (IS_ERR(neigh_entry))
1531 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001532 }
1533
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001534 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1535 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1536 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1537
1538 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1539 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1540
1541out:
1542 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001543 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001544 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001545}
1546
Jiri Pirkoe7322632016-09-01 10:37:43 +02001547int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1548 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001549{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001550 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001551 struct mlxsw_sp_port *mlxsw_sp_port;
1552 struct mlxsw_sp *mlxsw_sp;
1553 unsigned long interval;
1554 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001555 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001556
1557 switch (event) {
1558 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1559 p = ptr;
1560
1561 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001562 if (!p->dev || (p->tbl->family != AF_INET &&
1563 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001564 return NOTIFY_DONE;
1565
1566 /* We are in atomic context and can't take RTNL mutex,
1567 * so use RCU variant to walk the device chain.
1568 */
1569 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1570 if (!mlxsw_sp_port)
1571 return NOTIFY_DONE;
1572
1573 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1574 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001575 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001576
1577 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1578 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001579 case NETEVENT_NEIGH_UPDATE:
1580 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001581
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001582 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001583 return NOTIFY_DONE;
1584
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001585 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001586 if (!mlxsw_sp_port)
1587 return NOTIFY_DONE;
1588
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001589 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1590 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001591 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001592 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001593 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001594
1595 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1596 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1597 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001598
1599 /* Take a reference to ensure the neighbour won't be
1600 * destructed until we drop the reference in delayed
1601 * work.
1602 */
1603 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001604 mlxsw_core_schedule_work(&neigh_work->work);
1605 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001606 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001607 }
1608
1609 return NOTIFY_DONE;
1610}
1611
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001612static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1613{
Yotam Gigic723c7352016-07-05 11:27:43 +02001614 int err;
1615
Ido Schimmel9011b672017-05-16 19:38:25 +02001616 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001617 &mlxsw_sp_neigh_ht_params);
1618 if (err)
1619 return err;
1620
1621 /* Initialize the polling interval according to the default
1622 * table.
1623 */
1624 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1625
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001626 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001627 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001628 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001629 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001630 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001631 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1632 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001633 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001634}
1635
1636static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1637{
Ido Schimmel9011b672017-05-16 19:38:25 +02001638 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1639 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1640 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001641}
1642
Ido Schimmel9665b742017-02-08 11:16:42 +01001643static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001644 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001645{
1646 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1647
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001648 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001649 rif_list_node) {
1650 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001651 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001652 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001653}
1654
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001655struct mlxsw_sp_nexthop_key {
1656 struct fib_nh *fib_nh;
1657};
1658
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001659struct mlxsw_sp_nexthop {
1660 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01001661 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001662 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1663 * this belongs to
1664 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001665 struct rhash_head ht_node;
1666 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001667 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001668 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001669 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001670 u8 should_offload:1, /* set indicates this neigh is connected and
1671 * should be put to KVD linear area of this group.
1672 */
1673 offloaded:1, /* set in case the neigh is actually put into
1674 * KVD linear area of this group.
1675 */
1676 update:1; /* set indicates that MAC of this neigh should be
1677 * updated in HW
1678 */
1679 struct mlxsw_sp_neigh_entry *neigh_entry;
1680};
1681
1682struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001683 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001684 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001685 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02001686 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01001687 u8 adj_index_valid:1,
1688 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001689 u32 adj_index;
1690 u16 ecmp_size;
1691 u16 count;
1692 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001693#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001694};
1695
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001696static struct fib_info *
1697mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
1698{
1699 return nh_grp->priv;
1700}
1701
1702struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001703 enum mlxsw_sp_l3proto proto;
1704 union {
1705 struct fib_info *fi;
1706 struct mlxsw_sp_fib6_entry *fib6_entry;
1707 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001708};
1709
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001710static bool
1711mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
1712 const struct in6_addr *gw, int ifindex)
1713{
1714 int i;
1715
1716 for (i = 0; i < nh_grp->count; i++) {
1717 const struct mlxsw_sp_nexthop *nh;
1718
1719 nh = &nh_grp->nexthops[i];
1720 if (nh->ifindex == ifindex &&
1721 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
1722 return true;
1723 }
1724
1725 return false;
1726}
1727
1728static bool
1729mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
1730 const struct mlxsw_sp_fib6_entry *fib6_entry)
1731{
1732 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1733
1734 if (nh_grp->count != fib6_entry->nrt6)
1735 return false;
1736
1737 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1738 struct in6_addr *gw;
1739 int ifindex;
1740
1741 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
1742 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
1743 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
1744 return false;
1745 }
1746
1747 return true;
1748}
1749
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001750static int
1751mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
1752{
1753 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
1754 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
1755
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001756 switch (cmp_arg->proto) {
1757 case MLXSW_SP_L3_PROTO_IPV4:
1758 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
1759 case MLXSW_SP_L3_PROTO_IPV6:
1760 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
1761 cmp_arg->fib6_entry);
1762 default:
1763 WARN_ON(1);
1764 return 1;
1765 }
1766}
1767
1768static int
1769mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
1770{
1771 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001772}
1773
1774static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
1775{
1776 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001777 const struct mlxsw_sp_nexthop *nh;
1778 struct fib_info *fi;
1779 unsigned int val;
1780 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001781
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001782 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
1783 case AF_INET:
1784 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
1785 return jhash(&fi, sizeof(fi), seed);
1786 case AF_INET6:
1787 val = nh_grp->count;
1788 for (i = 0; i < nh_grp->count; i++) {
1789 nh = &nh_grp->nexthops[i];
1790 val ^= nh->ifindex;
1791 }
1792 return jhash(&val, sizeof(val), seed);
1793 default:
1794 WARN_ON(1);
1795 return 0;
1796 }
1797}
1798
1799static u32
1800mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
1801{
1802 unsigned int val = fib6_entry->nrt6;
1803 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
1804 struct net_device *dev;
1805
1806 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
1807 dev = mlxsw_sp_rt6->rt->dst.dev;
1808 val ^= dev->ifindex;
1809 }
1810
1811 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001812}
1813
1814static u32
1815mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
1816{
1817 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
1818
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001819 switch (cmp_arg->proto) {
1820 case MLXSW_SP_L3_PROTO_IPV4:
1821 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
1822 case MLXSW_SP_L3_PROTO_IPV6:
1823 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
1824 default:
1825 WARN_ON(1);
1826 return 0;
1827 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001828}
1829
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001830static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001831 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001832 .hashfn = mlxsw_sp_nexthop_group_hash,
1833 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
1834 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001835};
1836
1837static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1838 struct mlxsw_sp_nexthop_group *nh_grp)
1839{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001840 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1841 !nh_grp->gateway)
1842 return 0;
1843
Ido Schimmel9011b672017-05-16 19:38:25 +02001844 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001845 &nh_grp->ht_node,
1846 mlxsw_sp_nexthop_group_ht_params);
1847}
1848
1849static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1850 struct mlxsw_sp_nexthop_group *nh_grp)
1851{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001852 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
1853 !nh_grp->gateway)
1854 return;
1855
Ido Schimmel9011b672017-05-16 19:38:25 +02001856 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001857 &nh_grp->ht_node,
1858 mlxsw_sp_nexthop_group_ht_params);
1859}
1860
1861static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001862mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
1863 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001864{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001865 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1866
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001867 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02001868 cmp_arg.fi = fi;
1869 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1870 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01001871 mlxsw_sp_nexthop_group_ht_params);
1872}
1873
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02001874static struct mlxsw_sp_nexthop_group *
1875mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
1876 struct mlxsw_sp_fib6_entry *fib6_entry)
1877{
1878 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
1879
1880 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
1881 cmp_arg.fib6_entry = fib6_entry;
1882 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
1883 &cmp_arg,
1884 mlxsw_sp_nexthop_group_ht_params);
1885}
1886
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001887static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1888 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1889 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1890 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1891};
1892
1893static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1894 struct mlxsw_sp_nexthop *nh)
1895{
Ido Schimmel9011b672017-05-16 19:38:25 +02001896 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001897 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1898}
1899
1900static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1901 struct mlxsw_sp_nexthop *nh)
1902{
Ido Schimmel9011b672017-05-16 19:38:25 +02001903 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001904 mlxsw_sp_nexthop_ht_params);
1905}
1906
Ido Schimmelad178c82017-02-08 11:16:40 +01001907static struct mlxsw_sp_nexthop *
1908mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1909 struct mlxsw_sp_nexthop_key key)
1910{
Ido Schimmel9011b672017-05-16 19:38:25 +02001911 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01001912 mlxsw_sp_nexthop_ht_params);
1913}
1914
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001915static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001916 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001917 u32 adj_index, u16 ecmp_size,
1918 u32 new_adj_index,
1919 u16 new_ecmp_size)
1920{
1921 char raleu_pl[MLXSW_REG_RALEU_LEN];
1922
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001923 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01001924 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1925 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02001926 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001927 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1928}
1929
1930static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1931 struct mlxsw_sp_nexthop_group *nh_grp,
1932 u32 old_adj_index, u16 old_ecmp_size)
1933{
1934 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001935 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001936 int err;
1937
1938 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01001939 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001940 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01001941 fib = fib_entry->fib_node->fib;
1942 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001943 old_adj_index,
1944 old_ecmp_size,
1945 nh_grp->adj_index,
1946 nh_grp->ecmp_size);
1947 if (err)
1948 return err;
1949 }
1950 return 0;
1951}
1952
1953static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1954 struct mlxsw_sp_nexthop *nh)
1955{
1956 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1957 char ratr_pl[MLXSW_REG_RATR_LEN];
1958
1959 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02001960 true, MLXSW_REG_RATR_TYPE_ETHERNET,
1961 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001962 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1963 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1964}
1965
1966static int
1967mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
Ido Schimmela59b7e02017-01-23 11:11:42 +01001968 struct mlxsw_sp_nexthop_group *nh_grp,
1969 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001970{
1971 u32 adj_index = nh_grp->adj_index; /* base */
1972 struct mlxsw_sp_nexthop *nh;
1973 int i;
1974 int err;
1975
1976 for (i = 0; i < nh_grp->count; i++) {
1977 nh = &nh_grp->nexthops[i];
1978
1979 if (!nh->should_offload) {
1980 nh->offloaded = 0;
1981 continue;
1982 }
1983
Ido Schimmela59b7e02017-01-23 11:11:42 +01001984 if (nh->update || reallocate) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001985 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1986 adj_index, nh);
1987 if (err)
1988 return err;
1989 nh->update = 0;
1990 nh->offloaded = 1;
1991 }
1992 adj_index++;
1993 }
1994 return 0;
1995}
1996
1997static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1998 struct mlxsw_sp_fib_entry *fib_entry);
1999
Ido Schimmel1819ae32017-07-21 18:04:28 +02002000static bool
2001mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2002 const struct mlxsw_sp_fib_entry *fib_entry);
2003
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002004static int
2005mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2006 struct mlxsw_sp_nexthop_group *nh_grp)
2007{
2008 struct mlxsw_sp_fib_entry *fib_entry;
2009 int err;
2010
2011 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002012 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2013 fib_entry))
2014 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002015 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2016 if (err)
2017 return err;
2018 }
2019 return 0;
2020}
2021
2022static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002023mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2024 enum mlxsw_reg_ralue_op op, int err);
2025
2026static void
2027mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2028{
2029 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2030 struct mlxsw_sp_fib_entry *fib_entry;
2031
2032 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2033 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2034 fib_entry))
2035 continue;
2036 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2037 }
2038}
2039
2040static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002041mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2042 struct mlxsw_sp_nexthop_group *nh_grp)
2043{
2044 struct mlxsw_sp_nexthop *nh;
2045 bool offload_change = false;
2046 u32 adj_index;
2047 u16 ecmp_size = 0;
2048 bool old_adj_index_valid;
2049 u32 old_adj_index;
2050 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002051 int i;
2052 int err;
2053
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002054 if (!nh_grp->gateway) {
2055 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2056 return;
2057 }
2058
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002059 for (i = 0; i < nh_grp->count; i++) {
2060 nh = &nh_grp->nexthops[i];
2061
Petr Machata56b8a9e2017-07-31 09:27:29 +02002062 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002063 offload_change = true;
2064 if (nh->should_offload)
2065 nh->update = 1;
2066 }
2067 if (nh->should_offload)
2068 ecmp_size++;
2069 }
2070 if (!offload_change) {
2071 /* Nothing was added or removed, so no need to reallocate. Just
2072 * update MAC on existing adjacency indexes.
2073 */
Ido Schimmela59b7e02017-01-23 11:11:42 +01002074 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
2075 false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002076 if (err) {
2077 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2078 goto set_trap;
2079 }
2080 return;
2081 }
2082 if (!ecmp_size)
2083 /* No neigh of this group is connected so we just set
2084 * the trap and let everthing flow through kernel.
2085 */
2086 goto set_trap;
2087
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002088 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2089 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002090 /* We ran out of KVD linear space, just set the
2091 * trap and let everything flow through kernel.
2092 */
2093 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2094 goto set_trap;
2095 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002096 old_adj_index_valid = nh_grp->adj_index_valid;
2097 old_adj_index = nh_grp->adj_index;
2098 old_ecmp_size = nh_grp->ecmp_size;
2099 nh_grp->adj_index_valid = 1;
2100 nh_grp->adj_index = adj_index;
2101 nh_grp->ecmp_size = ecmp_size;
Ido Schimmela59b7e02017-01-23 11:11:42 +01002102 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002103 if (err) {
2104 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2105 goto set_trap;
2106 }
2107
2108 if (!old_adj_index_valid) {
2109 /* The trap was set for fib entries, so we have to call
2110 * fib entry update to unset it and use adjacency index.
2111 */
2112 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2113 if (err) {
2114 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2115 goto set_trap;
2116 }
2117 return;
2118 }
2119
2120 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2121 old_adj_index, old_ecmp_size);
2122 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2123 if (err) {
2124 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2125 goto set_trap;
2126 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002127
2128 /* Offload state within the group changed, so update the flags. */
2129 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2130
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002131 return;
2132
2133set_trap:
2134 old_adj_index_valid = nh_grp->adj_index_valid;
2135 nh_grp->adj_index_valid = 0;
2136 for (i = 0; i < nh_grp->count; i++) {
2137 nh = &nh_grp->nexthops[i];
2138 nh->offloaded = 0;
2139 }
2140 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2141 if (err)
2142 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2143 if (old_adj_index_valid)
2144 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2145}
2146
2147static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2148 bool removing)
2149{
Petr Machata213666a2017-07-31 09:27:30 +02002150 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002151 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002152 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002153 nh->should_offload = 0;
2154 nh->update = 1;
2155}
2156
2157static void
2158mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2159 struct mlxsw_sp_neigh_entry *neigh_entry,
2160 bool removing)
2161{
2162 struct mlxsw_sp_nexthop *nh;
2163
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002164 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2165 neigh_list_node) {
2166 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2167 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2168 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002169}
2170
Ido Schimmel9665b742017-02-08 11:16:42 +01002171static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002172 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002173{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002174 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002175 return;
2176
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002177 nh->rif = rif;
2178 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002179}
2180
2181static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2182{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002183 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002184 return;
2185
2186 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002187 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002188}
2189
Ido Schimmela8c97012017-02-08 11:16:35 +01002190static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2191 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002192{
2193 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002194 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002195 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002196 int err;
2197
Ido Schimmelad178c82017-02-08 11:16:40 +01002198 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002199 return 0;
2200
Jiri Pirko33b13412016-11-10 12:31:04 +01002201 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002202 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002203 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002204 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002205 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002206 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002207 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002208 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2209 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002210 if (IS_ERR(n))
2211 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002212 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002213 }
2214 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2215 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002216 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2217 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002218 err = -EINVAL;
2219 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002220 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002221 }
Yotam Gigib2157142016-07-05 11:27:51 +02002222
2223 /* If that is the first nexthop connected to that neigh, add to
2224 * nexthop_neighs_list
2225 */
2226 if (list_empty(&neigh_entry->nexthop_list))
2227 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002228 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002229
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002230 nh->neigh_entry = neigh_entry;
2231 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2232 read_lock_bh(&n->lock);
2233 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002234 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002235 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002236 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002237
2238 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002239
2240err_neigh_entry_create:
2241 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002242 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002243}
2244
Ido Schimmela8c97012017-02-08 11:16:35 +01002245static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2246 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002247{
2248 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002249 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002250
Ido Schimmelb8399a12017-02-08 11:16:33 +01002251 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002252 return;
2253 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002254
Ido Schimmel58312122016-12-23 09:32:50 +01002255 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002256 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002257 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002258
2259 /* If that is the last nexthop connected to that neigh, remove from
2260 * nexthop_neighs_list
2261 */
Ido Schimmele58be792017-02-08 11:16:28 +01002262 if (list_empty(&neigh_entry->nexthop_list))
2263 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002264
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002265 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2266 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2267
2268 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002269}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002270
Petr Machata6ddb7422017-09-02 23:49:19 +02002271static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2272 const struct net_device *dev,
2273 enum mlxsw_sp_ipip_type *p_type)
2274{
2275 struct mlxsw_sp_router *router = mlxsw_sp->router;
2276 const struct mlxsw_sp_ipip_ops *ipip_ops;
2277 enum mlxsw_sp_ipip_type ipipt;
2278
2279 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2280 ipip_ops = router->ipip_ops_arr[ipipt];
2281 if (dev->type == ipip_ops->dev_type) {
2282 if (p_type)
2283 *p_type = ipipt;
2284 return true;
2285 }
2286 }
2287 return false;
2288}
2289
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002290static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2291 struct mlxsw_sp_nexthop_group *nh_grp,
2292 struct mlxsw_sp_nexthop *nh,
2293 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002294{
2295 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002296 struct in_device *in_dev;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002297 struct mlxsw_sp_rif *rif;
Ido Schimmela8c97012017-02-08 11:16:35 +01002298 int err;
2299
2300 nh->nh_grp = nh_grp;
2301 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002302 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002303 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2304 if (err)
2305 return err;
2306
Ido Schimmel97989ee2017-03-10 08:53:38 +01002307 if (!dev)
2308 return 0;
2309
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002310 in_dev = __in_dev_get_rtnl(dev);
2311 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2312 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2313 return 0;
2314
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002315 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2316 if (!rif)
Ido Schimmela8c97012017-02-08 11:16:35 +01002317 return 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002318 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmela8c97012017-02-08 11:16:35 +01002319
2320 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2321 if (err)
2322 goto err_nexthop_neigh_init;
2323
2324 return 0;
2325
2326err_nexthop_neigh_init:
Ido Schimmela4e75b72017-07-12 09:12:52 +02002327 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002328 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2329 return err;
2330}
2331
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002332static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2333 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002334{
2335 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002336 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002337 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002338}
2339
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002340static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2341 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002342{
2343 struct mlxsw_sp_nexthop_key key;
2344 struct mlxsw_sp_nexthop *nh;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002345 struct mlxsw_sp_rif *rif;
Ido Schimmelad178c82017-02-08 11:16:40 +01002346
Ido Schimmel9011b672017-05-16 19:38:25 +02002347 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002348 return;
2349
2350 key.fib_nh = fib_nh;
2351 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2352 if (WARN_ON_ONCE(!nh))
2353 return;
2354
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002355 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
2356 if (!rif)
Ido Schimmelad178c82017-02-08 11:16:40 +01002357 return;
2358
2359 switch (event) {
2360 case FIB_EVENT_NH_ADD:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002361 mlxsw_sp_nexthop_rif_init(nh, rif);
Ido Schimmelad178c82017-02-08 11:16:40 +01002362 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2363 break;
2364 case FIB_EVENT_NH_DEL:
2365 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002366 mlxsw_sp_nexthop_rif_fini(nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002367 break;
2368 }
2369
2370 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2371}
2372
Ido Schimmel9665b742017-02-08 11:16:42 +01002373static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002374 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002375{
2376 struct mlxsw_sp_nexthop *nh, *tmp;
2377
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002378 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Ido Schimmel9665b742017-02-08 11:16:42 +01002379 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2380 mlxsw_sp_nexthop_rif_fini(nh);
2381 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2382 }
2383}
2384
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002385static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002386mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002387{
2388 struct mlxsw_sp_nexthop_group *nh_grp;
2389 struct mlxsw_sp_nexthop *nh;
2390 struct fib_nh *fib_nh;
2391 size_t alloc_size;
2392 int i;
2393 int err;
2394
2395 alloc_size = sizeof(*nh_grp) +
2396 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2397 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2398 if (!nh_grp)
2399 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002400 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002401 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002402 nh_grp->neigh_tbl = &arp_tbl;
2403
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002404 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002405 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002406 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002407 for (i = 0; i < nh_grp->count; i++) {
2408 nh = &nh_grp->nexthops[i];
2409 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002410 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002411 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002412 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002413 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002414 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2415 if (err)
2416 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002417 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2418 return nh_grp;
2419
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002420err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002421err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002422 for (i--; i >= 0; i--) {
2423 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002424 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002425 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002426 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002427 kfree(nh_grp);
2428 return ERR_PTR(err);
2429}
2430
2431static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002432mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2433 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002434{
2435 struct mlxsw_sp_nexthop *nh;
2436 int i;
2437
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002438 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002439 for (i = 0; i < nh_grp->count; i++) {
2440 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002441 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002442 }
Ido Schimmel58312122016-12-23 09:32:50 +01002443 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2444 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002445 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002446 kfree(nh_grp);
2447}
2448
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002449static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2450 struct mlxsw_sp_fib_entry *fib_entry,
2451 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002452{
2453 struct mlxsw_sp_nexthop_group *nh_grp;
2454
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002455 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002456 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002457 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002458 if (IS_ERR(nh_grp))
2459 return PTR_ERR(nh_grp);
2460 }
2461 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2462 fib_entry->nh_group = nh_grp;
2463 return 0;
2464}
2465
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002466static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2467 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002468{
2469 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2470
2471 list_del(&fib_entry->nexthop_group_node);
2472 if (!list_empty(&nh_grp->fib_list))
2473 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002474 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002475}
2476
Ido Schimmel013b20f2017-02-08 11:16:36 +01002477static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002478mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2479{
2480 struct mlxsw_sp_fib4_entry *fib4_entry;
2481
2482 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2483 common);
2484 return !fib4_entry->tos;
2485}
2486
2487static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002488mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2489{
2490 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2491
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002492 switch (fib_entry->fib_node->fib->proto) {
2493 case MLXSW_SP_L3_PROTO_IPV4:
2494 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2495 return false;
2496 break;
2497 case MLXSW_SP_L3_PROTO_IPV6:
2498 break;
2499 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002500
Ido Schimmel013b20f2017-02-08 11:16:36 +01002501 switch (fib_entry->type) {
2502 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2503 return !!nh_group->adj_index_valid;
2504 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002505 return !!nh_group->nh_rif;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002506 default:
2507 return false;
2508 }
2509}
2510
Ido Schimmel428b8512017-08-03 13:28:28 +02002511static struct mlxsw_sp_nexthop *
2512mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2513 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2514{
2515 int i;
2516
2517 for (i = 0; i < nh_grp->count; i++) {
2518 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2519 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2520
2521 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2522 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2523 &rt->rt6i_gateway))
2524 return nh;
2525 continue;
2526 }
2527
2528 return NULL;
2529}
2530
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002531static void
2532mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2533{
2534 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2535 int i;
2536
2537 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2538 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2539 return;
2540 }
2541
2542 for (i = 0; i < nh_grp->count; i++) {
2543 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2544
2545 if (nh->offloaded)
2546 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2547 else
2548 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2549 }
2550}
2551
2552static void
2553mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2554{
2555 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2556 int i;
2557
2558 for (i = 0; i < nh_grp->count; i++) {
2559 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2560
2561 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2562 }
2563}
2564
Ido Schimmel428b8512017-08-03 13:28:28 +02002565static void
2566mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2567{
2568 struct mlxsw_sp_fib6_entry *fib6_entry;
2569 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2570
2571 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2572 common);
2573
2574 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2575 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02002576 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002577 return;
2578 }
2579
2580 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2581 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2582 struct mlxsw_sp_nexthop *nh;
2583
2584 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
2585 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02002586 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002587 else
Ido Schimmelfe400792017-08-15 09:09:49 +02002588 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002589 }
2590}
2591
2592static void
2593mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2594{
2595 struct mlxsw_sp_fib6_entry *fib6_entry;
2596 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2597
2598 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
2599 common);
2600 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2601 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2602
Ido Schimmelfe400792017-08-15 09:09:49 +02002603 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02002604 }
2605}
2606
Ido Schimmel013b20f2017-02-08 11:16:36 +01002607static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2608{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002609 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002610 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002611 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002612 break;
2613 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002614 mlxsw_sp_fib6_entry_offload_set(fib_entry);
2615 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002616 }
2617}
2618
2619static void
2620mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2621{
Ido Schimmel76610eb2017-03-10 08:53:41 +01002622 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01002623 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002624 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002625 break;
2626 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02002627 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
2628 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002629 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01002630}
2631
2632static void
2633mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2634 enum mlxsw_reg_ralue_op op, int err)
2635{
2636 switch (op) {
2637 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01002638 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2639 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2640 if (err)
2641 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02002642 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002643 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02002644 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01002645 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2646 return;
2647 default:
2648 return;
2649 }
2650}
2651
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002652static void
2653mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2654 const struct mlxsw_sp_fib_entry *fib_entry,
2655 enum mlxsw_reg_ralue_op op)
2656{
2657 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
2658 enum mlxsw_reg_ralxx_protocol proto;
2659 u32 *p_dip;
2660
2661 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2662
2663 switch (fib->proto) {
2664 case MLXSW_SP_L3_PROTO_IPV4:
2665 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2666 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2667 fib_entry->fib_node->key.prefix_len,
2668 *p_dip);
2669 break;
2670 case MLXSW_SP_L3_PROTO_IPV6:
2671 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2672 fib_entry->fib_node->key.prefix_len,
2673 fib_entry->fib_node->key.addr);
2674 break;
2675 }
2676}
2677
2678static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2679 struct mlxsw_sp_fib_entry *fib_entry,
2680 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002681{
2682 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002683 enum mlxsw_reg_ralue_trap_action trap_action;
2684 u16 trap_id = 0;
2685 u32 adjacency_index = 0;
2686 u16 ecmp_size = 0;
2687
2688 /* In case the nexthop group adjacency index is valid, use it
2689 * with provided ECMP size. Otherwise, setup trap and pass
2690 * traffic to kernel.
2691 */
Ido Schimmel4b411472017-02-08 11:16:37 +01002692 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002693 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2694 adjacency_index = fib_entry->nh_group->adj_index;
2695 ecmp_size = fib_entry->nh_group->ecmp_size;
2696 } else {
2697 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2698 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2699 }
2700
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002701 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002702 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2703 adjacency_index, ecmp_size);
2704 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2705}
2706
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002707static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2708 struct mlxsw_sp_fib_entry *fib_entry,
2709 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002710{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002711 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002712 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002713 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01002714 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002715 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002716
2717 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2718 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002719 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01002720 } else {
2721 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2722 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2723 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02002724
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002725 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002726 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2727 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002728 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2729}
2730
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002731static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2732 struct mlxsw_sp_fib_entry *fib_entry,
2733 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002734{
2735 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02002736
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002737 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002738 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2739 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2740}
2741
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002742static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2743 struct mlxsw_sp_fib_entry *fib_entry,
2744 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002745{
2746 switch (fib_entry->type) {
2747 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002748 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002749 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002750 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002751 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002752 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002753 }
2754 return -EINVAL;
2755}
2756
2757static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2758 struct mlxsw_sp_fib_entry *fib_entry,
2759 enum mlxsw_reg_ralue_op op)
2760{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002761 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01002762
Ido Schimmel013b20f2017-02-08 11:16:36 +01002763 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02002764
Ido Schimmel013b20f2017-02-08 11:16:36 +01002765 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002766}
2767
2768static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2769 struct mlxsw_sp_fib_entry *fib_entry)
2770{
Jiri Pirko7146da32016-09-01 10:37:41 +02002771 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2772 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02002773}
2774
2775static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2776 struct mlxsw_sp_fib_entry *fib_entry)
2777{
2778 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2779 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2780}
2781
Jiri Pirko61c503f2016-07-04 08:23:11 +02002782static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01002783mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2784 const struct fib_entry_notifier_info *fen_info,
2785 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02002786{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002787 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02002788
Ido Schimmel97989ee2017-03-10 08:53:38 +01002789 switch (fen_info->type) {
2790 case RTN_BROADCAST: /* fall through */
2791 case RTN_LOCAL:
Jiri Pirko61c503f2016-07-04 08:23:11 +02002792 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2793 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002794 case RTN_UNREACHABLE: /* fall through */
2795 case RTN_BLACKHOLE: /* fall through */
2796 case RTN_PROHIBIT:
2797 /* Packets hitting these routes need to be trapped, but
2798 * can do so with a lower priority than packets directed
2799 * at the host, so use action type local instead of trap.
2800 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02002801 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01002802 return 0;
2803 case RTN_UNICAST:
2804 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2805 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2806 else
2807 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2808 return 0;
2809 default:
2810 return -EINVAL;
2811 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002812}
2813
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002814static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002815mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2816 struct mlxsw_sp_fib_node *fib_node,
2817 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02002818{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002819 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02002820 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002821 int err;
2822
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002823 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2824 if (!fib4_entry)
2825 return ERR_PTR(-ENOMEM);
2826 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002827
2828 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
2829 if (err)
2830 goto err_fib4_entry_type_set;
2831
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002832 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002833 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002834 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002835
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002836 fib4_entry->prio = fen_info->fi->fib_priority;
2837 fib4_entry->tb_id = fen_info->tb_id;
2838 fib4_entry->type = fen_info->type;
2839 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002840
2841 fib_entry->fib_node = fib_node;
2842
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002843 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002844
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002845err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01002846err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002847 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002848 return ERR_PTR(err);
2849}
2850
2851static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002852 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002853{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002854 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002855 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002856}
2857
2858static struct mlxsw_sp_fib_node *
Ido Schimmel160e22a2017-07-18 10:10:20 +02002859mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2860 size_t addr_len, unsigned char prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002861
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002862static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01002863mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2864 const struct fib_entry_notifier_info *fen_info)
2865{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002866 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002867 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02002868 struct mlxsw_sp_fib *fib;
2869 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002870
Ido Schimmel160e22a2017-07-18 10:10:20 +02002871 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2872 if (!vr)
2873 return NULL;
2874 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
2875
2876 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2877 sizeof(fen_info->dst),
2878 fen_info->dst_len);
2879 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01002880 return NULL;
2881
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002882 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2883 if (fib4_entry->tb_id == fen_info->tb_id &&
2884 fib4_entry->tos == fen_info->tos &&
2885 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002886 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
2887 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002888 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002889 }
2890 }
2891
2892 return NULL;
2893}
2894
2895static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2896 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2897 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2898 .key_len = sizeof(struct mlxsw_sp_fib_key),
2899 .automatic_shrinking = true,
2900};
2901
2902static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2903 struct mlxsw_sp_fib_node *fib_node)
2904{
2905 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2906 mlxsw_sp_fib_ht_params);
2907}
2908
2909static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2910 struct mlxsw_sp_fib_node *fib_node)
2911{
2912 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2913 mlxsw_sp_fib_ht_params);
2914}
2915
2916static struct mlxsw_sp_fib_node *
2917mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2918 size_t addr_len, unsigned char prefix_len)
2919{
2920 struct mlxsw_sp_fib_key key;
2921
2922 memset(&key, 0, sizeof(key));
2923 memcpy(key.addr, addr, addr_len);
2924 key.prefix_len = prefix_len;
2925 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2926}
2927
2928static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01002929mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01002930 size_t addr_len, unsigned char prefix_len)
2931{
2932 struct mlxsw_sp_fib_node *fib_node;
2933
2934 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2935 if (!fib_node)
2936 return NULL;
2937
2938 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01002939 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01002940 memcpy(fib_node->key.addr, addr, addr_len);
2941 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01002942
2943 return fib_node;
2944}
2945
2946static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2947{
Ido Schimmel9aecce12017-02-09 10:28:42 +01002948 list_del(&fib_node->list);
2949 WARN_ON(!list_empty(&fib_node->entry_list));
2950 kfree(fib_node);
2951}
2952
2953static bool
2954mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2955 const struct mlxsw_sp_fib_entry *fib_entry)
2956{
2957 return list_first_entry(&fib_node->entry_list,
2958 struct mlxsw_sp_fib_entry, list) == fib_entry;
2959}
2960
Ido Schimmelfc922bb2017-08-14 10:54:05 +02002961static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
2962 struct mlxsw_sp_fib *fib,
2963 struct mlxsw_sp_fib_node *fib_node)
2964{
2965 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
2966 struct mlxsw_sp_lpm_tree *lpm_tree;
2967 int err;
2968
2969 /* Since the tree is shared between all virtual routers we must
2970 * make sure it contains all the required prefix lengths. This
2971 * can be computed by either adding the new prefix length to the
2972 * existing prefix usage of a bound tree, or by aggregating the
2973 * prefix lengths across all virtual routers and adding the new
2974 * one as well.
2975 */
2976 if (fib->lpm_tree)
2977 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
2978 &fib->lpm_tree->prefix_usage);
2979 else
2980 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
2981 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2982
2983 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2984 fib->proto);
2985 if (IS_ERR(lpm_tree))
2986 return PTR_ERR(lpm_tree);
2987
2988 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
2989 return 0;
2990
2991 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
2992 if (err)
2993 return err;
2994
2995 return 0;
2996}
2997
2998static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
2999 struct mlxsw_sp_fib *fib)
3000{
3001 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3002 struct mlxsw_sp_lpm_tree *lpm_tree;
3003
3004 /* Aggregate prefix lengths across all virtual routers to make
3005 * sure we only have used prefix lengths in the LPM tree.
3006 */
3007 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3008 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3009 fib->proto);
3010 if (IS_ERR(lpm_tree))
3011 goto err_tree_get;
3012 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3013
3014err_tree_get:
3015 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3016 return;
3017 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3018 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3019 fib->lpm_tree = NULL;
3020}
3021
Ido Schimmel9aecce12017-02-09 10:28:42 +01003022static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3023{
3024 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003025 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003026
3027 if (fib->prefix_ref_count[prefix_len]++ == 0)
3028 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3029}
3030
3031static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3032{
3033 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003034 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003035
3036 if (--fib->prefix_ref_count[prefix_len] == 0)
3037 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3038}
3039
Ido Schimmel76610eb2017-03-10 08:53:41 +01003040static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3041 struct mlxsw_sp_fib_node *fib_node,
3042 struct mlxsw_sp_fib *fib)
3043{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003044 int err;
3045
3046 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3047 if (err)
3048 return err;
3049 fib_node->fib = fib;
3050
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003051 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3052 if (err)
3053 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003054
3055 mlxsw_sp_fib_node_prefix_inc(fib_node);
3056
3057 return 0;
3058
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003059err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003060 fib_node->fib = NULL;
3061 mlxsw_sp_fib_node_remove(fib, fib_node);
3062 return err;
3063}
3064
3065static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3066 struct mlxsw_sp_fib_node *fib_node)
3067{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003068 struct mlxsw_sp_fib *fib = fib_node->fib;
3069
3070 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003071 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003072 fib_node->fib = NULL;
3073 mlxsw_sp_fib_node_remove(fib, fib_node);
3074}
3075
Ido Schimmel9aecce12017-02-09 10:28:42 +01003076static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003077mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3078 size_t addr_len, unsigned char prefix_len,
3079 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003080{
3081 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003082 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003083 struct mlxsw_sp_vr *vr;
3084 int err;
3085
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003086 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003087 if (IS_ERR(vr))
3088 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003089 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003090
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003091 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003092 if (fib_node)
3093 return fib_node;
3094
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003095 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003096 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003097 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003098 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003099 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003100
Ido Schimmel76610eb2017-03-10 08:53:41 +01003101 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3102 if (err)
3103 goto err_fib_node_init;
3104
Ido Schimmel9aecce12017-02-09 10:28:42 +01003105 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003106
Ido Schimmel76610eb2017-03-10 08:53:41 +01003107err_fib_node_init:
3108 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003109err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003110 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003111 return ERR_PTR(err);
3112}
3113
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003114static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3115 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003116{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003117 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003118
Ido Schimmel9aecce12017-02-09 10:28:42 +01003119 if (!list_empty(&fib_node->entry_list))
3120 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003121 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003122 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003123 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003124}
3125
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003126static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003127mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003128 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003129{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003130 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003131
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003132 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3133 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003134 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003135 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003136 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003137 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003138 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003139 if (fib4_entry->prio >= new4_entry->prio ||
3140 fib4_entry->tos < new4_entry->tos)
3141 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003142 }
3143
3144 return NULL;
3145}
3146
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003147static int
3148mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3149 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003150{
3151 struct mlxsw_sp_fib_node *fib_node;
3152
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003153 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003154 return -EINVAL;
3155
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003156 fib_node = fib4_entry->common.fib_node;
3157 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3158 common.list) {
3159 if (fib4_entry->tb_id != new4_entry->tb_id ||
3160 fib4_entry->tos != new4_entry->tos ||
3161 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003162 break;
3163 }
3164
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003165 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003166 return 0;
3167}
3168
Ido Schimmel9aecce12017-02-09 10:28:42 +01003169static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003170mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003171 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003172{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003173 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003174 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003175
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003176 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003177
Ido Schimmel4283bce2017-02-09 10:28:43 +01003178 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003179 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3180 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003181 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003182
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003183 /* Insert new entry before replaced one, so that we can later
3184 * remove the second.
3185 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003186 if (fib4_entry) {
3187 list_add_tail(&new4_entry->common.list,
3188 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003189 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003190 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003191
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003192 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3193 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003194 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003195 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003196 }
3197
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003198 if (fib4_entry)
3199 list_add(&new4_entry->common.list,
3200 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003201 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003202 list_add(&new4_entry->common.list,
3203 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003204 }
3205
3206 return 0;
3207}
3208
3209static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003210mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003211{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003212 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003213}
3214
Ido Schimmel80c238f2017-07-18 10:10:29 +02003215static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3216 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003217{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003218 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3219
Ido Schimmel9aecce12017-02-09 10:28:42 +01003220 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3221 return 0;
3222
3223 /* To prevent packet loss, overwrite the previously offloaded
3224 * entry.
3225 */
3226 if (!list_is_singular(&fib_node->entry_list)) {
3227 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3228 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3229
3230 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3231 }
3232
3233 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3234}
3235
Ido Schimmel80c238f2017-07-18 10:10:29 +02003236static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3237 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003238{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003239 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3240
Ido Schimmel9aecce12017-02-09 10:28:42 +01003241 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3242 return;
3243
3244 /* Promote the next entry by overwriting the deleted entry */
3245 if (!list_is_singular(&fib_node->entry_list)) {
3246 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3247 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3248
3249 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3250 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3251 return;
3252 }
3253
3254 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3255}
3256
3257static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003258 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003259 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003260{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003261 int err;
3262
Ido Schimmel9efbee62017-07-18 10:10:28 +02003263 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003264 if (err)
3265 return err;
3266
Ido Schimmel80c238f2017-07-18 10:10:29 +02003267 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003268 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003269 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003270
Ido Schimmel9aecce12017-02-09 10:28:42 +01003271 return 0;
3272
Ido Schimmel80c238f2017-07-18 10:10:29 +02003273err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003274 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003275 return err;
3276}
3277
3278static void
3279mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003280 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003281{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003282 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003283 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003284}
3285
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003286static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003287 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003288 bool replace)
3289{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003290 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3291 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003292
3293 if (!replace)
3294 return;
3295
3296 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003297 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003298
3299 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3300 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003301 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003302}
3303
Ido Schimmel9aecce12017-02-09 10:28:42 +01003304static int
3305mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003306 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003307 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003308{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003309 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003310 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003311 int err;
3312
Ido Schimmel9011b672017-05-16 19:38:25 +02003313 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003314 return 0;
3315
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003316 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3317 &fen_info->dst, sizeof(fen_info->dst),
3318 fen_info->dst_len,
3319 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003320 if (IS_ERR(fib_node)) {
3321 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3322 return PTR_ERR(fib_node);
3323 }
3324
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003325 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3326 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003327 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003328 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003329 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003330 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003331
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003332 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003333 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003334 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003335 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3336 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003337 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003338
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003339 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003340
Jiri Pirko61c503f2016-07-04 08:23:11 +02003341 return 0;
3342
Ido Schimmel9aecce12017-02-09 10:28:42 +01003343err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003344 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003345err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003346 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003347 return err;
3348}
3349
Jiri Pirko37956d72016-10-20 16:05:43 +02003350static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3351 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003352{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003353 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003354 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003355
Ido Schimmel9011b672017-05-16 19:38:25 +02003356 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003357 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003358
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003359 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3360 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003361 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003362 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003363
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003364 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3365 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003366 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003367}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003368
Ido Schimmel428b8512017-08-03 13:28:28 +02003369static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3370{
3371 /* Packets with link-local destination IP arriving to the router
3372 * are trapped to the CPU, so no need to program specific routes
3373 * for them.
3374 */
3375 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3376 return true;
3377
3378 /* Multicast routes aren't supported, so ignore them. Neighbour
3379 * Discovery packets are specifically trapped.
3380 */
3381 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3382 return true;
3383
3384 /* Cloned routes are irrelevant in the forwarding path. */
3385 if (rt->rt6i_flags & RTF_CACHE)
3386 return true;
3387
3388 return false;
3389}
3390
3391static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3392{
3393 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3394
3395 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3396 if (!mlxsw_sp_rt6)
3397 return ERR_PTR(-ENOMEM);
3398
3399 /* In case of route replace, replaced route is deleted with
3400 * no notification. Take reference to prevent accessing freed
3401 * memory.
3402 */
3403 mlxsw_sp_rt6->rt = rt;
3404 rt6_hold(rt);
3405
3406 return mlxsw_sp_rt6;
3407}
3408
3409#if IS_ENABLED(CONFIG_IPV6)
3410static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3411{
3412 rt6_release(rt);
3413}
3414#else
3415static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3416{
3417}
3418#endif
3419
3420static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3421{
3422 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3423 kfree(mlxsw_sp_rt6);
3424}
3425
3426static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3427{
3428 /* RTF_CACHE routes are ignored */
3429 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3430}
3431
3432static struct rt6_info *
3433mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3434{
3435 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3436 list)->rt;
3437}
3438
3439static struct mlxsw_sp_fib6_entry *
3440mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003441 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003442{
3443 struct mlxsw_sp_fib6_entry *fib6_entry;
3444
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003445 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003446 return NULL;
3447
3448 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3449 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3450
3451 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3452 * virtual router.
3453 */
3454 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3455 continue;
3456 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3457 break;
3458 if (rt->rt6i_metric < nrt->rt6i_metric)
3459 continue;
3460 if (rt->rt6i_metric == nrt->rt6i_metric &&
3461 mlxsw_sp_fib6_rt_can_mp(rt))
3462 return fib6_entry;
3463 if (rt->rt6i_metric > nrt->rt6i_metric)
3464 break;
3465 }
3466
3467 return NULL;
3468}
3469
3470static struct mlxsw_sp_rt6 *
3471mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3472 const struct rt6_info *rt)
3473{
3474 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3475
3476 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3477 if (mlxsw_sp_rt6->rt == rt)
3478 return mlxsw_sp_rt6;
3479 }
3480
3481 return NULL;
3482}
3483
3484static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
3485 struct mlxsw_sp_nexthop_group *nh_grp,
3486 struct mlxsw_sp_nexthop *nh,
3487 const struct rt6_info *rt)
3488{
3489 struct net_device *dev = rt->dst.dev;
3490 struct mlxsw_sp_rif *rif;
3491 int err;
3492
3493 nh->nh_grp = nh_grp;
3494 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
3495
3496 if (!dev)
3497 return 0;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003498 nh->ifindex = dev->ifindex;
Ido Schimmel428b8512017-08-03 13:28:28 +02003499
3500 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3501 if (!rif)
3502 return 0;
3503 mlxsw_sp_nexthop_rif_init(nh, rif);
3504
3505 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3506 if (err)
3507 goto err_nexthop_neigh_init;
3508
3509 return 0;
3510
3511err_nexthop_neigh_init:
3512 mlxsw_sp_nexthop_rif_fini(nh);
3513 return err;
3514}
3515
3516static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
3517 struct mlxsw_sp_nexthop *nh)
3518{
3519 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
3520 mlxsw_sp_nexthop_rif_fini(nh);
3521}
3522
3523static struct mlxsw_sp_nexthop_group *
3524mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
3525 struct mlxsw_sp_fib6_entry *fib6_entry)
3526{
3527 struct mlxsw_sp_nexthop_group *nh_grp;
3528 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3529 struct mlxsw_sp_nexthop *nh;
3530 size_t alloc_size;
3531 int i = 0;
3532 int err;
3533
3534 alloc_size = sizeof(*nh_grp) +
3535 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
3536 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
3537 if (!nh_grp)
3538 return ERR_PTR(-ENOMEM);
3539 INIT_LIST_HEAD(&nh_grp->fib_list);
3540#if IS_ENABLED(CONFIG_IPV6)
3541 nh_grp->neigh_tbl = &nd_tbl;
3542#endif
3543 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
3544 struct mlxsw_sp_rt6, list);
3545 nh_grp->gateway = !!(mlxsw_sp_rt6->rt->rt6i_flags & RTF_GATEWAY);
3546 nh_grp->count = fib6_entry->nrt6;
3547 for (i = 0; i < nh_grp->count; i++) {
3548 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3549
3550 nh = &nh_grp->nexthops[i];
3551 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
3552 if (err)
3553 goto err_nexthop6_init;
3554 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
3555 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003556
3557 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
3558 if (err)
3559 goto err_nexthop_group_insert;
3560
Ido Schimmel428b8512017-08-03 13:28:28 +02003561 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3562 return nh_grp;
3563
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003564err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02003565err_nexthop6_init:
3566 for (i--; i >= 0; i--) {
3567 nh = &nh_grp->nexthops[i];
3568 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3569 }
3570 kfree(nh_grp);
3571 return ERR_PTR(err);
3572}
3573
3574static void
3575mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
3576 struct mlxsw_sp_nexthop_group *nh_grp)
3577{
3578 struct mlxsw_sp_nexthop *nh;
3579 int i = nh_grp->count;
3580
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003581 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02003582 for (i--; i >= 0; i--) {
3583 nh = &nh_grp->nexthops[i];
3584 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
3585 }
3586 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
3587 WARN_ON(nh_grp->adj_index_valid);
3588 kfree(nh_grp);
3589}
3590
3591static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
3592 struct mlxsw_sp_fib6_entry *fib6_entry)
3593{
3594 struct mlxsw_sp_nexthop_group *nh_grp;
3595
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02003596 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
3597 if (!nh_grp) {
3598 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
3599 if (IS_ERR(nh_grp))
3600 return PTR_ERR(nh_grp);
3601 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003602
3603 list_add_tail(&fib6_entry->common.nexthop_group_node,
3604 &nh_grp->fib_list);
3605 fib6_entry->common.nh_group = nh_grp;
3606
3607 return 0;
3608}
3609
3610static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
3611 struct mlxsw_sp_fib_entry *fib_entry)
3612{
3613 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3614
3615 list_del(&fib_entry->nexthop_group_node);
3616 if (!list_empty(&nh_grp->fib_list))
3617 return;
3618 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
3619}
3620
3621static int
3622mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
3623 struct mlxsw_sp_fib6_entry *fib6_entry)
3624{
3625 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
3626 int err;
3627
3628 fib6_entry->common.nh_group = NULL;
3629 list_del(&fib6_entry->common.nexthop_group_node);
3630
3631 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3632 if (err)
3633 goto err_nexthop6_group_get;
3634
3635 /* In case this entry is offloaded, then the adjacency index
3636 * currently associated with it in the device's table is that
3637 * of the old group. Start using the new one instead.
3638 */
3639 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3640 if (err)
3641 goto err_fib_node_entry_add;
3642
3643 if (list_empty(&old_nh_grp->fib_list))
3644 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
3645
3646 return 0;
3647
3648err_fib_node_entry_add:
3649 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3650err_nexthop6_group_get:
3651 list_add_tail(&fib6_entry->common.nexthop_group_node,
3652 &old_nh_grp->fib_list);
3653 fib6_entry->common.nh_group = old_nh_grp;
3654 return err;
3655}
3656
3657static int
3658mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
3659 struct mlxsw_sp_fib6_entry *fib6_entry,
3660 struct rt6_info *rt)
3661{
3662 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3663 int err;
3664
3665 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3666 if (IS_ERR(mlxsw_sp_rt6))
3667 return PTR_ERR(mlxsw_sp_rt6);
3668
3669 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3670 fib6_entry->nrt6++;
3671
3672 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3673 if (err)
3674 goto err_nexthop6_group_update;
3675
3676 return 0;
3677
3678err_nexthop6_group_update:
3679 fib6_entry->nrt6--;
3680 list_del(&mlxsw_sp_rt6->list);
3681 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3682 return err;
3683}
3684
3685static void
3686mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
3687 struct mlxsw_sp_fib6_entry *fib6_entry,
3688 struct rt6_info *rt)
3689{
3690 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3691
3692 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
3693 if (WARN_ON(!mlxsw_sp_rt6))
3694 return;
3695
3696 fib6_entry->nrt6--;
3697 list_del(&mlxsw_sp_rt6->list);
3698 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
3699 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3700}
3701
3702static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
3703 const struct rt6_info *rt)
3704{
3705 /* Packets hitting RTF_REJECT routes need to be discarded by the
3706 * stack. We can rely on their destination device not having a
3707 * RIF (it's the loopback device) and can thus use action type
3708 * local, which will cause them to be trapped with a lower
3709 * priority than packets that need to be locally received.
3710 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02003711 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02003712 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3713 else if (rt->rt6i_flags & RTF_REJECT)
3714 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3715 else if (rt->rt6i_flags & RTF_GATEWAY)
3716 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
3717 else
3718 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
3719}
3720
3721static void
3722mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
3723{
3724 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
3725
3726 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
3727 list) {
3728 fib6_entry->nrt6--;
3729 list_del(&mlxsw_sp_rt6->list);
3730 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3731 }
3732}
3733
3734static struct mlxsw_sp_fib6_entry *
3735mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
3736 struct mlxsw_sp_fib_node *fib_node,
3737 struct rt6_info *rt)
3738{
3739 struct mlxsw_sp_fib6_entry *fib6_entry;
3740 struct mlxsw_sp_fib_entry *fib_entry;
3741 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3742 int err;
3743
3744 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
3745 if (!fib6_entry)
3746 return ERR_PTR(-ENOMEM);
3747 fib_entry = &fib6_entry->common;
3748
3749 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
3750 if (IS_ERR(mlxsw_sp_rt6)) {
3751 err = PTR_ERR(mlxsw_sp_rt6);
3752 goto err_rt6_create;
3753 }
3754
3755 mlxsw_sp_fib6_entry_type_set(fib_entry, mlxsw_sp_rt6->rt);
3756
3757 INIT_LIST_HEAD(&fib6_entry->rt6_list);
3758 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
3759 fib6_entry->nrt6 = 1;
3760 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
3761 if (err)
3762 goto err_nexthop6_group_get;
3763
3764 fib_entry->fib_node = fib_node;
3765
3766 return fib6_entry;
3767
3768err_nexthop6_group_get:
3769 list_del(&mlxsw_sp_rt6->list);
3770 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
3771err_rt6_create:
3772 kfree(fib6_entry);
3773 return ERR_PTR(err);
3774}
3775
3776static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
3777 struct mlxsw_sp_fib6_entry *fib6_entry)
3778{
3779 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
3780 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
3781 WARN_ON(fib6_entry->nrt6);
3782 kfree(fib6_entry);
3783}
3784
3785static struct mlxsw_sp_fib6_entry *
3786mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003787 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003788{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003789 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003790
3791 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3792 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3793
3794 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3795 continue;
3796 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3797 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003798 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
3799 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
3800 mlxsw_sp_fib6_rt_can_mp(nrt))
3801 return fib6_entry;
3802 if (mlxsw_sp_fib6_rt_can_mp(nrt))
3803 fallback = fallback ?: fib6_entry;
3804 }
Ido Schimmel428b8512017-08-03 13:28:28 +02003805 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003806 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02003807 }
3808
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003809 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02003810}
3811
3812static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003813mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
3814 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003815{
3816 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
3817 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
3818 struct mlxsw_sp_fib6_entry *fib6_entry;
3819
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003820 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
3821
3822 if (replace && WARN_ON(!fib6_entry))
3823 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02003824
3825 if (fib6_entry) {
3826 list_add_tail(&new6_entry->common.list,
3827 &fib6_entry->common.list);
3828 } else {
3829 struct mlxsw_sp_fib6_entry *last;
3830
3831 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3832 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
3833
3834 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
3835 break;
3836 fib6_entry = last;
3837 }
3838
3839 if (fib6_entry)
3840 list_add(&new6_entry->common.list,
3841 &fib6_entry->common.list);
3842 else
3843 list_add(&new6_entry->common.list,
3844 &fib_node->entry_list);
3845 }
3846
3847 return 0;
3848}
3849
3850static void
3851mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
3852{
3853 list_del(&fib6_entry->common.list);
3854}
3855
3856static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003857 struct mlxsw_sp_fib6_entry *fib6_entry,
3858 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003859{
3860 int err;
3861
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003862 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003863 if (err)
3864 return err;
3865
3866 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
3867 if (err)
3868 goto err_fib_node_entry_add;
3869
3870 return 0;
3871
3872err_fib_node_entry_add:
3873 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3874 return err;
3875}
3876
3877static void
3878mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
3879 struct mlxsw_sp_fib6_entry *fib6_entry)
3880{
3881 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
3882 mlxsw_sp_fib6_node_list_remove(fib6_entry);
3883}
3884
3885static struct mlxsw_sp_fib6_entry *
3886mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3887 const struct rt6_info *rt)
3888{
3889 struct mlxsw_sp_fib6_entry *fib6_entry;
3890 struct mlxsw_sp_fib_node *fib_node;
3891 struct mlxsw_sp_fib *fib;
3892 struct mlxsw_sp_vr *vr;
3893
3894 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
3895 if (!vr)
3896 return NULL;
3897 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
3898
3899 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
3900 sizeof(rt->rt6i_dst.addr),
3901 rt->rt6i_dst.plen);
3902 if (!fib_node)
3903 return NULL;
3904
3905 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3906 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3907
3908 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
3909 rt->rt6i_metric == iter_rt->rt6i_metric &&
3910 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
3911 return fib6_entry;
3912 }
3913
3914 return NULL;
3915}
3916
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003917static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
3918 struct mlxsw_sp_fib6_entry *fib6_entry,
3919 bool replace)
3920{
3921 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
3922 struct mlxsw_sp_fib6_entry *replaced;
3923
3924 if (!replace)
3925 return;
3926
3927 replaced = list_next_entry(fib6_entry, common.list);
3928
3929 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
3930 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
3931 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3932}
3933
Ido Schimmel428b8512017-08-03 13:28:28 +02003934static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003935 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003936{
3937 struct mlxsw_sp_fib6_entry *fib6_entry;
3938 struct mlxsw_sp_fib_node *fib_node;
3939 int err;
3940
3941 if (mlxsw_sp->router->aborted)
3942 return 0;
3943
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02003944 if (rt->rt6i_src.plen)
3945 return -EINVAL;
3946
Ido Schimmel428b8512017-08-03 13:28:28 +02003947 if (mlxsw_sp_fib6_rt_should_ignore(rt))
3948 return 0;
3949
3950 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
3951 &rt->rt6i_dst.addr,
3952 sizeof(rt->rt6i_dst.addr),
3953 rt->rt6i_dst.plen,
3954 MLXSW_SP_L3_PROTO_IPV6);
3955 if (IS_ERR(fib_node))
3956 return PTR_ERR(fib_node);
3957
3958 /* Before creating a new entry, try to append route to an existing
3959 * multipath entry.
3960 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003961 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003962 if (fib6_entry) {
3963 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
3964 if (err)
3965 goto err_fib6_entry_nexthop_add;
3966 return 0;
3967 }
3968
3969 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
3970 if (IS_ERR(fib6_entry)) {
3971 err = PTR_ERR(fib6_entry);
3972 goto err_fib6_entry_create;
3973 }
3974
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003975 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02003976 if (err)
3977 goto err_fib6_node_entry_link;
3978
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003979 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
3980
Ido Schimmel428b8512017-08-03 13:28:28 +02003981 return 0;
3982
3983err_fib6_node_entry_link:
3984 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
3985err_fib6_entry_create:
3986err_fib6_entry_nexthop_add:
3987 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
3988 return err;
3989}
3990
3991static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
3992 struct rt6_info *rt)
3993{
3994 struct mlxsw_sp_fib6_entry *fib6_entry;
3995 struct mlxsw_sp_fib_node *fib_node;
3996
3997 if (mlxsw_sp->router->aborted)
3998 return;
3999
4000 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4001 return;
4002
4003 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4004 if (WARN_ON(!fib6_entry))
4005 return;
4006
4007 /* If route is part of a multipath entry, but not the last one
4008 * removed, then only reduce its nexthop group.
4009 */
4010 if (!list_is_singular(&fib6_entry->rt6_list)) {
4011 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4012 return;
4013 }
4014
4015 fib_node = fib6_entry->common.fib_node;
4016
4017 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4018 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4019 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4020}
4021
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004022static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4023 enum mlxsw_reg_ralxx_protocol proto,
4024 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004025{
4026 char ralta_pl[MLXSW_REG_RALTA_LEN];
4027 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004028 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004029
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004030 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004031 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4032 if (err)
4033 return err;
4034
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004035 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004036 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4037 if (err)
4038 return err;
4039
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004040 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004041 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004042 char raltb_pl[MLXSW_REG_RALTB_LEN];
4043 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004044
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004045 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004046 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4047 raltb_pl);
4048 if (err)
4049 return err;
4050
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004051 mlxsw_reg_ralue_pack(ralue_pl, proto,
4052 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004053 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4054 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4055 ralue_pl);
4056 if (err)
4057 return err;
4058 }
4059
4060 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004061}
4062
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004063static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4064{
4065 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4066 int err;
4067
4068 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4069 MLXSW_SP_LPM_TREE_MIN);
4070 if (err)
4071 return err;
4072
4073 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4074 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4075 MLXSW_SP_LPM_TREE_MIN + 1);
4076}
4077
Ido Schimmel9aecce12017-02-09 10:28:42 +01004078static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4079 struct mlxsw_sp_fib_node *fib_node)
4080{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004081 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004082
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004083 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4084 common.list) {
4085 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004086
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004087 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4088 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004089 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004090 /* Break when entry list is empty and node was freed.
4091 * Otherwise, we'll access freed memory in the next
4092 * iteration.
4093 */
4094 if (do_break)
4095 break;
4096 }
4097}
4098
Ido Schimmel428b8512017-08-03 13:28:28 +02004099static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4100 struct mlxsw_sp_fib_node *fib_node)
4101{
4102 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4103
4104 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4105 common.list) {
4106 bool do_break = &tmp->common.list == &fib_node->entry_list;
4107
4108 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4109 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4110 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4111 if (do_break)
4112 break;
4113 }
4114}
4115
Ido Schimmel9aecce12017-02-09 10:28:42 +01004116static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4117 struct mlxsw_sp_fib_node *fib_node)
4118{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004119 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004120 case MLXSW_SP_L3_PROTO_IPV4:
4121 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4122 break;
4123 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004124 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004125 break;
4126 }
4127}
4128
Ido Schimmel76610eb2017-03-10 08:53:41 +01004129static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4130 struct mlxsw_sp_vr *vr,
4131 enum mlxsw_sp_l3proto proto)
4132{
4133 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4134 struct mlxsw_sp_fib_node *fib_node, *tmp;
4135
4136 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4137 bool do_break = &tmp->list == &fib->node_list;
4138
4139 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4140 if (do_break)
4141 break;
4142 }
4143}
4144
Ido Schimmelac571de2016-11-14 11:26:32 +01004145static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004146{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004147 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004148
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004149 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004150 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004151
Ido Schimmel76610eb2017-03-10 08:53:41 +01004152 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004153 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004154 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004155
4156 /* If virtual router was only used for IPv4, then it's no
4157 * longer used.
4158 */
4159 if (!mlxsw_sp_vr_is_used(vr))
4160 continue;
4161 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004162 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004163}
4164
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004165static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004166{
4167 int err;
4168
Ido Schimmel9011b672017-05-16 19:38:25 +02004169 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004170 return;
4171 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 +01004172 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004173 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004174 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4175 if (err)
4176 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4177}
4178
Ido Schimmel30572242016-12-03 16:45:01 +01004179struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004180 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004181 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004182 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004183 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004184 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004185 struct fib_nh_notifier_info fnh_info;
4186 };
Ido Schimmel30572242016-12-03 16:45:01 +01004187 struct mlxsw_sp *mlxsw_sp;
4188 unsigned long event;
4189};
4190
Ido Schimmel66a57632017-08-03 13:28:26 +02004191static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004192{
Ido Schimmel30572242016-12-03 16:45:01 +01004193 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004194 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004195 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004196 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004197 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004198 int err;
4199
Ido Schimmel30572242016-12-03 16:45:01 +01004200 /* Protect internal structures from changes */
4201 rtnl_lock();
4202 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004203 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004204 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004205 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004206 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004207 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4208 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004209 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004210 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004211 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004212 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004213 break;
4214 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004215 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4216 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004217 break;
4218 case FIB_EVENT_RULE_ADD: /* fall through */
4219 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004220 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004221 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004222 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004223 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004224 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004225 case FIB_EVENT_NH_ADD: /* fall through */
4226 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004227 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4228 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004229 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4230 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004231 }
Ido Schimmel30572242016-12-03 16:45:01 +01004232 rtnl_unlock();
4233 kfree(fib_work);
4234}
4235
Ido Schimmel66a57632017-08-03 13:28:26 +02004236static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4237{
Ido Schimmel583419f2017-08-03 13:28:27 +02004238 struct mlxsw_sp_fib_event_work *fib_work =
4239 container_of(work, struct mlxsw_sp_fib_event_work, work);
4240 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4241 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004242 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004243 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004244
4245 rtnl_lock();
4246 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004247 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004248 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004249 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004250 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004251 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004252 if (err)
4253 mlxsw_sp_router_fib_abort(mlxsw_sp);
4254 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4255 break;
4256 case FIB_EVENT_ENTRY_DEL:
4257 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4258 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4259 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004260 case FIB_EVENT_RULE_ADD: /* fall through */
4261 case FIB_EVENT_RULE_DEL:
4262 rule = fib_work->fr_info.rule;
4263 if (!fib6_rule_default(rule) && !rule->l3mdev)
4264 mlxsw_sp_router_fib_abort(mlxsw_sp);
4265 fib_rule_put(rule);
4266 break;
4267 }
4268 rtnl_unlock();
4269 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004270}
4271
4272static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4273 struct fib_notifier_info *info)
4274{
4275 switch (fib_work->event) {
4276 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4277 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4278 case FIB_EVENT_ENTRY_ADD: /* fall through */
4279 case FIB_EVENT_ENTRY_DEL:
4280 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4281 /* Take referece on fib_info to prevent it from being
4282 * freed while work is queued. Release it afterwards.
4283 */
4284 fib_info_hold(fib_work->fen_info.fi);
4285 break;
4286 case FIB_EVENT_RULE_ADD: /* fall through */
4287 case FIB_EVENT_RULE_DEL:
4288 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4289 fib_rule_get(fib_work->fr_info.rule);
4290 break;
4291 case FIB_EVENT_NH_ADD: /* fall through */
4292 case FIB_EVENT_NH_DEL:
4293 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4294 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4295 break;
4296 }
4297}
4298
4299static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4300 struct fib_notifier_info *info)
4301{
Ido Schimmel583419f2017-08-03 13:28:27 +02004302 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004303 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004304 case FIB_EVENT_ENTRY_ADD: /* fall through */
4305 case FIB_EVENT_ENTRY_DEL:
4306 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4307 rt6_hold(fib_work->fen6_info.rt);
4308 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004309 case FIB_EVENT_RULE_ADD: /* fall through */
4310 case FIB_EVENT_RULE_DEL:
4311 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4312 fib_rule_get(fib_work->fr_info.rule);
4313 break;
4314 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004315}
4316
Ido Schimmel30572242016-12-03 16:45:01 +01004317/* Called with rcu_read_lock() */
4318static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4319 unsigned long event, void *ptr)
4320{
Ido Schimmel30572242016-12-03 16:45:01 +01004321 struct mlxsw_sp_fib_event_work *fib_work;
4322 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004323 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004324
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004325 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004326 return NOTIFY_DONE;
4327
4328 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4329 if (WARN_ON(!fib_work))
4330 return NOTIFY_BAD;
4331
Ido Schimmel7e39d112017-05-16 19:38:28 +02004332 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4333 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004334 fib_work->event = event;
4335
Ido Schimmel66a57632017-08-03 13:28:26 +02004336 switch (info->family) {
4337 case AF_INET:
4338 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4339 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004340 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004341 case AF_INET6:
4342 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4343 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004344 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004345 }
4346
Ido Schimmela0e47612017-02-06 16:20:10 +01004347 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004348
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004349 return NOTIFY_DONE;
4350}
4351
Ido Schimmel4724ba562017-03-10 08:53:39 +01004352static struct mlxsw_sp_rif *
4353mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4354 const struct net_device *dev)
4355{
4356 int i;
4357
4358 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004359 if (mlxsw_sp->router->rifs[i] &&
4360 mlxsw_sp->router->rifs[i]->dev == dev)
4361 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004362
4363 return NULL;
4364}
4365
4366static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4367{
4368 char ritr_pl[MLXSW_REG_RITR_LEN];
4369 int err;
4370
4371 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4372 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4373 if (WARN_ON_ONCE(err))
4374 return err;
4375
4376 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4377 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4378}
4379
4380static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004381 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004382{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004383 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4384 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4385 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004386}
4387
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004388static bool
4389mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4390 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004391{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004392 struct inet6_dev *inet6_dev;
4393 bool addr_list_empty = true;
4394 struct in_device *idev;
4395
Ido Schimmel4724ba562017-03-10 08:53:39 +01004396 switch (event) {
4397 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004398 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004399 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004400 idev = __in_dev_get_rtnl(dev);
4401 if (idev && idev->ifa_list)
4402 addr_list_empty = false;
4403
4404 inet6_dev = __in6_dev_get(dev);
4405 if (addr_list_empty && inet6_dev &&
4406 !list_empty(&inet6_dev->addr_list))
4407 addr_list_empty = false;
4408
4409 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004410 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004411 return true;
4412 /* It is possible we already removed the RIF ourselves
4413 * if it was assigned to a netdev that is now a bridge
4414 * or LAG slave.
4415 */
4416 return false;
4417 }
4418
4419 return false;
4420}
4421
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004422static enum mlxsw_sp_rif_type
4423mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4424 const struct net_device *dev)
4425{
4426 enum mlxsw_sp_fid_type type;
4427
Petr Machata6ddb7422017-09-02 23:49:19 +02004428 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4429 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4430
4431 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004432 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4433 type = MLXSW_SP_FID_TYPE_8021Q;
4434 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4435 type = MLXSW_SP_FID_TYPE_8021Q;
4436 else if (netif_is_bridge_master(dev))
4437 type = MLXSW_SP_FID_TYPE_8021D;
4438 else
4439 type = MLXSW_SP_FID_TYPE_RFID;
4440
4441 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4442}
4443
Ido Schimmelde5ed992017-06-04 16:53:40 +02004444static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004445{
4446 int i;
4447
Ido Schimmelde5ed992017-06-04 16:53:40 +02004448 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4449 if (!mlxsw_sp->router->rifs[i]) {
4450 *p_rif_index = i;
4451 return 0;
4452 }
4453 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004454
Ido Schimmelde5ed992017-06-04 16:53:40 +02004455 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004456}
4457
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004458static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4459 u16 vr_id,
4460 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004461{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004462 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004463
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004464 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004465 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004466 return NULL;
4467
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004468 INIT_LIST_HEAD(&rif->nexthop_list);
4469 INIT_LIST_HEAD(&rif->neigh_list);
4470 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4471 rif->mtu = l3_dev->mtu;
4472 rif->vr_id = vr_id;
4473 rif->dev = l3_dev;
4474 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004475
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004476 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004477}
4478
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004479struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
4480 u16 rif_index)
4481{
4482 return mlxsw_sp->router->rifs[rif_index];
4483}
4484
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02004485u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
4486{
4487 return rif->rif_index;
4488}
4489
4490int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
4491{
4492 return rif->dev->ifindex;
4493}
4494
Ido Schimmel4724ba562017-03-10 08:53:39 +01004495static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004496mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
4497 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004498{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004499 u32 tb_id = l3mdev_fib_table(params->dev);
4500 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02004501 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004502 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004503 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004504 struct mlxsw_sp_vr *vr;
4505 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004506 int err;
4507
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004508 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
4509 ops = mlxsw_sp->router->rif_ops_arr[type];
4510
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004511 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
4512 if (IS_ERR(vr))
4513 return ERR_CAST(vr);
4514
Ido Schimmelde5ed992017-06-04 16:53:40 +02004515 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
4516 if (err)
4517 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004518
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004519 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02004520 if (!rif) {
4521 err = -ENOMEM;
4522 goto err_rif_alloc;
4523 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004524 rif->mlxsw_sp = mlxsw_sp;
4525 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02004526
Petr Machata010cadf2017-09-02 23:49:18 +02004527 if (ops->fid_get) {
4528 fid = ops->fid_get(rif);
4529 if (IS_ERR(fid)) {
4530 err = PTR_ERR(fid);
4531 goto err_fid_get;
4532 }
4533 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02004534 }
4535
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004536 if (ops->setup)
4537 ops->setup(rif, params);
4538
4539 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004540 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004541 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004542
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004543 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004544 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01004545 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004546
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004547 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004548
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004549err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02004550 if (fid)
4551 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02004552err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004553 kfree(rif);
4554err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02004555err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004556 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004557 return ERR_PTR(err);
4558}
4559
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004560void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004561{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004562 const struct mlxsw_sp_rif_ops *ops = rif->ops;
4563 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02004564 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004565 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004566
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004567 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004568 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02004569
Ido Schimmel69132292017-03-10 08:53:42 +01004570 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004571 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004572 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004573 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02004574 if (fid)
4575 /* Loopback RIFs are not associated with a FID. */
4576 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004577 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02004578 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004579}
4580
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004581static void
4582mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
4583 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4584{
4585 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
4586
4587 params->vid = mlxsw_sp_port_vlan->vid;
4588 params->lag = mlxsw_sp_port->lagged;
4589 if (params->lag)
4590 params->lag_id = mlxsw_sp_port->lag_id;
4591 else
4592 params->system_port = mlxsw_sp_port->local_port;
4593}
4594
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004595static int
Ido Schimmela1107482017-05-26 08:37:39 +02004596mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004597 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004598{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004599 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004600 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004601 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004602 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004603 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004604 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004605
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02004606 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004607 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004608 struct mlxsw_sp_rif_params params = {
4609 .dev = l3_dev,
4610 };
4611
4612 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
4613 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004614 if (IS_ERR(rif))
4615 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004616 }
4617
Ido Schimmela1107482017-05-26 08:37:39 +02004618 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004619 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02004620 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
4621 if (err)
4622 goto err_fid_port_vid_map;
4623
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004624 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004625 if (err)
4626 goto err_port_vid_learning_set;
4627
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004628 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004629 BR_STATE_FORWARDING);
4630 if (err)
4631 goto err_port_vid_stp_set;
4632
Ido Schimmela1107482017-05-26 08:37:39 +02004633 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004634
Ido Schimmel4724ba562017-03-10 08:53:39 +01004635 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004636
4637err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004638 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004639err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02004640 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4641err_fid_port_vid_map:
4642 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02004643 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004644}
4645
Ido Schimmela1107482017-05-26 08:37:39 +02004646void
4647mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004648{
Ido Schimmelce95e152017-05-26 08:37:27 +02004649 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004650 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004651 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02004652
Ido Schimmela1107482017-05-26 08:37:39 +02004653 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
4654 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02004655
Ido Schimmela1107482017-05-26 08:37:39 +02004656 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004657 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
4658 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02004659 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
4660 /* If router port holds the last reference on the rFID, then the
4661 * associated Sub-port RIF will be destroyed.
4662 */
4663 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004664}
4665
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004666static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
4667 struct net_device *port_dev,
4668 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004669{
4670 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02004671 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004672
Ido Schimmelce95e152017-05-26 08:37:27 +02004673 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004674 if (WARN_ON(!mlxsw_sp_port_vlan))
4675 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004676
4677 switch (event) {
4678 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02004679 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004680 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004681 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004682 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004683 break;
4684 }
4685
4686 return 0;
4687}
4688
4689static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
4690 unsigned long event)
4691{
Jiri Pirko2b94e582017-04-18 16:55:37 +02004692 if (netif_is_bridge_port(port_dev) ||
4693 netif_is_lag_port(port_dev) ||
4694 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004695 return 0;
4696
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004697 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004698}
4699
4700static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
4701 struct net_device *lag_dev,
4702 unsigned long event, u16 vid)
4703{
4704 struct net_device *port_dev;
4705 struct list_head *iter;
4706 int err;
4707
4708 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
4709 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004710 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
4711 port_dev,
4712 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004713 if (err)
4714 return err;
4715 }
4716 }
4717
4718 return 0;
4719}
4720
4721static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
4722 unsigned long event)
4723{
4724 if (netif_is_bridge_port(lag_dev))
4725 return 0;
4726
4727 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
4728}
4729
Ido Schimmel4724ba562017-03-10 08:53:39 +01004730static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004731 unsigned long event)
4732{
4733 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004734 struct mlxsw_sp_rif_params params = {
4735 .dev = l3_dev,
4736 };
Ido Schimmela1107482017-05-26 08:37:39 +02004737 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004738
4739 switch (event) {
4740 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004741 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
4742 if (IS_ERR(rif))
4743 return PTR_ERR(rif);
4744 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004745 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02004746 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004747 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004748 break;
4749 }
4750
4751 return 0;
4752}
4753
4754static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
4755 unsigned long event)
4756{
4757 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004758 u16 vid = vlan_dev_vlan_id(vlan_dev);
4759
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03004760 if (netif_is_bridge_port(vlan_dev))
4761 return 0;
4762
Ido Schimmel4724ba562017-03-10 08:53:39 +01004763 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02004764 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
4765 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004766 else if (netif_is_lag_master(real_dev))
4767 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
4768 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02004769 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004770 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004771
4772 return 0;
4773}
4774
Ido Schimmelb1e45522017-04-30 19:47:14 +03004775static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
4776 unsigned long event)
4777{
4778 if (mlxsw_sp_port_dev_check(dev))
4779 return mlxsw_sp_inetaddr_port_event(dev, event);
4780 else if (netif_is_lag_master(dev))
4781 return mlxsw_sp_inetaddr_lag_event(dev, event);
4782 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02004783 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03004784 else if (is_vlan_dev(dev))
4785 return mlxsw_sp_inetaddr_vlan_event(dev, event);
4786 else
4787 return 0;
4788}
4789
Ido Schimmel4724ba562017-03-10 08:53:39 +01004790int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
4791 unsigned long event, void *ptr)
4792{
4793 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
4794 struct net_device *dev = ifa->ifa_dev->dev;
4795 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004796 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004797 int err = 0;
4798
4799 mlxsw_sp = mlxsw_sp_lower_get(dev);
4800 if (!mlxsw_sp)
4801 goto out;
4802
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004803 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004804 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004805 goto out;
4806
Ido Schimmelb1e45522017-04-30 19:47:14 +03004807 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004808out:
4809 return notifier_from_errno(err);
4810}
4811
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004812struct mlxsw_sp_inet6addr_event_work {
4813 struct work_struct work;
4814 struct net_device *dev;
4815 unsigned long event;
4816};
4817
4818static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
4819{
4820 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
4821 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
4822 struct net_device *dev = inet6addr_work->dev;
4823 unsigned long event = inet6addr_work->event;
4824 struct mlxsw_sp *mlxsw_sp;
4825 struct mlxsw_sp_rif *rif;
4826
4827 rtnl_lock();
4828 mlxsw_sp = mlxsw_sp_lower_get(dev);
4829 if (!mlxsw_sp)
4830 goto out;
4831
4832 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4833 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4834 goto out;
4835
4836 __mlxsw_sp_inetaddr_event(dev, event);
4837out:
4838 rtnl_unlock();
4839 dev_put(dev);
4840 kfree(inet6addr_work);
4841}
4842
4843/* Called with rcu_read_lock() */
4844int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
4845 unsigned long event, void *ptr)
4846{
4847 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
4848 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
4849 struct net_device *dev = if6->idev->dev;
4850
4851 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
4852 return NOTIFY_DONE;
4853
4854 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
4855 if (!inet6addr_work)
4856 return NOTIFY_BAD;
4857
4858 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
4859 inet6addr_work->dev = dev;
4860 inet6addr_work->event = event;
4861 dev_hold(dev);
4862 mlxsw_core_schedule_work(&inet6addr_work->work);
4863
4864 return NOTIFY_DONE;
4865}
4866
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004867static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01004868 const char *mac, int mtu)
4869{
4870 char ritr_pl[MLXSW_REG_RITR_LEN];
4871 int err;
4872
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004873 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004874 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4875 if (err)
4876 return err;
4877
4878 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
4879 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
4880 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
4881 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4882}
4883
4884int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
4885{
4886 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004887 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02004888 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004889 int err;
4890
4891 mlxsw_sp = mlxsw_sp_lower_get(dev);
4892 if (!mlxsw_sp)
4893 return 0;
4894
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004895 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4896 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004897 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02004898 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004899
Ido Schimmela1107482017-05-26 08:37:39 +02004900 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004901 if (err)
4902 return err;
4903
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004904 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
4905 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004906 if (err)
4907 goto err_rif_edit;
4908
Ido Schimmela1107482017-05-26 08:37:39 +02004909 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004910 if (err)
4911 goto err_rif_fdb_op;
4912
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004913 ether_addr_copy(rif->addr, dev->dev_addr);
4914 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004915
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004916 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004917
4918 return 0;
4919
4920err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004921 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004922err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02004923 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004924 return err;
4925}
4926
Ido Schimmelb1e45522017-04-30 19:47:14 +03004927static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
4928 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004929{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004930 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004931
Ido Schimmelb1e45522017-04-30 19:47:14 +03004932 /* If netdev is already associated with a RIF, then we need to
4933 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01004934 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03004935 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4936 if (rif)
4937 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004938
Ido Schimmelb1e45522017-04-30 19:47:14 +03004939 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004940}
4941
Ido Schimmelb1e45522017-04-30 19:47:14 +03004942static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
4943 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004944{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004945 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01004946
Ido Schimmelb1e45522017-04-30 19:47:14 +03004947 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
4948 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01004949 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03004950 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01004951}
4952
Ido Schimmelb1e45522017-04-30 19:47:14 +03004953int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
4954 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004955{
Ido Schimmelb1e45522017-04-30 19:47:14 +03004956 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
4957 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004958
Ido Schimmelb1e45522017-04-30 19:47:14 +03004959 if (!mlxsw_sp)
4960 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004961
Ido Schimmelb1e45522017-04-30 19:47:14 +03004962 switch (event) {
4963 case NETDEV_PRECHANGEUPPER:
4964 return 0;
4965 case NETDEV_CHANGEUPPER:
4966 if (info->linking)
4967 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
4968 else
4969 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
4970 break;
4971 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004972
Ido Schimmelb1e45522017-04-30 19:47:14 +03004973 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01004974}
4975
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004976static struct mlxsw_sp_rif_subport *
4977mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02004978{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004979 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02004980}
4981
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004982static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
4983 const struct mlxsw_sp_rif_params *params)
4984{
4985 struct mlxsw_sp_rif_subport *rif_subport;
4986
4987 rif_subport = mlxsw_sp_rif_subport_rif(rif);
4988 rif_subport->vid = params->vid;
4989 rif_subport->lag = params->lag;
4990 if (params->lag)
4991 rif_subport->lag_id = params->lag_id;
4992 else
4993 rif_subport->system_port = params->system_port;
4994}
4995
4996static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
4997{
4998 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
4999 struct mlxsw_sp_rif_subport *rif_subport;
5000 char ritr_pl[MLXSW_REG_RITR_LEN];
5001
5002 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5003 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005004 rif->rif_index, rif->vr_id, rif->dev->mtu);
5005 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005006 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5007 rif_subport->lag ? rif_subport->lag_id :
5008 rif_subport->system_port,
5009 rif_subport->vid);
5010
5011 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5012}
5013
5014static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5015{
Petr Machata010cadf2017-09-02 23:49:18 +02005016 int err;
5017
5018 err = mlxsw_sp_rif_subport_op(rif, true);
5019 if (err)
5020 return err;
5021
5022 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5023 mlxsw_sp_fid_index(rif->fid), true);
5024 if (err)
5025 goto err_rif_fdb_op;
5026
5027 mlxsw_sp_fid_rif_set(rif->fid, rif);
5028 return 0;
5029
5030err_rif_fdb_op:
5031 mlxsw_sp_rif_subport_op(rif, false);
5032 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005033}
5034
5035static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5036{
Petr Machata010cadf2017-09-02 23:49:18 +02005037 struct mlxsw_sp_fid *fid = rif->fid;
5038
5039 mlxsw_sp_fid_rif_set(fid, NULL);
5040 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5041 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005042 mlxsw_sp_rif_subport_op(rif, false);
5043}
5044
5045static struct mlxsw_sp_fid *
5046mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5047{
5048 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5049}
5050
5051static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5052 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5053 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5054 .setup = mlxsw_sp_rif_subport_setup,
5055 .configure = mlxsw_sp_rif_subport_configure,
5056 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5057 .fid_get = mlxsw_sp_rif_subport_fid_get,
5058};
5059
5060static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5061 enum mlxsw_reg_ritr_if_type type,
5062 u16 vid_fid, bool enable)
5063{
5064 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5065 char ritr_pl[MLXSW_REG_RITR_LEN];
5066
5067 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005068 rif->dev->mtu);
5069 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005070 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5071
5072 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5073}
5074
5075static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5076{
5077 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5078}
5079
5080static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5081{
5082 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5083 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5084 int err;
5085
5086 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5087 if (err)
5088 return err;
5089
Ido Schimmel0d284812017-07-18 10:10:12 +02005090 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5091 mlxsw_sp_router_port(mlxsw_sp), true);
5092 if (err)
5093 goto err_fid_mc_flood_set;
5094
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005095 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5096 mlxsw_sp_router_port(mlxsw_sp), true);
5097 if (err)
5098 goto err_fid_bc_flood_set;
5099
Petr Machata010cadf2017-09-02 23:49:18 +02005100 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5101 mlxsw_sp_fid_index(rif->fid), true);
5102 if (err)
5103 goto err_rif_fdb_op;
5104
5105 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005106 return 0;
5107
Petr Machata010cadf2017-09-02 23:49:18 +02005108err_rif_fdb_op:
5109 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5110 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005111err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005112 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5113 mlxsw_sp_router_port(mlxsw_sp), false);
5114err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005115 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5116 return err;
5117}
5118
5119static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5120{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005121 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005122 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5123 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005124
Petr Machata010cadf2017-09-02 23:49:18 +02005125 mlxsw_sp_fid_rif_set(fid, NULL);
5126 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5127 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005128 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5129 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005130 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5131 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005132 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5133}
5134
5135static struct mlxsw_sp_fid *
5136mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5137{
5138 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5139
5140 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5141}
5142
5143static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5144 .type = MLXSW_SP_RIF_TYPE_VLAN,
5145 .rif_size = sizeof(struct mlxsw_sp_rif),
5146 .configure = mlxsw_sp_rif_vlan_configure,
5147 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5148 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5149};
5150
5151static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5152{
5153 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5154 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5155 int err;
5156
5157 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5158 true);
5159 if (err)
5160 return err;
5161
Ido Schimmel0d284812017-07-18 10:10:12 +02005162 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5163 mlxsw_sp_router_port(mlxsw_sp), true);
5164 if (err)
5165 goto err_fid_mc_flood_set;
5166
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005167 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5168 mlxsw_sp_router_port(mlxsw_sp), true);
5169 if (err)
5170 goto err_fid_bc_flood_set;
5171
Petr Machata010cadf2017-09-02 23:49:18 +02005172 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5173 mlxsw_sp_fid_index(rif->fid), true);
5174 if (err)
5175 goto err_rif_fdb_op;
5176
5177 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005178 return 0;
5179
Petr Machata010cadf2017-09-02 23:49:18 +02005180err_rif_fdb_op:
5181 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5182 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005183err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005184 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5185 mlxsw_sp_router_port(mlxsw_sp), false);
5186err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005187 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5188 return err;
5189}
5190
5191static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5192{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005193 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005194 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5195 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005196
Petr Machata010cadf2017-09-02 23:49:18 +02005197 mlxsw_sp_fid_rif_set(fid, NULL);
5198 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5199 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005200 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5201 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005202 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5203 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005204 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5205}
5206
5207static struct mlxsw_sp_fid *
5208mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5209{
5210 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5211}
5212
5213static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5214 .type = MLXSW_SP_RIF_TYPE_FID,
5215 .rif_size = sizeof(struct mlxsw_sp_rif),
5216 .configure = mlxsw_sp_rif_fid_configure,
5217 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5218 .fid_get = mlxsw_sp_rif_fid_fid_get,
5219};
5220
Petr Machata6ddb7422017-09-02 23:49:19 +02005221static struct mlxsw_sp_rif_ipip_lb *
5222mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5223{
5224 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5225}
5226
5227static void
5228mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5229 const struct mlxsw_sp_rif_params *params)
5230{
5231 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5232 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5233
5234 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5235 common);
5236 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5237 rif_lb->lb_config = params_lb->lb_config;
5238}
5239
5240static int
5241mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5242 struct mlxsw_sp_vr *ul_vr, bool enable)
5243{
5244 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5245 struct mlxsw_sp_rif *rif = &lb_rif->common;
5246 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5247 char ritr_pl[MLXSW_REG_RITR_LEN];
5248 u32 saddr4;
5249
5250 switch (lb_cf.ul_protocol) {
5251 case MLXSW_SP_L3_PROTO_IPV4:
5252 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5253 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5254 rif->rif_index, rif->vr_id, rif->dev->mtu);
5255 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5256 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5257 ul_vr->id, saddr4, lb_cf.okey);
5258 break;
5259
5260 case MLXSW_SP_L3_PROTO_IPV6:
5261 return -EAFNOSUPPORT;
5262 }
5263
5264 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5265}
5266
5267static int
5268mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5269{
5270 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5271 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5272 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5273 struct mlxsw_sp_vr *ul_vr;
5274 int err;
5275
5276 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5277 if (IS_ERR(ul_vr))
5278 return PTR_ERR(ul_vr);
5279
5280 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5281 if (err)
5282 goto err_loopback_op;
5283
5284 lb_rif->ul_vr_id = ul_vr->id;
5285 ++ul_vr->rif_count;
5286 return 0;
5287
5288err_loopback_op:
5289 mlxsw_sp_vr_put(ul_vr);
5290 return err;
5291}
5292
5293static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5294{
5295 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5296 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5297 struct mlxsw_sp_vr *ul_vr;
5298
5299 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5300 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5301
5302 --ul_vr->rif_count;
5303 mlxsw_sp_vr_put(ul_vr);
5304}
5305
5306static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5307 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5308 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5309 .setup = mlxsw_sp_rif_ipip_lb_setup,
5310 .configure = mlxsw_sp_rif_ipip_lb_configure,
5311 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5312};
5313
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005314static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5315 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5316 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5317 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005318 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005319};
5320
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005321static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5322{
5323 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5324
5325 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5326 sizeof(struct mlxsw_sp_rif *),
5327 GFP_KERNEL);
5328 if (!mlxsw_sp->router->rifs)
5329 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005330
5331 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5332
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005333 return 0;
5334}
5335
5336static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5337{
5338 int i;
5339
5340 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5341 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5342
5343 kfree(mlxsw_sp->router->rifs);
5344}
5345
Petr Machata38ebc0f2017-09-02 23:49:17 +02005346static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5347{
5348 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
5349 return 0;
5350}
5351
5352static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5353{
5354}
5355
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005356static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5357{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005358 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005359
5360 /* Flush pending FIB notifications and then flush the device's
5361 * table before requesting another dump. The FIB notification
5362 * block is unregistered, so no need to take RTNL.
5363 */
5364 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005365 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5366 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005367}
5368
Ido Schimmel4724ba562017-03-10 08:53:39 +01005369static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5370{
5371 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5372 u64 max_rifs;
5373 int err;
5374
5375 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5376 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005377 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005378
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005379 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005380 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5381 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5382 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005383 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005384 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005385}
5386
5387static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5388{
5389 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005390
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005391 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005392 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005393}
5394
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005395int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5396{
Ido Schimmel9011b672017-05-16 19:38:25 +02005397 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005398 int err;
5399
Ido Schimmel9011b672017-05-16 19:38:25 +02005400 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5401 if (!router)
5402 return -ENOMEM;
5403 mlxsw_sp->router = router;
5404 router->mlxsw_sp = mlxsw_sp;
5405
5406 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005407 err = __mlxsw_sp_router_init(mlxsw_sp);
5408 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005409 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005410
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005411 err = mlxsw_sp_rifs_init(mlxsw_sp);
5412 if (err)
5413 goto err_rifs_init;
5414
Petr Machata38ebc0f2017-09-02 23:49:17 +02005415 err = mlxsw_sp_ipips_init(mlxsw_sp);
5416 if (err)
5417 goto err_ipips_init;
5418
Ido Schimmel9011b672017-05-16 19:38:25 +02005419 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005420 &mlxsw_sp_nexthop_ht_params);
5421 if (err)
5422 goto err_nexthop_ht_init;
5423
Ido Schimmel9011b672017-05-16 19:38:25 +02005424 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005425 &mlxsw_sp_nexthop_group_ht_params);
5426 if (err)
5427 goto err_nexthop_group_ht_init;
5428
Ido Schimmel8494ab02017-03-24 08:02:47 +01005429 err = mlxsw_sp_lpm_init(mlxsw_sp);
5430 if (err)
5431 goto err_lpm_init;
5432
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005433 err = mlxsw_sp_vrs_init(mlxsw_sp);
5434 if (err)
5435 goto err_vrs_init;
5436
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005437 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005438 if (err)
5439 goto err_neigh_init;
5440
Ido Schimmel7e39d112017-05-16 19:38:28 +02005441 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5442 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005443 mlxsw_sp_router_fib_dump_flush);
5444 if (err)
5445 goto err_register_fib_notifier;
5446
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005447 return 0;
5448
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005449err_register_fib_notifier:
5450 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005451err_neigh_init:
5452 mlxsw_sp_vrs_fini(mlxsw_sp);
5453err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005454 mlxsw_sp_lpm_fini(mlxsw_sp);
5455err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005456 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005457err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005458 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005459err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02005460 mlxsw_sp_ipips_fini(mlxsw_sp);
5461err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005462 mlxsw_sp_rifs_fini(mlxsw_sp);
5463err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005464 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005465err_router_init:
5466 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005467 return err;
5468}
5469
5470void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5471{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005472 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005473 mlxsw_sp_neigh_fini(mlxsw_sp);
5474 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01005475 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005476 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
5477 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005478 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005479 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005480 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005481 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005482}