blob: 3917b4dd4202fb46e6f83b714287a6abe71197ce [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;
Petr Machata1012b9a2017-09-02 23:49:23 +020092 struct list_head ipip_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020093 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020094 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020095 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020096 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020097};
98
Ido Schimmel4724ba562017-03-10 08:53:39 +010099struct mlxsw_sp_rif {
100 struct list_head nexthop_list;
101 struct list_head neigh_list;
102 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200103 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100104 unsigned char addr[ETH_ALEN];
105 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100106 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100107 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200108 const struct mlxsw_sp_rif_ops *ops;
109 struct mlxsw_sp *mlxsw_sp;
110
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200111 unsigned int counter_ingress;
112 bool counter_ingress_valid;
113 unsigned int counter_egress;
114 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100115};
116
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200117struct mlxsw_sp_rif_params {
118 struct net_device *dev;
119 union {
120 u16 system_port;
121 u16 lag_id;
122 };
123 u16 vid;
124 bool lag;
125};
126
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200127struct mlxsw_sp_rif_subport {
128 struct mlxsw_sp_rif common;
129 union {
130 u16 system_port;
131 u16 lag_id;
132 };
133 u16 vid;
134 bool lag;
135};
136
Petr Machata6ddb7422017-09-02 23:49:19 +0200137struct mlxsw_sp_rif_ipip_lb {
138 struct mlxsw_sp_rif common;
139 struct mlxsw_sp_rif_ipip_lb_config lb_config;
140 u16 ul_vr_id; /* Reserved for Spectrum-2. */
141};
142
143struct mlxsw_sp_rif_params_ipip_lb {
144 struct mlxsw_sp_rif_params common;
145 struct mlxsw_sp_rif_ipip_lb_config lb_config;
146};
147
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200148struct mlxsw_sp_rif_ops {
149 enum mlxsw_sp_rif_type type;
150 size_t rif_size;
151
152 void (*setup)(struct mlxsw_sp_rif *rif,
153 const struct mlxsw_sp_rif_params *params);
154 int (*configure)(struct mlxsw_sp_rif *rif);
155 void (*deconfigure)(struct mlxsw_sp_rif *rif);
156 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
157};
158
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200159static unsigned int *
160mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
161 enum mlxsw_sp_rif_counter_dir dir)
162{
163 switch (dir) {
164 case MLXSW_SP_RIF_COUNTER_EGRESS:
165 return &rif->counter_egress;
166 case MLXSW_SP_RIF_COUNTER_INGRESS:
167 return &rif->counter_ingress;
168 }
169 return NULL;
170}
171
172static bool
173mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
174 enum mlxsw_sp_rif_counter_dir dir)
175{
176 switch (dir) {
177 case MLXSW_SP_RIF_COUNTER_EGRESS:
178 return rif->counter_egress_valid;
179 case MLXSW_SP_RIF_COUNTER_INGRESS:
180 return rif->counter_ingress_valid;
181 }
182 return false;
183}
184
185static void
186mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
187 enum mlxsw_sp_rif_counter_dir dir,
188 bool valid)
189{
190 switch (dir) {
191 case MLXSW_SP_RIF_COUNTER_EGRESS:
192 rif->counter_egress_valid = valid;
193 break;
194 case MLXSW_SP_RIF_COUNTER_INGRESS:
195 rif->counter_ingress_valid = valid;
196 break;
197 }
198}
199
200static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
201 unsigned int counter_index, bool enable,
202 enum mlxsw_sp_rif_counter_dir dir)
203{
204 char ritr_pl[MLXSW_REG_RITR_LEN];
205 bool is_egress = false;
206 int err;
207
208 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
209 is_egress = true;
210 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
211 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
212 if (err)
213 return err;
214
215 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
216 is_egress);
217 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
218}
219
220int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
221 struct mlxsw_sp_rif *rif,
222 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
223{
224 char ricnt_pl[MLXSW_REG_RICNT_LEN];
225 unsigned int *p_counter_index;
226 bool valid;
227 int err;
228
229 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
230 if (!valid)
231 return -EINVAL;
232
233 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
234 if (!p_counter_index)
235 return -EINVAL;
236 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
237 MLXSW_REG_RICNT_OPCODE_NOP);
238 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
239 if (err)
240 return err;
241 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
242 return 0;
243}
244
245static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
246 unsigned int counter_index)
247{
248 char ricnt_pl[MLXSW_REG_RICNT_LEN];
249
250 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
251 MLXSW_REG_RICNT_OPCODE_CLEAR);
252 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
253}
254
255int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
256 struct mlxsw_sp_rif *rif,
257 enum mlxsw_sp_rif_counter_dir dir)
258{
259 unsigned int *p_counter_index;
260 int err;
261
262 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
263 if (!p_counter_index)
264 return -EINVAL;
265 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
266 p_counter_index);
267 if (err)
268 return err;
269
270 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
271 if (err)
272 goto err_counter_clear;
273
274 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
275 *p_counter_index, true, dir);
276 if (err)
277 goto err_counter_edit;
278 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
279 return 0;
280
281err_counter_edit:
282err_counter_clear:
283 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
284 *p_counter_index);
285 return err;
286}
287
288void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
289 struct mlxsw_sp_rif *rif,
290 enum mlxsw_sp_rif_counter_dir dir)
291{
292 unsigned int *p_counter_index;
293
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200294 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
295 return;
296
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200297 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
298 if (WARN_ON(!p_counter_index))
299 return;
300 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
301 *p_counter_index, false, dir);
302 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
303 *p_counter_index);
304 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
305}
306
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200307static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
308{
309 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
310 struct devlink *devlink;
311
312 devlink = priv_to_devlink(mlxsw_sp->core);
313 if (!devlink_dpipe_table_counter_enabled(devlink,
314 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
315 return;
316 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
317}
318
319static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
320{
321 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
322
323 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
324}
325
Ido Schimmel4724ba562017-03-10 08:53:39 +0100326static struct mlxsw_sp_rif *
327mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
328 const struct net_device *dev);
329
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200330#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200331
332struct mlxsw_sp_prefix_usage {
333 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
334};
335
Jiri Pirko53342022016-07-04 08:23:08 +0200336#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
337 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
338
339static bool
340mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
341 struct mlxsw_sp_prefix_usage *prefix_usage2)
342{
343 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
344}
345
Jiri Pirko6b75c482016-07-04 08:23:09 +0200346static bool
347mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
348{
349 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
350
351 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
352}
353
354static void
355mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
356 struct mlxsw_sp_prefix_usage *prefix_usage2)
357{
358 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
359}
360
361static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200362mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
363 unsigned char prefix_len)
364{
365 set_bit(prefix_len, prefix_usage->b);
366}
367
368static void
369mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
370 unsigned char prefix_len)
371{
372 clear_bit(prefix_len, prefix_usage->b);
373}
374
375struct mlxsw_sp_fib_key {
376 unsigned char addr[sizeof(struct in6_addr)];
377 unsigned char prefix_len;
378};
379
Jiri Pirko61c503f2016-07-04 08:23:11 +0200380enum mlxsw_sp_fib_entry_type {
381 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
382 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
383 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
Petr Machata4607f6d2017-09-02 23:49:25 +0200384
385 /* This is a special case of local delivery, where a packet should be
386 * decapsulated on reception. Note that there is no corresponding ENCAP,
387 * because that's a type of next hop, not of FIB entry. (There can be
388 * several next hops in a REMOTE entry, and some of them may be
389 * encapsulating entries.)
390 */
391 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
Jiri Pirko61c503f2016-07-04 08:23:11 +0200392};
393
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200394struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200395struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200396
Ido Schimmel9aecce12017-02-09 10:28:42 +0100397struct mlxsw_sp_fib_node {
398 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200399 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100400 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100401 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100402 struct mlxsw_sp_fib_key key;
403};
404
Petr Machata4607f6d2017-09-02 23:49:25 +0200405struct mlxsw_sp_fib_entry_decap {
406 struct mlxsw_sp_ipip_entry *ipip_entry;
407 u32 tunnel_index;
408};
409
Ido Schimmel9aecce12017-02-09 10:28:42 +0100410struct mlxsw_sp_fib_entry {
411 struct list_head list;
412 struct mlxsw_sp_fib_node *fib_node;
413 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200414 struct list_head nexthop_group_node;
415 struct mlxsw_sp_nexthop_group *nh_group;
Petr Machata4607f6d2017-09-02 23:49:25 +0200416 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200417};
418
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200419struct mlxsw_sp_fib4_entry {
420 struct mlxsw_sp_fib_entry common;
421 u32 tb_id;
422 u32 prio;
423 u8 tos;
424 u8 type;
425};
426
Ido Schimmel428b8512017-08-03 13:28:28 +0200427struct mlxsw_sp_fib6_entry {
428 struct mlxsw_sp_fib_entry common;
429 struct list_head rt6_list;
430 unsigned int nrt6;
431};
432
433struct mlxsw_sp_rt6 {
434 struct list_head list;
435 struct rt6_info *rt;
436};
437
Ido Schimmel9011b672017-05-16 19:38:25 +0200438struct mlxsw_sp_lpm_tree {
439 u8 id; /* tree ID */
440 unsigned int ref_count;
441 enum mlxsw_sp_l3proto proto;
442 struct mlxsw_sp_prefix_usage prefix_usage;
443};
444
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200445struct mlxsw_sp_fib {
446 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100447 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100448 struct mlxsw_sp_vr *vr;
449 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200450 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
451 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100452 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200453};
454
Ido Schimmel9011b672017-05-16 19:38:25 +0200455struct mlxsw_sp_vr {
456 u16 id; /* virtual router ID */
457 u32 tb_id; /* kernel fib table id */
458 unsigned int rif_count;
459 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200460 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200461};
462
Ido Schimmel9aecce12017-02-09 10:28:42 +0100463static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200464
Ido Schimmel76610eb2017-03-10 08:53:41 +0100465static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
466 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200467{
468 struct mlxsw_sp_fib *fib;
469 int err;
470
471 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
472 if (!fib)
473 return ERR_PTR(-ENOMEM);
474 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
475 if (err)
476 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100477 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100478 fib->proto = proto;
479 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200480 return fib;
481
482err_rhashtable_init:
483 kfree(fib);
484 return ERR_PTR(err);
485}
486
487static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
488{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100489 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100490 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200491 rhashtable_destroy(&fib->ht);
492 kfree(fib);
493}
494
Jiri Pirko53342022016-07-04 08:23:08 +0200495static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100496mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200497{
498 static struct mlxsw_sp_lpm_tree *lpm_tree;
499 int i;
500
Ido Schimmel9011b672017-05-16 19:38:25 +0200501 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
502 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100503 if (lpm_tree->ref_count == 0)
504 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200505 }
506 return NULL;
507}
508
509static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
510 struct mlxsw_sp_lpm_tree *lpm_tree)
511{
512 char ralta_pl[MLXSW_REG_RALTA_LEN];
513
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200514 mlxsw_reg_ralta_pack(ralta_pl, true,
515 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
516 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200517 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
518}
519
Ido Schimmelcc702672017-08-14 10:54:03 +0200520static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
521 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200522{
523 char ralta_pl[MLXSW_REG_RALTA_LEN];
524
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200525 mlxsw_reg_ralta_pack(ralta_pl, false,
526 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
527 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200528 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200529}
530
531static int
532mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
533 struct mlxsw_sp_prefix_usage *prefix_usage,
534 struct mlxsw_sp_lpm_tree *lpm_tree)
535{
536 char ralst_pl[MLXSW_REG_RALST_LEN];
537 u8 root_bin = 0;
538 u8 prefix;
539 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
540
541 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
542 root_bin = prefix;
543
544 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
545 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
546 if (prefix == 0)
547 continue;
548 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
549 MLXSW_REG_RALST_BIN_NO_CHILD);
550 last_prefix = prefix;
551 }
552 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
553}
554
555static struct mlxsw_sp_lpm_tree *
556mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
557 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100558 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200559{
560 struct mlxsw_sp_lpm_tree *lpm_tree;
561 int err;
562
Ido Schimmel382dbb42017-03-10 08:53:40 +0100563 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200564 if (!lpm_tree)
565 return ERR_PTR(-EBUSY);
566 lpm_tree->proto = proto;
567 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
568 if (err)
569 return ERR_PTR(err);
570
571 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
572 lpm_tree);
573 if (err)
574 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200575 memcpy(&lpm_tree->prefix_usage, prefix_usage,
576 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200577 return lpm_tree;
578
579err_left_struct_set:
580 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
581 return ERR_PTR(err);
582}
583
Ido Schimmelcc702672017-08-14 10:54:03 +0200584static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
585 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200586{
Ido Schimmelcc702672017-08-14 10:54:03 +0200587 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200588}
589
590static struct mlxsw_sp_lpm_tree *
591mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
592 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100593 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200594{
595 struct mlxsw_sp_lpm_tree *lpm_tree;
596 int i;
597
Ido Schimmel9011b672017-05-16 19:38:25 +0200598 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
599 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200600 if (lpm_tree->ref_count != 0 &&
601 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200602 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
603 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200604 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200605 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200606 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
607}
Jiri Pirko53342022016-07-04 08:23:08 +0200608
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200609static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
610{
Jiri Pirko53342022016-07-04 08:23:08 +0200611 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200612}
613
Ido Schimmelcc702672017-08-14 10:54:03 +0200614static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
615 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200616{
617 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200618 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200619}
620
Ido Schimmeld7a60302017-06-08 08:47:43 +0200621#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100622
623static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200624{
625 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100626 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200627 int i;
628
Ido Schimmel8494ab02017-03-24 08:02:47 +0100629 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
630 return -EIO;
631
632 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200633 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
634 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100635 sizeof(struct mlxsw_sp_lpm_tree),
636 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200637 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100638 return -ENOMEM;
639
Ido Schimmel9011b672017-05-16 19:38:25 +0200640 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
641 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200642 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
643 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100644
645 return 0;
646}
647
648static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
649{
Ido Schimmel9011b672017-05-16 19:38:25 +0200650 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200651}
652
Ido Schimmel76610eb2017-03-10 08:53:41 +0100653static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
654{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200655 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100656}
657
Jiri Pirko6b75c482016-07-04 08:23:09 +0200658static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
659{
660 struct mlxsw_sp_vr *vr;
661 int i;
662
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200663 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200664 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100665 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200666 return vr;
667 }
668 return NULL;
669}
670
671static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200672 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200673{
674 char raltb_pl[MLXSW_REG_RALTB_LEN];
675
Ido Schimmel76610eb2017-03-10 08:53:41 +0100676 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
677 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200678 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200679 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
680}
681
682static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100683 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200684{
685 char raltb_pl[MLXSW_REG_RALTB_LEN];
686
687 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100688 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
689 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200690 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
691}
692
693static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
694{
695 /* For our purpose, squash main and local table into one */
696 if (tb_id == RT_TABLE_LOCAL)
697 tb_id = RT_TABLE_MAIN;
698 return tb_id;
699}
700
701static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100702 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200703{
704 struct mlxsw_sp_vr *vr;
705 int i;
706
707 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200708
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200709 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200710 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100711 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200712 return vr;
713 }
714 return NULL;
715}
716
Ido Schimmel76610eb2017-03-10 08:53:41 +0100717static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
718 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200719{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100720 switch (proto) {
721 case MLXSW_SP_L3_PROTO_IPV4:
722 return vr->fib4;
723 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200724 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100725 }
726 return NULL;
727}
728
729static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
730 u32 tb_id)
731{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200732 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200733 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200734
735 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
736 if (!vr)
737 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100738 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
739 if (IS_ERR(vr->fib4))
740 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200741 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
742 if (IS_ERR(vr->fib6)) {
743 err = PTR_ERR(vr->fib6);
744 goto err_fib6_create;
745 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200748
749err_fib6_create:
750 mlxsw_sp_fib_destroy(vr->fib4);
751 vr->fib4 = NULL;
752 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200753}
754
Ido Schimmel76610eb2017-03-10 08:53:41 +0100755static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200756{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200757 mlxsw_sp_fib_destroy(vr->fib6);
758 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100759 mlxsw_sp_fib_destroy(vr->fib4);
760 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200761}
762
Ido Schimmel76610eb2017-03-10 08:53:41 +0100763static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200764{
765 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200766
767 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100768 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
769 if (!vr)
770 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200771 return vr;
772}
773
Ido Schimmel76610eb2017-03-10 08:53:41 +0100774static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200775{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200776 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
777 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100778 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200779}
780
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200781static bool
782mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
783 enum mlxsw_sp_l3proto proto, u8 tree_id)
784{
785 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
786
787 if (!mlxsw_sp_vr_is_used(vr))
788 return false;
789 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
790 return true;
791 return false;
792}
793
794static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
795 struct mlxsw_sp_fib *fib,
796 struct mlxsw_sp_lpm_tree *new_tree)
797{
798 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
799 int err;
800
801 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
802 if (err)
803 return err;
804 fib->lpm_tree = new_tree;
805 mlxsw_sp_lpm_tree_hold(new_tree);
806 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
807 return 0;
808}
809
810static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
811 struct mlxsw_sp_fib *fib,
812 struct mlxsw_sp_lpm_tree *new_tree)
813{
814 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
815 enum mlxsw_sp_l3proto proto = fib->proto;
816 u8 old_id, new_id = new_tree->id;
817 struct mlxsw_sp_vr *vr;
818 int i, err;
819
820 if (!old_tree)
821 goto no_replace;
822 old_id = old_tree->id;
823
824 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
825 vr = &mlxsw_sp->router->vrs[i];
826 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
827 continue;
828 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
829 mlxsw_sp_vr_fib(vr, proto),
830 new_tree);
831 if (err)
832 goto err_tree_replace;
833 }
834
835 return 0;
836
837err_tree_replace:
838 for (i--; i >= 0; i--) {
839 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
840 continue;
841 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
842 mlxsw_sp_vr_fib(vr, proto),
843 old_tree);
844 }
845 return err;
846
847no_replace:
848 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
849 if (err)
850 return err;
851 fib->lpm_tree = new_tree;
852 mlxsw_sp_lpm_tree_hold(new_tree);
853 return 0;
854}
855
856static void
857mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
858 enum mlxsw_sp_l3proto proto,
859 struct mlxsw_sp_prefix_usage *req_prefix_usage)
860{
861 int i;
862
863 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
864 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
865 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
866 unsigned char prefix;
867
868 if (!mlxsw_sp_vr_is_used(vr))
869 continue;
870 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
871 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
872 }
873}
874
Nogah Frankel9497c042016-09-20 11:16:54 +0200875static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200876{
877 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200878 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200879 int i;
880
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200881 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200882 return -EIO;
883
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200884 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200885 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
886 GFP_KERNEL);
887 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200888 return -ENOMEM;
889
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200890 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200891 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200892 vr->id = i;
893 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200894
895 return 0;
896}
897
Ido Schimmelac571de2016-11-14 11:26:32 +0100898static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
899
Nogah Frankel9497c042016-09-20 11:16:54 +0200900static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
901{
Ido Schimmel30572242016-12-03 16:45:01 +0100902 /* At this stage we're guaranteed not to have new incoming
903 * FIB notifications and the work queue is free from FIBs
904 * sitting on top of mlxsw netdevs. However, we can still
905 * have other FIBs queued. Flush the queue before flushing
906 * the device's tables. No need for locks, as we're the only
907 * writer.
908 */
909 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100910 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200911 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200912}
913
Petr Machata6ddb7422017-09-02 23:49:19 +0200914static struct net_device *
915__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
916{
917 struct ip_tunnel *tun = netdev_priv(ol_dev);
918 struct net *net = dev_net(ol_dev);
919
920 return __dev_get_by_index(net, tun->parms.link);
921}
922
923static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
924{
925 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
926
927 if (d)
928 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
929 else
930 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
931}
932
Petr Machata1012b9a2017-09-02 23:49:23 +0200933static struct mlxsw_sp_rif *
934mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
935 const struct mlxsw_sp_rif_params *params);
936
937static struct mlxsw_sp_rif_ipip_lb *
938mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
939 enum mlxsw_sp_ipip_type ipipt,
940 struct net_device *ol_dev)
941{
942 struct mlxsw_sp_rif_params_ipip_lb lb_params;
943 const struct mlxsw_sp_ipip_ops *ipip_ops;
944 struct mlxsw_sp_rif *rif;
945
946 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
947 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
948 .common.dev = ol_dev,
949 .common.lag = false,
950 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
951 };
952
953 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common);
954 if (IS_ERR(rif))
955 return ERR_CAST(rif);
956 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
957}
958
959static struct mlxsw_sp_ipip_entry *
960mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
961 enum mlxsw_sp_ipip_type ipipt,
962 struct net_device *ol_dev)
963{
964 struct mlxsw_sp_ipip_entry *ipip_entry;
965 struct mlxsw_sp_ipip_entry *ret = NULL;
966
967 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
968 if (!ipip_entry)
969 return ERR_PTR(-ENOMEM);
970
971 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
972 ol_dev);
973 if (IS_ERR(ipip_entry->ol_lb)) {
974 ret = ERR_CAST(ipip_entry->ol_lb);
975 goto err_ol_ipip_lb_create;
976 }
977
978 ipip_entry->ipipt = ipipt;
979 ipip_entry->ol_dev = ol_dev;
980
981 return ipip_entry;
982
983err_ol_ipip_lb_create:
984 kfree(ipip_entry);
985 return ret;
986}
987
988static void
989mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry)
990{
991 WARN_ON(ipip_entry->ref_count > 0);
992 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
993 kfree(ipip_entry);
994}
995
996static __be32
997mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev)
998{
999 struct ip_tunnel *tun = netdev_priv(ol_dev);
1000
1001 return tun->parms.iph.saddr;
1002}
1003
1004union mlxsw_sp_l3addr
1005mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
1006 const struct net_device *ol_dev)
1007{
1008 switch (proto) {
1009 case MLXSW_SP_L3_PROTO_IPV4:
1010 return (union mlxsw_sp_l3addr) {
1011 .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev),
1012 };
1013 case MLXSW_SP_L3_PROTO_IPV6:
1014 break;
1015 };
1016
1017 WARN_ON(1);
1018 return (union mlxsw_sp_l3addr) {
1019 .addr4 = 0,
1020 };
1021}
1022
Petr Machataee954d1a2017-09-02 23:49:29 +02001023__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
1024{
1025 struct ip_tunnel *tun = netdev_priv(ol_dev);
1026
1027 return tun->parms.iph.daddr;
1028}
1029
1030union mlxsw_sp_l3addr
1031mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1032 const struct net_device *ol_dev)
1033{
1034 switch (proto) {
1035 case MLXSW_SP_L3_PROTO_IPV4:
1036 return (union mlxsw_sp_l3addr) {
1037 .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev),
1038 };
1039 case MLXSW_SP_L3_PROTO_IPV6:
1040 break;
1041 };
1042
1043 WARN_ON(1);
1044 return (union mlxsw_sp_l3addr) {
1045 .addr4 = 0,
1046 };
1047}
1048
Petr Machata1012b9a2017-09-02 23:49:23 +02001049static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1050 const union mlxsw_sp_l3addr *addr2)
1051{
1052 return !memcmp(addr1, addr2, sizeof(*addr1));
1053}
1054
1055static bool
1056mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
1057 const enum mlxsw_sp_l3proto ul_proto,
1058 union mlxsw_sp_l3addr saddr,
1059 u32 ul_tb_id,
1060 struct mlxsw_sp_ipip_entry *ipip_entry)
1061{
1062 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1063 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1064 union mlxsw_sp_l3addr tun_saddr;
1065
1066 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1067 return false;
1068
1069 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
1070 return tun_ul_tb_id == ul_tb_id &&
1071 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
1072}
1073
Petr Machata4607f6d2017-09-02 23:49:25 +02001074static int
1075mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
1076 struct mlxsw_sp_fib_entry *fib_entry,
1077 struct mlxsw_sp_ipip_entry *ipip_entry)
1078{
1079 u32 tunnel_index;
1080 int err;
1081
1082 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1083 if (err)
1084 return err;
1085
1086 ipip_entry->decap_fib_entry = fib_entry;
1087 fib_entry->decap.ipip_entry = ipip_entry;
1088 fib_entry->decap.tunnel_index = tunnel_index;
1089 return 0;
1090}
1091
1092static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1093 struct mlxsw_sp_fib_entry *fib_entry)
1094{
1095 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1096 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1097 fib_entry->decap.ipip_entry = NULL;
1098 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1099}
1100
Petr Machata1cc38fb2017-09-02 23:49:26 +02001101static struct mlxsw_sp_fib_node *
1102mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1103 size_t addr_len, unsigned char prefix_len);
Petr Machata4607f6d2017-09-02 23:49:25 +02001104static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1105 struct mlxsw_sp_fib_entry *fib_entry);
1106
1107static void
1108mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1109 struct mlxsw_sp_ipip_entry *ipip_entry)
1110{
1111 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1112
1113 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1114 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1115
1116 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1117}
1118
Petr Machata1cc38fb2017-09-02 23:49:26 +02001119static void
1120mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1121 struct mlxsw_sp_ipip_entry *ipip_entry,
1122 struct mlxsw_sp_fib_entry *decap_fib_entry)
1123{
1124 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1125 ipip_entry))
1126 return;
1127 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1128
1129 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1130 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1131}
1132
1133/* Given an IPIP entry, find the corresponding decap route. */
1134static struct mlxsw_sp_fib_entry *
1135mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1136 struct mlxsw_sp_ipip_entry *ipip_entry)
1137{
1138 static struct mlxsw_sp_fib_node *fib_node;
1139 const struct mlxsw_sp_ipip_ops *ipip_ops;
1140 struct mlxsw_sp_fib_entry *fib_entry;
1141 unsigned char saddr_prefix_len;
1142 union mlxsw_sp_l3addr saddr;
1143 struct mlxsw_sp_fib *ul_fib;
1144 struct mlxsw_sp_vr *ul_vr;
1145 const void *saddrp;
1146 size_t saddr_len;
1147 u32 ul_tb_id;
1148 u32 saddr4;
1149
1150 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1151
1152 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1153 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1154 if (!ul_vr)
1155 return NULL;
1156
1157 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1158 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1159 ipip_entry->ol_dev);
1160
1161 switch (ipip_ops->ul_proto) {
1162 case MLXSW_SP_L3_PROTO_IPV4:
1163 saddr4 = be32_to_cpu(saddr.addr4);
1164 saddrp = &saddr4;
1165 saddr_len = 4;
1166 saddr_prefix_len = 32;
1167 break;
1168 case MLXSW_SP_L3_PROTO_IPV6:
1169 WARN_ON(1);
1170 return NULL;
1171 }
1172
1173 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1174 saddr_prefix_len);
1175 if (!fib_node || list_empty(&fib_node->entry_list))
1176 return NULL;
1177
1178 fib_entry = list_first_entry(&fib_node->entry_list,
1179 struct mlxsw_sp_fib_entry, list);
1180 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1181 return NULL;
1182
1183 return fib_entry;
1184}
1185
Petr Machata1012b9a2017-09-02 23:49:23 +02001186static struct mlxsw_sp_ipip_entry *
1187mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1188 enum mlxsw_sp_ipip_type ipipt,
1189 struct net_device *ol_dev)
1190{
1191 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1192 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata1cc38fb2017-09-02 23:49:26 +02001193 struct mlxsw_sp_fib_entry *decap_fib_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02001194 struct mlxsw_sp_ipip_entry *ipip_entry;
1195 enum mlxsw_sp_l3proto ul_proto;
1196 union mlxsw_sp_l3addr saddr;
1197
1198 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1199 ipip_list_node) {
1200 if (ipip_entry->ol_dev == ol_dev)
1201 goto inc_ref_count;
1202
1203 /* The configuration where several tunnels have the same local
1204 * address in the same underlay table needs special treatment in
1205 * the HW. That is currently not implemented in the driver.
1206 */
1207 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1208 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1209 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1210 ul_tb_id, ipip_entry))
1211 return ERR_PTR(-EEXIST);
1212 }
1213
1214 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1215 if (IS_ERR(ipip_entry))
1216 return ipip_entry;
1217
Petr Machata1cc38fb2017-09-02 23:49:26 +02001218 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
1219 if (decap_fib_entry)
1220 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1221 decap_fib_entry);
1222
Petr Machata1012b9a2017-09-02 23:49:23 +02001223 list_add_tail(&ipip_entry->ipip_list_node,
1224 &mlxsw_sp->router->ipip_list);
1225
1226inc_ref_count:
1227 ++ipip_entry->ref_count;
1228 return ipip_entry;
1229}
1230
1231static void
1232mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1233 struct mlxsw_sp_ipip_entry *ipip_entry)
1234{
1235 if (--ipip_entry->ref_count == 0) {
1236 list_del(&ipip_entry->ipip_list_node);
Petr Machata4607f6d2017-09-02 23:49:25 +02001237 if (ipip_entry->decap_fib_entry)
1238 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
Petr Machata1012b9a2017-09-02 23:49:23 +02001239 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1240 }
1241}
1242
Petr Machata4607f6d2017-09-02 23:49:25 +02001243static bool
1244mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1245 const struct net_device *ul_dev,
1246 enum mlxsw_sp_l3proto ul_proto,
1247 union mlxsw_sp_l3addr ul_dip,
1248 struct mlxsw_sp_ipip_entry *ipip_entry)
1249{
1250 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1251 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1252 struct net_device *ipip_ul_dev;
1253
1254 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1255 return false;
1256
1257 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1258 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1259 ul_tb_id, ipip_entry) &&
1260 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1261}
1262
1263/* Given decap parameters, find the corresponding IPIP entry. */
1264static struct mlxsw_sp_ipip_entry *
1265mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1266 const struct net_device *ul_dev,
1267 enum mlxsw_sp_l3proto ul_proto,
1268 union mlxsw_sp_l3addr ul_dip)
1269{
1270 struct mlxsw_sp_ipip_entry *ipip_entry;
1271
1272 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1273 ipip_list_node)
1274 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1275 ul_proto, ul_dip,
1276 ipip_entry))
1277 return ipip_entry;
1278
1279 return NULL;
1280}
1281
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001282struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +01001283 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001284};
1285
1286struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +01001287 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001288 struct rhash_head ht_node;
1289 struct mlxsw_sp_neigh_key key;
1290 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001291 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001292 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001293 struct list_head nexthop_list; /* list of nexthops using
1294 * this neigh entry
1295 */
Yotam Gigib2157142016-07-05 11:27:51 +02001296 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001297 unsigned int counter_index;
1298 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001299};
1300
1301static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1302 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1303 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1304 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1305};
1306
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001307struct mlxsw_sp_neigh_entry *
1308mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1309 struct mlxsw_sp_neigh_entry *neigh_entry)
1310{
1311 if (!neigh_entry) {
1312 if (list_empty(&rif->neigh_list))
1313 return NULL;
1314 else
1315 return list_first_entry(&rif->neigh_list,
1316 typeof(*neigh_entry),
1317 rif_list_node);
1318 }
1319 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
1320 return NULL;
1321 return list_next_entry(neigh_entry, rif_list_node);
1322}
1323
1324int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1325{
1326 return neigh_entry->key.n->tbl->family;
1327}
1328
1329unsigned char *
1330mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1331{
1332 return neigh_entry->ha;
1333}
1334
1335u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1336{
1337 struct neighbour *n;
1338
1339 n = neigh_entry->key.n;
1340 return ntohl(*((__be32 *) n->primary_key));
1341}
1342
Arkadi Sharshevsky02507682017-08-31 17:59:15 +02001343struct in6_addr *
1344mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1345{
1346 struct neighbour *n;
1347
1348 n = neigh_entry->key.n;
1349 return (struct in6_addr *) &n->primary_key;
1350}
1351
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001352int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1353 struct mlxsw_sp_neigh_entry *neigh_entry,
1354 u64 *p_counter)
1355{
1356 if (!neigh_entry->counter_valid)
1357 return -EINVAL;
1358
1359 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1360 p_counter, NULL);
1361}
1362
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001363static struct mlxsw_sp_neigh_entry *
1364mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1365 u16 rif)
1366{
1367 struct mlxsw_sp_neigh_entry *neigh_entry;
1368
1369 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1370 if (!neigh_entry)
1371 return NULL;
1372
1373 neigh_entry->key.n = n;
1374 neigh_entry->rif = rif;
1375 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1376
1377 return neigh_entry;
1378}
1379
1380static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1381{
1382 kfree(neigh_entry);
1383}
1384
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001385static int
1386mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1387 struct mlxsw_sp_neigh_entry *neigh_entry)
1388{
Ido Schimmel9011b672017-05-16 19:38:25 +02001389 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001390 &neigh_entry->ht_node,
1391 mlxsw_sp_neigh_ht_params);
1392}
1393
1394static void
1395mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1396 struct mlxsw_sp_neigh_entry *neigh_entry)
1397{
Ido Schimmel9011b672017-05-16 19:38:25 +02001398 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001399 &neigh_entry->ht_node,
1400 mlxsw_sp_neigh_ht_params);
1401}
1402
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001403static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001404mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1405 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001406{
1407 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001408 const char *table_name;
1409
1410 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1411 case AF_INET:
1412 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1413 break;
1414 case AF_INET6:
1415 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1416 break;
1417 default:
1418 WARN_ON(1);
1419 return false;
1420 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001421
1422 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001423 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001424}
1425
1426static void
1427mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1428 struct mlxsw_sp_neigh_entry *neigh_entry)
1429{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001430 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001431 return;
1432
1433 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1434 return;
1435
1436 neigh_entry->counter_valid = true;
1437}
1438
1439static void
1440mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1441 struct mlxsw_sp_neigh_entry *neigh_entry)
1442{
1443 if (!neigh_entry->counter_valid)
1444 return;
1445 mlxsw_sp_flow_counter_free(mlxsw_sp,
1446 neigh_entry->counter_index);
1447 neigh_entry->counter_valid = false;
1448}
1449
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001450static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001451mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001452{
1453 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001454 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001455 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001456
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001457 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1458 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001459 return ERR_PTR(-EINVAL);
1460
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001461 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001462 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001463 return ERR_PTR(-ENOMEM);
1464
1465 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1466 if (err)
1467 goto err_neigh_entry_insert;
1468
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001469 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001470 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001471
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001472 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001473
1474err_neigh_entry_insert:
1475 mlxsw_sp_neigh_entry_free(neigh_entry);
1476 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001477}
1478
1479static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001480mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1481 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001482{
Ido Schimmel9665b742017-02-08 11:16:42 +01001483 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001484 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001485 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1486 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001487}
1488
1489static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001490mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001491{
Jiri Pirko33b13412016-11-10 12:31:04 +01001492 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001493
Jiri Pirko33b13412016-11-10 12:31:04 +01001494 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001495 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001496 &key, mlxsw_sp_neigh_ht_params);
1497}
1498
Yotam Gigic723c7352016-07-05 11:27:43 +02001499static void
1500mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1501{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001502 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001503
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001504#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001505 interval = min_t(unsigned long,
1506 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1507 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001508#else
1509 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1510#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001511 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001512}
1513
1514static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1515 char *rauhtd_pl,
1516 int ent_index)
1517{
1518 struct net_device *dev;
1519 struct neighbour *n;
1520 __be32 dipn;
1521 u32 dip;
1522 u16 rif;
1523
1524 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1525
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001526 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001527 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1528 return;
1529 }
1530
1531 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001532 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001533 n = neigh_lookup(&arp_tbl, &dipn, dev);
1534 if (!n) {
1535 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1536 &dip);
1537 return;
1538 }
1539
1540 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1541 neigh_event_send(n, NULL);
1542 neigh_release(n);
1543}
1544
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001545#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001546static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1547 char *rauhtd_pl,
1548 int rec_index)
1549{
1550 struct net_device *dev;
1551 struct neighbour *n;
1552 struct in6_addr dip;
1553 u16 rif;
1554
1555 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1556 (char *) &dip);
1557
1558 if (!mlxsw_sp->router->rifs[rif]) {
1559 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1560 return;
1561 }
1562
1563 dev = mlxsw_sp->router->rifs[rif]->dev;
1564 n = neigh_lookup(&nd_tbl, &dip, dev);
1565 if (!n) {
1566 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1567 &dip);
1568 return;
1569 }
1570
1571 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1572 neigh_event_send(n, NULL);
1573 neigh_release(n);
1574}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001575#else
1576static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1577 char *rauhtd_pl,
1578 int rec_index)
1579{
1580}
1581#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001582
Yotam Gigic723c7352016-07-05 11:27:43 +02001583static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1584 char *rauhtd_pl,
1585 int rec_index)
1586{
1587 u8 num_entries;
1588 int i;
1589
1590 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1591 rec_index);
1592 /* Hardware starts counting at 0, so add 1. */
1593 num_entries++;
1594
1595 /* Each record consists of several neighbour entries. */
1596 for (i = 0; i < num_entries; i++) {
1597 int ent_index;
1598
1599 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1600 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1601 ent_index);
1602 }
1603
1604}
1605
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001606static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1607 char *rauhtd_pl,
1608 int rec_index)
1609{
1610 /* One record contains one entry. */
1611 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1612 rec_index);
1613}
1614
Yotam Gigic723c7352016-07-05 11:27:43 +02001615static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1616 char *rauhtd_pl, int rec_index)
1617{
1618 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1619 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1620 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1621 rec_index);
1622 break;
1623 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001624 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1625 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001626 break;
1627 }
1628}
1629
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001630static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1631{
1632 u8 num_rec, last_rec_index, num_entries;
1633
1634 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1635 last_rec_index = num_rec - 1;
1636
1637 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1638 return false;
1639 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1640 MLXSW_REG_RAUHTD_TYPE_IPV6)
1641 return true;
1642
1643 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1644 last_rec_index);
1645 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1646 return true;
1647 return false;
1648}
1649
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001650static int
1651__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1652 char *rauhtd_pl,
1653 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001654{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001655 int i, num_rec;
1656 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001657
1658 /* Make sure the neighbour's netdev isn't removed in the
1659 * process.
1660 */
1661 rtnl_lock();
1662 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001663 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001664 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1665 rauhtd_pl);
1666 if (err) {
1667 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1668 break;
1669 }
1670 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1671 for (i = 0; i < num_rec; i++)
1672 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1673 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001674 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001675 rtnl_unlock();
1676
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001677 return err;
1678}
1679
1680static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1681{
1682 enum mlxsw_reg_rauhtd_type type;
1683 char *rauhtd_pl;
1684 int err;
1685
1686 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1687 if (!rauhtd_pl)
1688 return -ENOMEM;
1689
1690 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1691 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1692 if (err)
1693 goto out;
1694
1695 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1696 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1697out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001698 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001699 return err;
1700}
1701
1702static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1703{
1704 struct mlxsw_sp_neigh_entry *neigh_entry;
1705
1706 /* Take RTNL mutex here to prevent lists from changes */
1707 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001708 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001709 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001710 /* If this neigh have nexthops, make the kernel think this neigh
1711 * is active regardless of the traffic.
1712 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001713 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001714 rtnl_unlock();
1715}
1716
1717static void
1718mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1719{
Ido Schimmel9011b672017-05-16 19:38:25 +02001720 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001721
Ido Schimmel9011b672017-05-16 19:38:25 +02001722 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001723 msecs_to_jiffies(interval));
1724}
1725
1726static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1727{
Ido Schimmel9011b672017-05-16 19:38:25 +02001728 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001729 int err;
1730
Ido Schimmel9011b672017-05-16 19:38:25 +02001731 router = container_of(work, struct mlxsw_sp_router,
1732 neighs_update.dw.work);
1733 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001734 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001735 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001736
Ido Schimmel9011b672017-05-16 19:38:25 +02001737 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001738
Ido Schimmel9011b672017-05-16 19:38:25 +02001739 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001740}
1741
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001742static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1743{
1744 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001745 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001746
Ido Schimmel9011b672017-05-16 19:38:25 +02001747 router = container_of(work, struct mlxsw_sp_router,
1748 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001749 /* Iterate over nexthop neighbours, find those who are unresolved and
1750 * send arp on them. This solves the chicken-egg problem when
1751 * the nexthop wouldn't get offloaded until the neighbor is resolved
1752 * but it wouldn't get resolved ever in case traffic is flowing in HW
1753 * using different nexthop.
1754 *
1755 * Take RTNL mutex here to prevent lists from changes.
1756 */
1757 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001758 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001759 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001760 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001761 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001762 rtnl_unlock();
1763
Ido Schimmel9011b672017-05-16 19:38:25 +02001764 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001765 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1766}
1767
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001768static void
1769mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1770 struct mlxsw_sp_neigh_entry *neigh_entry,
1771 bool removing);
1772
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001773static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001774{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001775 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1776 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1777}
1778
1779static void
1780mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1781 struct mlxsw_sp_neigh_entry *neigh_entry,
1782 enum mlxsw_reg_rauht_op op)
1783{
Jiri Pirko33b13412016-11-10 12:31:04 +01001784 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001785 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001786 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001787
1788 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1789 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001790 if (neigh_entry->counter_valid)
1791 mlxsw_reg_rauht_pack_counter(rauht_pl,
1792 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001793 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1794}
1795
1796static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001797mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1798 struct mlxsw_sp_neigh_entry *neigh_entry,
1799 enum mlxsw_reg_rauht_op op)
1800{
1801 struct neighbour *n = neigh_entry->key.n;
1802 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1803 const char *dip = n->primary_key;
1804
1805 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1806 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001807 if (neigh_entry->counter_valid)
1808 mlxsw_reg_rauht_pack_counter(rauht_pl,
1809 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001810 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1811}
1812
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001813bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001814{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001815 struct neighbour *n = neigh_entry->key.n;
1816
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001817 /* Packets with a link-local destination address are trapped
1818 * after LPM lookup and never reach the neighbour table, so
1819 * there is no need to program such neighbours to the device.
1820 */
1821 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1822 IPV6_ADDR_LINKLOCAL)
1823 return true;
1824 return false;
1825}
1826
1827static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001828mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1829 struct mlxsw_sp_neigh_entry *neigh_entry,
1830 bool adding)
1831{
1832 if (!adding && !neigh_entry->connected)
1833 return;
1834 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001835 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001836 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1837 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001838 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001839 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001840 return;
1841 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1842 mlxsw_sp_rauht_op(adding));
1843 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001844 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001845 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001846}
1847
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001848void
1849mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1850 struct mlxsw_sp_neigh_entry *neigh_entry,
1851 bool adding)
1852{
1853 if (adding)
1854 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1855 else
1856 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1857 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1858}
1859
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001860struct mlxsw_sp_neigh_event_work {
1861 struct work_struct work;
1862 struct mlxsw_sp *mlxsw_sp;
1863 struct neighbour *n;
1864};
1865
1866static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1867{
1868 struct mlxsw_sp_neigh_event_work *neigh_work =
1869 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1870 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1871 struct mlxsw_sp_neigh_entry *neigh_entry;
1872 struct neighbour *n = neigh_work->n;
1873 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001874 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001875 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001876
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001877 /* If these parameters are changed after we release the lock,
1878 * then we are guaranteed to receive another event letting us
1879 * know about it.
1880 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001881 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001882 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001883 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001884 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001885 read_unlock_bh(&n->lock);
1886
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001887 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001888 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001889 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1890 if (!entry_connected && !neigh_entry)
1891 goto out;
1892 if (!neigh_entry) {
1893 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1894 if (IS_ERR(neigh_entry))
1895 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001896 }
1897
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001898 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1899 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1900 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1901
1902 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1903 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1904
1905out:
1906 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001907 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001908 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001909}
1910
Jiri Pirkoe7322632016-09-01 10:37:43 +02001911int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1912 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001913{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001914 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001915 struct mlxsw_sp_port *mlxsw_sp_port;
1916 struct mlxsw_sp *mlxsw_sp;
1917 unsigned long interval;
1918 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001919 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001920
1921 switch (event) {
1922 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1923 p = ptr;
1924
1925 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001926 if (!p->dev || (p->tbl->family != AF_INET &&
1927 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001928 return NOTIFY_DONE;
1929
1930 /* We are in atomic context and can't take RTNL mutex,
1931 * so use RCU variant to walk the device chain.
1932 */
1933 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1934 if (!mlxsw_sp_port)
1935 return NOTIFY_DONE;
1936
1937 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1938 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001939 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001940
1941 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1942 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001943 case NETEVENT_NEIGH_UPDATE:
1944 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001945
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001946 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001947 return NOTIFY_DONE;
1948
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001949 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001950 if (!mlxsw_sp_port)
1951 return NOTIFY_DONE;
1952
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001953 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1954 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001955 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001956 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001957 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001958
1959 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1960 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1961 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001962
1963 /* Take a reference to ensure the neighbour won't be
1964 * destructed until we drop the reference in delayed
1965 * work.
1966 */
1967 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001968 mlxsw_core_schedule_work(&neigh_work->work);
1969 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001970 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001971 }
1972
1973 return NOTIFY_DONE;
1974}
1975
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001976static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1977{
Yotam Gigic723c7352016-07-05 11:27:43 +02001978 int err;
1979
Ido Schimmel9011b672017-05-16 19:38:25 +02001980 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001981 &mlxsw_sp_neigh_ht_params);
1982 if (err)
1983 return err;
1984
1985 /* Initialize the polling interval according to the default
1986 * table.
1987 */
1988 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1989
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001990 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001991 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001992 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001993 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001994 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001995 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1996 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001997 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001998}
1999
2000static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
2001{
Ido Schimmel9011b672017-05-16 19:38:25 +02002002 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
2003 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
2004 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002005}
2006
Ido Schimmel9665b742017-02-08 11:16:42 +01002007static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002008 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002009{
2010 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
2011
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002012 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002013 rif_list_node) {
2014 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01002015 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002016 }
Ido Schimmel9665b742017-02-08 11:16:42 +01002017}
2018
Petr Machata35225e42017-09-02 23:49:22 +02002019enum mlxsw_sp_nexthop_type {
2020 MLXSW_SP_NEXTHOP_TYPE_ETH,
Petr Machata1012b9a2017-09-02 23:49:23 +02002021 MLXSW_SP_NEXTHOP_TYPE_IPIP,
Petr Machata35225e42017-09-02 23:49:22 +02002022};
2023
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002024struct mlxsw_sp_nexthop_key {
2025 struct fib_nh *fib_nh;
2026};
2027
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002028struct mlxsw_sp_nexthop {
2029 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01002030 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2032 * this belongs to
2033 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002034 struct rhash_head ht_node;
2035 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002036 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002037 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002038 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002039 u8 should_offload:1, /* set indicates this neigh is connected and
2040 * should be put to KVD linear area of this group.
2041 */
2042 offloaded:1, /* set in case the neigh is actually put into
2043 * KVD linear area of this group.
2044 */
2045 update:1; /* set indicates that MAC of this neigh should be
2046 * updated in HW
2047 */
Petr Machata35225e42017-09-02 23:49:22 +02002048 enum mlxsw_sp_nexthop_type type;
2049 union {
2050 struct mlxsw_sp_neigh_entry *neigh_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02002051 struct mlxsw_sp_ipip_entry *ipip_entry;
Petr Machata35225e42017-09-02 23:49:22 +02002052 };
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002053};
2054
2055struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002056 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002057 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002058 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002059 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002060 u8 adj_index_valid:1,
2061 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002062 u32 adj_index;
2063 u16 ecmp_size;
2064 u16 count;
2065 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002066#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002067};
2068
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002069static struct fib_info *
2070mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2071{
2072 return nh_grp->priv;
2073}
2074
2075struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002076 enum mlxsw_sp_l3proto proto;
2077 union {
2078 struct fib_info *fi;
2079 struct mlxsw_sp_fib6_entry *fib6_entry;
2080 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002081};
2082
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002083static bool
2084mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2085 const struct in6_addr *gw, int ifindex)
2086{
2087 int i;
2088
2089 for (i = 0; i < nh_grp->count; i++) {
2090 const struct mlxsw_sp_nexthop *nh;
2091
2092 nh = &nh_grp->nexthops[i];
2093 if (nh->ifindex == ifindex &&
2094 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2095 return true;
2096 }
2097
2098 return false;
2099}
2100
2101static bool
2102mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2103 const struct mlxsw_sp_fib6_entry *fib6_entry)
2104{
2105 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2106
2107 if (nh_grp->count != fib6_entry->nrt6)
2108 return false;
2109
2110 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2111 struct in6_addr *gw;
2112 int ifindex;
2113
2114 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2115 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2116 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2117 return false;
2118 }
2119
2120 return true;
2121}
2122
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002123static int
2124mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2125{
2126 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2127 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2128
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002129 switch (cmp_arg->proto) {
2130 case MLXSW_SP_L3_PROTO_IPV4:
2131 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2132 case MLXSW_SP_L3_PROTO_IPV6:
2133 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2134 cmp_arg->fib6_entry);
2135 default:
2136 WARN_ON(1);
2137 return 1;
2138 }
2139}
2140
2141static int
2142mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2143{
2144 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002145}
2146
2147static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2148{
2149 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002150 const struct mlxsw_sp_nexthop *nh;
2151 struct fib_info *fi;
2152 unsigned int val;
2153 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002154
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002155 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2156 case AF_INET:
2157 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2158 return jhash(&fi, sizeof(fi), seed);
2159 case AF_INET6:
2160 val = nh_grp->count;
2161 for (i = 0; i < nh_grp->count; i++) {
2162 nh = &nh_grp->nexthops[i];
2163 val ^= nh->ifindex;
2164 }
2165 return jhash(&val, sizeof(val), seed);
2166 default:
2167 WARN_ON(1);
2168 return 0;
2169 }
2170}
2171
2172static u32
2173mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2174{
2175 unsigned int val = fib6_entry->nrt6;
2176 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2177 struct net_device *dev;
2178
2179 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2180 dev = mlxsw_sp_rt6->rt->dst.dev;
2181 val ^= dev->ifindex;
2182 }
2183
2184 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002185}
2186
2187static u32
2188mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2189{
2190 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2191
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002192 switch (cmp_arg->proto) {
2193 case MLXSW_SP_L3_PROTO_IPV4:
2194 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2195 case MLXSW_SP_L3_PROTO_IPV6:
2196 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2197 default:
2198 WARN_ON(1);
2199 return 0;
2200 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002201}
2202
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002203static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002204 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002205 .hashfn = mlxsw_sp_nexthop_group_hash,
2206 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2207 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002208};
2209
2210static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2211 struct mlxsw_sp_nexthop_group *nh_grp)
2212{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002213 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2214 !nh_grp->gateway)
2215 return 0;
2216
Ido Schimmel9011b672017-05-16 19:38:25 +02002217 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002218 &nh_grp->ht_node,
2219 mlxsw_sp_nexthop_group_ht_params);
2220}
2221
2222static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2223 struct mlxsw_sp_nexthop_group *nh_grp)
2224{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002225 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2226 !nh_grp->gateway)
2227 return;
2228
Ido Schimmel9011b672017-05-16 19:38:25 +02002229 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002230 &nh_grp->ht_node,
2231 mlxsw_sp_nexthop_group_ht_params);
2232}
2233
2234static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002235mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2236 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002237{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002238 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2239
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002240 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002241 cmp_arg.fi = fi;
2242 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2243 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002244 mlxsw_sp_nexthop_group_ht_params);
2245}
2246
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002247static struct mlxsw_sp_nexthop_group *
2248mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2249 struct mlxsw_sp_fib6_entry *fib6_entry)
2250{
2251 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2252
2253 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2254 cmp_arg.fib6_entry = fib6_entry;
2255 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2256 &cmp_arg,
2257 mlxsw_sp_nexthop_group_ht_params);
2258}
2259
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002260static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2261 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2262 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2263 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2264};
2265
2266static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2267 struct mlxsw_sp_nexthop *nh)
2268{
Ido Schimmel9011b672017-05-16 19:38:25 +02002269 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002270 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2271}
2272
2273static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2274 struct mlxsw_sp_nexthop *nh)
2275{
Ido Schimmel9011b672017-05-16 19:38:25 +02002276 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002277 mlxsw_sp_nexthop_ht_params);
2278}
2279
Ido Schimmelad178c82017-02-08 11:16:40 +01002280static struct mlxsw_sp_nexthop *
2281mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2282 struct mlxsw_sp_nexthop_key key)
2283{
Ido Schimmel9011b672017-05-16 19:38:25 +02002284 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002285 mlxsw_sp_nexthop_ht_params);
2286}
2287
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002288static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002289 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002290 u32 adj_index, u16 ecmp_size,
2291 u32 new_adj_index,
2292 u16 new_ecmp_size)
2293{
2294 char raleu_pl[MLXSW_REG_RALEU_LEN];
2295
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002296 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002297 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2298 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002299 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002300 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2301}
2302
2303static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2304 struct mlxsw_sp_nexthop_group *nh_grp,
2305 u32 old_adj_index, u16 old_ecmp_size)
2306{
2307 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002308 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002309 int err;
2310
2311 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002312 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002313 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002314 fib = fib_entry->fib_node->fib;
2315 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002316 old_adj_index,
2317 old_ecmp_size,
2318 nh_grp->adj_index,
2319 nh_grp->ecmp_size);
2320 if (err)
2321 return err;
2322 }
2323 return 0;
2324}
2325
2326static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2327 struct mlxsw_sp_nexthop *nh)
2328{
2329 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2330 char ratr_pl[MLXSW_REG_RATR_LEN];
2331
2332 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002333 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2334 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002335 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2336 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2337}
2338
Petr Machata1012b9a2017-09-02 23:49:23 +02002339static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2340 u32 adj_index,
2341 struct mlxsw_sp_nexthop *nh)
2342{
2343 const struct mlxsw_sp_ipip_ops *ipip_ops;
2344
2345 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2346 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2347}
2348
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002349static int
Petr Machata35225e42017-09-02 23:49:22 +02002350mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2351 struct mlxsw_sp_nexthop_group *nh_grp,
2352 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002353{
2354 u32 adj_index = nh_grp->adj_index; /* base */
2355 struct mlxsw_sp_nexthop *nh;
2356 int i;
2357 int err;
2358
2359 for (i = 0; i < nh_grp->count; i++) {
2360 nh = &nh_grp->nexthops[i];
2361
2362 if (!nh->should_offload) {
2363 nh->offloaded = 0;
2364 continue;
2365 }
2366
Ido Schimmela59b7e02017-01-23 11:11:42 +01002367 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002368 switch (nh->type) {
2369 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2370 err = mlxsw_sp_nexthop_mac_update
2371 (mlxsw_sp, adj_index, nh);
2372 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002373 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2374 err = mlxsw_sp_nexthop_ipip_update
2375 (mlxsw_sp, adj_index, nh);
2376 break;
Petr Machata35225e42017-09-02 23:49:22 +02002377 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002378 if (err)
2379 return err;
2380 nh->update = 0;
2381 nh->offloaded = 1;
2382 }
2383 adj_index++;
2384 }
2385 return 0;
2386}
2387
Ido Schimmel1819ae32017-07-21 18:04:28 +02002388static bool
2389mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2390 const struct mlxsw_sp_fib_entry *fib_entry);
2391
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002392static int
2393mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2394 struct mlxsw_sp_nexthop_group *nh_grp)
2395{
2396 struct mlxsw_sp_fib_entry *fib_entry;
2397 int err;
2398
2399 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002400 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2401 fib_entry))
2402 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002403 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2404 if (err)
2405 return err;
2406 }
2407 return 0;
2408}
2409
2410static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002411mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2412 enum mlxsw_reg_ralue_op op, int err);
2413
2414static void
2415mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2416{
2417 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2418 struct mlxsw_sp_fib_entry *fib_entry;
2419
2420 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2421 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2422 fib_entry))
2423 continue;
2424 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2425 }
2426}
2427
2428static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002429mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2430 struct mlxsw_sp_nexthop_group *nh_grp)
2431{
2432 struct mlxsw_sp_nexthop *nh;
2433 bool offload_change = false;
2434 u32 adj_index;
2435 u16 ecmp_size = 0;
2436 bool old_adj_index_valid;
2437 u32 old_adj_index;
2438 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002439 int i;
2440 int err;
2441
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002442 if (!nh_grp->gateway) {
2443 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2444 return;
2445 }
2446
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002447 for (i = 0; i < nh_grp->count; i++) {
2448 nh = &nh_grp->nexthops[i];
2449
Petr Machata56b8a9e2017-07-31 09:27:29 +02002450 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002451 offload_change = true;
2452 if (nh->should_offload)
2453 nh->update = 1;
2454 }
2455 if (nh->should_offload)
2456 ecmp_size++;
2457 }
2458 if (!offload_change) {
2459 /* Nothing was added or removed, so no need to reallocate. Just
2460 * update MAC on existing adjacency indexes.
2461 */
Petr Machata35225e42017-09-02 23:49:22 +02002462 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002463 if (err) {
2464 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2465 goto set_trap;
2466 }
2467 return;
2468 }
2469 if (!ecmp_size)
2470 /* No neigh of this group is connected so we just set
2471 * the trap and let everthing flow through kernel.
2472 */
2473 goto set_trap;
2474
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002475 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2476 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002477 /* We ran out of KVD linear space, just set the
2478 * trap and let everything flow through kernel.
2479 */
2480 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2481 goto set_trap;
2482 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002483 old_adj_index_valid = nh_grp->adj_index_valid;
2484 old_adj_index = nh_grp->adj_index;
2485 old_ecmp_size = nh_grp->ecmp_size;
2486 nh_grp->adj_index_valid = 1;
2487 nh_grp->adj_index = adj_index;
2488 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002489 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002490 if (err) {
2491 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2492 goto set_trap;
2493 }
2494
2495 if (!old_adj_index_valid) {
2496 /* The trap was set for fib entries, so we have to call
2497 * fib entry update to unset it and use adjacency index.
2498 */
2499 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2500 if (err) {
2501 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2502 goto set_trap;
2503 }
2504 return;
2505 }
2506
2507 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2508 old_adj_index, old_ecmp_size);
2509 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2510 if (err) {
2511 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2512 goto set_trap;
2513 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002514
2515 /* Offload state within the group changed, so update the flags. */
2516 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2517
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002518 return;
2519
2520set_trap:
2521 old_adj_index_valid = nh_grp->adj_index_valid;
2522 nh_grp->adj_index_valid = 0;
2523 for (i = 0; i < nh_grp->count; i++) {
2524 nh = &nh_grp->nexthops[i];
2525 nh->offloaded = 0;
2526 }
2527 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2528 if (err)
2529 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2530 if (old_adj_index_valid)
2531 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2532}
2533
2534static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2535 bool removing)
2536{
Petr Machata213666a2017-07-31 09:27:30 +02002537 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002538 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002539 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002540 nh->should_offload = 0;
2541 nh->update = 1;
2542}
2543
2544static void
2545mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2546 struct mlxsw_sp_neigh_entry *neigh_entry,
2547 bool removing)
2548{
2549 struct mlxsw_sp_nexthop *nh;
2550
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002551 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2552 neigh_list_node) {
2553 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2554 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2555 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002556}
2557
Ido Schimmel9665b742017-02-08 11:16:42 +01002558static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002559 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002560{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002561 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002562 return;
2563
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002564 nh->rif = rif;
2565 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002566}
2567
2568static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2569{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002570 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002571 return;
2572
2573 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002574 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002575}
2576
Ido Schimmela8c97012017-02-08 11:16:35 +01002577static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2578 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002579{
2580 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002581 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002582 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002583 int err;
2584
Ido Schimmelad178c82017-02-08 11:16:40 +01002585 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002586 return 0;
2587
Jiri Pirko33b13412016-11-10 12:31:04 +01002588 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002589 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002590 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002591 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002592 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002593 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002594 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002595 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2596 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002597 if (IS_ERR(n))
2598 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002599 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002600 }
2601 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2602 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002603 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2604 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002605 err = -EINVAL;
2606 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002607 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002608 }
Yotam Gigib2157142016-07-05 11:27:51 +02002609
2610 /* If that is the first nexthop connected to that neigh, add to
2611 * nexthop_neighs_list
2612 */
2613 if (list_empty(&neigh_entry->nexthop_list))
2614 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002615 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002616
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002617 nh->neigh_entry = neigh_entry;
2618 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2619 read_lock_bh(&n->lock);
2620 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002621 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002622 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002623 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002624
2625 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002626
2627err_neigh_entry_create:
2628 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002629 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002630}
2631
Ido Schimmela8c97012017-02-08 11:16:35 +01002632static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2633 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002634{
2635 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002636 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002637
Ido Schimmelb8399a12017-02-08 11:16:33 +01002638 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002639 return;
2640 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002641
Ido Schimmel58312122016-12-23 09:32:50 +01002642 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002643 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002644 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002645
2646 /* If that is the last nexthop connected to that neigh, remove from
2647 * nexthop_neighs_list
2648 */
Ido Schimmele58be792017-02-08 11:16:28 +01002649 if (list_empty(&neigh_entry->nexthop_list))
2650 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002651
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002652 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2653 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2654
2655 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002656}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002657
Petr Machata6ddb7422017-09-02 23:49:19 +02002658static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2659 const struct net_device *dev,
2660 enum mlxsw_sp_ipip_type *p_type)
2661{
2662 struct mlxsw_sp_router *router = mlxsw_sp->router;
2663 const struct mlxsw_sp_ipip_ops *ipip_ops;
2664 enum mlxsw_sp_ipip_type ipipt;
2665
2666 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2667 ipip_ops = router->ipip_ops_arr[ipipt];
2668 if (dev->type == ipip_ops->dev_type) {
2669 if (p_type)
2670 *p_type = ipipt;
2671 return true;
2672 }
2673 }
2674 return false;
2675}
2676
Petr Machata1012b9a2017-09-02 23:49:23 +02002677static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2678 enum mlxsw_sp_ipip_type ipipt,
2679 struct mlxsw_sp_nexthop *nh,
2680 struct net_device *ol_dev)
2681{
2682 if (!nh->nh_grp->gateway || nh->ipip_entry)
2683 return 0;
2684
2685 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2686 if (IS_ERR(nh->ipip_entry))
2687 return PTR_ERR(nh->ipip_entry);
2688
2689 __mlxsw_sp_nexthop_neigh_update(nh, false);
2690 return 0;
2691}
2692
2693static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2694 struct mlxsw_sp_nexthop *nh)
2695{
2696 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2697
2698 if (!ipip_entry)
2699 return;
2700
2701 __mlxsw_sp_nexthop_neigh_update(nh, true);
2702 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2703 nh->ipip_entry = NULL;
2704}
2705
2706static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2707 const struct fib_nh *fib_nh,
2708 enum mlxsw_sp_ipip_type *p_ipipt)
2709{
2710 struct net_device *dev = fib_nh->nh_dev;
2711
2712 return dev &&
2713 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2714 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2715}
2716
Petr Machata35225e42017-09-02 23:49:22 +02002717static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2718 struct mlxsw_sp_nexthop *nh)
2719{
2720 switch (nh->type) {
2721 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2722 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2723 mlxsw_sp_nexthop_rif_fini(nh);
2724 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002725 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2726 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2727 break;
Petr Machata35225e42017-09-02 23:49:22 +02002728 }
2729}
2730
2731static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2732 struct mlxsw_sp_nexthop *nh,
2733 struct fib_nh *fib_nh)
2734{
Petr Machata1012b9a2017-09-02 23:49:23 +02002735 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002736 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002737 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002738 struct mlxsw_sp_rif *rif;
2739 int err;
2740
Petr Machata1012b9a2017-09-02 23:49:23 +02002741 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2742 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2743 MLXSW_SP_L3_PROTO_IPV4)) {
2744 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
2745 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2746 }
2747
Petr Machata35225e42017-09-02 23:49:22 +02002748 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2749 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2750 if (!rif)
2751 return 0;
2752
2753 mlxsw_sp_nexthop_rif_init(nh, rif);
2754 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2755 if (err)
2756 goto err_neigh_init;
2757
2758 return 0;
2759
2760err_neigh_init:
2761 mlxsw_sp_nexthop_rif_fini(nh);
2762 return err;
2763}
2764
2765static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2766 struct mlxsw_sp_nexthop *nh)
2767{
2768 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2769}
2770
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002771static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2772 struct mlxsw_sp_nexthop_group *nh_grp,
2773 struct mlxsw_sp_nexthop *nh,
2774 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002775{
2776 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002777 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01002778 int err;
2779
2780 nh->nh_grp = nh_grp;
2781 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002782 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002783 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2784 if (err)
2785 return err;
2786
Ido Schimmel97989ee2017-03-10 08:53:38 +01002787 if (!dev)
2788 return 0;
2789
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002790 in_dev = __in_dev_get_rtnl(dev);
2791 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2792 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2793 return 0;
2794
Petr Machata35225e42017-09-02 23:49:22 +02002795 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002796 if (err)
2797 goto err_nexthop_neigh_init;
2798
2799 return 0;
2800
2801err_nexthop_neigh_init:
2802 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2803 return err;
2804}
2805
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002806static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2807 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002808{
Petr Machata35225e42017-09-02 23:49:22 +02002809 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002810 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002811}
2812
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002813static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2814 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002815{
2816 struct mlxsw_sp_nexthop_key key;
2817 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01002818
Ido Schimmel9011b672017-05-16 19:38:25 +02002819 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002820 return;
2821
2822 key.fib_nh = fib_nh;
2823 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2824 if (WARN_ON_ONCE(!nh))
2825 return;
2826
Ido Schimmelad178c82017-02-08 11:16:40 +01002827 switch (event) {
2828 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02002829 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002830 break;
2831 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02002832 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002833 break;
2834 }
2835
2836 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2837}
2838
Ido Schimmel9665b742017-02-08 11:16:42 +01002839static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002840 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002841{
2842 struct mlxsw_sp_nexthop *nh, *tmp;
2843
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002844 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02002845 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002846 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2847 }
2848}
2849
Petr Machata9b014512017-09-02 23:49:20 +02002850static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2851 const struct fib_info *fi)
2852{
Petr Machata1012b9a2017-09-02 23:49:23 +02002853 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2854 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02002855}
2856
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002857static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002858mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002859{
2860 struct mlxsw_sp_nexthop_group *nh_grp;
2861 struct mlxsw_sp_nexthop *nh;
2862 struct fib_nh *fib_nh;
2863 size_t alloc_size;
2864 int i;
2865 int err;
2866
2867 alloc_size = sizeof(*nh_grp) +
2868 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2869 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2870 if (!nh_grp)
2871 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002872 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002873 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002874 nh_grp->neigh_tbl = &arp_tbl;
2875
Petr Machata9b014512017-09-02 23:49:20 +02002876 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002877 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002878 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002879 for (i = 0; i < nh_grp->count; i++) {
2880 nh = &nh_grp->nexthops[i];
2881 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002882 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002883 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002884 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002885 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002886 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2887 if (err)
2888 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002889 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2890 return nh_grp;
2891
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002892err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002893err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002894 for (i--; i >= 0; i--) {
2895 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002896 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002897 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002898 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002899 kfree(nh_grp);
2900 return ERR_PTR(err);
2901}
2902
2903static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002904mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2905 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002906{
2907 struct mlxsw_sp_nexthop *nh;
2908 int i;
2909
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002910 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002911 for (i = 0; i < nh_grp->count; i++) {
2912 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002913 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002914 }
Ido Schimmel58312122016-12-23 09:32:50 +01002915 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2916 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002917 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002918 kfree(nh_grp);
2919}
2920
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002921static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2922 struct mlxsw_sp_fib_entry *fib_entry,
2923 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002924{
2925 struct mlxsw_sp_nexthop_group *nh_grp;
2926
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002927 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002928 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002929 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002930 if (IS_ERR(nh_grp))
2931 return PTR_ERR(nh_grp);
2932 }
2933 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2934 fib_entry->nh_group = nh_grp;
2935 return 0;
2936}
2937
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002938static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2939 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002940{
2941 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2942
2943 list_del(&fib_entry->nexthop_group_node);
2944 if (!list_empty(&nh_grp->fib_list))
2945 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002946 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002947}
2948
Ido Schimmel013b20f2017-02-08 11:16:36 +01002949static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002950mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2951{
2952 struct mlxsw_sp_fib4_entry *fib4_entry;
2953
2954 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2955 common);
2956 return !fib4_entry->tos;
2957}
2958
2959static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002960mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2961{
2962 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2963
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002964 switch (fib_entry->fib_node->fib->proto) {
2965 case MLXSW_SP_L3_PROTO_IPV4:
2966 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2967 return false;
2968 break;
2969 case MLXSW_SP_L3_PROTO_IPV6:
2970 break;
2971 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002972
Ido Schimmel013b20f2017-02-08 11:16:36 +01002973 switch (fib_entry->type) {
2974 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2975 return !!nh_group->adj_index_valid;
2976 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002977 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02002978 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
2979 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002980 default:
2981 return false;
2982 }
2983}
2984
Ido Schimmel428b8512017-08-03 13:28:28 +02002985static struct mlxsw_sp_nexthop *
2986mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2987 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2988{
2989 int i;
2990
2991 for (i = 0; i < nh_grp->count; i++) {
2992 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2993 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2994
2995 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2996 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2997 &rt->rt6i_gateway))
2998 return nh;
2999 continue;
3000 }
3001
3002 return NULL;
3003}
3004
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003005static void
3006mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3007{
3008 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3009 int i;
3010
Petr Machata4607f6d2017-09-02 23:49:25 +02003011 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3012 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003013 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3014 return;
3015 }
3016
3017 for (i = 0; i < nh_grp->count; i++) {
3018 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3019
3020 if (nh->offloaded)
3021 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3022 else
3023 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3024 }
3025}
3026
3027static void
3028mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3029{
3030 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3031 int i;
3032
3033 for (i = 0; i < nh_grp->count; i++) {
3034 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3035
3036 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3037 }
3038}
3039
Ido Schimmel428b8512017-08-03 13:28:28 +02003040static void
3041mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3042{
3043 struct mlxsw_sp_fib6_entry *fib6_entry;
3044 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3045
3046 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3047 common);
3048
3049 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3050 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003051 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003052 return;
3053 }
3054
3055 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3056 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3057 struct mlxsw_sp_nexthop *nh;
3058
3059 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3060 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003061 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003062 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003063 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003064 }
3065}
3066
3067static void
3068mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3069{
3070 struct mlxsw_sp_fib6_entry *fib6_entry;
3071 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3072
3073 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3074 common);
3075 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3076 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3077
Ido Schimmelfe400792017-08-15 09:09:49 +02003078 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003079 }
3080}
3081
Ido Schimmel013b20f2017-02-08 11:16:36 +01003082static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3083{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003084 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003085 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003086 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003087 break;
3088 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003089 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3090 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003091 }
3092}
3093
3094static void
3095mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3096{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003097 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003098 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003099 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003100 break;
3101 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003102 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3103 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003104 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003105}
3106
3107static void
3108mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3109 enum mlxsw_reg_ralue_op op, int err)
3110{
3111 switch (op) {
3112 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003113 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3114 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3115 if (err)
3116 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003117 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003118 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02003119 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003120 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3121 return;
3122 default:
3123 return;
3124 }
3125}
3126
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003127static void
3128mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3129 const struct mlxsw_sp_fib_entry *fib_entry,
3130 enum mlxsw_reg_ralue_op op)
3131{
3132 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3133 enum mlxsw_reg_ralxx_protocol proto;
3134 u32 *p_dip;
3135
3136 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3137
3138 switch (fib->proto) {
3139 case MLXSW_SP_L3_PROTO_IPV4:
3140 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3141 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3142 fib_entry->fib_node->key.prefix_len,
3143 *p_dip);
3144 break;
3145 case MLXSW_SP_L3_PROTO_IPV6:
3146 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3147 fib_entry->fib_node->key.prefix_len,
3148 fib_entry->fib_node->key.addr);
3149 break;
3150 }
3151}
3152
3153static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3154 struct mlxsw_sp_fib_entry *fib_entry,
3155 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003156{
3157 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003158 enum mlxsw_reg_ralue_trap_action trap_action;
3159 u16 trap_id = 0;
3160 u32 adjacency_index = 0;
3161 u16 ecmp_size = 0;
3162
3163 /* In case the nexthop group adjacency index is valid, use it
3164 * with provided ECMP size. Otherwise, setup trap and pass
3165 * traffic to kernel.
3166 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003167 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003168 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3169 adjacency_index = fib_entry->nh_group->adj_index;
3170 ecmp_size = fib_entry->nh_group->ecmp_size;
3171 } else {
3172 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3173 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3174 }
3175
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003176 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003177 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3178 adjacency_index, ecmp_size);
3179 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3180}
3181
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003182static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3183 struct mlxsw_sp_fib_entry *fib_entry,
3184 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003185{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003186 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003187 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003188 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003189 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003190 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003191
3192 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3193 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003194 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003195 } else {
3196 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3197 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3198 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003199
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003200 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003201 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3202 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003203 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3204}
3205
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003206static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3207 struct mlxsw_sp_fib_entry *fib_entry,
3208 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003209{
3210 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003211
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003212 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003213 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3214 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3215}
3216
Petr Machata4607f6d2017-09-02 23:49:25 +02003217static int
3218mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3219 struct mlxsw_sp_fib_entry *fib_entry,
3220 enum mlxsw_reg_ralue_op op)
3221{
3222 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3223 const struct mlxsw_sp_ipip_ops *ipip_ops;
3224
3225 if (WARN_ON(!ipip_entry))
3226 return -EINVAL;
3227
3228 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3229 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3230 fib_entry->decap.tunnel_index);
3231}
3232
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003233static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3234 struct mlxsw_sp_fib_entry *fib_entry,
3235 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003236{
3237 switch (fib_entry->type) {
3238 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003239 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003240 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003241 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003242 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003243 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003244 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3245 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3246 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003247 }
3248 return -EINVAL;
3249}
3250
3251static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3252 struct mlxsw_sp_fib_entry *fib_entry,
3253 enum mlxsw_reg_ralue_op op)
3254{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003255 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003256
Ido Schimmel013b20f2017-02-08 11:16:36 +01003257 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003258
Ido Schimmel013b20f2017-02-08 11:16:36 +01003259 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003260}
3261
3262static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3263 struct mlxsw_sp_fib_entry *fib_entry)
3264{
Jiri Pirko7146da32016-09-01 10:37:41 +02003265 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3266 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003267}
3268
3269static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3270 struct mlxsw_sp_fib_entry *fib_entry)
3271{
3272 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3273 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3274}
3275
Jiri Pirko61c503f2016-07-04 08:23:11 +02003276static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003277mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3278 const struct fib_entry_notifier_info *fen_info,
3279 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003280{
Petr Machata4607f6d2017-09-02 23:49:25 +02003281 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3282 struct net_device *dev = fen_info->fi->fib_dev;
3283 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003284 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003285
Ido Schimmel97989ee2017-03-10 08:53:38 +01003286 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003287 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003288 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3289 MLXSW_SP_L3_PROTO_IPV4, dip);
3290 if (ipip_entry) {
3291 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3292 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3293 fib_entry,
3294 ipip_entry);
3295 }
3296 /* fall through */
3297 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003298 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3299 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003300 case RTN_UNREACHABLE: /* fall through */
3301 case RTN_BLACKHOLE: /* fall through */
3302 case RTN_PROHIBIT:
3303 /* Packets hitting these routes need to be trapped, but
3304 * can do so with a lower priority than packets directed
3305 * at the host, so use action type local instead of trap.
3306 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003307 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003308 return 0;
3309 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003310 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003311 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003312 else
3313 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003314 return 0;
3315 default:
3316 return -EINVAL;
3317 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003318}
3319
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003320static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003321mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3322 struct mlxsw_sp_fib_node *fib_node,
3323 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003324{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003325 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003326 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003327 int err;
3328
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003329 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3330 if (!fib4_entry)
3331 return ERR_PTR(-ENOMEM);
3332 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003333
3334 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3335 if (err)
3336 goto err_fib4_entry_type_set;
3337
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003338 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003339 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003340 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003341
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003342 fib4_entry->prio = fen_info->fi->fib_priority;
3343 fib4_entry->tb_id = fen_info->tb_id;
3344 fib4_entry->type = fen_info->type;
3345 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003346
3347 fib_entry->fib_node = fib_node;
3348
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003349 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003350
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003351err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003352err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003353 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003354 return ERR_PTR(err);
3355}
3356
3357static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003358 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003359{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003360 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003361 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003362}
3363
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003364static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003365mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3366 const struct fib_entry_notifier_info *fen_info)
3367{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003368 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003369 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003370 struct mlxsw_sp_fib *fib;
3371 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003372
Ido Schimmel160e22a2017-07-18 10:10:20 +02003373 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3374 if (!vr)
3375 return NULL;
3376 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3377
3378 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3379 sizeof(fen_info->dst),
3380 fen_info->dst_len);
3381 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003382 return NULL;
3383
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003384 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3385 if (fib4_entry->tb_id == fen_info->tb_id &&
3386 fib4_entry->tos == fen_info->tos &&
3387 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003388 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3389 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003390 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003391 }
3392 }
3393
3394 return NULL;
3395}
3396
3397static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3398 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3399 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3400 .key_len = sizeof(struct mlxsw_sp_fib_key),
3401 .automatic_shrinking = true,
3402};
3403
3404static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3405 struct mlxsw_sp_fib_node *fib_node)
3406{
3407 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3408 mlxsw_sp_fib_ht_params);
3409}
3410
3411static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3412 struct mlxsw_sp_fib_node *fib_node)
3413{
3414 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3415 mlxsw_sp_fib_ht_params);
3416}
3417
3418static struct mlxsw_sp_fib_node *
3419mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3420 size_t addr_len, unsigned char prefix_len)
3421{
3422 struct mlxsw_sp_fib_key key;
3423
3424 memset(&key, 0, sizeof(key));
3425 memcpy(key.addr, addr, addr_len);
3426 key.prefix_len = prefix_len;
3427 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3428}
3429
3430static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003431mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003432 size_t addr_len, unsigned char prefix_len)
3433{
3434 struct mlxsw_sp_fib_node *fib_node;
3435
3436 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3437 if (!fib_node)
3438 return NULL;
3439
3440 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003441 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003442 memcpy(fib_node->key.addr, addr, addr_len);
3443 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003444
3445 return fib_node;
3446}
3447
3448static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3449{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003450 list_del(&fib_node->list);
3451 WARN_ON(!list_empty(&fib_node->entry_list));
3452 kfree(fib_node);
3453}
3454
3455static bool
3456mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3457 const struct mlxsw_sp_fib_entry *fib_entry)
3458{
3459 return list_first_entry(&fib_node->entry_list,
3460 struct mlxsw_sp_fib_entry, list) == fib_entry;
3461}
3462
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003463static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3464 struct mlxsw_sp_fib *fib,
3465 struct mlxsw_sp_fib_node *fib_node)
3466{
3467 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3468 struct mlxsw_sp_lpm_tree *lpm_tree;
3469 int err;
3470
3471 /* Since the tree is shared between all virtual routers we must
3472 * make sure it contains all the required prefix lengths. This
3473 * can be computed by either adding the new prefix length to the
3474 * existing prefix usage of a bound tree, or by aggregating the
3475 * prefix lengths across all virtual routers and adding the new
3476 * one as well.
3477 */
3478 if (fib->lpm_tree)
3479 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3480 &fib->lpm_tree->prefix_usage);
3481 else
3482 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3483 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3484
3485 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3486 fib->proto);
3487 if (IS_ERR(lpm_tree))
3488 return PTR_ERR(lpm_tree);
3489
3490 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3491 return 0;
3492
3493 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3494 if (err)
3495 return err;
3496
3497 return 0;
3498}
3499
3500static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3501 struct mlxsw_sp_fib *fib)
3502{
3503 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3504 struct mlxsw_sp_lpm_tree *lpm_tree;
3505
3506 /* Aggregate prefix lengths across all virtual routers to make
3507 * sure we only have used prefix lengths in the LPM tree.
3508 */
3509 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3510 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3511 fib->proto);
3512 if (IS_ERR(lpm_tree))
3513 goto err_tree_get;
3514 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3515
3516err_tree_get:
3517 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3518 return;
3519 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3520 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3521 fib->lpm_tree = NULL;
3522}
3523
Ido Schimmel9aecce12017-02-09 10:28:42 +01003524static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3525{
3526 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003527 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003528
3529 if (fib->prefix_ref_count[prefix_len]++ == 0)
3530 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3531}
3532
3533static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3534{
3535 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003536 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003537
3538 if (--fib->prefix_ref_count[prefix_len] == 0)
3539 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3540}
3541
Ido Schimmel76610eb2017-03-10 08:53:41 +01003542static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3543 struct mlxsw_sp_fib_node *fib_node,
3544 struct mlxsw_sp_fib *fib)
3545{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003546 int err;
3547
3548 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3549 if (err)
3550 return err;
3551 fib_node->fib = fib;
3552
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003553 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3554 if (err)
3555 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003556
3557 mlxsw_sp_fib_node_prefix_inc(fib_node);
3558
3559 return 0;
3560
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003561err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003562 fib_node->fib = NULL;
3563 mlxsw_sp_fib_node_remove(fib, fib_node);
3564 return err;
3565}
3566
3567static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3568 struct mlxsw_sp_fib_node *fib_node)
3569{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003570 struct mlxsw_sp_fib *fib = fib_node->fib;
3571
3572 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003573 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003574 fib_node->fib = NULL;
3575 mlxsw_sp_fib_node_remove(fib, fib_node);
3576}
3577
Ido Schimmel9aecce12017-02-09 10:28:42 +01003578static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003579mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3580 size_t addr_len, unsigned char prefix_len,
3581 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003582{
3583 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003584 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003585 struct mlxsw_sp_vr *vr;
3586 int err;
3587
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003588 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003589 if (IS_ERR(vr))
3590 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003591 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003592
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003593 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003594 if (fib_node)
3595 return fib_node;
3596
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003597 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003598 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003599 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003600 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003601 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003602
Ido Schimmel76610eb2017-03-10 08:53:41 +01003603 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3604 if (err)
3605 goto err_fib_node_init;
3606
Ido Schimmel9aecce12017-02-09 10:28:42 +01003607 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003608
Ido Schimmel76610eb2017-03-10 08:53:41 +01003609err_fib_node_init:
3610 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003611err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003612 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003613 return ERR_PTR(err);
3614}
3615
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003616static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3617 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003618{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003619 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003620
Ido Schimmel9aecce12017-02-09 10:28:42 +01003621 if (!list_empty(&fib_node->entry_list))
3622 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003623 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003624 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003625 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003626}
3627
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003628static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003629mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003630 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003631{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003632 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003633
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003634 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3635 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003636 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003637 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003638 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003639 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003640 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003641 if (fib4_entry->prio >= new4_entry->prio ||
3642 fib4_entry->tos < new4_entry->tos)
3643 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003644 }
3645
3646 return NULL;
3647}
3648
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003649static int
3650mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3651 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003652{
3653 struct mlxsw_sp_fib_node *fib_node;
3654
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003655 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003656 return -EINVAL;
3657
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003658 fib_node = fib4_entry->common.fib_node;
3659 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3660 common.list) {
3661 if (fib4_entry->tb_id != new4_entry->tb_id ||
3662 fib4_entry->tos != new4_entry->tos ||
3663 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003664 break;
3665 }
3666
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003667 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003668 return 0;
3669}
3670
Ido Schimmel9aecce12017-02-09 10:28:42 +01003671static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003672mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003673 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003674{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003675 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003676 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003677
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003678 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003679
Ido Schimmel4283bce2017-02-09 10:28:43 +01003680 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003681 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3682 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003683 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003684
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003685 /* Insert new entry before replaced one, so that we can later
3686 * remove the second.
3687 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003688 if (fib4_entry) {
3689 list_add_tail(&new4_entry->common.list,
3690 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003691 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003692 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003693
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003694 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3695 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003696 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003697 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003698 }
3699
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003700 if (fib4_entry)
3701 list_add(&new4_entry->common.list,
3702 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003703 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003704 list_add(&new4_entry->common.list,
3705 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003706 }
3707
3708 return 0;
3709}
3710
3711static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003712mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003713{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003714 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003715}
3716
Ido Schimmel80c238f2017-07-18 10:10:29 +02003717static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3718 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003719{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003720 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3721
Ido Schimmel9aecce12017-02-09 10:28:42 +01003722 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3723 return 0;
3724
3725 /* To prevent packet loss, overwrite the previously offloaded
3726 * entry.
3727 */
3728 if (!list_is_singular(&fib_node->entry_list)) {
3729 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3730 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3731
3732 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3733 }
3734
3735 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3736}
3737
Ido Schimmel80c238f2017-07-18 10:10:29 +02003738static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3739 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003740{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003741 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3742
Ido Schimmel9aecce12017-02-09 10:28:42 +01003743 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3744 return;
3745
3746 /* Promote the next entry by overwriting the deleted entry */
3747 if (!list_is_singular(&fib_node->entry_list)) {
3748 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3749 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3750
3751 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3752 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3753 return;
3754 }
3755
3756 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3757}
3758
3759static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003760 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003761 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003762{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003763 int err;
3764
Ido Schimmel9efbee62017-07-18 10:10:28 +02003765 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003766 if (err)
3767 return err;
3768
Ido Schimmel80c238f2017-07-18 10:10:29 +02003769 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003770 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003771 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003772
Ido Schimmel9aecce12017-02-09 10:28:42 +01003773 return 0;
3774
Ido Schimmel80c238f2017-07-18 10:10:29 +02003775err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003776 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003777 return err;
3778}
3779
3780static void
3781mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003782 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003783{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003784 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003785 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02003786
3787 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3788 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003789}
3790
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003791static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003792 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003793 bool replace)
3794{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003795 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3796 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003797
3798 if (!replace)
3799 return;
3800
3801 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003802 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003803
3804 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3805 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003806 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003807}
3808
Ido Schimmel9aecce12017-02-09 10:28:42 +01003809static int
3810mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003811 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003812 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003813{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003814 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003815 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003816 int err;
3817
Ido Schimmel9011b672017-05-16 19:38:25 +02003818 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003819 return 0;
3820
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003821 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3822 &fen_info->dst, sizeof(fen_info->dst),
3823 fen_info->dst_len,
3824 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003825 if (IS_ERR(fib_node)) {
3826 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3827 return PTR_ERR(fib_node);
3828 }
3829
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003830 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3831 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003832 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003833 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003834 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003835 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003836
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003837 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003838 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003839 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003840 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3841 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003842 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003843
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003844 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003845
Jiri Pirko61c503f2016-07-04 08:23:11 +02003846 return 0;
3847
Ido Schimmel9aecce12017-02-09 10:28:42 +01003848err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003849 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003850err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003851 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003852 return err;
3853}
3854
Jiri Pirko37956d72016-10-20 16:05:43 +02003855static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3856 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003857{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003858 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003859 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003860
Ido Schimmel9011b672017-05-16 19:38:25 +02003861 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003862 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003863
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003864 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3865 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003866 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003867 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003868
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003869 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3870 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003871 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003872}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003873
Ido Schimmel428b8512017-08-03 13:28:28 +02003874static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3875{
3876 /* Packets with link-local destination IP arriving to the router
3877 * are trapped to the CPU, so no need to program specific routes
3878 * for them.
3879 */
3880 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3881 return true;
3882
3883 /* Multicast routes aren't supported, so ignore them. Neighbour
3884 * Discovery packets are specifically trapped.
3885 */
3886 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3887 return true;
3888
3889 /* Cloned routes are irrelevant in the forwarding path. */
3890 if (rt->rt6i_flags & RTF_CACHE)
3891 return true;
3892
3893 return false;
3894}
3895
3896static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3897{
3898 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3899
3900 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3901 if (!mlxsw_sp_rt6)
3902 return ERR_PTR(-ENOMEM);
3903
3904 /* In case of route replace, replaced route is deleted with
3905 * no notification. Take reference to prevent accessing freed
3906 * memory.
3907 */
3908 mlxsw_sp_rt6->rt = rt;
3909 rt6_hold(rt);
3910
3911 return mlxsw_sp_rt6;
3912}
3913
3914#if IS_ENABLED(CONFIG_IPV6)
3915static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3916{
3917 rt6_release(rt);
3918}
3919#else
3920static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3921{
3922}
3923#endif
3924
3925static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3926{
3927 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3928 kfree(mlxsw_sp_rt6);
3929}
3930
3931static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3932{
3933 /* RTF_CACHE routes are ignored */
3934 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3935}
3936
3937static struct rt6_info *
3938mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3939{
3940 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3941 list)->rt;
3942}
3943
3944static struct mlxsw_sp_fib6_entry *
3945mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003946 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003947{
3948 struct mlxsw_sp_fib6_entry *fib6_entry;
3949
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003950 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003951 return NULL;
3952
3953 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3954 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3955
3956 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3957 * virtual router.
3958 */
3959 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3960 continue;
3961 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3962 break;
3963 if (rt->rt6i_metric < nrt->rt6i_metric)
3964 continue;
3965 if (rt->rt6i_metric == nrt->rt6i_metric &&
3966 mlxsw_sp_fib6_rt_can_mp(rt))
3967 return fib6_entry;
3968 if (rt->rt6i_metric > nrt->rt6i_metric)
3969 break;
3970 }
3971
3972 return NULL;
3973}
3974
3975static struct mlxsw_sp_rt6 *
3976mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3977 const struct rt6_info *rt)
3978{
3979 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3980
3981 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3982 if (mlxsw_sp_rt6->rt == rt)
3983 return mlxsw_sp_rt6;
3984 }
3985
3986 return NULL;
3987}
3988
Petr Machata8f28a302017-09-02 23:49:24 +02003989static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
3990 const struct rt6_info *rt,
3991 enum mlxsw_sp_ipip_type *ret)
3992{
3993 return rt->dst.dev &&
3994 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
3995}
3996
Petr Machata35225e42017-09-02 23:49:22 +02003997static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
3998 struct mlxsw_sp_nexthop_group *nh_grp,
3999 struct mlxsw_sp_nexthop *nh,
4000 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02004001{
Petr Machata8f28a302017-09-02 23:49:24 +02004002 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02004003 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02004004 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02004005 struct mlxsw_sp_rif *rif;
4006 int err;
4007
Petr Machata8f28a302017-09-02 23:49:24 +02004008 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4009 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4010 MLXSW_SP_L3_PROTO_IPV6)) {
4011 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
4012 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4013 }
4014
Petr Machata35225e42017-09-02 23:49:22 +02004015 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02004016 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4017 if (!rif)
4018 return 0;
4019 mlxsw_sp_nexthop_rif_init(nh, rif);
4020
4021 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4022 if (err)
4023 goto err_nexthop_neigh_init;
4024
4025 return 0;
4026
4027err_nexthop_neigh_init:
4028 mlxsw_sp_nexthop_rif_fini(nh);
4029 return err;
4030}
4031
Petr Machata35225e42017-09-02 23:49:22 +02004032static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4033 struct mlxsw_sp_nexthop *nh)
4034{
4035 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4036}
4037
4038static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4039 struct mlxsw_sp_nexthop_group *nh_grp,
4040 struct mlxsw_sp_nexthop *nh,
4041 const struct rt6_info *rt)
4042{
4043 struct net_device *dev = rt->dst.dev;
4044
4045 nh->nh_grp = nh_grp;
4046 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4047
4048 if (!dev)
4049 return 0;
4050 nh->ifindex = dev->ifindex;
4051
4052 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4053}
4054
Ido Schimmel428b8512017-08-03 13:28:28 +02004055static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4056 struct mlxsw_sp_nexthop *nh)
4057{
Petr Machata35225e42017-09-02 23:49:22 +02004058 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Ido Schimmel428b8512017-08-03 13:28:28 +02004059}
4060
Petr Machataf6050ee2017-09-02 23:49:21 +02004061static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4062 const struct rt6_info *rt)
4063{
Petr Machata8f28a302017-09-02 23:49:24 +02004064 return rt->rt6i_flags & RTF_GATEWAY ||
4065 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004066}
4067
Ido Schimmel428b8512017-08-03 13:28:28 +02004068static struct mlxsw_sp_nexthop_group *
4069mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4070 struct mlxsw_sp_fib6_entry *fib6_entry)
4071{
4072 struct mlxsw_sp_nexthop_group *nh_grp;
4073 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4074 struct mlxsw_sp_nexthop *nh;
4075 size_t alloc_size;
4076 int i = 0;
4077 int err;
4078
4079 alloc_size = sizeof(*nh_grp) +
4080 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4081 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4082 if (!nh_grp)
4083 return ERR_PTR(-ENOMEM);
4084 INIT_LIST_HEAD(&nh_grp->fib_list);
4085#if IS_ENABLED(CONFIG_IPV6)
4086 nh_grp->neigh_tbl = &nd_tbl;
4087#endif
4088 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4089 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004090 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004091 nh_grp->count = fib6_entry->nrt6;
4092 for (i = 0; i < nh_grp->count; i++) {
4093 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4094
4095 nh = &nh_grp->nexthops[i];
4096 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4097 if (err)
4098 goto err_nexthop6_init;
4099 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4100 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004101
4102 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4103 if (err)
4104 goto err_nexthop_group_insert;
4105
Ido Schimmel428b8512017-08-03 13:28:28 +02004106 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4107 return nh_grp;
4108
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004109err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004110err_nexthop6_init:
4111 for (i--; i >= 0; i--) {
4112 nh = &nh_grp->nexthops[i];
4113 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4114 }
4115 kfree(nh_grp);
4116 return ERR_PTR(err);
4117}
4118
4119static void
4120mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4121 struct mlxsw_sp_nexthop_group *nh_grp)
4122{
4123 struct mlxsw_sp_nexthop *nh;
4124 int i = nh_grp->count;
4125
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004126 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004127 for (i--; i >= 0; i--) {
4128 nh = &nh_grp->nexthops[i];
4129 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4130 }
4131 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4132 WARN_ON(nh_grp->adj_index_valid);
4133 kfree(nh_grp);
4134}
4135
4136static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4137 struct mlxsw_sp_fib6_entry *fib6_entry)
4138{
4139 struct mlxsw_sp_nexthop_group *nh_grp;
4140
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004141 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4142 if (!nh_grp) {
4143 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4144 if (IS_ERR(nh_grp))
4145 return PTR_ERR(nh_grp);
4146 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004147
4148 list_add_tail(&fib6_entry->common.nexthop_group_node,
4149 &nh_grp->fib_list);
4150 fib6_entry->common.nh_group = nh_grp;
4151
4152 return 0;
4153}
4154
4155static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4156 struct mlxsw_sp_fib_entry *fib_entry)
4157{
4158 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4159
4160 list_del(&fib_entry->nexthop_group_node);
4161 if (!list_empty(&nh_grp->fib_list))
4162 return;
4163 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4164}
4165
4166static int
4167mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4168 struct mlxsw_sp_fib6_entry *fib6_entry)
4169{
4170 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4171 int err;
4172
4173 fib6_entry->common.nh_group = NULL;
4174 list_del(&fib6_entry->common.nexthop_group_node);
4175
4176 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4177 if (err)
4178 goto err_nexthop6_group_get;
4179
4180 /* In case this entry is offloaded, then the adjacency index
4181 * currently associated with it in the device's table is that
4182 * of the old group. Start using the new one instead.
4183 */
4184 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4185 if (err)
4186 goto err_fib_node_entry_add;
4187
4188 if (list_empty(&old_nh_grp->fib_list))
4189 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4190
4191 return 0;
4192
4193err_fib_node_entry_add:
4194 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4195err_nexthop6_group_get:
4196 list_add_tail(&fib6_entry->common.nexthop_group_node,
4197 &old_nh_grp->fib_list);
4198 fib6_entry->common.nh_group = old_nh_grp;
4199 return err;
4200}
4201
4202static int
4203mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4204 struct mlxsw_sp_fib6_entry *fib6_entry,
4205 struct rt6_info *rt)
4206{
4207 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4208 int err;
4209
4210 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4211 if (IS_ERR(mlxsw_sp_rt6))
4212 return PTR_ERR(mlxsw_sp_rt6);
4213
4214 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4215 fib6_entry->nrt6++;
4216
4217 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4218 if (err)
4219 goto err_nexthop6_group_update;
4220
4221 return 0;
4222
4223err_nexthop6_group_update:
4224 fib6_entry->nrt6--;
4225 list_del(&mlxsw_sp_rt6->list);
4226 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4227 return err;
4228}
4229
4230static void
4231mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4232 struct mlxsw_sp_fib6_entry *fib6_entry,
4233 struct rt6_info *rt)
4234{
4235 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4236
4237 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4238 if (WARN_ON(!mlxsw_sp_rt6))
4239 return;
4240
4241 fib6_entry->nrt6--;
4242 list_del(&mlxsw_sp_rt6->list);
4243 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4244 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4245}
4246
Petr Machataf6050ee2017-09-02 23:49:21 +02004247static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4248 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004249 const struct rt6_info *rt)
4250{
4251 /* Packets hitting RTF_REJECT routes need to be discarded by the
4252 * stack. We can rely on their destination device not having a
4253 * RIF (it's the loopback device) and can thus use action type
4254 * local, which will cause them to be trapped with a lower
4255 * priority than packets that need to be locally received.
4256 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004257 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004258 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4259 else if (rt->rt6i_flags & RTF_REJECT)
4260 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004261 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004262 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4263 else
4264 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4265}
4266
4267static void
4268mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4269{
4270 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4271
4272 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4273 list) {
4274 fib6_entry->nrt6--;
4275 list_del(&mlxsw_sp_rt6->list);
4276 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4277 }
4278}
4279
4280static struct mlxsw_sp_fib6_entry *
4281mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4282 struct mlxsw_sp_fib_node *fib_node,
4283 struct rt6_info *rt)
4284{
4285 struct mlxsw_sp_fib6_entry *fib6_entry;
4286 struct mlxsw_sp_fib_entry *fib_entry;
4287 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4288 int err;
4289
4290 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4291 if (!fib6_entry)
4292 return ERR_PTR(-ENOMEM);
4293 fib_entry = &fib6_entry->common;
4294
4295 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4296 if (IS_ERR(mlxsw_sp_rt6)) {
4297 err = PTR_ERR(mlxsw_sp_rt6);
4298 goto err_rt6_create;
4299 }
4300
Petr Machataf6050ee2017-09-02 23:49:21 +02004301 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004302
4303 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4304 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4305 fib6_entry->nrt6 = 1;
4306 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4307 if (err)
4308 goto err_nexthop6_group_get;
4309
4310 fib_entry->fib_node = fib_node;
4311
4312 return fib6_entry;
4313
4314err_nexthop6_group_get:
4315 list_del(&mlxsw_sp_rt6->list);
4316 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4317err_rt6_create:
4318 kfree(fib6_entry);
4319 return ERR_PTR(err);
4320}
4321
4322static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4323 struct mlxsw_sp_fib6_entry *fib6_entry)
4324{
4325 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4326 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4327 WARN_ON(fib6_entry->nrt6);
4328 kfree(fib6_entry);
4329}
4330
4331static struct mlxsw_sp_fib6_entry *
4332mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004333 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004334{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004335 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004336
4337 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4338 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4339
4340 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4341 continue;
4342 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4343 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004344 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4345 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4346 mlxsw_sp_fib6_rt_can_mp(nrt))
4347 return fib6_entry;
4348 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4349 fallback = fallback ?: fib6_entry;
4350 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004351 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004352 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004353 }
4354
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004355 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004356}
4357
4358static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004359mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4360 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004361{
4362 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4363 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4364 struct mlxsw_sp_fib6_entry *fib6_entry;
4365
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004366 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4367
4368 if (replace && WARN_ON(!fib6_entry))
4369 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004370
4371 if (fib6_entry) {
4372 list_add_tail(&new6_entry->common.list,
4373 &fib6_entry->common.list);
4374 } else {
4375 struct mlxsw_sp_fib6_entry *last;
4376
4377 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4378 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4379
4380 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4381 break;
4382 fib6_entry = last;
4383 }
4384
4385 if (fib6_entry)
4386 list_add(&new6_entry->common.list,
4387 &fib6_entry->common.list);
4388 else
4389 list_add(&new6_entry->common.list,
4390 &fib_node->entry_list);
4391 }
4392
4393 return 0;
4394}
4395
4396static void
4397mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4398{
4399 list_del(&fib6_entry->common.list);
4400}
4401
4402static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004403 struct mlxsw_sp_fib6_entry *fib6_entry,
4404 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004405{
4406 int err;
4407
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004408 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004409 if (err)
4410 return err;
4411
4412 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4413 if (err)
4414 goto err_fib_node_entry_add;
4415
4416 return 0;
4417
4418err_fib_node_entry_add:
4419 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4420 return err;
4421}
4422
4423static void
4424mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4425 struct mlxsw_sp_fib6_entry *fib6_entry)
4426{
4427 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4428 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4429}
4430
4431static struct mlxsw_sp_fib6_entry *
4432mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4433 const struct rt6_info *rt)
4434{
4435 struct mlxsw_sp_fib6_entry *fib6_entry;
4436 struct mlxsw_sp_fib_node *fib_node;
4437 struct mlxsw_sp_fib *fib;
4438 struct mlxsw_sp_vr *vr;
4439
4440 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4441 if (!vr)
4442 return NULL;
4443 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4444
4445 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4446 sizeof(rt->rt6i_dst.addr),
4447 rt->rt6i_dst.plen);
4448 if (!fib_node)
4449 return NULL;
4450
4451 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4452 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4453
4454 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4455 rt->rt6i_metric == iter_rt->rt6i_metric &&
4456 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4457 return fib6_entry;
4458 }
4459
4460 return NULL;
4461}
4462
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004463static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4464 struct mlxsw_sp_fib6_entry *fib6_entry,
4465 bool replace)
4466{
4467 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4468 struct mlxsw_sp_fib6_entry *replaced;
4469
4470 if (!replace)
4471 return;
4472
4473 replaced = list_next_entry(fib6_entry, common.list);
4474
4475 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4476 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4477 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4478}
4479
Ido Schimmel428b8512017-08-03 13:28:28 +02004480static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004481 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004482{
4483 struct mlxsw_sp_fib6_entry *fib6_entry;
4484 struct mlxsw_sp_fib_node *fib_node;
4485 int err;
4486
4487 if (mlxsw_sp->router->aborted)
4488 return 0;
4489
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004490 if (rt->rt6i_src.plen)
4491 return -EINVAL;
4492
Ido Schimmel428b8512017-08-03 13:28:28 +02004493 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4494 return 0;
4495
4496 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4497 &rt->rt6i_dst.addr,
4498 sizeof(rt->rt6i_dst.addr),
4499 rt->rt6i_dst.plen,
4500 MLXSW_SP_L3_PROTO_IPV6);
4501 if (IS_ERR(fib_node))
4502 return PTR_ERR(fib_node);
4503
4504 /* Before creating a new entry, try to append route to an existing
4505 * multipath entry.
4506 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004507 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004508 if (fib6_entry) {
4509 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4510 if (err)
4511 goto err_fib6_entry_nexthop_add;
4512 return 0;
4513 }
4514
4515 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4516 if (IS_ERR(fib6_entry)) {
4517 err = PTR_ERR(fib6_entry);
4518 goto err_fib6_entry_create;
4519 }
4520
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004521 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004522 if (err)
4523 goto err_fib6_node_entry_link;
4524
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004525 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4526
Ido Schimmel428b8512017-08-03 13:28:28 +02004527 return 0;
4528
4529err_fib6_node_entry_link:
4530 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4531err_fib6_entry_create:
4532err_fib6_entry_nexthop_add:
4533 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4534 return err;
4535}
4536
4537static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4538 struct rt6_info *rt)
4539{
4540 struct mlxsw_sp_fib6_entry *fib6_entry;
4541 struct mlxsw_sp_fib_node *fib_node;
4542
4543 if (mlxsw_sp->router->aborted)
4544 return;
4545
4546 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4547 return;
4548
4549 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4550 if (WARN_ON(!fib6_entry))
4551 return;
4552
4553 /* If route is part of a multipath entry, but not the last one
4554 * removed, then only reduce its nexthop group.
4555 */
4556 if (!list_is_singular(&fib6_entry->rt6_list)) {
4557 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4558 return;
4559 }
4560
4561 fib_node = fib6_entry->common.fib_node;
4562
4563 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4564 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4565 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4566}
4567
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004568static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4569 enum mlxsw_reg_ralxx_protocol proto,
4570 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004571{
4572 char ralta_pl[MLXSW_REG_RALTA_LEN];
4573 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004574 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004575
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004576 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004577 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4578 if (err)
4579 return err;
4580
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004581 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004582 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4583 if (err)
4584 return err;
4585
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004586 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004587 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004588 char raltb_pl[MLXSW_REG_RALTB_LEN];
4589 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004590
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004591 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004592 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4593 raltb_pl);
4594 if (err)
4595 return err;
4596
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004597 mlxsw_reg_ralue_pack(ralue_pl, proto,
4598 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004599 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4600 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4601 ralue_pl);
4602 if (err)
4603 return err;
4604 }
4605
4606 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004607}
4608
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004609static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4610{
4611 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4612 int err;
4613
4614 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4615 MLXSW_SP_LPM_TREE_MIN);
4616 if (err)
4617 return err;
4618
4619 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4620 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4621 MLXSW_SP_LPM_TREE_MIN + 1);
4622}
4623
Ido Schimmel9aecce12017-02-09 10:28:42 +01004624static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4625 struct mlxsw_sp_fib_node *fib_node)
4626{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004627 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004628
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004629 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4630 common.list) {
4631 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004632
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004633 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4634 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004635 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004636 /* Break when entry list is empty and node was freed.
4637 * Otherwise, we'll access freed memory in the next
4638 * iteration.
4639 */
4640 if (do_break)
4641 break;
4642 }
4643}
4644
Ido Schimmel428b8512017-08-03 13:28:28 +02004645static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4646 struct mlxsw_sp_fib_node *fib_node)
4647{
4648 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4649
4650 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4651 common.list) {
4652 bool do_break = &tmp->common.list == &fib_node->entry_list;
4653
4654 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4655 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4656 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4657 if (do_break)
4658 break;
4659 }
4660}
4661
Ido Schimmel9aecce12017-02-09 10:28:42 +01004662static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4663 struct mlxsw_sp_fib_node *fib_node)
4664{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004665 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004666 case MLXSW_SP_L3_PROTO_IPV4:
4667 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4668 break;
4669 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004670 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004671 break;
4672 }
4673}
4674
Ido Schimmel76610eb2017-03-10 08:53:41 +01004675static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4676 struct mlxsw_sp_vr *vr,
4677 enum mlxsw_sp_l3proto proto)
4678{
4679 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4680 struct mlxsw_sp_fib_node *fib_node, *tmp;
4681
4682 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4683 bool do_break = &tmp->list == &fib->node_list;
4684
4685 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4686 if (do_break)
4687 break;
4688 }
4689}
4690
Ido Schimmelac571de2016-11-14 11:26:32 +01004691static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004692{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004693 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004694
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004695 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004696 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004697
Ido Schimmel76610eb2017-03-10 08:53:41 +01004698 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004699 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004700 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004701
4702 /* If virtual router was only used for IPv4, then it's no
4703 * longer used.
4704 */
4705 if (!mlxsw_sp_vr_is_used(vr))
4706 continue;
4707 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004708 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004709}
4710
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004711static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004712{
4713 int err;
4714
Ido Schimmel9011b672017-05-16 19:38:25 +02004715 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004716 return;
4717 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 +01004718 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004719 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004720 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4721 if (err)
4722 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4723}
4724
Ido Schimmel30572242016-12-03 16:45:01 +01004725struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004726 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004727 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004728 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004729 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004730 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004731 struct fib_nh_notifier_info fnh_info;
4732 };
Ido Schimmel30572242016-12-03 16:45:01 +01004733 struct mlxsw_sp *mlxsw_sp;
4734 unsigned long event;
4735};
4736
Ido Schimmel66a57632017-08-03 13:28:26 +02004737static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004738{
Ido Schimmel30572242016-12-03 16:45:01 +01004739 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004740 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004741 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004742 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004743 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004744 int err;
4745
Ido Schimmel30572242016-12-03 16:45:01 +01004746 /* Protect internal structures from changes */
4747 rtnl_lock();
4748 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004749 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004750 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004751 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004752 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004753 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4754 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004755 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004756 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004757 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004758 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004759 break;
4760 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004761 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4762 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004763 break;
4764 case FIB_EVENT_RULE_ADD: /* fall through */
4765 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004766 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004767 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004768 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004769 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004770 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004771 case FIB_EVENT_NH_ADD: /* fall through */
4772 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004773 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4774 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004775 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4776 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004777 }
Ido Schimmel30572242016-12-03 16:45:01 +01004778 rtnl_unlock();
4779 kfree(fib_work);
4780}
4781
Ido Schimmel66a57632017-08-03 13:28:26 +02004782static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4783{
Ido Schimmel583419f2017-08-03 13:28:27 +02004784 struct mlxsw_sp_fib_event_work *fib_work =
4785 container_of(work, struct mlxsw_sp_fib_event_work, work);
4786 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4787 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004788 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004789 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004790
4791 rtnl_lock();
4792 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004793 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004794 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004795 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004796 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004797 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004798 if (err)
4799 mlxsw_sp_router_fib_abort(mlxsw_sp);
4800 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4801 break;
4802 case FIB_EVENT_ENTRY_DEL:
4803 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4804 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4805 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004806 case FIB_EVENT_RULE_ADD: /* fall through */
4807 case FIB_EVENT_RULE_DEL:
4808 rule = fib_work->fr_info.rule;
4809 if (!fib6_rule_default(rule) && !rule->l3mdev)
4810 mlxsw_sp_router_fib_abort(mlxsw_sp);
4811 fib_rule_put(rule);
4812 break;
4813 }
4814 rtnl_unlock();
4815 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004816}
4817
4818static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4819 struct fib_notifier_info *info)
4820{
4821 switch (fib_work->event) {
4822 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4823 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4824 case FIB_EVENT_ENTRY_ADD: /* fall through */
4825 case FIB_EVENT_ENTRY_DEL:
4826 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4827 /* Take referece on fib_info to prevent it from being
4828 * freed while work is queued. Release it afterwards.
4829 */
4830 fib_info_hold(fib_work->fen_info.fi);
4831 break;
4832 case FIB_EVENT_RULE_ADD: /* fall through */
4833 case FIB_EVENT_RULE_DEL:
4834 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4835 fib_rule_get(fib_work->fr_info.rule);
4836 break;
4837 case FIB_EVENT_NH_ADD: /* fall through */
4838 case FIB_EVENT_NH_DEL:
4839 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4840 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4841 break;
4842 }
4843}
4844
4845static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4846 struct fib_notifier_info *info)
4847{
Ido Schimmel583419f2017-08-03 13:28:27 +02004848 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004849 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004850 case FIB_EVENT_ENTRY_ADD: /* fall through */
4851 case FIB_EVENT_ENTRY_DEL:
4852 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4853 rt6_hold(fib_work->fen6_info.rt);
4854 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004855 case FIB_EVENT_RULE_ADD: /* fall through */
4856 case FIB_EVENT_RULE_DEL:
4857 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4858 fib_rule_get(fib_work->fr_info.rule);
4859 break;
4860 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004861}
4862
Ido Schimmel30572242016-12-03 16:45:01 +01004863/* Called with rcu_read_lock() */
4864static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4865 unsigned long event, void *ptr)
4866{
Ido Schimmel30572242016-12-03 16:45:01 +01004867 struct mlxsw_sp_fib_event_work *fib_work;
4868 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004869 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004870
Ido Schimmel8e29f972017-09-15 15:31:07 +02004871 if (!net_eq(info->net, &init_net) ||
4872 (info->family != AF_INET && info->family != AF_INET6))
Ido Schimmel30572242016-12-03 16:45:01 +01004873 return NOTIFY_DONE;
4874
4875 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4876 if (WARN_ON(!fib_work))
4877 return NOTIFY_BAD;
4878
Ido Schimmel7e39d112017-05-16 19:38:28 +02004879 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4880 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004881 fib_work->event = event;
4882
Ido Schimmel66a57632017-08-03 13:28:26 +02004883 switch (info->family) {
4884 case AF_INET:
4885 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4886 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004887 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004888 case AF_INET6:
4889 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4890 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004891 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004892 }
4893
Ido Schimmela0e47612017-02-06 16:20:10 +01004894 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004895
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004896 return NOTIFY_DONE;
4897}
4898
Ido Schimmel4724ba562017-03-10 08:53:39 +01004899static struct mlxsw_sp_rif *
4900mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4901 const struct net_device *dev)
4902{
4903 int i;
4904
4905 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004906 if (mlxsw_sp->router->rifs[i] &&
4907 mlxsw_sp->router->rifs[i]->dev == dev)
4908 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004909
4910 return NULL;
4911}
4912
4913static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4914{
4915 char ritr_pl[MLXSW_REG_RITR_LEN];
4916 int err;
4917
4918 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4919 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4920 if (WARN_ON_ONCE(err))
4921 return err;
4922
4923 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4924 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4925}
4926
4927static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004928 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004929{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004930 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4931 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4932 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004933}
4934
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004935static bool
4936mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4937 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004938{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004939 struct inet6_dev *inet6_dev;
4940 bool addr_list_empty = true;
4941 struct in_device *idev;
4942
Ido Schimmel4724ba562017-03-10 08:53:39 +01004943 switch (event) {
4944 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004945 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004946 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004947 idev = __in_dev_get_rtnl(dev);
4948 if (idev && idev->ifa_list)
4949 addr_list_empty = false;
4950
4951 inet6_dev = __in6_dev_get(dev);
4952 if (addr_list_empty && inet6_dev &&
4953 !list_empty(&inet6_dev->addr_list))
4954 addr_list_empty = false;
4955
4956 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004957 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004958 return true;
4959 /* It is possible we already removed the RIF ourselves
4960 * if it was assigned to a netdev that is now a bridge
4961 * or LAG slave.
4962 */
4963 return false;
4964 }
4965
4966 return false;
4967}
4968
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004969static enum mlxsw_sp_rif_type
4970mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4971 const struct net_device *dev)
4972{
4973 enum mlxsw_sp_fid_type type;
4974
Petr Machata6ddb7422017-09-02 23:49:19 +02004975 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4976 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4977
4978 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004979 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4980 type = MLXSW_SP_FID_TYPE_8021Q;
4981 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4982 type = MLXSW_SP_FID_TYPE_8021Q;
4983 else if (netif_is_bridge_master(dev))
4984 type = MLXSW_SP_FID_TYPE_8021D;
4985 else
4986 type = MLXSW_SP_FID_TYPE_RFID;
4987
4988 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4989}
4990
Ido Schimmelde5ed992017-06-04 16:53:40 +02004991static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004992{
4993 int i;
4994
Ido Schimmelde5ed992017-06-04 16:53:40 +02004995 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4996 if (!mlxsw_sp->router->rifs[i]) {
4997 *p_rif_index = i;
4998 return 0;
4999 }
5000 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01005001
Ido Schimmelde5ed992017-06-04 16:53:40 +02005002 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005003}
5004
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005005static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5006 u16 vr_id,
5007 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005008{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005009 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005010
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005011 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005012 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005013 return NULL;
5014
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005015 INIT_LIST_HEAD(&rif->nexthop_list);
5016 INIT_LIST_HEAD(&rif->neigh_list);
5017 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5018 rif->mtu = l3_dev->mtu;
5019 rif->vr_id = vr_id;
5020 rif->dev = l3_dev;
5021 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005022
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005023 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005024}
5025
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005026struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5027 u16 rif_index)
5028{
5029 return mlxsw_sp->router->rifs[rif_index];
5030}
5031
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005032u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5033{
5034 return rif->rif_index;
5035}
5036
Petr Machata92107cf2017-09-02 23:49:28 +02005037u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5038{
5039 return lb_rif->common.rif_index;
5040}
5041
5042u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5043{
5044 return lb_rif->ul_vr_id;
5045}
5046
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005047int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5048{
5049 return rif->dev->ifindex;
5050}
5051
Ido Schimmel4724ba562017-03-10 08:53:39 +01005052static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005053mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5054 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005055{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005056 u32 tb_id = l3mdev_fib_table(params->dev);
5057 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005058 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005059 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005060 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005061 struct mlxsw_sp_vr *vr;
5062 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005063 int err;
5064
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005065 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5066 ops = mlxsw_sp->router->rif_ops_arr[type];
5067
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005068 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5069 if (IS_ERR(vr))
5070 return ERR_CAST(vr);
Petr Machata28a04c72017-10-02 12:14:56 +02005071 vr->rif_count++;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005072
Ido Schimmelde5ed992017-06-04 16:53:40 +02005073 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5074 if (err)
5075 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005076
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005077 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005078 if (!rif) {
5079 err = -ENOMEM;
5080 goto err_rif_alloc;
5081 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005082 rif->mlxsw_sp = mlxsw_sp;
5083 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005084
Petr Machata010cadf2017-09-02 23:49:18 +02005085 if (ops->fid_get) {
5086 fid = ops->fid_get(rif);
5087 if (IS_ERR(fid)) {
5088 err = PTR_ERR(fid);
5089 goto err_fid_get;
5090 }
5091 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005092 }
5093
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005094 if (ops->setup)
5095 ops->setup(rif, params);
5096
5097 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005098 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005099 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005100
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005101 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005102 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005103
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005104 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005105
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005106err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005107 if (fid)
5108 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005109err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005110 kfree(rif);
5111err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005112err_rif_index_alloc:
Petr Machata28a04c72017-10-02 12:14:56 +02005113 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005114 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005115 return ERR_PTR(err);
5116}
5117
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005118void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005119{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005120 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5121 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005122 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005123 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005124
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005125 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005126 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005127
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005128 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005129 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005130 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005131 if (fid)
5132 /* Loopback RIFs are not associated with a FID. */
5133 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005134 kfree(rif);
Petr Machata28a04c72017-10-02 12:14:56 +02005135 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005136 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005137}
5138
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005139static void
5140mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5141 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5142{
5143 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5144
5145 params->vid = mlxsw_sp_port_vlan->vid;
5146 params->lag = mlxsw_sp_port->lagged;
5147 if (params->lag)
5148 params->lag_id = mlxsw_sp_port->lag_id;
5149 else
5150 params->system_port = mlxsw_sp_port->local_port;
5151}
5152
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005153static int
Ido Schimmela1107482017-05-26 08:37:39 +02005154mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005155 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005156{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005157 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005158 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005159 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005160 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005161 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005162 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005163
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005164 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005165 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005166 struct mlxsw_sp_rif_params params = {
5167 .dev = l3_dev,
5168 };
5169
5170 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5171 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005172 if (IS_ERR(rif))
5173 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005174 }
5175
Ido Schimmela1107482017-05-26 08:37:39 +02005176 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005177 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005178 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5179 if (err)
5180 goto err_fid_port_vid_map;
5181
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005182 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005183 if (err)
5184 goto err_port_vid_learning_set;
5185
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005186 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005187 BR_STATE_FORWARDING);
5188 if (err)
5189 goto err_port_vid_stp_set;
5190
Ido Schimmela1107482017-05-26 08:37:39 +02005191 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005192
Ido Schimmel4724ba562017-03-10 08:53:39 +01005193 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005194
5195err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005196 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005197err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005198 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5199err_fid_port_vid_map:
5200 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005201 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005202}
5203
Ido Schimmela1107482017-05-26 08:37:39 +02005204void
5205mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005206{
Ido Schimmelce95e152017-05-26 08:37:27 +02005207 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005208 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005209 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005210
Ido Schimmela1107482017-05-26 08:37:39 +02005211 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5212 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005213
Ido Schimmela1107482017-05-26 08:37:39 +02005214 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005215 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5216 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005217 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5218 /* If router port holds the last reference on the rFID, then the
5219 * associated Sub-port RIF will be destroyed.
5220 */
5221 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005222}
5223
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005224static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5225 struct net_device *port_dev,
5226 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005227{
5228 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005229 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005230
Ido Schimmelce95e152017-05-26 08:37:27 +02005231 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005232 if (WARN_ON(!mlxsw_sp_port_vlan))
5233 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005234
5235 switch (event) {
5236 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005237 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005238 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005239 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005240 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005241 break;
5242 }
5243
5244 return 0;
5245}
5246
5247static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5248 unsigned long event)
5249{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005250 if (netif_is_bridge_port(port_dev) ||
5251 netif_is_lag_port(port_dev) ||
5252 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005253 return 0;
5254
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005255 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005256}
5257
5258static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5259 struct net_device *lag_dev,
5260 unsigned long event, u16 vid)
5261{
5262 struct net_device *port_dev;
5263 struct list_head *iter;
5264 int err;
5265
5266 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5267 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005268 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5269 port_dev,
5270 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005271 if (err)
5272 return err;
5273 }
5274 }
5275
5276 return 0;
5277}
5278
5279static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5280 unsigned long event)
5281{
5282 if (netif_is_bridge_port(lag_dev))
5283 return 0;
5284
5285 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5286}
5287
Ido Schimmel4724ba562017-03-10 08:53:39 +01005288static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005289 unsigned long event)
5290{
5291 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005292 struct mlxsw_sp_rif_params params = {
5293 .dev = l3_dev,
5294 };
Ido Schimmela1107482017-05-26 08:37:39 +02005295 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005296
5297 switch (event) {
5298 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005299 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5300 if (IS_ERR(rif))
5301 return PTR_ERR(rif);
5302 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005303 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005304 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005305 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005306 break;
5307 }
5308
5309 return 0;
5310}
5311
5312static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5313 unsigned long event)
5314{
5315 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005316 u16 vid = vlan_dev_vlan_id(vlan_dev);
5317
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005318 if (netif_is_bridge_port(vlan_dev))
5319 return 0;
5320
Ido Schimmel4724ba562017-03-10 08:53:39 +01005321 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005322 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5323 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005324 else if (netif_is_lag_master(real_dev))
5325 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5326 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005327 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005328 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005329
5330 return 0;
5331}
5332
Ido Schimmelb1e45522017-04-30 19:47:14 +03005333static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5334 unsigned long event)
5335{
5336 if (mlxsw_sp_port_dev_check(dev))
5337 return mlxsw_sp_inetaddr_port_event(dev, event);
5338 else if (netif_is_lag_master(dev))
5339 return mlxsw_sp_inetaddr_lag_event(dev, event);
5340 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005341 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005342 else if (is_vlan_dev(dev))
5343 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5344 else
5345 return 0;
5346}
5347
Ido Schimmel4724ba562017-03-10 08:53:39 +01005348int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5349 unsigned long event, void *ptr)
5350{
5351 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5352 struct net_device *dev = ifa->ifa_dev->dev;
5353 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005354 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005355 int err = 0;
5356
5357 mlxsw_sp = mlxsw_sp_lower_get(dev);
5358 if (!mlxsw_sp)
5359 goto out;
5360
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005361 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005362 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005363 goto out;
5364
Ido Schimmelb1e45522017-04-30 19:47:14 +03005365 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005366out:
5367 return notifier_from_errno(err);
5368}
5369
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005370struct mlxsw_sp_inet6addr_event_work {
5371 struct work_struct work;
5372 struct net_device *dev;
5373 unsigned long event;
5374};
5375
5376static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5377{
5378 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5379 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5380 struct net_device *dev = inet6addr_work->dev;
5381 unsigned long event = inet6addr_work->event;
5382 struct mlxsw_sp *mlxsw_sp;
5383 struct mlxsw_sp_rif *rif;
5384
5385 rtnl_lock();
5386 mlxsw_sp = mlxsw_sp_lower_get(dev);
5387 if (!mlxsw_sp)
5388 goto out;
5389
5390 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5391 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5392 goto out;
5393
5394 __mlxsw_sp_inetaddr_event(dev, event);
5395out:
5396 rtnl_unlock();
5397 dev_put(dev);
5398 kfree(inet6addr_work);
5399}
5400
5401/* Called with rcu_read_lock() */
5402int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5403 unsigned long event, void *ptr)
5404{
5405 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5406 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5407 struct net_device *dev = if6->idev->dev;
5408
5409 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5410 return NOTIFY_DONE;
5411
5412 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5413 if (!inet6addr_work)
5414 return NOTIFY_BAD;
5415
5416 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5417 inet6addr_work->dev = dev;
5418 inet6addr_work->event = event;
5419 dev_hold(dev);
5420 mlxsw_core_schedule_work(&inet6addr_work->work);
5421
5422 return NOTIFY_DONE;
5423}
5424
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005425static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005426 const char *mac, int mtu)
5427{
5428 char ritr_pl[MLXSW_REG_RITR_LEN];
5429 int err;
5430
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005431 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005432 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5433 if (err)
5434 return err;
5435
5436 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5437 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5438 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5439 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5440}
5441
5442int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5443{
5444 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005445 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005446 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005447 int err;
5448
5449 mlxsw_sp = mlxsw_sp_lower_get(dev);
5450 if (!mlxsw_sp)
5451 return 0;
5452
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005453 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5454 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005455 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005456 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005457
Ido Schimmela1107482017-05-26 08:37:39 +02005458 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005459 if (err)
5460 return err;
5461
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005462 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5463 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005464 if (err)
5465 goto err_rif_edit;
5466
Ido Schimmela1107482017-05-26 08:37:39 +02005467 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005468 if (err)
5469 goto err_rif_fdb_op;
5470
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005471 ether_addr_copy(rif->addr, dev->dev_addr);
5472 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005473
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005474 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005475
5476 return 0;
5477
5478err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005479 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005480err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005481 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005482 return err;
5483}
5484
Ido Schimmelb1e45522017-04-30 19:47:14 +03005485static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5486 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005487{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005488 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005489
Ido Schimmelb1e45522017-04-30 19:47:14 +03005490 /* If netdev is already associated with a RIF, then we need to
5491 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005492 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005493 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5494 if (rif)
5495 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005496
Ido Schimmelb1e45522017-04-30 19:47:14 +03005497 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005498}
5499
Ido Schimmelb1e45522017-04-30 19:47:14 +03005500static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5501 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005502{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005503 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005504
Ido Schimmelb1e45522017-04-30 19:47:14 +03005505 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5506 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005507 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005508 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005509}
5510
Ido Schimmelb1e45522017-04-30 19:47:14 +03005511int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5512 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005513{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005514 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5515 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005516
Ido Schimmelb1e45522017-04-30 19:47:14 +03005517 if (!mlxsw_sp)
5518 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005519
Ido Schimmelb1e45522017-04-30 19:47:14 +03005520 switch (event) {
5521 case NETDEV_PRECHANGEUPPER:
5522 return 0;
5523 case NETDEV_CHANGEUPPER:
5524 if (info->linking)
5525 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5526 else
5527 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5528 break;
5529 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005530
Ido Schimmelb1e45522017-04-30 19:47:14 +03005531 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005532}
5533
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005534static struct mlxsw_sp_rif_subport *
5535mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005536{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005537 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005538}
5539
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005540static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5541 const struct mlxsw_sp_rif_params *params)
5542{
5543 struct mlxsw_sp_rif_subport *rif_subport;
5544
5545 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5546 rif_subport->vid = params->vid;
5547 rif_subport->lag = params->lag;
5548 if (params->lag)
5549 rif_subport->lag_id = params->lag_id;
5550 else
5551 rif_subport->system_port = params->system_port;
5552}
5553
5554static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5555{
5556 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5557 struct mlxsw_sp_rif_subport *rif_subport;
5558 char ritr_pl[MLXSW_REG_RITR_LEN];
5559
5560 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5561 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005562 rif->rif_index, rif->vr_id, rif->dev->mtu);
5563 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005564 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5565 rif_subport->lag ? rif_subport->lag_id :
5566 rif_subport->system_port,
5567 rif_subport->vid);
5568
5569 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5570}
5571
5572static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5573{
Petr Machata010cadf2017-09-02 23:49:18 +02005574 int err;
5575
5576 err = mlxsw_sp_rif_subport_op(rif, true);
5577 if (err)
5578 return err;
5579
5580 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5581 mlxsw_sp_fid_index(rif->fid), true);
5582 if (err)
5583 goto err_rif_fdb_op;
5584
5585 mlxsw_sp_fid_rif_set(rif->fid, rif);
5586 return 0;
5587
5588err_rif_fdb_op:
5589 mlxsw_sp_rif_subport_op(rif, false);
5590 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005591}
5592
5593static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5594{
Petr Machata010cadf2017-09-02 23:49:18 +02005595 struct mlxsw_sp_fid *fid = rif->fid;
5596
5597 mlxsw_sp_fid_rif_set(fid, NULL);
5598 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5599 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005600 mlxsw_sp_rif_subport_op(rif, false);
5601}
5602
5603static struct mlxsw_sp_fid *
5604mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5605{
5606 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5607}
5608
5609static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5610 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5611 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5612 .setup = mlxsw_sp_rif_subport_setup,
5613 .configure = mlxsw_sp_rif_subport_configure,
5614 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5615 .fid_get = mlxsw_sp_rif_subport_fid_get,
5616};
5617
5618static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5619 enum mlxsw_reg_ritr_if_type type,
5620 u16 vid_fid, bool enable)
5621{
5622 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5623 char ritr_pl[MLXSW_REG_RITR_LEN];
5624
5625 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005626 rif->dev->mtu);
5627 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005628 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5629
5630 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5631}
5632
5633static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5634{
5635 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5636}
5637
5638static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5639{
5640 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5641 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5642 int err;
5643
5644 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5645 if (err)
5646 return err;
5647
Ido Schimmel0d284812017-07-18 10:10:12 +02005648 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5649 mlxsw_sp_router_port(mlxsw_sp), true);
5650 if (err)
5651 goto err_fid_mc_flood_set;
5652
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005653 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5654 mlxsw_sp_router_port(mlxsw_sp), true);
5655 if (err)
5656 goto err_fid_bc_flood_set;
5657
Petr Machata010cadf2017-09-02 23:49:18 +02005658 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5659 mlxsw_sp_fid_index(rif->fid), true);
5660 if (err)
5661 goto err_rif_fdb_op;
5662
5663 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005664 return 0;
5665
Petr Machata010cadf2017-09-02 23:49:18 +02005666err_rif_fdb_op:
5667 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5668 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005669err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005670 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5671 mlxsw_sp_router_port(mlxsw_sp), false);
5672err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005673 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5674 return err;
5675}
5676
5677static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5678{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005679 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005680 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5681 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005682
Petr Machata010cadf2017-09-02 23:49:18 +02005683 mlxsw_sp_fid_rif_set(fid, NULL);
5684 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5685 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005686 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5687 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005688 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5689 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005690 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5691}
5692
5693static struct mlxsw_sp_fid *
5694mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5695{
5696 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5697
5698 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5699}
5700
5701static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5702 .type = MLXSW_SP_RIF_TYPE_VLAN,
5703 .rif_size = sizeof(struct mlxsw_sp_rif),
5704 .configure = mlxsw_sp_rif_vlan_configure,
5705 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5706 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5707};
5708
5709static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5710{
5711 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5712 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5713 int err;
5714
5715 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5716 true);
5717 if (err)
5718 return err;
5719
Ido Schimmel0d284812017-07-18 10:10:12 +02005720 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5721 mlxsw_sp_router_port(mlxsw_sp), true);
5722 if (err)
5723 goto err_fid_mc_flood_set;
5724
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005725 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5726 mlxsw_sp_router_port(mlxsw_sp), true);
5727 if (err)
5728 goto err_fid_bc_flood_set;
5729
Petr Machata010cadf2017-09-02 23:49:18 +02005730 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5731 mlxsw_sp_fid_index(rif->fid), true);
5732 if (err)
5733 goto err_rif_fdb_op;
5734
5735 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005736 return 0;
5737
Petr Machata010cadf2017-09-02 23:49:18 +02005738err_rif_fdb_op:
5739 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5740 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005741err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005742 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5743 mlxsw_sp_router_port(mlxsw_sp), false);
5744err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005745 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5746 return err;
5747}
5748
5749static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5750{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005751 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005752 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5753 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005754
Petr Machata010cadf2017-09-02 23:49:18 +02005755 mlxsw_sp_fid_rif_set(fid, NULL);
5756 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5757 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005758 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5759 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005760 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5761 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005762 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5763}
5764
5765static struct mlxsw_sp_fid *
5766mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5767{
5768 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5769}
5770
5771static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5772 .type = MLXSW_SP_RIF_TYPE_FID,
5773 .rif_size = sizeof(struct mlxsw_sp_rif),
5774 .configure = mlxsw_sp_rif_fid_configure,
5775 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5776 .fid_get = mlxsw_sp_rif_fid_fid_get,
5777};
5778
Petr Machata6ddb7422017-09-02 23:49:19 +02005779static struct mlxsw_sp_rif_ipip_lb *
5780mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5781{
5782 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5783}
5784
5785static void
5786mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5787 const struct mlxsw_sp_rif_params *params)
5788{
5789 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5790 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5791
5792 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5793 common);
5794 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5795 rif_lb->lb_config = params_lb->lb_config;
5796}
5797
5798static int
5799mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5800 struct mlxsw_sp_vr *ul_vr, bool enable)
5801{
5802 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5803 struct mlxsw_sp_rif *rif = &lb_rif->common;
5804 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5805 char ritr_pl[MLXSW_REG_RITR_LEN];
5806 u32 saddr4;
5807
5808 switch (lb_cf.ul_protocol) {
5809 case MLXSW_SP_L3_PROTO_IPV4:
5810 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5811 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5812 rif->rif_index, rif->vr_id, rif->dev->mtu);
5813 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5814 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5815 ul_vr->id, saddr4, lb_cf.okey);
5816 break;
5817
5818 case MLXSW_SP_L3_PROTO_IPV6:
5819 return -EAFNOSUPPORT;
5820 }
5821
5822 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5823}
5824
5825static int
5826mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5827{
5828 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5829 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5830 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5831 struct mlxsw_sp_vr *ul_vr;
5832 int err;
5833
5834 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5835 if (IS_ERR(ul_vr))
5836 return PTR_ERR(ul_vr);
5837
5838 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5839 if (err)
5840 goto err_loopback_op;
5841
5842 lb_rif->ul_vr_id = ul_vr->id;
5843 ++ul_vr->rif_count;
5844 return 0;
5845
5846err_loopback_op:
5847 mlxsw_sp_vr_put(ul_vr);
5848 return err;
5849}
5850
5851static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5852{
5853 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5854 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5855 struct mlxsw_sp_vr *ul_vr;
5856
5857 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5858 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5859
5860 --ul_vr->rif_count;
5861 mlxsw_sp_vr_put(ul_vr);
5862}
5863
5864static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5865 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5866 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5867 .setup = mlxsw_sp_rif_ipip_lb_setup,
5868 .configure = mlxsw_sp_rif_ipip_lb_configure,
5869 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5870};
5871
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005872static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5873 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5874 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5875 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005876 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005877};
5878
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005879static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5880{
5881 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5882
5883 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5884 sizeof(struct mlxsw_sp_rif *),
5885 GFP_KERNEL);
5886 if (!mlxsw_sp->router->rifs)
5887 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005888
5889 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5890
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005891 return 0;
5892}
5893
5894static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5895{
5896 int i;
5897
5898 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5899 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5900
5901 kfree(mlxsw_sp->router->rifs);
5902}
5903
Petr Machata38ebc0f2017-09-02 23:49:17 +02005904static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5905{
5906 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02005907 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005908 return 0;
5909}
5910
5911static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5912{
Petr Machata1012b9a2017-09-02 23:49:23 +02005913 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02005914}
5915
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005916static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5917{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005918 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005919
5920 /* Flush pending FIB notifications and then flush the device's
5921 * table before requesting another dump. The FIB notification
5922 * block is unregistered, so no need to take RTNL.
5923 */
5924 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005925 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5926 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005927}
5928
Ido Schimmel4724ba562017-03-10 08:53:39 +01005929static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5930{
5931 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5932 u64 max_rifs;
5933 int err;
5934
5935 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5936 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005937 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005938
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005939 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005940 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5941 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5942 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005943 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005944 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005945}
5946
5947static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5948{
5949 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005950
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005951 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005952 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005953}
5954
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005955int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5956{
Ido Schimmel9011b672017-05-16 19:38:25 +02005957 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005958 int err;
5959
Ido Schimmel9011b672017-05-16 19:38:25 +02005960 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5961 if (!router)
5962 return -ENOMEM;
5963 mlxsw_sp->router = router;
5964 router->mlxsw_sp = mlxsw_sp;
5965
5966 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005967 err = __mlxsw_sp_router_init(mlxsw_sp);
5968 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005969 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005970
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005971 err = mlxsw_sp_rifs_init(mlxsw_sp);
5972 if (err)
5973 goto err_rifs_init;
5974
Petr Machata38ebc0f2017-09-02 23:49:17 +02005975 err = mlxsw_sp_ipips_init(mlxsw_sp);
5976 if (err)
5977 goto err_ipips_init;
5978
Ido Schimmel9011b672017-05-16 19:38:25 +02005979 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005980 &mlxsw_sp_nexthop_ht_params);
5981 if (err)
5982 goto err_nexthop_ht_init;
5983
Ido Schimmel9011b672017-05-16 19:38:25 +02005984 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005985 &mlxsw_sp_nexthop_group_ht_params);
5986 if (err)
5987 goto err_nexthop_group_ht_init;
5988
Ido Schimmel8494ab02017-03-24 08:02:47 +01005989 err = mlxsw_sp_lpm_init(mlxsw_sp);
5990 if (err)
5991 goto err_lpm_init;
5992
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005993 err = mlxsw_sp_vrs_init(mlxsw_sp);
5994 if (err)
5995 goto err_vrs_init;
5996
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005997 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005998 if (err)
5999 goto err_neigh_init;
6000
Ido Schimmel7e39d112017-05-16 19:38:28 +02006001 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6002 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006003 mlxsw_sp_router_fib_dump_flush);
6004 if (err)
6005 goto err_register_fib_notifier;
6006
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006007 return 0;
6008
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006009err_register_fib_notifier:
6010 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006011err_neigh_init:
6012 mlxsw_sp_vrs_fini(mlxsw_sp);
6013err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01006014 mlxsw_sp_lpm_fini(mlxsw_sp);
6015err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006016 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006017err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006018 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006019err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02006020 mlxsw_sp_ipips_fini(mlxsw_sp);
6021err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006022 mlxsw_sp_rifs_fini(mlxsw_sp);
6023err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006024 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006025err_router_init:
6026 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006027 return err;
6028}
6029
6030void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6031{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006032 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006033 mlxsw_sp_neigh_fini(mlxsw_sp);
6034 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006035 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006036 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6037 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006038 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006039 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006040 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006041 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006042}