blob: 6068eea8152fe676dfb8ab0bf0c1deedceaa4274 [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
1023static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1024 const union mlxsw_sp_l3addr *addr2)
1025{
1026 return !memcmp(addr1, addr2, sizeof(*addr1));
1027}
1028
1029static bool
1030mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
1031 const enum mlxsw_sp_l3proto ul_proto,
1032 union mlxsw_sp_l3addr saddr,
1033 u32 ul_tb_id,
1034 struct mlxsw_sp_ipip_entry *ipip_entry)
1035{
1036 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1037 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1038 union mlxsw_sp_l3addr tun_saddr;
1039
1040 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1041 return false;
1042
1043 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
1044 return tun_ul_tb_id == ul_tb_id &&
1045 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
1046}
1047
Petr Machata4607f6d2017-09-02 23:49:25 +02001048static int
1049mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
1050 struct mlxsw_sp_fib_entry *fib_entry,
1051 struct mlxsw_sp_ipip_entry *ipip_entry)
1052{
1053 u32 tunnel_index;
1054 int err;
1055
1056 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1057 if (err)
1058 return err;
1059
1060 ipip_entry->decap_fib_entry = fib_entry;
1061 fib_entry->decap.ipip_entry = ipip_entry;
1062 fib_entry->decap.tunnel_index = tunnel_index;
1063 return 0;
1064}
1065
1066static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1067 struct mlxsw_sp_fib_entry *fib_entry)
1068{
1069 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1070 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1071 fib_entry->decap.ipip_entry = NULL;
1072 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1073}
1074
Petr Machata1cc38fb2017-09-02 23:49:26 +02001075static struct mlxsw_sp_fib_node *
1076mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1077 size_t addr_len, unsigned char prefix_len);
Petr Machata4607f6d2017-09-02 23:49:25 +02001078static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1079 struct mlxsw_sp_fib_entry *fib_entry);
1080
1081static void
1082mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1083 struct mlxsw_sp_ipip_entry *ipip_entry)
1084{
1085 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1086
1087 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1088 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1089
1090 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1091}
1092
Petr Machata1cc38fb2017-09-02 23:49:26 +02001093static void
1094mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1095 struct mlxsw_sp_ipip_entry *ipip_entry,
1096 struct mlxsw_sp_fib_entry *decap_fib_entry)
1097{
1098 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1099 ipip_entry))
1100 return;
1101 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1102
1103 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1104 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1105}
1106
1107/* Given an IPIP entry, find the corresponding decap route. */
1108static struct mlxsw_sp_fib_entry *
1109mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1110 struct mlxsw_sp_ipip_entry *ipip_entry)
1111{
1112 static struct mlxsw_sp_fib_node *fib_node;
1113 const struct mlxsw_sp_ipip_ops *ipip_ops;
1114 struct mlxsw_sp_fib_entry *fib_entry;
1115 unsigned char saddr_prefix_len;
1116 union mlxsw_sp_l3addr saddr;
1117 struct mlxsw_sp_fib *ul_fib;
1118 struct mlxsw_sp_vr *ul_vr;
1119 const void *saddrp;
1120 size_t saddr_len;
1121 u32 ul_tb_id;
1122 u32 saddr4;
1123
1124 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1125
1126 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1127 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1128 if (!ul_vr)
1129 return NULL;
1130
1131 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1132 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1133 ipip_entry->ol_dev);
1134
1135 switch (ipip_ops->ul_proto) {
1136 case MLXSW_SP_L3_PROTO_IPV4:
1137 saddr4 = be32_to_cpu(saddr.addr4);
1138 saddrp = &saddr4;
1139 saddr_len = 4;
1140 saddr_prefix_len = 32;
1141 break;
1142 case MLXSW_SP_L3_PROTO_IPV6:
1143 WARN_ON(1);
1144 return NULL;
1145 }
1146
1147 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1148 saddr_prefix_len);
1149 if (!fib_node || list_empty(&fib_node->entry_list))
1150 return NULL;
1151
1152 fib_entry = list_first_entry(&fib_node->entry_list,
1153 struct mlxsw_sp_fib_entry, list);
1154 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1155 return NULL;
1156
1157 return fib_entry;
1158}
1159
Petr Machata1012b9a2017-09-02 23:49:23 +02001160static struct mlxsw_sp_ipip_entry *
1161mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1162 enum mlxsw_sp_ipip_type ipipt,
1163 struct net_device *ol_dev)
1164{
1165 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1166 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata1cc38fb2017-09-02 23:49:26 +02001167 struct mlxsw_sp_fib_entry *decap_fib_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02001168 struct mlxsw_sp_ipip_entry *ipip_entry;
1169 enum mlxsw_sp_l3proto ul_proto;
1170 union mlxsw_sp_l3addr saddr;
1171
1172 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1173 ipip_list_node) {
1174 if (ipip_entry->ol_dev == ol_dev)
1175 goto inc_ref_count;
1176
1177 /* The configuration where several tunnels have the same local
1178 * address in the same underlay table needs special treatment in
1179 * the HW. That is currently not implemented in the driver.
1180 */
1181 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1182 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1183 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1184 ul_tb_id, ipip_entry))
1185 return ERR_PTR(-EEXIST);
1186 }
1187
1188 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1189 if (IS_ERR(ipip_entry))
1190 return ipip_entry;
1191
Petr Machata1cc38fb2017-09-02 23:49:26 +02001192 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
1193 if (decap_fib_entry)
1194 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1195 decap_fib_entry);
1196
Petr Machata1012b9a2017-09-02 23:49:23 +02001197 list_add_tail(&ipip_entry->ipip_list_node,
1198 &mlxsw_sp->router->ipip_list);
1199
1200inc_ref_count:
1201 ++ipip_entry->ref_count;
1202 return ipip_entry;
1203}
1204
1205static void
1206mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1207 struct mlxsw_sp_ipip_entry *ipip_entry)
1208{
1209 if (--ipip_entry->ref_count == 0) {
1210 list_del(&ipip_entry->ipip_list_node);
Petr Machata4607f6d2017-09-02 23:49:25 +02001211 if (ipip_entry->decap_fib_entry)
1212 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
Petr Machata1012b9a2017-09-02 23:49:23 +02001213 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1214 }
1215}
1216
Petr Machata4607f6d2017-09-02 23:49:25 +02001217static bool
1218mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1219 const struct net_device *ul_dev,
1220 enum mlxsw_sp_l3proto ul_proto,
1221 union mlxsw_sp_l3addr ul_dip,
1222 struct mlxsw_sp_ipip_entry *ipip_entry)
1223{
1224 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1225 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1226 struct net_device *ipip_ul_dev;
1227
1228 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1229 return false;
1230
1231 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1232 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1233 ul_tb_id, ipip_entry) &&
1234 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1235}
1236
1237/* Given decap parameters, find the corresponding IPIP entry. */
1238static struct mlxsw_sp_ipip_entry *
1239mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1240 const struct net_device *ul_dev,
1241 enum mlxsw_sp_l3proto ul_proto,
1242 union mlxsw_sp_l3addr ul_dip)
1243{
1244 struct mlxsw_sp_ipip_entry *ipip_entry;
1245
1246 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1247 ipip_list_node)
1248 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1249 ul_proto, ul_dip,
1250 ipip_entry))
1251 return ipip_entry;
1252
1253 return NULL;
1254}
1255
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001256struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +01001257 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001258};
1259
1260struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +01001261 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001262 struct rhash_head ht_node;
1263 struct mlxsw_sp_neigh_key key;
1264 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001265 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001266 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001267 struct list_head nexthop_list; /* list of nexthops using
1268 * this neigh entry
1269 */
Yotam Gigib2157142016-07-05 11:27:51 +02001270 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001271 unsigned int counter_index;
1272 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001273};
1274
1275static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1276 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1277 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1278 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1279};
1280
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001281struct mlxsw_sp_neigh_entry *
1282mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1283 struct mlxsw_sp_neigh_entry *neigh_entry)
1284{
1285 if (!neigh_entry) {
1286 if (list_empty(&rif->neigh_list))
1287 return NULL;
1288 else
1289 return list_first_entry(&rif->neigh_list,
1290 typeof(*neigh_entry),
1291 rif_list_node);
1292 }
1293 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
1294 return NULL;
1295 return list_next_entry(neigh_entry, rif_list_node);
1296}
1297
1298int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1299{
1300 return neigh_entry->key.n->tbl->family;
1301}
1302
1303unsigned char *
1304mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1305{
1306 return neigh_entry->ha;
1307}
1308
1309u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1310{
1311 struct neighbour *n;
1312
1313 n = neigh_entry->key.n;
1314 return ntohl(*((__be32 *) n->primary_key));
1315}
1316
Arkadi Sharshevsky02507682017-08-31 17:59:15 +02001317struct in6_addr *
1318mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1319{
1320 struct neighbour *n;
1321
1322 n = neigh_entry->key.n;
1323 return (struct in6_addr *) &n->primary_key;
1324}
1325
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001326int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1327 struct mlxsw_sp_neigh_entry *neigh_entry,
1328 u64 *p_counter)
1329{
1330 if (!neigh_entry->counter_valid)
1331 return -EINVAL;
1332
1333 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1334 p_counter, NULL);
1335}
1336
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001337static struct mlxsw_sp_neigh_entry *
1338mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1339 u16 rif)
1340{
1341 struct mlxsw_sp_neigh_entry *neigh_entry;
1342
1343 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1344 if (!neigh_entry)
1345 return NULL;
1346
1347 neigh_entry->key.n = n;
1348 neigh_entry->rif = rif;
1349 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1350
1351 return neigh_entry;
1352}
1353
1354static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1355{
1356 kfree(neigh_entry);
1357}
1358
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001359static int
1360mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1361 struct mlxsw_sp_neigh_entry *neigh_entry)
1362{
Ido Schimmel9011b672017-05-16 19:38:25 +02001363 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001364 &neigh_entry->ht_node,
1365 mlxsw_sp_neigh_ht_params);
1366}
1367
1368static void
1369mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1370 struct mlxsw_sp_neigh_entry *neigh_entry)
1371{
Ido Schimmel9011b672017-05-16 19:38:25 +02001372 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001373 &neigh_entry->ht_node,
1374 mlxsw_sp_neigh_ht_params);
1375}
1376
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001377static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001378mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1379 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001380{
1381 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001382 const char *table_name;
1383
1384 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1385 case AF_INET:
1386 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1387 break;
1388 case AF_INET6:
1389 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1390 break;
1391 default:
1392 WARN_ON(1);
1393 return false;
1394 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001395
1396 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001397 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001398}
1399
1400static void
1401mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1402 struct mlxsw_sp_neigh_entry *neigh_entry)
1403{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001404 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001405 return;
1406
1407 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1408 return;
1409
1410 neigh_entry->counter_valid = true;
1411}
1412
1413static void
1414mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1415 struct mlxsw_sp_neigh_entry *neigh_entry)
1416{
1417 if (!neigh_entry->counter_valid)
1418 return;
1419 mlxsw_sp_flow_counter_free(mlxsw_sp,
1420 neigh_entry->counter_index);
1421 neigh_entry->counter_valid = false;
1422}
1423
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001424static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001425mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001426{
1427 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001428 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001429 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001430
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001431 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1432 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001433 return ERR_PTR(-EINVAL);
1434
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001435 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001436 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001437 return ERR_PTR(-ENOMEM);
1438
1439 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1440 if (err)
1441 goto err_neigh_entry_insert;
1442
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001443 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001444 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001445
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001446 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001447
1448err_neigh_entry_insert:
1449 mlxsw_sp_neigh_entry_free(neigh_entry);
1450 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001451}
1452
1453static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001454mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1455 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001456{
Ido Schimmel9665b742017-02-08 11:16:42 +01001457 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001458 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001459 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1460 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001461}
1462
1463static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001464mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001465{
Jiri Pirko33b13412016-11-10 12:31:04 +01001466 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001467
Jiri Pirko33b13412016-11-10 12:31:04 +01001468 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001469 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001470 &key, mlxsw_sp_neigh_ht_params);
1471}
1472
Yotam Gigic723c7352016-07-05 11:27:43 +02001473static void
1474mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1475{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001476 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001477
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001478#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001479 interval = min_t(unsigned long,
1480 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1481 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001482#else
1483 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1484#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001485 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001486}
1487
1488static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1489 char *rauhtd_pl,
1490 int ent_index)
1491{
1492 struct net_device *dev;
1493 struct neighbour *n;
1494 __be32 dipn;
1495 u32 dip;
1496 u16 rif;
1497
1498 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1499
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001500 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001501 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1502 return;
1503 }
1504
1505 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001506 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001507 n = neigh_lookup(&arp_tbl, &dipn, dev);
1508 if (!n) {
1509 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1510 &dip);
1511 return;
1512 }
1513
1514 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1515 neigh_event_send(n, NULL);
1516 neigh_release(n);
1517}
1518
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001519#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001520static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1521 char *rauhtd_pl,
1522 int rec_index)
1523{
1524 struct net_device *dev;
1525 struct neighbour *n;
1526 struct in6_addr dip;
1527 u16 rif;
1528
1529 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1530 (char *) &dip);
1531
1532 if (!mlxsw_sp->router->rifs[rif]) {
1533 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1534 return;
1535 }
1536
1537 dev = mlxsw_sp->router->rifs[rif]->dev;
1538 n = neigh_lookup(&nd_tbl, &dip, dev);
1539 if (!n) {
1540 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1541 &dip);
1542 return;
1543 }
1544
1545 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1546 neigh_event_send(n, NULL);
1547 neigh_release(n);
1548}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001549#else
1550static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1551 char *rauhtd_pl,
1552 int rec_index)
1553{
1554}
1555#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001556
Yotam Gigic723c7352016-07-05 11:27:43 +02001557static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1558 char *rauhtd_pl,
1559 int rec_index)
1560{
1561 u8 num_entries;
1562 int i;
1563
1564 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1565 rec_index);
1566 /* Hardware starts counting at 0, so add 1. */
1567 num_entries++;
1568
1569 /* Each record consists of several neighbour entries. */
1570 for (i = 0; i < num_entries; i++) {
1571 int ent_index;
1572
1573 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1574 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1575 ent_index);
1576 }
1577
1578}
1579
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001580static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1581 char *rauhtd_pl,
1582 int rec_index)
1583{
1584 /* One record contains one entry. */
1585 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1586 rec_index);
1587}
1588
Yotam Gigic723c7352016-07-05 11:27:43 +02001589static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1590 char *rauhtd_pl, int rec_index)
1591{
1592 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1593 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1594 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1595 rec_index);
1596 break;
1597 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001598 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1599 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001600 break;
1601 }
1602}
1603
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001604static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1605{
1606 u8 num_rec, last_rec_index, num_entries;
1607
1608 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1609 last_rec_index = num_rec - 1;
1610
1611 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1612 return false;
1613 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1614 MLXSW_REG_RAUHTD_TYPE_IPV6)
1615 return true;
1616
1617 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1618 last_rec_index);
1619 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1620 return true;
1621 return false;
1622}
1623
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001624static int
1625__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1626 char *rauhtd_pl,
1627 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001628{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001629 int i, num_rec;
1630 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001631
1632 /* Make sure the neighbour's netdev isn't removed in the
1633 * process.
1634 */
1635 rtnl_lock();
1636 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001637 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001638 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1639 rauhtd_pl);
1640 if (err) {
1641 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1642 break;
1643 }
1644 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1645 for (i = 0; i < num_rec; i++)
1646 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1647 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001648 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001649 rtnl_unlock();
1650
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001651 return err;
1652}
1653
1654static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1655{
1656 enum mlxsw_reg_rauhtd_type type;
1657 char *rauhtd_pl;
1658 int err;
1659
1660 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1661 if (!rauhtd_pl)
1662 return -ENOMEM;
1663
1664 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1665 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1666 if (err)
1667 goto out;
1668
1669 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1670 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1671out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001672 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001673 return err;
1674}
1675
1676static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1677{
1678 struct mlxsw_sp_neigh_entry *neigh_entry;
1679
1680 /* Take RTNL mutex here to prevent lists from changes */
1681 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001682 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001683 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001684 /* If this neigh have nexthops, make the kernel think this neigh
1685 * is active regardless of the traffic.
1686 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001687 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001688 rtnl_unlock();
1689}
1690
1691static void
1692mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1693{
Ido Schimmel9011b672017-05-16 19:38:25 +02001694 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001695
Ido Schimmel9011b672017-05-16 19:38:25 +02001696 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001697 msecs_to_jiffies(interval));
1698}
1699
1700static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1701{
Ido Schimmel9011b672017-05-16 19:38:25 +02001702 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001703 int err;
1704
Ido Schimmel9011b672017-05-16 19:38:25 +02001705 router = container_of(work, struct mlxsw_sp_router,
1706 neighs_update.dw.work);
1707 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001708 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001709 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001710
Ido Schimmel9011b672017-05-16 19:38:25 +02001711 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001712
Ido Schimmel9011b672017-05-16 19:38:25 +02001713 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001714}
1715
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001716static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1717{
1718 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001719 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001720
Ido Schimmel9011b672017-05-16 19:38:25 +02001721 router = container_of(work, struct mlxsw_sp_router,
1722 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001723 /* Iterate over nexthop neighbours, find those who are unresolved and
1724 * send arp on them. This solves the chicken-egg problem when
1725 * the nexthop wouldn't get offloaded until the neighbor is resolved
1726 * but it wouldn't get resolved ever in case traffic is flowing in HW
1727 * using different nexthop.
1728 *
1729 * Take RTNL mutex here to prevent lists from changes.
1730 */
1731 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001732 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001733 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001734 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001735 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001736 rtnl_unlock();
1737
Ido Schimmel9011b672017-05-16 19:38:25 +02001738 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001739 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1740}
1741
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001742static void
1743mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1744 struct mlxsw_sp_neigh_entry *neigh_entry,
1745 bool removing);
1746
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001747static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001748{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001749 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1750 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1751}
1752
1753static void
1754mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1755 struct mlxsw_sp_neigh_entry *neigh_entry,
1756 enum mlxsw_reg_rauht_op op)
1757{
Jiri Pirko33b13412016-11-10 12:31:04 +01001758 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001759 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001760 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001761
1762 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1763 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001764 if (neigh_entry->counter_valid)
1765 mlxsw_reg_rauht_pack_counter(rauht_pl,
1766 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001767 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1768}
1769
1770static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001771mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1772 struct mlxsw_sp_neigh_entry *neigh_entry,
1773 enum mlxsw_reg_rauht_op op)
1774{
1775 struct neighbour *n = neigh_entry->key.n;
1776 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1777 const char *dip = n->primary_key;
1778
1779 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1780 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001781 if (neigh_entry->counter_valid)
1782 mlxsw_reg_rauht_pack_counter(rauht_pl,
1783 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001784 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1785}
1786
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001787bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001788{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001789 struct neighbour *n = neigh_entry->key.n;
1790
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001791 /* Packets with a link-local destination address are trapped
1792 * after LPM lookup and never reach the neighbour table, so
1793 * there is no need to program such neighbours to the device.
1794 */
1795 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1796 IPV6_ADDR_LINKLOCAL)
1797 return true;
1798 return false;
1799}
1800
1801static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001802mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1803 struct mlxsw_sp_neigh_entry *neigh_entry,
1804 bool adding)
1805{
1806 if (!adding && !neigh_entry->connected)
1807 return;
1808 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001809 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001810 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1811 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001812 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001813 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001814 return;
1815 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1816 mlxsw_sp_rauht_op(adding));
1817 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001818 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001819 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001820}
1821
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001822void
1823mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1824 struct mlxsw_sp_neigh_entry *neigh_entry,
1825 bool adding)
1826{
1827 if (adding)
1828 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1829 else
1830 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1831 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1832}
1833
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001834struct mlxsw_sp_neigh_event_work {
1835 struct work_struct work;
1836 struct mlxsw_sp *mlxsw_sp;
1837 struct neighbour *n;
1838};
1839
1840static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1841{
1842 struct mlxsw_sp_neigh_event_work *neigh_work =
1843 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1844 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1845 struct mlxsw_sp_neigh_entry *neigh_entry;
1846 struct neighbour *n = neigh_work->n;
1847 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001848 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001849 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001850
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001851 /* If these parameters are changed after we release the lock,
1852 * then we are guaranteed to receive another event letting us
1853 * know about it.
1854 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001855 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001856 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001857 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001858 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001859 read_unlock_bh(&n->lock);
1860
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001861 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001862 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001863 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1864 if (!entry_connected && !neigh_entry)
1865 goto out;
1866 if (!neigh_entry) {
1867 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1868 if (IS_ERR(neigh_entry))
1869 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001870 }
1871
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001872 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1873 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1874 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1875
1876 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1877 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1878
1879out:
1880 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001881 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001882 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001883}
1884
Jiri Pirkoe7322632016-09-01 10:37:43 +02001885int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1886 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001887{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001888 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001889 struct mlxsw_sp_port *mlxsw_sp_port;
1890 struct mlxsw_sp *mlxsw_sp;
1891 unsigned long interval;
1892 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001893 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001894
1895 switch (event) {
1896 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1897 p = ptr;
1898
1899 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001900 if (!p->dev || (p->tbl->family != AF_INET &&
1901 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001902 return NOTIFY_DONE;
1903
1904 /* We are in atomic context and can't take RTNL mutex,
1905 * so use RCU variant to walk the device chain.
1906 */
1907 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1908 if (!mlxsw_sp_port)
1909 return NOTIFY_DONE;
1910
1911 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1912 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001913 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001914
1915 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1916 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001917 case NETEVENT_NEIGH_UPDATE:
1918 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001919
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001920 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001921 return NOTIFY_DONE;
1922
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001923 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001924 if (!mlxsw_sp_port)
1925 return NOTIFY_DONE;
1926
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001927 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1928 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001929 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001930 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001931 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001932
1933 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1934 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1935 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001936
1937 /* Take a reference to ensure the neighbour won't be
1938 * destructed until we drop the reference in delayed
1939 * work.
1940 */
1941 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001942 mlxsw_core_schedule_work(&neigh_work->work);
1943 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001944 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001945 }
1946
1947 return NOTIFY_DONE;
1948}
1949
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001950static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1951{
Yotam Gigic723c7352016-07-05 11:27:43 +02001952 int err;
1953
Ido Schimmel9011b672017-05-16 19:38:25 +02001954 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001955 &mlxsw_sp_neigh_ht_params);
1956 if (err)
1957 return err;
1958
1959 /* Initialize the polling interval according to the default
1960 * table.
1961 */
1962 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1963
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001964 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001965 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001966 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001967 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001968 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001969 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1970 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001971 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001972}
1973
1974static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1975{
Ido Schimmel9011b672017-05-16 19:38:25 +02001976 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1977 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1978 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001979}
1980
Ido Schimmel9665b742017-02-08 11:16:42 +01001981static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001982 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01001983{
1984 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1985
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001986 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001987 rif_list_node) {
1988 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01001989 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02001990 }
Ido Schimmel9665b742017-02-08 11:16:42 +01001991}
1992
Petr Machata35225e42017-09-02 23:49:22 +02001993enum mlxsw_sp_nexthop_type {
1994 MLXSW_SP_NEXTHOP_TYPE_ETH,
Petr Machata1012b9a2017-09-02 23:49:23 +02001995 MLXSW_SP_NEXTHOP_TYPE_IPIP,
Petr Machata35225e42017-09-02 23:49:22 +02001996};
1997
Ido Schimmelc53b8e12017-02-08 11:16:30 +01001998struct mlxsw_sp_nexthop_key {
1999 struct fib_nh *fib_nh;
2000};
2001
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002002struct mlxsw_sp_nexthop {
2003 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01002004 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002005 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2006 * this belongs to
2007 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002008 struct rhash_head ht_node;
2009 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002010 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002011 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002012 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002013 u8 should_offload:1, /* set indicates this neigh is connected and
2014 * should be put to KVD linear area of this group.
2015 */
2016 offloaded:1, /* set in case the neigh is actually put into
2017 * KVD linear area of this group.
2018 */
2019 update:1; /* set indicates that MAC of this neigh should be
2020 * updated in HW
2021 */
Petr Machata35225e42017-09-02 23:49:22 +02002022 enum mlxsw_sp_nexthop_type type;
2023 union {
2024 struct mlxsw_sp_neigh_entry *neigh_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02002025 struct mlxsw_sp_ipip_entry *ipip_entry;
Petr Machata35225e42017-09-02 23:49:22 +02002026 };
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002027};
2028
2029struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002030 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002031 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002032 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002033 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002034 u8 adj_index_valid:1,
2035 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002036 u32 adj_index;
2037 u16 ecmp_size;
2038 u16 count;
2039 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002040#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002041};
2042
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002043static struct fib_info *
2044mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2045{
2046 return nh_grp->priv;
2047}
2048
2049struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002050 enum mlxsw_sp_l3proto proto;
2051 union {
2052 struct fib_info *fi;
2053 struct mlxsw_sp_fib6_entry *fib6_entry;
2054 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002055};
2056
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002057static bool
2058mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2059 const struct in6_addr *gw, int ifindex)
2060{
2061 int i;
2062
2063 for (i = 0; i < nh_grp->count; i++) {
2064 const struct mlxsw_sp_nexthop *nh;
2065
2066 nh = &nh_grp->nexthops[i];
2067 if (nh->ifindex == ifindex &&
2068 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2069 return true;
2070 }
2071
2072 return false;
2073}
2074
2075static bool
2076mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2077 const struct mlxsw_sp_fib6_entry *fib6_entry)
2078{
2079 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2080
2081 if (nh_grp->count != fib6_entry->nrt6)
2082 return false;
2083
2084 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2085 struct in6_addr *gw;
2086 int ifindex;
2087
2088 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2089 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2090 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2091 return false;
2092 }
2093
2094 return true;
2095}
2096
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002097static int
2098mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2099{
2100 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2101 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2102
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002103 switch (cmp_arg->proto) {
2104 case MLXSW_SP_L3_PROTO_IPV4:
2105 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2106 case MLXSW_SP_L3_PROTO_IPV6:
2107 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2108 cmp_arg->fib6_entry);
2109 default:
2110 WARN_ON(1);
2111 return 1;
2112 }
2113}
2114
2115static int
2116mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2117{
2118 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002119}
2120
2121static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2122{
2123 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002124 const struct mlxsw_sp_nexthop *nh;
2125 struct fib_info *fi;
2126 unsigned int val;
2127 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002128
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002129 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2130 case AF_INET:
2131 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2132 return jhash(&fi, sizeof(fi), seed);
2133 case AF_INET6:
2134 val = nh_grp->count;
2135 for (i = 0; i < nh_grp->count; i++) {
2136 nh = &nh_grp->nexthops[i];
2137 val ^= nh->ifindex;
2138 }
2139 return jhash(&val, sizeof(val), seed);
2140 default:
2141 WARN_ON(1);
2142 return 0;
2143 }
2144}
2145
2146static u32
2147mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2148{
2149 unsigned int val = fib6_entry->nrt6;
2150 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2151 struct net_device *dev;
2152
2153 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2154 dev = mlxsw_sp_rt6->rt->dst.dev;
2155 val ^= dev->ifindex;
2156 }
2157
2158 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002159}
2160
2161static u32
2162mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2163{
2164 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2165
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002166 switch (cmp_arg->proto) {
2167 case MLXSW_SP_L3_PROTO_IPV4:
2168 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2169 case MLXSW_SP_L3_PROTO_IPV6:
2170 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2171 default:
2172 WARN_ON(1);
2173 return 0;
2174 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002175}
2176
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002177static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002178 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002179 .hashfn = mlxsw_sp_nexthop_group_hash,
2180 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2181 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002182};
2183
2184static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2185 struct mlxsw_sp_nexthop_group *nh_grp)
2186{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002187 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2188 !nh_grp->gateway)
2189 return 0;
2190
Ido Schimmel9011b672017-05-16 19:38:25 +02002191 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002192 &nh_grp->ht_node,
2193 mlxsw_sp_nexthop_group_ht_params);
2194}
2195
2196static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2197 struct mlxsw_sp_nexthop_group *nh_grp)
2198{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002199 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2200 !nh_grp->gateway)
2201 return;
2202
Ido Schimmel9011b672017-05-16 19:38:25 +02002203 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002204 &nh_grp->ht_node,
2205 mlxsw_sp_nexthop_group_ht_params);
2206}
2207
2208static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002209mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2210 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002211{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002212 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2213
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002214 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002215 cmp_arg.fi = fi;
2216 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2217 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002218 mlxsw_sp_nexthop_group_ht_params);
2219}
2220
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002221static struct mlxsw_sp_nexthop_group *
2222mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2223 struct mlxsw_sp_fib6_entry *fib6_entry)
2224{
2225 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2226
2227 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2228 cmp_arg.fib6_entry = fib6_entry;
2229 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2230 &cmp_arg,
2231 mlxsw_sp_nexthop_group_ht_params);
2232}
2233
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002234static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2235 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2236 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2237 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2238};
2239
2240static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2241 struct mlxsw_sp_nexthop *nh)
2242{
Ido Schimmel9011b672017-05-16 19:38:25 +02002243 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002244 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2245}
2246
2247static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2248 struct mlxsw_sp_nexthop *nh)
2249{
Ido Schimmel9011b672017-05-16 19:38:25 +02002250 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002251 mlxsw_sp_nexthop_ht_params);
2252}
2253
Ido Schimmelad178c82017-02-08 11:16:40 +01002254static struct mlxsw_sp_nexthop *
2255mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2256 struct mlxsw_sp_nexthop_key key)
2257{
Ido Schimmel9011b672017-05-16 19:38:25 +02002258 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002259 mlxsw_sp_nexthop_ht_params);
2260}
2261
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002262static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002263 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002264 u32 adj_index, u16 ecmp_size,
2265 u32 new_adj_index,
2266 u16 new_ecmp_size)
2267{
2268 char raleu_pl[MLXSW_REG_RALEU_LEN];
2269
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002270 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002271 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2272 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002273 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002274 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2275}
2276
2277static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2278 struct mlxsw_sp_nexthop_group *nh_grp,
2279 u32 old_adj_index, u16 old_ecmp_size)
2280{
2281 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002282 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002283 int err;
2284
2285 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002286 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002287 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002288 fib = fib_entry->fib_node->fib;
2289 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002290 old_adj_index,
2291 old_ecmp_size,
2292 nh_grp->adj_index,
2293 nh_grp->ecmp_size);
2294 if (err)
2295 return err;
2296 }
2297 return 0;
2298}
2299
2300static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2301 struct mlxsw_sp_nexthop *nh)
2302{
2303 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2304 char ratr_pl[MLXSW_REG_RATR_LEN];
2305
2306 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002307 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2308 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002309 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2310 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2311}
2312
Petr Machata1012b9a2017-09-02 23:49:23 +02002313static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2314 u32 adj_index,
2315 struct mlxsw_sp_nexthop *nh)
2316{
2317 const struct mlxsw_sp_ipip_ops *ipip_ops;
2318
2319 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2320 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2321}
2322
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002323static int
Petr Machata35225e42017-09-02 23:49:22 +02002324mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2325 struct mlxsw_sp_nexthop_group *nh_grp,
2326 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002327{
2328 u32 adj_index = nh_grp->adj_index; /* base */
2329 struct mlxsw_sp_nexthop *nh;
2330 int i;
2331 int err;
2332
2333 for (i = 0; i < nh_grp->count; i++) {
2334 nh = &nh_grp->nexthops[i];
2335
2336 if (!nh->should_offload) {
2337 nh->offloaded = 0;
2338 continue;
2339 }
2340
Ido Schimmela59b7e02017-01-23 11:11:42 +01002341 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002342 switch (nh->type) {
2343 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2344 err = mlxsw_sp_nexthop_mac_update
2345 (mlxsw_sp, adj_index, nh);
2346 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002347 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2348 err = mlxsw_sp_nexthop_ipip_update
2349 (mlxsw_sp, adj_index, nh);
2350 break;
Petr Machata35225e42017-09-02 23:49:22 +02002351 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002352 if (err)
2353 return err;
2354 nh->update = 0;
2355 nh->offloaded = 1;
2356 }
2357 adj_index++;
2358 }
2359 return 0;
2360}
2361
Ido Schimmel1819ae32017-07-21 18:04:28 +02002362static bool
2363mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2364 const struct mlxsw_sp_fib_entry *fib_entry);
2365
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002366static int
2367mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2368 struct mlxsw_sp_nexthop_group *nh_grp)
2369{
2370 struct mlxsw_sp_fib_entry *fib_entry;
2371 int err;
2372
2373 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002374 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2375 fib_entry))
2376 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002377 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2378 if (err)
2379 return err;
2380 }
2381 return 0;
2382}
2383
2384static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002385mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2386 enum mlxsw_reg_ralue_op op, int err);
2387
2388static void
2389mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2390{
2391 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2392 struct mlxsw_sp_fib_entry *fib_entry;
2393
2394 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2395 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2396 fib_entry))
2397 continue;
2398 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2399 }
2400}
2401
2402static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002403mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2404 struct mlxsw_sp_nexthop_group *nh_grp)
2405{
2406 struct mlxsw_sp_nexthop *nh;
2407 bool offload_change = false;
2408 u32 adj_index;
2409 u16 ecmp_size = 0;
2410 bool old_adj_index_valid;
2411 u32 old_adj_index;
2412 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002413 int i;
2414 int err;
2415
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002416 if (!nh_grp->gateway) {
2417 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2418 return;
2419 }
2420
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002421 for (i = 0; i < nh_grp->count; i++) {
2422 nh = &nh_grp->nexthops[i];
2423
Petr Machata56b8a9e2017-07-31 09:27:29 +02002424 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002425 offload_change = true;
2426 if (nh->should_offload)
2427 nh->update = 1;
2428 }
2429 if (nh->should_offload)
2430 ecmp_size++;
2431 }
2432 if (!offload_change) {
2433 /* Nothing was added or removed, so no need to reallocate. Just
2434 * update MAC on existing adjacency indexes.
2435 */
Petr Machata35225e42017-09-02 23:49:22 +02002436 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002437 if (err) {
2438 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2439 goto set_trap;
2440 }
2441 return;
2442 }
2443 if (!ecmp_size)
2444 /* No neigh of this group is connected so we just set
2445 * the trap and let everthing flow through kernel.
2446 */
2447 goto set_trap;
2448
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002449 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2450 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002451 /* We ran out of KVD linear space, just set the
2452 * trap and let everything flow through kernel.
2453 */
2454 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2455 goto set_trap;
2456 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002457 old_adj_index_valid = nh_grp->adj_index_valid;
2458 old_adj_index = nh_grp->adj_index;
2459 old_ecmp_size = nh_grp->ecmp_size;
2460 nh_grp->adj_index_valid = 1;
2461 nh_grp->adj_index = adj_index;
2462 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002463 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002464 if (err) {
2465 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2466 goto set_trap;
2467 }
2468
2469 if (!old_adj_index_valid) {
2470 /* The trap was set for fib entries, so we have to call
2471 * fib entry update to unset it and use adjacency index.
2472 */
2473 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2474 if (err) {
2475 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2476 goto set_trap;
2477 }
2478 return;
2479 }
2480
2481 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2482 old_adj_index, old_ecmp_size);
2483 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2484 if (err) {
2485 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2486 goto set_trap;
2487 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002488
2489 /* Offload state within the group changed, so update the flags. */
2490 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2491
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002492 return;
2493
2494set_trap:
2495 old_adj_index_valid = nh_grp->adj_index_valid;
2496 nh_grp->adj_index_valid = 0;
2497 for (i = 0; i < nh_grp->count; i++) {
2498 nh = &nh_grp->nexthops[i];
2499 nh->offloaded = 0;
2500 }
2501 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2502 if (err)
2503 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2504 if (old_adj_index_valid)
2505 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2506}
2507
2508static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2509 bool removing)
2510{
Petr Machata213666a2017-07-31 09:27:30 +02002511 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002512 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002513 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002514 nh->should_offload = 0;
2515 nh->update = 1;
2516}
2517
2518static void
2519mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2520 struct mlxsw_sp_neigh_entry *neigh_entry,
2521 bool removing)
2522{
2523 struct mlxsw_sp_nexthop *nh;
2524
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002525 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2526 neigh_list_node) {
2527 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2528 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2529 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002530}
2531
Ido Schimmel9665b742017-02-08 11:16:42 +01002532static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002533 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002534{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002535 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002536 return;
2537
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002538 nh->rif = rif;
2539 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002540}
2541
2542static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2543{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002544 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002545 return;
2546
2547 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002548 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002549}
2550
Ido Schimmela8c97012017-02-08 11:16:35 +01002551static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2552 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002553{
2554 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002555 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002556 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002557 int err;
2558
Ido Schimmelad178c82017-02-08 11:16:40 +01002559 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002560 return 0;
2561
Jiri Pirko33b13412016-11-10 12:31:04 +01002562 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002563 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002564 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002565 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002566 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002567 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002568 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002569 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2570 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002571 if (IS_ERR(n))
2572 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002573 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002574 }
2575 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2576 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002577 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2578 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002579 err = -EINVAL;
2580 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002581 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002582 }
Yotam Gigib2157142016-07-05 11:27:51 +02002583
2584 /* If that is the first nexthop connected to that neigh, add to
2585 * nexthop_neighs_list
2586 */
2587 if (list_empty(&neigh_entry->nexthop_list))
2588 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002589 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002590
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002591 nh->neigh_entry = neigh_entry;
2592 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2593 read_lock_bh(&n->lock);
2594 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002595 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002596 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002597 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002598
2599 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002600
2601err_neigh_entry_create:
2602 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002603 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002604}
2605
Ido Schimmela8c97012017-02-08 11:16:35 +01002606static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2607 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002608{
2609 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002610 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002611
Ido Schimmelb8399a12017-02-08 11:16:33 +01002612 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002613 return;
2614 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002615
Ido Schimmel58312122016-12-23 09:32:50 +01002616 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002617 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002618 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002619
2620 /* If that is the last nexthop connected to that neigh, remove from
2621 * nexthop_neighs_list
2622 */
Ido Schimmele58be792017-02-08 11:16:28 +01002623 if (list_empty(&neigh_entry->nexthop_list))
2624 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002625
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002626 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2627 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2628
2629 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002630}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002631
Petr Machata6ddb7422017-09-02 23:49:19 +02002632static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2633 const struct net_device *dev,
2634 enum mlxsw_sp_ipip_type *p_type)
2635{
2636 struct mlxsw_sp_router *router = mlxsw_sp->router;
2637 const struct mlxsw_sp_ipip_ops *ipip_ops;
2638 enum mlxsw_sp_ipip_type ipipt;
2639
2640 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2641 ipip_ops = router->ipip_ops_arr[ipipt];
2642 if (dev->type == ipip_ops->dev_type) {
2643 if (p_type)
2644 *p_type = ipipt;
2645 return true;
2646 }
2647 }
2648 return false;
2649}
2650
Petr Machata1012b9a2017-09-02 23:49:23 +02002651static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2652 enum mlxsw_sp_ipip_type ipipt,
2653 struct mlxsw_sp_nexthop *nh,
2654 struct net_device *ol_dev)
2655{
2656 if (!nh->nh_grp->gateway || nh->ipip_entry)
2657 return 0;
2658
2659 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2660 if (IS_ERR(nh->ipip_entry))
2661 return PTR_ERR(nh->ipip_entry);
2662
2663 __mlxsw_sp_nexthop_neigh_update(nh, false);
2664 return 0;
2665}
2666
2667static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2668 struct mlxsw_sp_nexthop *nh)
2669{
2670 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2671
2672 if (!ipip_entry)
2673 return;
2674
2675 __mlxsw_sp_nexthop_neigh_update(nh, true);
2676 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2677 nh->ipip_entry = NULL;
2678}
2679
2680static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2681 const struct fib_nh *fib_nh,
2682 enum mlxsw_sp_ipip_type *p_ipipt)
2683{
2684 struct net_device *dev = fib_nh->nh_dev;
2685
2686 return dev &&
2687 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2688 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2689}
2690
Petr Machata35225e42017-09-02 23:49:22 +02002691static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2692 struct mlxsw_sp_nexthop *nh)
2693{
2694 switch (nh->type) {
2695 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2696 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2697 mlxsw_sp_nexthop_rif_fini(nh);
2698 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002699 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2700 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2701 break;
Petr Machata35225e42017-09-02 23:49:22 +02002702 }
2703}
2704
2705static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2706 struct mlxsw_sp_nexthop *nh,
2707 struct fib_nh *fib_nh)
2708{
Petr Machata1012b9a2017-09-02 23:49:23 +02002709 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002710 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002711 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002712 struct mlxsw_sp_rif *rif;
2713 int err;
2714
Petr Machata1012b9a2017-09-02 23:49:23 +02002715 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2716 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2717 MLXSW_SP_L3_PROTO_IPV4)) {
2718 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
2719 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2720 }
2721
Petr Machata35225e42017-09-02 23:49:22 +02002722 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2723 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2724 if (!rif)
2725 return 0;
2726
2727 mlxsw_sp_nexthop_rif_init(nh, rif);
2728 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2729 if (err)
2730 goto err_neigh_init;
2731
2732 return 0;
2733
2734err_neigh_init:
2735 mlxsw_sp_nexthop_rif_fini(nh);
2736 return err;
2737}
2738
2739static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2740 struct mlxsw_sp_nexthop *nh)
2741{
2742 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2743}
2744
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002745static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2746 struct mlxsw_sp_nexthop_group *nh_grp,
2747 struct mlxsw_sp_nexthop *nh,
2748 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002749{
2750 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002751 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01002752 int err;
2753
2754 nh->nh_grp = nh_grp;
2755 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002756 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002757 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2758 if (err)
2759 return err;
2760
Ido Schimmel97989ee2017-03-10 08:53:38 +01002761 if (!dev)
2762 return 0;
2763
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002764 in_dev = __in_dev_get_rtnl(dev);
2765 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2766 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2767 return 0;
2768
Petr Machata35225e42017-09-02 23:49:22 +02002769 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002770 if (err)
2771 goto err_nexthop_neigh_init;
2772
2773 return 0;
2774
2775err_nexthop_neigh_init:
2776 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2777 return err;
2778}
2779
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002780static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2781 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002782{
Petr Machata35225e42017-09-02 23:49:22 +02002783 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002784 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002785}
2786
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002787static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2788 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002789{
2790 struct mlxsw_sp_nexthop_key key;
2791 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01002792
Ido Schimmel9011b672017-05-16 19:38:25 +02002793 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002794 return;
2795
2796 key.fib_nh = fib_nh;
2797 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2798 if (WARN_ON_ONCE(!nh))
2799 return;
2800
Ido Schimmelad178c82017-02-08 11:16:40 +01002801 switch (event) {
2802 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02002803 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002804 break;
2805 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02002806 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002807 break;
2808 }
2809
2810 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2811}
2812
Ido Schimmel9665b742017-02-08 11:16:42 +01002813static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002814 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002815{
2816 struct mlxsw_sp_nexthop *nh, *tmp;
2817
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002818 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02002819 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002820 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2821 }
2822}
2823
Petr Machata9b014512017-09-02 23:49:20 +02002824static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2825 const struct fib_info *fi)
2826{
Petr Machata1012b9a2017-09-02 23:49:23 +02002827 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2828 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02002829}
2830
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002831static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002832mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002833{
2834 struct mlxsw_sp_nexthop_group *nh_grp;
2835 struct mlxsw_sp_nexthop *nh;
2836 struct fib_nh *fib_nh;
2837 size_t alloc_size;
2838 int i;
2839 int err;
2840
2841 alloc_size = sizeof(*nh_grp) +
2842 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2843 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2844 if (!nh_grp)
2845 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002846 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002847 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002848 nh_grp->neigh_tbl = &arp_tbl;
2849
Petr Machata9b014512017-09-02 23:49:20 +02002850 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002851 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002852 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002853 for (i = 0; i < nh_grp->count; i++) {
2854 nh = &nh_grp->nexthops[i];
2855 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002856 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002857 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002858 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002859 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002860 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2861 if (err)
2862 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002863 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2864 return nh_grp;
2865
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002866err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002867err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002868 for (i--; i >= 0; i--) {
2869 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002870 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002871 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002872 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002873 kfree(nh_grp);
2874 return ERR_PTR(err);
2875}
2876
2877static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002878mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2879 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002880{
2881 struct mlxsw_sp_nexthop *nh;
2882 int i;
2883
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002884 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002885 for (i = 0; i < nh_grp->count; i++) {
2886 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002887 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002888 }
Ido Schimmel58312122016-12-23 09:32:50 +01002889 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2890 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002891 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002892 kfree(nh_grp);
2893}
2894
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002895static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2896 struct mlxsw_sp_fib_entry *fib_entry,
2897 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002898{
2899 struct mlxsw_sp_nexthop_group *nh_grp;
2900
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002901 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002902 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002903 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002904 if (IS_ERR(nh_grp))
2905 return PTR_ERR(nh_grp);
2906 }
2907 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2908 fib_entry->nh_group = nh_grp;
2909 return 0;
2910}
2911
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002912static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2913 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002914{
2915 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2916
2917 list_del(&fib_entry->nexthop_group_node);
2918 if (!list_empty(&nh_grp->fib_list))
2919 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002920 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002921}
2922
Ido Schimmel013b20f2017-02-08 11:16:36 +01002923static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002924mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2925{
2926 struct mlxsw_sp_fib4_entry *fib4_entry;
2927
2928 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2929 common);
2930 return !fib4_entry->tos;
2931}
2932
2933static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002934mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2935{
2936 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2937
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002938 switch (fib_entry->fib_node->fib->proto) {
2939 case MLXSW_SP_L3_PROTO_IPV4:
2940 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2941 return false;
2942 break;
2943 case MLXSW_SP_L3_PROTO_IPV6:
2944 break;
2945 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002946
Ido Schimmel013b20f2017-02-08 11:16:36 +01002947 switch (fib_entry->type) {
2948 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2949 return !!nh_group->adj_index_valid;
2950 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002951 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02002952 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
2953 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002954 default:
2955 return false;
2956 }
2957}
2958
Ido Schimmel428b8512017-08-03 13:28:28 +02002959static struct mlxsw_sp_nexthop *
2960mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2961 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2962{
2963 int i;
2964
2965 for (i = 0; i < nh_grp->count; i++) {
2966 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2967 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2968
2969 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2970 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2971 &rt->rt6i_gateway))
2972 return nh;
2973 continue;
2974 }
2975
2976 return NULL;
2977}
2978
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002979static void
2980mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2981{
2982 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2983 int i;
2984
Petr Machata4607f6d2017-09-02 23:49:25 +02002985 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
2986 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02002987 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2988 return;
2989 }
2990
2991 for (i = 0; i < nh_grp->count; i++) {
2992 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2993
2994 if (nh->offloaded)
2995 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2996 else
2997 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2998 }
2999}
3000
3001static void
3002mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3003{
3004 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3005 int i;
3006
3007 for (i = 0; i < nh_grp->count; i++) {
3008 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3009
3010 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3011 }
3012}
3013
Ido Schimmel428b8512017-08-03 13:28:28 +02003014static void
3015mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3016{
3017 struct mlxsw_sp_fib6_entry *fib6_entry;
3018 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3019
3020 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3021 common);
3022
3023 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3024 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003025 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003026 return;
3027 }
3028
3029 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3030 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3031 struct mlxsw_sp_nexthop *nh;
3032
3033 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3034 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003035 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003036 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003037 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003038 }
3039}
3040
3041static void
3042mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3043{
3044 struct mlxsw_sp_fib6_entry *fib6_entry;
3045 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3046
3047 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3048 common);
3049 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3050 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3051
Ido Schimmelfe400792017-08-15 09:09:49 +02003052 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003053 }
3054}
3055
Ido Schimmel013b20f2017-02-08 11:16:36 +01003056static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3057{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003058 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003059 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003060 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003061 break;
3062 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003063 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3064 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003065 }
3066}
3067
3068static void
3069mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3070{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003071 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003072 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003073 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003074 break;
3075 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003076 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3077 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003078 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003079}
3080
3081static void
3082mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3083 enum mlxsw_reg_ralue_op op, int err)
3084{
3085 switch (op) {
3086 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003087 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3088 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3089 if (err)
3090 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003091 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003092 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02003093 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003094 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3095 return;
3096 default:
3097 return;
3098 }
3099}
3100
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003101static void
3102mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3103 const struct mlxsw_sp_fib_entry *fib_entry,
3104 enum mlxsw_reg_ralue_op op)
3105{
3106 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3107 enum mlxsw_reg_ralxx_protocol proto;
3108 u32 *p_dip;
3109
3110 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3111
3112 switch (fib->proto) {
3113 case MLXSW_SP_L3_PROTO_IPV4:
3114 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3115 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3116 fib_entry->fib_node->key.prefix_len,
3117 *p_dip);
3118 break;
3119 case MLXSW_SP_L3_PROTO_IPV6:
3120 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3121 fib_entry->fib_node->key.prefix_len,
3122 fib_entry->fib_node->key.addr);
3123 break;
3124 }
3125}
3126
3127static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3128 struct mlxsw_sp_fib_entry *fib_entry,
3129 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003130{
3131 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003132 enum mlxsw_reg_ralue_trap_action trap_action;
3133 u16 trap_id = 0;
3134 u32 adjacency_index = 0;
3135 u16 ecmp_size = 0;
3136
3137 /* In case the nexthop group adjacency index is valid, use it
3138 * with provided ECMP size. Otherwise, setup trap and pass
3139 * traffic to kernel.
3140 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003141 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003142 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3143 adjacency_index = fib_entry->nh_group->adj_index;
3144 ecmp_size = fib_entry->nh_group->ecmp_size;
3145 } else {
3146 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3147 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3148 }
3149
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003150 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003151 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3152 adjacency_index, ecmp_size);
3153 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3154}
3155
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003156static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3157 struct mlxsw_sp_fib_entry *fib_entry,
3158 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003159{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003160 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003161 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003162 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003163 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003164 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003165
3166 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3167 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003168 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003169 } else {
3170 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3171 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3172 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003173
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003174 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003175 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3176 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003177 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3178}
3179
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003180static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3181 struct mlxsw_sp_fib_entry *fib_entry,
3182 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003183{
3184 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003185
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003186 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003187 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3188 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3189}
3190
Petr Machata4607f6d2017-09-02 23:49:25 +02003191static int
3192mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3193 struct mlxsw_sp_fib_entry *fib_entry,
3194 enum mlxsw_reg_ralue_op op)
3195{
3196 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3197 const struct mlxsw_sp_ipip_ops *ipip_ops;
3198
3199 if (WARN_ON(!ipip_entry))
3200 return -EINVAL;
3201
3202 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3203 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3204 fib_entry->decap.tunnel_index);
3205}
3206
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003207static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3208 struct mlxsw_sp_fib_entry *fib_entry,
3209 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003210{
3211 switch (fib_entry->type) {
3212 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003213 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003214 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003215 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003216 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003217 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003218 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3219 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3220 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003221 }
3222 return -EINVAL;
3223}
3224
3225static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3226 struct mlxsw_sp_fib_entry *fib_entry,
3227 enum mlxsw_reg_ralue_op op)
3228{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003229 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003230
Ido Schimmel013b20f2017-02-08 11:16:36 +01003231 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003232
Ido Schimmel013b20f2017-02-08 11:16:36 +01003233 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003234}
3235
3236static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3237 struct mlxsw_sp_fib_entry *fib_entry)
3238{
Jiri Pirko7146da32016-09-01 10:37:41 +02003239 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3240 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003241}
3242
3243static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3244 struct mlxsw_sp_fib_entry *fib_entry)
3245{
3246 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3247 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3248}
3249
Jiri Pirko61c503f2016-07-04 08:23:11 +02003250static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003251mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3252 const struct fib_entry_notifier_info *fen_info,
3253 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003254{
Petr Machata4607f6d2017-09-02 23:49:25 +02003255 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3256 struct net_device *dev = fen_info->fi->fib_dev;
3257 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003258 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003259
Ido Schimmel97989ee2017-03-10 08:53:38 +01003260 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003261 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003262 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3263 MLXSW_SP_L3_PROTO_IPV4, dip);
3264 if (ipip_entry) {
3265 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3266 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3267 fib_entry,
3268 ipip_entry);
3269 }
3270 /* fall through */
3271 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003272 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3273 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003274 case RTN_UNREACHABLE: /* fall through */
3275 case RTN_BLACKHOLE: /* fall through */
3276 case RTN_PROHIBIT:
3277 /* Packets hitting these routes need to be trapped, but
3278 * can do so with a lower priority than packets directed
3279 * at the host, so use action type local instead of trap.
3280 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003281 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003282 return 0;
3283 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003284 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003285 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003286 else
3287 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003288 return 0;
3289 default:
3290 return -EINVAL;
3291 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003292}
3293
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003294static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003295mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3296 struct mlxsw_sp_fib_node *fib_node,
3297 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003298{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003299 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003300 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003301 int err;
3302
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003303 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3304 if (!fib4_entry)
3305 return ERR_PTR(-ENOMEM);
3306 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003307
3308 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3309 if (err)
3310 goto err_fib4_entry_type_set;
3311
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003312 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003313 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003314 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003315
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003316 fib4_entry->prio = fen_info->fi->fib_priority;
3317 fib4_entry->tb_id = fen_info->tb_id;
3318 fib4_entry->type = fen_info->type;
3319 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003320
3321 fib_entry->fib_node = fib_node;
3322
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003323 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003324
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003325err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003326err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003327 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003328 return ERR_PTR(err);
3329}
3330
3331static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003332 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003333{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003334 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003335 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003336}
3337
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003338static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003339mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3340 const struct fib_entry_notifier_info *fen_info)
3341{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003342 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003343 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003344 struct mlxsw_sp_fib *fib;
3345 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003346
Ido Schimmel160e22a2017-07-18 10:10:20 +02003347 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3348 if (!vr)
3349 return NULL;
3350 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3351
3352 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3353 sizeof(fen_info->dst),
3354 fen_info->dst_len);
3355 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003356 return NULL;
3357
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003358 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3359 if (fib4_entry->tb_id == fen_info->tb_id &&
3360 fib4_entry->tos == fen_info->tos &&
3361 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003362 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3363 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003364 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003365 }
3366 }
3367
3368 return NULL;
3369}
3370
3371static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3372 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3373 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3374 .key_len = sizeof(struct mlxsw_sp_fib_key),
3375 .automatic_shrinking = true,
3376};
3377
3378static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3379 struct mlxsw_sp_fib_node *fib_node)
3380{
3381 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3382 mlxsw_sp_fib_ht_params);
3383}
3384
3385static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3386 struct mlxsw_sp_fib_node *fib_node)
3387{
3388 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3389 mlxsw_sp_fib_ht_params);
3390}
3391
3392static struct mlxsw_sp_fib_node *
3393mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3394 size_t addr_len, unsigned char prefix_len)
3395{
3396 struct mlxsw_sp_fib_key key;
3397
3398 memset(&key, 0, sizeof(key));
3399 memcpy(key.addr, addr, addr_len);
3400 key.prefix_len = prefix_len;
3401 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3402}
3403
3404static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003405mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003406 size_t addr_len, unsigned char prefix_len)
3407{
3408 struct mlxsw_sp_fib_node *fib_node;
3409
3410 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3411 if (!fib_node)
3412 return NULL;
3413
3414 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003415 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003416 memcpy(fib_node->key.addr, addr, addr_len);
3417 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003418
3419 return fib_node;
3420}
3421
3422static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3423{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003424 list_del(&fib_node->list);
3425 WARN_ON(!list_empty(&fib_node->entry_list));
3426 kfree(fib_node);
3427}
3428
3429static bool
3430mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3431 const struct mlxsw_sp_fib_entry *fib_entry)
3432{
3433 return list_first_entry(&fib_node->entry_list,
3434 struct mlxsw_sp_fib_entry, list) == fib_entry;
3435}
3436
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003437static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3438 struct mlxsw_sp_fib *fib,
3439 struct mlxsw_sp_fib_node *fib_node)
3440{
3441 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3442 struct mlxsw_sp_lpm_tree *lpm_tree;
3443 int err;
3444
3445 /* Since the tree is shared between all virtual routers we must
3446 * make sure it contains all the required prefix lengths. This
3447 * can be computed by either adding the new prefix length to the
3448 * existing prefix usage of a bound tree, or by aggregating the
3449 * prefix lengths across all virtual routers and adding the new
3450 * one as well.
3451 */
3452 if (fib->lpm_tree)
3453 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3454 &fib->lpm_tree->prefix_usage);
3455 else
3456 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3457 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3458
3459 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3460 fib->proto);
3461 if (IS_ERR(lpm_tree))
3462 return PTR_ERR(lpm_tree);
3463
3464 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3465 return 0;
3466
3467 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3468 if (err)
3469 return err;
3470
3471 return 0;
3472}
3473
3474static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3475 struct mlxsw_sp_fib *fib)
3476{
3477 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3478 struct mlxsw_sp_lpm_tree *lpm_tree;
3479
3480 /* Aggregate prefix lengths across all virtual routers to make
3481 * sure we only have used prefix lengths in the LPM tree.
3482 */
3483 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3484 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3485 fib->proto);
3486 if (IS_ERR(lpm_tree))
3487 goto err_tree_get;
3488 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3489
3490err_tree_get:
3491 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3492 return;
3493 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3494 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3495 fib->lpm_tree = NULL;
3496}
3497
Ido Schimmel9aecce12017-02-09 10:28:42 +01003498static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3499{
3500 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003501 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003502
3503 if (fib->prefix_ref_count[prefix_len]++ == 0)
3504 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3505}
3506
3507static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3508{
3509 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003510 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003511
3512 if (--fib->prefix_ref_count[prefix_len] == 0)
3513 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3514}
3515
Ido Schimmel76610eb2017-03-10 08:53:41 +01003516static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3517 struct mlxsw_sp_fib_node *fib_node,
3518 struct mlxsw_sp_fib *fib)
3519{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003520 int err;
3521
3522 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3523 if (err)
3524 return err;
3525 fib_node->fib = fib;
3526
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003527 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3528 if (err)
3529 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003530
3531 mlxsw_sp_fib_node_prefix_inc(fib_node);
3532
3533 return 0;
3534
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003535err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003536 fib_node->fib = NULL;
3537 mlxsw_sp_fib_node_remove(fib, fib_node);
3538 return err;
3539}
3540
3541static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3542 struct mlxsw_sp_fib_node *fib_node)
3543{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003544 struct mlxsw_sp_fib *fib = fib_node->fib;
3545
3546 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003547 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003548 fib_node->fib = NULL;
3549 mlxsw_sp_fib_node_remove(fib, fib_node);
3550}
3551
Ido Schimmel9aecce12017-02-09 10:28:42 +01003552static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003553mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3554 size_t addr_len, unsigned char prefix_len,
3555 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003556{
3557 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003558 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003559 struct mlxsw_sp_vr *vr;
3560 int err;
3561
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003562 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003563 if (IS_ERR(vr))
3564 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003565 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003566
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003567 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003568 if (fib_node)
3569 return fib_node;
3570
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003571 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003572 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003573 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003574 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003575 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003576
Ido Schimmel76610eb2017-03-10 08:53:41 +01003577 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3578 if (err)
3579 goto err_fib_node_init;
3580
Ido Schimmel9aecce12017-02-09 10:28:42 +01003581 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003582
Ido Schimmel76610eb2017-03-10 08:53:41 +01003583err_fib_node_init:
3584 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003585err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003586 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003587 return ERR_PTR(err);
3588}
3589
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003590static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3591 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003592{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003593 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003594
Ido Schimmel9aecce12017-02-09 10:28:42 +01003595 if (!list_empty(&fib_node->entry_list))
3596 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003597 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003598 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003599 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003600}
3601
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003602static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003603mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003604 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003605{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003606 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003607
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003608 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3609 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003610 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003611 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003612 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003613 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003614 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003615 if (fib4_entry->prio >= new4_entry->prio ||
3616 fib4_entry->tos < new4_entry->tos)
3617 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003618 }
3619
3620 return NULL;
3621}
3622
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003623static int
3624mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3625 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003626{
3627 struct mlxsw_sp_fib_node *fib_node;
3628
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003629 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003630 return -EINVAL;
3631
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003632 fib_node = fib4_entry->common.fib_node;
3633 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3634 common.list) {
3635 if (fib4_entry->tb_id != new4_entry->tb_id ||
3636 fib4_entry->tos != new4_entry->tos ||
3637 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003638 break;
3639 }
3640
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003641 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003642 return 0;
3643}
3644
Ido Schimmel9aecce12017-02-09 10:28:42 +01003645static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003646mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003647 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003648{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003649 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003650 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003651
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003652 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003653
Ido Schimmel4283bce2017-02-09 10:28:43 +01003654 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003655 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3656 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003657 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003658
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003659 /* Insert new entry before replaced one, so that we can later
3660 * remove the second.
3661 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003662 if (fib4_entry) {
3663 list_add_tail(&new4_entry->common.list,
3664 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003665 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003666 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003667
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003668 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3669 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003670 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003671 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003672 }
3673
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003674 if (fib4_entry)
3675 list_add(&new4_entry->common.list,
3676 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003677 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003678 list_add(&new4_entry->common.list,
3679 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003680 }
3681
3682 return 0;
3683}
3684
3685static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003686mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003687{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003688 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003689}
3690
Ido Schimmel80c238f2017-07-18 10:10:29 +02003691static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3692 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003693{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003694 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3695
Ido Schimmel9aecce12017-02-09 10:28:42 +01003696 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3697 return 0;
3698
3699 /* To prevent packet loss, overwrite the previously offloaded
3700 * entry.
3701 */
3702 if (!list_is_singular(&fib_node->entry_list)) {
3703 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3704 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3705
3706 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3707 }
3708
3709 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3710}
3711
Ido Schimmel80c238f2017-07-18 10:10:29 +02003712static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3713 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003714{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003715 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3716
Ido Schimmel9aecce12017-02-09 10:28:42 +01003717 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3718 return;
3719
3720 /* Promote the next entry by overwriting the deleted entry */
3721 if (!list_is_singular(&fib_node->entry_list)) {
3722 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3723 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3724
3725 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3726 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3727 return;
3728 }
3729
3730 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3731}
3732
3733static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003734 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003735 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003736{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003737 int err;
3738
Ido Schimmel9efbee62017-07-18 10:10:28 +02003739 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003740 if (err)
3741 return err;
3742
Ido Schimmel80c238f2017-07-18 10:10:29 +02003743 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003744 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003745 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003746
Ido Schimmel9aecce12017-02-09 10:28:42 +01003747 return 0;
3748
Ido Schimmel80c238f2017-07-18 10:10:29 +02003749err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003750 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003751 return err;
3752}
3753
3754static void
3755mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003756 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003757{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003758 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003759 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02003760
3761 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3762 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003763}
3764
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003765static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003766 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003767 bool replace)
3768{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003769 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3770 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003771
3772 if (!replace)
3773 return;
3774
3775 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003776 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003777
3778 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3779 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003780 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003781}
3782
Ido Schimmel9aecce12017-02-09 10:28:42 +01003783static int
3784mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003785 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003786 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003787{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003788 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003789 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003790 int err;
3791
Ido Schimmel9011b672017-05-16 19:38:25 +02003792 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003793 return 0;
3794
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003795 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3796 &fen_info->dst, sizeof(fen_info->dst),
3797 fen_info->dst_len,
3798 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003799 if (IS_ERR(fib_node)) {
3800 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3801 return PTR_ERR(fib_node);
3802 }
3803
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003804 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3805 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003806 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003807 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003808 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003809 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003810
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003811 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003812 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003813 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003814 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3815 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003816 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003817
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003818 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003819
Jiri Pirko61c503f2016-07-04 08:23:11 +02003820 return 0;
3821
Ido Schimmel9aecce12017-02-09 10:28:42 +01003822err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003823 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003824err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003825 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003826 return err;
3827}
3828
Jiri Pirko37956d72016-10-20 16:05:43 +02003829static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3830 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003831{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003832 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003833 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003834
Ido Schimmel9011b672017-05-16 19:38:25 +02003835 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003836 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003837
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003838 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3839 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003840 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003841 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003842
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003843 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3844 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003845 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003846}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003847
Ido Schimmel428b8512017-08-03 13:28:28 +02003848static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3849{
3850 /* Packets with link-local destination IP arriving to the router
3851 * are trapped to the CPU, so no need to program specific routes
3852 * for them.
3853 */
3854 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3855 return true;
3856
3857 /* Multicast routes aren't supported, so ignore them. Neighbour
3858 * Discovery packets are specifically trapped.
3859 */
3860 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3861 return true;
3862
3863 /* Cloned routes are irrelevant in the forwarding path. */
3864 if (rt->rt6i_flags & RTF_CACHE)
3865 return true;
3866
3867 return false;
3868}
3869
3870static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3871{
3872 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3873
3874 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3875 if (!mlxsw_sp_rt6)
3876 return ERR_PTR(-ENOMEM);
3877
3878 /* In case of route replace, replaced route is deleted with
3879 * no notification. Take reference to prevent accessing freed
3880 * memory.
3881 */
3882 mlxsw_sp_rt6->rt = rt;
3883 rt6_hold(rt);
3884
3885 return mlxsw_sp_rt6;
3886}
3887
3888#if IS_ENABLED(CONFIG_IPV6)
3889static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3890{
3891 rt6_release(rt);
3892}
3893#else
3894static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3895{
3896}
3897#endif
3898
3899static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3900{
3901 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3902 kfree(mlxsw_sp_rt6);
3903}
3904
3905static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3906{
3907 /* RTF_CACHE routes are ignored */
3908 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3909}
3910
3911static struct rt6_info *
3912mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3913{
3914 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3915 list)->rt;
3916}
3917
3918static struct mlxsw_sp_fib6_entry *
3919mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003920 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003921{
3922 struct mlxsw_sp_fib6_entry *fib6_entry;
3923
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003924 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003925 return NULL;
3926
3927 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3928 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3929
3930 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3931 * virtual router.
3932 */
3933 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3934 continue;
3935 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3936 break;
3937 if (rt->rt6i_metric < nrt->rt6i_metric)
3938 continue;
3939 if (rt->rt6i_metric == nrt->rt6i_metric &&
3940 mlxsw_sp_fib6_rt_can_mp(rt))
3941 return fib6_entry;
3942 if (rt->rt6i_metric > nrt->rt6i_metric)
3943 break;
3944 }
3945
3946 return NULL;
3947}
3948
3949static struct mlxsw_sp_rt6 *
3950mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3951 const struct rt6_info *rt)
3952{
3953 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3954
3955 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3956 if (mlxsw_sp_rt6->rt == rt)
3957 return mlxsw_sp_rt6;
3958 }
3959
3960 return NULL;
3961}
3962
Petr Machata8f28a302017-09-02 23:49:24 +02003963static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
3964 const struct rt6_info *rt,
3965 enum mlxsw_sp_ipip_type *ret)
3966{
3967 return rt->dst.dev &&
3968 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
3969}
3970
Petr Machata35225e42017-09-02 23:49:22 +02003971static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
3972 struct mlxsw_sp_nexthop_group *nh_grp,
3973 struct mlxsw_sp_nexthop *nh,
3974 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02003975{
Petr Machata8f28a302017-09-02 23:49:24 +02003976 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02003977 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02003978 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02003979 struct mlxsw_sp_rif *rif;
3980 int err;
3981
Petr Machata8f28a302017-09-02 23:49:24 +02003982 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
3983 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
3984 MLXSW_SP_L3_PROTO_IPV6)) {
3985 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
3986 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
3987 }
3988
Petr Machata35225e42017-09-02 23:49:22 +02003989 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02003990 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3991 if (!rif)
3992 return 0;
3993 mlxsw_sp_nexthop_rif_init(nh, rif);
3994
3995 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
3996 if (err)
3997 goto err_nexthop_neigh_init;
3998
3999 return 0;
4000
4001err_nexthop_neigh_init:
4002 mlxsw_sp_nexthop_rif_fini(nh);
4003 return err;
4004}
4005
Petr Machata35225e42017-09-02 23:49:22 +02004006static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4007 struct mlxsw_sp_nexthop *nh)
4008{
4009 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4010}
4011
4012static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4013 struct mlxsw_sp_nexthop_group *nh_grp,
4014 struct mlxsw_sp_nexthop *nh,
4015 const struct rt6_info *rt)
4016{
4017 struct net_device *dev = rt->dst.dev;
4018
4019 nh->nh_grp = nh_grp;
4020 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4021
4022 if (!dev)
4023 return 0;
4024 nh->ifindex = dev->ifindex;
4025
4026 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4027}
4028
Ido Schimmel428b8512017-08-03 13:28:28 +02004029static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4030 struct mlxsw_sp_nexthop *nh)
4031{
Petr Machata35225e42017-09-02 23:49:22 +02004032 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Ido Schimmel428b8512017-08-03 13:28:28 +02004033}
4034
Petr Machataf6050ee2017-09-02 23:49:21 +02004035static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4036 const struct rt6_info *rt)
4037{
Petr Machata8f28a302017-09-02 23:49:24 +02004038 return rt->rt6i_flags & RTF_GATEWAY ||
4039 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004040}
4041
Ido Schimmel428b8512017-08-03 13:28:28 +02004042static struct mlxsw_sp_nexthop_group *
4043mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4044 struct mlxsw_sp_fib6_entry *fib6_entry)
4045{
4046 struct mlxsw_sp_nexthop_group *nh_grp;
4047 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4048 struct mlxsw_sp_nexthop *nh;
4049 size_t alloc_size;
4050 int i = 0;
4051 int err;
4052
4053 alloc_size = sizeof(*nh_grp) +
4054 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4055 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4056 if (!nh_grp)
4057 return ERR_PTR(-ENOMEM);
4058 INIT_LIST_HEAD(&nh_grp->fib_list);
4059#if IS_ENABLED(CONFIG_IPV6)
4060 nh_grp->neigh_tbl = &nd_tbl;
4061#endif
4062 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4063 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004064 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004065 nh_grp->count = fib6_entry->nrt6;
4066 for (i = 0; i < nh_grp->count; i++) {
4067 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4068
4069 nh = &nh_grp->nexthops[i];
4070 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4071 if (err)
4072 goto err_nexthop6_init;
4073 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4074 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004075
4076 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4077 if (err)
4078 goto err_nexthop_group_insert;
4079
Ido Schimmel428b8512017-08-03 13:28:28 +02004080 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4081 return nh_grp;
4082
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004083err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004084err_nexthop6_init:
4085 for (i--; i >= 0; i--) {
4086 nh = &nh_grp->nexthops[i];
4087 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4088 }
4089 kfree(nh_grp);
4090 return ERR_PTR(err);
4091}
4092
4093static void
4094mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4095 struct mlxsw_sp_nexthop_group *nh_grp)
4096{
4097 struct mlxsw_sp_nexthop *nh;
4098 int i = nh_grp->count;
4099
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004100 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004101 for (i--; i >= 0; i--) {
4102 nh = &nh_grp->nexthops[i];
4103 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4104 }
4105 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4106 WARN_ON(nh_grp->adj_index_valid);
4107 kfree(nh_grp);
4108}
4109
4110static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4111 struct mlxsw_sp_fib6_entry *fib6_entry)
4112{
4113 struct mlxsw_sp_nexthop_group *nh_grp;
4114
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004115 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4116 if (!nh_grp) {
4117 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4118 if (IS_ERR(nh_grp))
4119 return PTR_ERR(nh_grp);
4120 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004121
4122 list_add_tail(&fib6_entry->common.nexthop_group_node,
4123 &nh_grp->fib_list);
4124 fib6_entry->common.nh_group = nh_grp;
4125
4126 return 0;
4127}
4128
4129static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4130 struct mlxsw_sp_fib_entry *fib_entry)
4131{
4132 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4133
4134 list_del(&fib_entry->nexthop_group_node);
4135 if (!list_empty(&nh_grp->fib_list))
4136 return;
4137 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4138}
4139
4140static int
4141mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4142 struct mlxsw_sp_fib6_entry *fib6_entry)
4143{
4144 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4145 int err;
4146
4147 fib6_entry->common.nh_group = NULL;
4148 list_del(&fib6_entry->common.nexthop_group_node);
4149
4150 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4151 if (err)
4152 goto err_nexthop6_group_get;
4153
4154 /* In case this entry is offloaded, then the adjacency index
4155 * currently associated with it in the device's table is that
4156 * of the old group. Start using the new one instead.
4157 */
4158 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4159 if (err)
4160 goto err_fib_node_entry_add;
4161
4162 if (list_empty(&old_nh_grp->fib_list))
4163 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4164
4165 return 0;
4166
4167err_fib_node_entry_add:
4168 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4169err_nexthop6_group_get:
4170 list_add_tail(&fib6_entry->common.nexthop_group_node,
4171 &old_nh_grp->fib_list);
4172 fib6_entry->common.nh_group = old_nh_grp;
4173 return err;
4174}
4175
4176static int
4177mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4178 struct mlxsw_sp_fib6_entry *fib6_entry,
4179 struct rt6_info *rt)
4180{
4181 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4182 int err;
4183
4184 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4185 if (IS_ERR(mlxsw_sp_rt6))
4186 return PTR_ERR(mlxsw_sp_rt6);
4187
4188 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4189 fib6_entry->nrt6++;
4190
4191 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4192 if (err)
4193 goto err_nexthop6_group_update;
4194
4195 return 0;
4196
4197err_nexthop6_group_update:
4198 fib6_entry->nrt6--;
4199 list_del(&mlxsw_sp_rt6->list);
4200 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4201 return err;
4202}
4203
4204static void
4205mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4206 struct mlxsw_sp_fib6_entry *fib6_entry,
4207 struct rt6_info *rt)
4208{
4209 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4210
4211 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4212 if (WARN_ON(!mlxsw_sp_rt6))
4213 return;
4214
4215 fib6_entry->nrt6--;
4216 list_del(&mlxsw_sp_rt6->list);
4217 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4218 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4219}
4220
Petr Machataf6050ee2017-09-02 23:49:21 +02004221static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4222 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004223 const struct rt6_info *rt)
4224{
4225 /* Packets hitting RTF_REJECT routes need to be discarded by the
4226 * stack. We can rely on their destination device not having a
4227 * RIF (it's the loopback device) and can thus use action type
4228 * local, which will cause them to be trapped with a lower
4229 * priority than packets that need to be locally received.
4230 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004231 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004232 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4233 else if (rt->rt6i_flags & RTF_REJECT)
4234 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004235 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004236 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4237 else
4238 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4239}
4240
4241static void
4242mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4243{
4244 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4245
4246 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4247 list) {
4248 fib6_entry->nrt6--;
4249 list_del(&mlxsw_sp_rt6->list);
4250 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4251 }
4252}
4253
4254static struct mlxsw_sp_fib6_entry *
4255mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4256 struct mlxsw_sp_fib_node *fib_node,
4257 struct rt6_info *rt)
4258{
4259 struct mlxsw_sp_fib6_entry *fib6_entry;
4260 struct mlxsw_sp_fib_entry *fib_entry;
4261 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4262 int err;
4263
4264 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4265 if (!fib6_entry)
4266 return ERR_PTR(-ENOMEM);
4267 fib_entry = &fib6_entry->common;
4268
4269 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4270 if (IS_ERR(mlxsw_sp_rt6)) {
4271 err = PTR_ERR(mlxsw_sp_rt6);
4272 goto err_rt6_create;
4273 }
4274
Petr Machataf6050ee2017-09-02 23:49:21 +02004275 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004276
4277 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4278 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4279 fib6_entry->nrt6 = 1;
4280 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4281 if (err)
4282 goto err_nexthop6_group_get;
4283
4284 fib_entry->fib_node = fib_node;
4285
4286 return fib6_entry;
4287
4288err_nexthop6_group_get:
4289 list_del(&mlxsw_sp_rt6->list);
4290 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4291err_rt6_create:
4292 kfree(fib6_entry);
4293 return ERR_PTR(err);
4294}
4295
4296static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4297 struct mlxsw_sp_fib6_entry *fib6_entry)
4298{
4299 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4300 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4301 WARN_ON(fib6_entry->nrt6);
4302 kfree(fib6_entry);
4303}
4304
4305static struct mlxsw_sp_fib6_entry *
4306mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004307 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004308{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004309 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004310
4311 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4312 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4313
4314 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4315 continue;
4316 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4317 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004318 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4319 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4320 mlxsw_sp_fib6_rt_can_mp(nrt))
4321 return fib6_entry;
4322 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4323 fallback = fallback ?: fib6_entry;
4324 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004325 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004326 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004327 }
4328
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004329 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004330}
4331
4332static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004333mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4334 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004335{
4336 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4337 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4338 struct mlxsw_sp_fib6_entry *fib6_entry;
4339
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004340 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4341
4342 if (replace && WARN_ON(!fib6_entry))
4343 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004344
4345 if (fib6_entry) {
4346 list_add_tail(&new6_entry->common.list,
4347 &fib6_entry->common.list);
4348 } else {
4349 struct mlxsw_sp_fib6_entry *last;
4350
4351 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4352 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4353
4354 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4355 break;
4356 fib6_entry = last;
4357 }
4358
4359 if (fib6_entry)
4360 list_add(&new6_entry->common.list,
4361 &fib6_entry->common.list);
4362 else
4363 list_add(&new6_entry->common.list,
4364 &fib_node->entry_list);
4365 }
4366
4367 return 0;
4368}
4369
4370static void
4371mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4372{
4373 list_del(&fib6_entry->common.list);
4374}
4375
4376static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004377 struct mlxsw_sp_fib6_entry *fib6_entry,
4378 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004379{
4380 int err;
4381
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004382 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004383 if (err)
4384 return err;
4385
4386 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4387 if (err)
4388 goto err_fib_node_entry_add;
4389
4390 return 0;
4391
4392err_fib_node_entry_add:
4393 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4394 return err;
4395}
4396
4397static void
4398mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4399 struct mlxsw_sp_fib6_entry *fib6_entry)
4400{
4401 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4402 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4403}
4404
4405static struct mlxsw_sp_fib6_entry *
4406mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4407 const struct rt6_info *rt)
4408{
4409 struct mlxsw_sp_fib6_entry *fib6_entry;
4410 struct mlxsw_sp_fib_node *fib_node;
4411 struct mlxsw_sp_fib *fib;
4412 struct mlxsw_sp_vr *vr;
4413
4414 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4415 if (!vr)
4416 return NULL;
4417 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4418
4419 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4420 sizeof(rt->rt6i_dst.addr),
4421 rt->rt6i_dst.plen);
4422 if (!fib_node)
4423 return NULL;
4424
4425 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4426 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4427
4428 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4429 rt->rt6i_metric == iter_rt->rt6i_metric &&
4430 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4431 return fib6_entry;
4432 }
4433
4434 return NULL;
4435}
4436
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004437static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4438 struct mlxsw_sp_fib6_entry *fib6_entry,
4439 bool replace)
4440{
4441 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4442 struct mlxsw_sp_fib6_entry *replaced;
4443
4444 if (!replace)
4445 return;
4446
4447 replaced = list_next_entry(fib6_entry, common.list);
4448
4449 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4450 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4451 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4452}
4453
Ido Schimmel428b8512017-08-03 13:28:28 +02004454static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004455 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004456{
4457 struct mlxsw_sp_fib6_entry *fib6_entry;
4458 struct mlxsw_sp_fib_node *fib_node;
4459 int err;
4460
4461 if (mlxsw_sp->router->aborted)
4462 return 0;
4463
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004464 if (rt->rt6i_src.plen)
4465 return -EINVAL;
4466
Ido Schimmel428b8512017-08-03 13:28:28 +02004467 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4468 return 0;
4469
4470 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4471 &rt->rt6i_dst.addr,
4472 sizeof(rt->rt6i_dst.addr),
4473 rt->rt6i_dst.plen,
4474 MLXSW_SP_L3_PROTO_IPV6);
4475 if (IS_ERR(fib_node))
4476 return PTR_ERR(fib_node);
4477
4478 /* Before creating a new entry, try to append route to an existing
4479 * multipath entry.
4480 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004481 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004482 if (fib6_entry) {
4483 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4484 if (err)
4485 goto err_fib6_entry_nexthop_add;
4486 return 0;
4487 }
4488
4489 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4490 if (IS_ERR(fib6_entry)) {
4491 err = PTR_ERR(fib6_entry);
4492 goto err_fib6_entry_create;
4493 }
4494
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004495 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004496 if (err)
4497 goto err_fib6_node_entry_link;
4498
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004499 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4500
Ido Schimmel428b8512017-08-03 13:28:28 +02004501 return 0;
4502
4503err_fib6_node_entry_link:
4504 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4505err_fib6_entry_create:
4506err_fib6_entry_nexthop_add:
4507 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4508 return err;
4509}
4510
4511static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4512 struct rt6_info *rt)
4513{
4514 struct mlxsw_sp_fib6_entry *fib6_entry;
4515 struct mlxsw_sp_fib_node *fib_node;
4516
4517 if (mlxsw_sp->router->aborted)
4518 return;
4519
4520 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4521 return;
4522
4523 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4524 if (WARN_ON(!fib6_entry))
4525 return;
4526
4527 /* If route is part of a multipath entry, but not the last one
4528 * removed, then only reduce its nexthop group.
4529 */
4530 if (!list_is_singular(&fib6_entry->rt6_list)) {
4531 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4532 return;
4533 }
4534
4535 fib_node = fib6_entry->common.fib_node;
4536
4537 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4538 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4539 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4540}
4541
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004542static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4543 enum mlxsw_reg_ralxx_protocol proto,
4544 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004545{
4546 char ralta_pl[MLXSW_REG_RALTA_LEN];
4547 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004548 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004549
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004550 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004551 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4552 if (err)
4553 return err;
4554
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004555 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004556 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4557 if (err)
4558 return err;
4559
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004560 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004561 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004562 char raltb_pl[MLXSW_REG_RALTB_LEN];
4563 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004564
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004565 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004566 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4567 raltb_pl);
4568 if (err)
4569 return err;
4570
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004571 mlxsw_reg_ralue_pack(ralue_pl, proto,
4572 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004573 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4574 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4575 ralue_pl);
4576 if (err)
4577 return err;
4578 }
4579
4580 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004581}
4582
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004583static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4584{
4585 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4586 int err;
4587
4588 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4589 MLXSW_SP_LPM_TREE_MIN);
4590 if (err)
4591 return err;
4592
4593 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4594 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4595 MLXSW_SP_LPM_TREE_MIN + 1);
4596}
4597
Ido Schimmel9aecce12017-02-09 10:28:42 +01004598static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4599 struct mlxsw_sp_fib_node *fib_node)
4600{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004601 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004602
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004603 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4604 common.list) {
4605 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004606
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004607 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4608 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004609 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004610 /* Break when entry list is empty and node was freed.
4611 * Otherwise, we'll access freed memory in the next
4612 * iteration.
4613 */
4614 if (do_break)
4615 break;
4616 }
4617}
4618
Ido Schimmel428b8512017-08-03 13:28:28 +02004619static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4620 struct mlxsw_sp_fib_node *fib_node)
4621{
4622 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4623
4624 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4625 common.list) {
4626 bool do_break = &tmp->common.list == &fib_node->entry_list;
4627
4628 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4629 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4630 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4631 if (do_break)
4632 break;
4633 }
4634}
4635
Ido Schimmel9aecce12017-02-09 10:28:42 +01004636static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4637 struct mlxsw_sp_fib_node *fib_node)
4638{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004639 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004640 case MLXSW_SP_L3_PROTO_IPV4:
4641 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4642 break;
4643 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004644 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004645 break;
4646 }
4647}
4648
Ido Schimmel76610eb2017-03-10 08:53:41 +01004649static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4650 struct mlxsw_sp_vr *vr,
4651 enum mlxsw_sp_l3proto proto)
4652{
4653 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4654 struct mlxsw_sp_fib_node *fib_node, *tmp;
4655
4656 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4657 bool do_break = &tmp->list == &fib->node_list;
4658
4659 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4660 if (do_break)
4661 break;
4662 }
4663}
4664
Ido Schimmelac571de2016-11-14 11:26:32 +01004665static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004666{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004667 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004668
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004669 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004670 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004671
Ido Schimmel76610eb2017-03-10 08:53:41 +01004672 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004673 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004674 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004675
4676 /* If virtual router was only used for IPv4, then it's no
4677 * longer used.
4678 */
4679 if (!mlxsw_sp_vr_is_used(vr))
4680 continue;
4681 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004682 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004683}
4684
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004685static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004686{
4687 int err;
4688
Ido Schimmel9011b672017-05-16 19:38:25 +02004689 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004690 return;
4691 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 +01004692 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004693 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004694 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4695 if (err)
4696 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4697}
4698
Ido Schimmel30572242016-12-03 16:45:01 +01004699struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004700 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004701 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004702 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004703 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004704 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004705 struct fib_nh_notifier_info fnh_info;
4706 };
Ido Schimmel30572242016-12-03 16:45:01 +01004707 struct mlxsw_sp *mlxsw_sp;
4708 unsigned long event;
4709};
4710
Ido Schimmel66a57632017-08-03 13:28:26 +02004711static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004712{
Ido Schimmel30572242016-12-03 16:45:01 +01004713 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004714 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004715 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004716 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004717 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004718 int err;
4719
Ido Schimmel30572242016-12-03 16:45:01 +01004720 /* Protect internal structures from changes */
4721 rtnl_lock();
4722 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004723 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004724 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004725 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004726 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004727 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4728 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004729 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004730 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004731 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004732 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004733 break;
4734 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004735 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4736 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004737 break;
4738 case FIB_EVENT_RULE_ADD: /* fall through */
4739 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004740 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004741 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004742 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004743 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004744 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004745 case FIB_EVENT_NH_ADD: /* fall through */
4746 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004747 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4748 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004749 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4750 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004751 }
Ido Schimmel30572242016-12-03 16:45:01 +01004752 rtnl_unlock();
4753 kfree(fib_work);
4754}
4755
Ido Schimmel66a57632017-08-03 13:28:26 +02004756static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4757{
Ido Schimmel583419f2017-08-03 13:28:27 +02004758 struct mlxsw_sp_fib_event_work *fib_work =
4759 container_of(work, struct mlxsw_sp_fib_event_work, work);
4760 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4761 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004762 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004763 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004764
4765 rtnl_lock();
4766 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004767 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004768 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004769 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004770 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004771 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004772 if (err)
4773 mlxsw_sp_router_fib_abort(mlxsw_sp);
4774 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4775 break;
4776 case FIB_EVENT_ENTRY_DEL:
4777 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4778 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4779 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004780 case FIB_EVENT_RULE_ADD: /* fall through */
4781 case FIB_EVENT_RULE_DEL:
4782 rule = fib_work->fr_info.rule;
4783 if (!fib6_rule_default(rule) && !rule->l3mdev)
4784 mlxsw_sp_router_fib_abort(mlxsw_sp);
4785 fib_rule_put(rule);
4786 break;
4787 }
4788 rtnl_unlock();
4789 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004790}
4791
4792static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4793 struct fib_notifier_info *info)
4794{
4795 switch (fib_work->event) {
4796 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4797 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4798 case FIB_EVENT_ENTRY_ADD: /* fall through */
4799 case FIB_EVENT_ENTRY_DEL:
4800 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4801 /* Take referece on fib_info to prevent it from being
4802 * freed while work is queued. Release it afterwards.
4803 */
4804 fib_info_hold(fib_work->fen_info.fi);
4805 break;
4806 case FIB_EVENT_RULE_ADD: /* fall through */
4807 case FIB_EVENT_RULE_DEL:
4808 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4809 fib_rule_get(fib_work->fr_info.rule);
4810 break;
4811 case FIB_EVENT_NH_ADD: /* fall through */
4812 case FIB_EVENT_NH_DEL:
4813 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4814 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4815 break;
4816 }
4817}
4818
4819static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4820 struct fib_notifier_info *info)
4821{
Ido Schimmel583419f2017-08-03 13:28:27 +02004822 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004823 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004824 case FIB_EVENT_ENTRY_ADD: /* fall through */
4825 case FIB_EVENT_ENTRY_DEL:
4826 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4827 rt6_hold(fib_work->fen6_info.rt);
4828 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004829 case FIB_EVENT_RULE_ADD: /* fall through */
4830 case FIB_EVENT_RULE_DEL:
4831 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4832 fib_rule_get(fib_work->fr_info.rule);
4833 break;
4834 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004835}
4836
Ido Schimmel30572242016-12-03 16:45:01 +01004837/* Called with rcu_read_lock() */
4838static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4839 unsigned long event, void *ptr)
4840{
Ido Schimmel30572242016-12-03 16:45:01 +01004841 struct mlxsw_sp_fib_event_work *fib_work;
4842 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004843 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004844
Ido Schimmel65e65ec2017-08-03 13:28:31 +02004845 if (!net_eq(info->net, &init_net))
Ido Schimmel30572242016-12-03 16:45:01 +01004846 return NOTIFY_DONE;
4847
4848 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4849 if (WARN_ON(!fib_work))
4850 return NOTIFY_BAD;
4851
Ido Schimmel7e39d112017-05-16 19:38:28 +02004852 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4853 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004854 fib_work->event = event;
4855
Ido Schimmel66a57632017-08-03 13:28:26 +02004856 switch (info->family) {
4857 case AF_INET:
4858 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4859 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004860 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004861 case AF_INET6:
4862 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4863 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004864 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004865 }
4866
Ido Schimmela0e47612017-02-06 16:20:10 +01004867 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004868
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004869 return NOTIFY_DONE;
4870}
4871
Ido Schimmel4724ba562017-03-10 08:53:39 +01004872static struct mlxsw_sp_rif *
4873mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4874 const struct net_device *dev)
4875{
4876 int i;
4877
4878 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004879 if (mlxsw_sp->router->rifs[i] &&
4880 mlxsw_sp->router->rifs[i]->dev == dev)
4881 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004882
4883 return NULL;
4884}
4885
4886static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4887{
4888 char ritr_pl[MLXSW_REG_RITR_LEN];
4889 int err;
4890
4891 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4892 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4893 if (WARN_ON_ONCE(err))
4894 return err;
4895
4896 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4897 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4898}
4899
4900static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004901 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004902{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004903 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4904 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4905 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004906}
4907
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004908static bool
4909mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4910 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004911{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004912 struct inet6_dev *inet6_dev;
4913 bool addr_list_empty = true;
4914 struct in_device *idev;
4915
Ido Schimmel4724ba562017-03-10 08:53:39 +01004916 switch (event) {
4917 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004918 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004919 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004920 idev = __in_dev_get_rtnl(dev);
4921 if (idev && idev->ifa_list)
4922 addr_list_empty = false;
4923
4924 inet6_dev = __in6_dev_get(dev);
4925 if (addr_list_empty && inet6_dev &&
4926 !list_empty(&inet6_dev->addr_list))
4927 addr_list_empty = false;
4928
4929 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004930 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004931 return true;
4932 /* It is possible we already removed the RIF ourselves
4933 * if it was assigned to a netdev that is now a bridge
4934 * or LAG slave.
4935 */
4936 return false;
4937 }
4938
4939 return false;
4940}
4941
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004942static enum mlxsw_sp_rif_type
4943mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4944 const struct net_device *dev)
4945{
4946 enum mlxsw_sp_fid_type type;
4947
Petr Machata6ddb7422017-09-02 23:49:19 +02004948 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4949 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4950
4951 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004952 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4953 type = MLXSW_SP_FID_TYPE_8021Q;
4954 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4955 type = MLXSW_SP_FID_TYPE_8021Q;
4956 else if (netif_is_bridge_master(dev))
4957 type = MLXSW_SP_FID_TYPE_8021D;
4958 else
4959 type = MLXSW_SP_FID_TYPE_RFID;
4960
4961 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4962}
4963
Ido Schimmelde5ed992017-06-04 16:53:40 +02004964static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004965{
4966 int i;
4967
Ido Schimmelde5ed992017-06-04 16:53:40 +02004968 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4969 if (!mlxsw_sp->router->rifs[i]) {
4970 *p_rif_index = i;
4971 return 0;
4972 }
4973 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004974
Ido Schimmelde5ed992017-06-04 16:53:40 +02004975 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004976}
4977
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004978static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
4979 u16 vr_id,
4980 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004981{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004982 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004983
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004984 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004985 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004986 return NULL;
4987
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004988 INIT_LIST_HEAD(&rif->nexthop_list);
4989 INIT_LIST_HEAD(&rif->neigh_list);
4990 ether_addr_copy(rif->addr, l3_dev->dev_addr);
4991 rif->mtu = l3_dev->mtu;
4992 rif->vr_id = vr_id;
4993 rif->dev = l3_dev;
4994 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004995
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004996 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004997}
4998
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004999struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5000 u16 rif_index)
5001{
5002 return mlxsw_sp->router->rifs[rif_index];
5003}
5004
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005005u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5006{
5007 return rif->rif_index;
5008}
5009
Petr Machata92107cf2017-09-02 23:49:28 +02005010u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5011{
5012 return lb_rif->common.rif_index;
5013}
5014
5015u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5016{
5017 return lb_rif->ul_vr_id;
5018}
5019
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005020int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5021{
5022 return rif->dev->ifindex;
5023}
5024
Ido Schimmel4724ba562017-03-10 08:53:39 +01005025static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005026mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5027 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005028{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005029 u32 tb_id = l3mdev_fib_table(params->dev);
5030 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005031 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005032 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005033 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005034 struct mlxsw_sp_vr *vr;
5035 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005036 int err;
5037
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005038 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5039 ops = mlxsw_sp->router->rif_ops_arr[type];
5040
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005041 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5042 if (IS_ERR(vr))
5043 return ERR_CAST(vr);
5044
Ido Schimmelde5ed992017-06-04 16:53:40 +02005045 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5046 if (err)
5047 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005048
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005049 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005050 if (!rif) {
5051 err = -ENOMEM;
5052 goto err_rif_alloc;
5053 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005054 rif->mlxsw_sp = mlxsw_sp;
5055 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005056
Petr Machata010cadf2017-09-02 23:49:18 +02005057 if (ops->fid_get) {
5058 fid = ops->fid_get(rif);
5059 if (IS_ERR(fid)) {
5060 err = PTR_ERR(fid);
5061 goto err_fid_get;
5062 }
5063 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005064 }
5065
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005066 if (ops->setup)
5067 ops->setup(rif, params);
5068
5069 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005070 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005071 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005072
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005073 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005074 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel69132292017-03-10 08:53:42 +01005075 vr->rif_count++;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005076
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005077 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005078
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005079err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005080 if (fid)
5081 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005082err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005083 kfree(rif);
5084err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005085err_rif_index_alloc:
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005086 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005087 return ERR_PTR(err);
5088}
5089
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005090void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005091{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005092 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5093 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005094 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005095 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005096
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005097 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005098 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005099
Ido Schimmel69132292017-03-10 08:53:42 +01005100 vr->rif_count--;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005101 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005102 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005103 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005104 if (fid)
5105 /* Loopback RIFs are not associated with a FID. */
5106 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005107 kfree(rif);
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005108 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005109}
5110
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005111static void
5112mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5113 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5114{
5115 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5116
5117 params->vid = mlxsw_sp_port_vlan->vid;
5118 params->lag = mlxsw_sp_port->lagged;
5119 if (params->lag)
5120 params->lag_id = mlxsw_sp_port->lag_id;
5121 else
5122 params->system_port = mlxsw_sp_port->local_port;
5123}
5124
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005125static int
Ido Schimmela1107482017-05-26 08:37:39 +02005126mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005127 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005128{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005129 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005130 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005131 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005132 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005133 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005134 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005135
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005136 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005137 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005138 struct mlxsw_sp_rif_params params = {
5139 .dev = l3_dev,
5140 };
5141
5142 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5143 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005144 if (IS_ERR(rif))
5145 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005146 }
5147
Ido Schimmela1107482017-05-26 08:37:39 +02005148 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005149 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005150 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5151 if (err)
5152 goto err_fid_port_vid_map;
5153
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005154 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005155 if (err)
5156 goto err_port_vid_learning_set;
5157
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005158 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005159 BR_STATE_FORWARDING);
5160 if (err)
5161 goto err_port_vid_stp_set;
5162
Ido Schimmela1107482017-05-26 08:37:39 +02005163 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005164
Ido Schimmel4724ba562017-03-10 08:53:39 +01005165 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005166
5167err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005168 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005169err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005170 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5171err_fid_port_vid_map:
5172 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005173 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005174}
5175
Ido Schimmela1107482017-05-26 08:37:39 +02005176void
5177mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005178{
Ido Schimmelce95e152017-05-26 08:37:27 +02005179 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005180 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005181 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005182
Ido Schimmela1107482017-05-26 08:37:39 +02005183 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5184 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005185
Ido Schimmela1107482017-05-26 08:37:39 +02005186 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005187 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5188 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005189 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5190 /* If router port holds the last reference on the rFID, then the
5191 * associated Sub-port RIF will be destroyed.
5192 */
5193 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005194}
5195
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005196static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5197 struct net_device *port_dev,
5198 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005199{
5200 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005201 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005202
Ido Schimmelce95e152017-05-26 08:37:27 +02005203 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005204 if (WARN_ON(!mlxsw_sp_port_vlan))
5205 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005206
5207 switch (event) {
5208 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005209 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005210 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005211 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005212 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005213 break;
5214 }
5215
5216 return 0;
5217}
5218
5219static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5220 unsigned long event)
5221{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005222 if (netif_is_bridge_port(port_dev) ||
5223 netif_is_lag_port(port_dev) ||
5224 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005225 return 0;
5226
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005227 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005228}
5229
5230static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5231 struct net_device *lag_dev,
5232 unsigned long event, u16 vid)
5233{
5234 struct net_device *port_dev;
5235 struct list_head *iter;
5236 int err;
5237
5238 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5239 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005240 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5241 port_dev,
5242 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005243 if (err)
5244 return err;
5245 }
5246 }
5247
5248 return 0;
5249}
5250
5251static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5252 unsigned long event)
5253{
5254 if (netif_is_bridge_port(lag_dev))
5255 return 0;
5256
5257 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5258}
5259
Ido Schimmel4724ba562017-03-10 08:53:39 +01005260static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005261 unsigned long event)
5262{
5263 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005264 struct mlxsw_sp_rif_params params = {
5265 .dev = l3_dev,
5266 };
Ido Schimmela1107482017-05-26 08:37:39 +02005267 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005268
5269 switch (event) {
5270 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005271 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5272 if (IS_ERR(rif))
5273 return PTR_ERR(rif);
5274 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005275 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005276 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005277 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005278 break;
5279 }
5280
5281 return 0;
5282}
5283
5284static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5285 unsigned long event)
5286{
5287 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005288 u16 vid = vlan_dev_vlan_id(vlan_dev);
5289
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005290 if (netif_is_bridge_port(vlan_dev))
5291 return 0;
5292
Ido Schimmel4724ba562017-03-10 08:53:39 +01005293 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005294 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5295 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005296 else if (netif_is_lag_master(real_dev))
5297 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5298 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005299 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005300 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005301
5302 return 0;
5303}
5304
Ido Schimmelb1e45522017-04-30 19:47:14 +03005305static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5306 unsigned long event)
5307{
5308 if (mlxsw_sp_port_dev_check(dev))
5309 return mlxsw_sp_inetaddr_port_event(dev, event);
5310 else if (netif_is_lag_master(dev))
5311 return mlxsw_sp_inetaddr_lag_event(dev, event);
5312 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005313 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005314 else if (is_vlan_dev(dev))
5315 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5316 else
5317 return 0;
5318}
5319
Ido Schimmel4724ba562017-03-10 08:53:39 +01005320int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5321 unsigned long event, void *ptr)
5322{
5323 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5324 struct net_device *dev = ifa->ifa_dev->dev;
5325 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005326 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005327 int err = 0;
5328
5329 mlxsw_sp = mlxsw_sp_lower_get(dev);
5330 if (!mlxsw_sp)
5331 goto out;
5332
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005333 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005334 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005335 goto out;
5336
Ido Schimmelb1e45522017-04-30 19:47:14 +03005337 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005338out:
5339 return notifier_from_errno(err);
5340}
5341
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005342struct mlxsw_sp_inet6addr_event_work {
5343 struct work_struct work;
5344 struct net_device *dev;
5345 unsigned long event;
5346};
5347
5348static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5349{
5350 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5351 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5352 struct net_device *dev = inet6addr_work->dev;
5353 unsigned long event = inet6addr_work->event;
5354 struct mlxsw_sp *mlxsw_sp;
5355 struct mlxsw_sp_rif *rif;
5356
5357 rtnl_lock();
5358 mlxsw_sp = mlxsw_sp_lower_get(dev);
5359 if (!mlxsw_sp)
5360 goto out;
5361
5362 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5363 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5364 goto out;
5365
5366 __mlxsw_sp_inetaddr_event(dev, event);
5367out:
5368 rtnl_unlock();
5369 dev_put(dev);
5370 kfree(inet6addr_work);
5371}
5372
5373/* Called with rcu_read_lock() */
5374int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5375 unsigned long event, void *ptr)
5376{
5377 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5378 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5379 struct net_device *dev = if6->idev->dev;
5380
5381 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5382 return NOTIFY_DONE;
5383
5384 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5385 if (!inet6addr_work)
5386 return NOTIFY_BAD;
5387
5388 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5389 inet6addr_work->dev = dev;
5390 inet6addr_work->event = event;
5391 dev_hold(dev);
5392 mlxsw_core_schedule_work(&inet6addr_work->work);
5393
5394 return NOTIFY_DONE;
5395}
5396
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005397static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005398 const char *mac, int mtu)
5399{
5400 char ritr_pl[MLXSW_REG_RITR_LEN];
5401 int err;
5402
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005403 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005404 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5405 if (err)
5406 return err;
5407
5408 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5409 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5410 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5411 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5412}
5413
5414int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5415{
5416 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005417 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005418 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005419 int err;
5420
5421 mlxsw_sp = mlxsw_sp_lower_get(dev);
5422 if (!mlxsw_sp)
5423 return 0;
5424
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005425 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5426 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005427 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005428 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005429
Ido Schimmela1107482017-05-26 08:37:39 +02005430 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005431 if (err)
5432 return err;
5433
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005434 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5435 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005436 if (err)
5437 goto err_rif_edit;
5438
Ido Schimmela1107482017-05-26 08:37:39 +02005439 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005440 if (err)
5441 goto err_rif_fdb_op;
5442
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005443 ether_addr_copy(rif->addr, dev->dev_addr);
5444 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005445
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005446 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005447
5448 return 0;
5449
5450err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005451 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005452err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005453 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005454 return err;
5455}
5456
Ido Schimmelb1e45522017-04-30 19:47:14 +03005457static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5458 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005459{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005460 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005461
Ido Schimmelb1e45522017-04-30 19:47:14 +03005462 /* If netdev is already associated with a RIF, then we need to
5463 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005464 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005465 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5466 if (rif)
5467 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005468
Ido Schimmelb1e45522017-04-30 19:47:14 +03005469 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005470}
5471
Ido Schimmelb1e45522017-04-30 19:47:14 +03005472static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5473 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005474{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005475 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005476
Ido Schimmelb1e45522017-04-30 19:47:14 +03005477 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5478 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005479 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005480 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005481}
5482
Ido Schimmelb1e45522017-04-30 19:47:14 +03005483int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5484 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005485{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005486 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5487 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005488
Ido Schimmelb1e45522017-04-30 19:47:14 +03005489 if (!mlxsw_sp)
5490 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005491
Ido Schimmelb1e45522017-04-30 19:47:14 +03005492 switch (event) {
5493 case NETDEV_PRECHANGEUPPER:
5494 return 0;
5495 case NETDEV_CHANGEUPPER:
5496 if (info->linking)
5497 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5498 else
5499 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5500 break;
5501 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005502
Ido Schimmelb1e45522017-04-30 19:47:14 +03005503 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005504}
5505
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005506static struct mlxsw_sp_rif_subport *
5507mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005508{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005509 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005510}
5511
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005512static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5513 const struct mlxsw_sp_rif_params *params)
5514{
5515 struct mlxsw_sp_rif_subport *rif_subport;
5516
5517 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5518 rif_subport->vid = params->vid;
5519 rif_subport->lag = params->lag;
5520 if (params->lag)
5521 rif_subport->lag_id = params->lag_id;
5522 else
5523 rif_subport->system_port = params->system_port;
5524}
5525
5526static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5527{
5528 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5529 struct mlxsw_sp_rif_subport *rif_subport;
5530 char ritr_pl[MLXSW_REG_RITR_LEN];
5531
5532 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5533 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005534 rif->rif_index, rif->vr_id, rif->dev->mtu);
5535 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005536 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5537 rif_subport->lag ? rif_subport->lag_id :
5538 rif_subport->system_port,
5539 rif_subport->vid);
5540
5541 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5542}
5543
5544static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5545{
Petr Machata010cadf2017-09-02 23:49:18 +02005546 int err;
5547
5548 err = mlxsw_sp_rif_subport_op(rif, true);
5549 if (err)
5550 return err;
5551
5552 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5553 mlxsw_sp_fid_index(rif->fid), true);
5554 if (err)
5555 goto err_rif_fdb_op;
5556
5557 mlxsw_sp_fid_rif_set(rif->fid, rif);
5558 return 0;
5559
5560err_rif_fdb_op:
5561 mlxsw_sp_rif_subport_op(rif, false);
5562 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005563}
5564
5565static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5566{
Petr Machata010cadf2017-09-02 23:49:18 +02005567 struct mlxsw_sp_fid *fid = rif->fid;
5568
5569 mlxsw_sp_fid_rif_set(fid, NULL);
5570 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5571 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005572 mlxsw_sp_rif_subport_op(rif, false);
5573}
5574
5575static struct mlxsw_sp_fid *
5576mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5577{
5578 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5579}
5580
5581static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5582 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5583 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5584 .setup = mlxsw_sp_rif_subport_setup,
5585 .configure = mlxsw_sp_rif_subport_configure,
5586 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5587 .fid_get = mlxsw_sp_rif_subport_fid_get,
5588};
5589
5590static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5591 enum mlxsw_reg_ritr_if_type type,
5592 u16 vid_fid, bool enable)
5593{
5594 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5595 char ritr_pl[MLXSW_REG_RITR_LEN];
5596
5597 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005598 rif->dev->mtu);
5599 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005600 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5601
5602 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5603}
5604
5605static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5606{
5607 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5608}
5609
5610static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5611{
5612 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5613 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5614 int err;
5615
5616 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5617 if (err)
5618 return err;
5619
Ido Schimmel0d284812017-07-18 10:10:12 +02005620 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5621 mlxsw_sp_router_port(mlxsw_sp), true);
5622 if (err)
5623 goto err_fid_mc_flood_set;
5624
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005625 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5626 mlxsw_sp_router_port(mlxsw_sp), true);
5627 if (err)
5628 goto err_fid_bc_flood_set;
5629
Petr Machata010cadf2017-09-02 23:49:18 +02005630 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5631 mlxsw_sp_fid_index(rif->fid), true);
5632 if (err)
5633 goto err_rif_fdb_op;
5634
5635 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005636 return 0;
5637
Petr Machata010cadf2017-09-02 23:49:18 +02005638err_rif_fdb_op:
5639 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5640 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005641err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005642 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5643 mlxsw_sp_router_port(mlxsw_sp), false);
5644err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005645 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5646 return err;
5647}
5648
5649static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5650{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005651 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005652 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5653 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005654
Petr Machata010cadf2017-09-02 23:49:18 +02005655 mlxsw_sp_fid_rif_set(fid, NULL);
5656 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5657 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005658 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5659 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005660 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5661 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005662 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5663}
5664
5665static struct mlxsw_sp_fid *
5666mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5667{
5668 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5669
5670 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5671}
5672
5673static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5674 .type = MLXSW_SP_RIF_TYPE_VLAN,
5675 .rif_size = sizeof(struct mlxsw_sp_rif),
5676 .configure = mlxsw_sp_rif_vlan_configure,
5677 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5678 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5679};
5680
5681static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5682{
5683 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5684 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5685 int err;
5686
5687 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5688 true);
5689 if (err)
5690 return err;
5691
Ido Schimmel0d284812017-07-18 10:10:12 +02005692 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5693 mlxsw_sp_router_port(mlxsw_sp), true);
5694 if (err)
5695 goto err_fid_mc_flood_set;
5696
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005697 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5698 mlxsw_sp_router_port(mlxsw_sp), true);
5699 if (err)
5700 goto err_fid_bc_flood_set;
5701
Petr Machata010cadf2017-09-02 23:49:18 +02005702 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5703 mlxsw_sp_fid_index(rif->fid), true);
5704 if (err)
5705 goto err_rif_fdb_op;
5706
5707 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005708 return 0;
5709
Petr Machata010cadf2017-09-02 23:49:18 +02005710err_rif_fdb_op:
5711 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5712 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005713err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005714 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5715 mlxsw_sp_router_port(mlxsw_sp), false);
5716err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005717 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5718 return err;
5719}
5720
5721static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5722{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005723 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005724 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5725 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005726
Petr Machata010cadf2017-09-02 23:49:18 +02005727 mlxsw_sp_fid_rif_set(fid, NULL);
5728 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5729 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005730 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5731 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005732 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5733 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005734 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5735}
5736
5737static struct mlxsw_sp_fid *
5738mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5739{
5740 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5741}
5742
5743static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5744 .type = MLXSW_SP_RIF_TYPE_FID,
5745 .rif_size = sizeof(struct mlxsw_sp_rif),
5746 .configure = mlxsw_sp_rif_fid_configure,
5747 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5748 .fid_get = mlxsw_sp_rif_fid_fid_get,
5749};
5750
Petr Machata6ddb7422017-09-02 23:49:19 +02005751static struct mlxsw_sp_rif_ipip_lb *
5752mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5753{
5754 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5755}
5756
5757static void
5758mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5759 const struct mlxsw_sp_rif_params *params)
5760{
5761 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5762 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5763
5764 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5765 common);
5766 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5767 rif_lb->lb_config = params_lb->lb_config;
5768}
5769
5770static int
5771mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5772 struct mlxsw_sp_vr *ul_vr, bool enable)
5773{
5774 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5775 struct mlxsw_sp_rif *rif = &lb_rif->common;
5776 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5777 char ritr_pl[MLXSW_REG_RITR_LEN];
5778 u32 saddr4;
5779
5780 switch (lb_cf.ul_protocol) {
5781 case MLXSW_SP_L3_PROTO_IPV4:
5782 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5783 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5784 rif->rif_index, rif->vr_id, rif->dev->mtu);
5785 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5786 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5787 ul_vr->id, saddr4, lb_cf.okey);
5788 break;
5789
5790 case MLXSW_SP_L3_PROTO_IPV6:
5791 return -EAFNOSUPPORT;
5792 }
5793
5794 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5795}
5796
5797static int
5798mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5799{
5800 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5801 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5802 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5803 struct mlxsw_sp_vr *ul_vr;
5804 int err;
5805
5806 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5807 if (IS_ERR(ul_vr))
5808 return PTR_ERR(ul_vr);
5809
5810 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5811 if (err)
5812 goto err_loopback_op;
5813
5814 lb_rif->ul_vr_id = ul_vr->id;
5815 ++ul_vr->rif_count;
5816 return 0;
5817
5818err_loopback_op:
5819 mlxsw_sp_vr_put(ul_vr);
5820 return err;
5821}
5822
5823static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5824{
5825 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5826 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5827 struct mlxsw_sp_vr *ul_vr;
5828
5829 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5830 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5831
5832 --ul_vr->rif_count;
5833 mlxsw_sp_vr_put(ul_vr);
5834}
5835
5836static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5837 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5838 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5839 .setup = mlxsw_sp_rif_ipip_lb_setup,
5840 .configure = mlxsw_sp_rif_ipip_lb_configure,
5841 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5842};
5843
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005844static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5845 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5846 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5847 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005848 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005849};
5850
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005851static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5852{
5853 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5854
5855 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5856 sizeof(struct mlxsw_sp_rif *),
5857 GFP_KERNEL);
5858 if (!mlxsw_sp->router->rifs)
5859 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005860
5861 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5862
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005863 return 0;
5864}
5865
5866static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5867{
5868 int i;
5869
5870 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5871 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5872
5873 kfree(mlxsw_sp->router->rifs);
5874}
5875
Petr Machata38ebc0f2017-09-02 23:49:17 +02005876static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5877{
5878 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02005879 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005880 return 0;
5881}
5882
5883static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5884{
Petr Machata1012b9a2017-09-02 23:49:23 +02005885 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02005886}
5887
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005888static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5889{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005890 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005891
5892 /* Flush pending FIB notifications and then flush the device's
5893 * table before requesting another dump. The FIB notification
5894 * block is unregistered, so no need to take RTNL.
5895 */
5896 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005897 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5898 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005899}
5900
Ido Schimmel4724ba562017-03-10 08:53:39 +01005901static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5902{
5903 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5904 u64 max_rifs;
5905 int err;
5906
5907 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5908 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005909 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005910
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005911 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005912 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5913 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5914 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005915 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005916 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005917}
5918
5919static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5920{
5921 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005922
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005923 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005924 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005925}
5926
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005927int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5928{
Ido Schimmel9011b672017-05-16 19:38:25 +02005929 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005930 int err;
5931
Ido Schimmel9011b672017-05-16 19:38:25 +02005932 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5933 if (!router)
5934 return -ENOMEM;
5935 mlxsw_sp->router = router;
5936 router->mlxsw_sp = mlxsw_sp;
5937
5938 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005939 err = __mlxsw_sp_router_init(mlxsw_sp);
5940 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005941 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005942
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005943 err = mlxsw_sp_rifs_init(mlxsw_sp);
5944 if (err)
5945 goto err_rifs_init;
5946
Petr Machata38ebc0f2017-09-02 23:49:17 +02005947 err = mlxsw_sp_ipips_init(mlxsw_sp);
5948 if (err)
5949 goto err_ipips_init;
5950
Ido Schimmel9011b672017-05-16 19:38:25 +02005951 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005952 &mlxsw_sp_nexthop_ht_params);
5953 if (err)
5954 goto err_nexthop_ht_init;
5955
Ido Schimmel9011b672017-05-16 19:38:25 +02005956 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005957 &mlxsw_sp_nexthop_group_ht_params);
5958 if (err)
5959 goto err_nexthop_group_ht_init;
5960
Ido Schimmel8494ab02017-03-24 08:02:47 +01005961 err = mlxsw_sp_lpm_init(mlxsw_sp);
5962 if (err)
5963 goto err_lpm_init;
5964
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005965 err = mlxsw_sp_vrs_init(mlxsw_sp);
5966 if (err)
5967 goto err_vrs_init;
5968
Ido Schimmel8c9583a2016-10-27 15:12:57 +02005969 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005970 if (err)
5971 goto err_neigh_init;
5972
Ido Schimmel7e39d112017-05-16 19:38:28 +02005973 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
5974 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005975 mlxsw_sp_router_fib_dump_flush);
5976 if (err)
5977 goto err_register_fib_notifier;
5978
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005979 return 0;
5980
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005981err_register_fib_notifier:
5982 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005983err_neigh_init:
5984 mlxsw_sp_vrs_fini(mlxsw_sp);
5985err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01005986 mlxsw_sp_lpm_fini(mlxsw_sp);
5987err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005988 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005989err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02005990 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005991err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02005992 mlxsw_sp_ipips_fini(mlxsw_sp);
5993err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005994 mlxsw_sp_rifs_fini(mlxsw_sp);
5995err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005996 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02005997err_router_init:
5998 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005999 return err;
6000}
6001
6002void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6003{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006004 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006005 mlxsw_sp_neigh_fini(mlxsw_sp);
6006 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006007 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006008 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6009 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006010 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006011 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006012 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006013 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006014}