blob: 5189022a1c8c335c42901b5d288bbdf830512f46 [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Petr Machata6ddb7422017-09-02 23:49:19 +02003 * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
Ido Schimmel464dce12016-07-02 11:00:15 +02004 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
Yotam Gigic723c7352016-07-05 11:27:43 +02006 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
Petr Machata6ddb7422017-09-02 23:49:19 +02007 * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
Ido Schimmel464dce12016-07-02 11:00:15 +02008 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * Alternatively, this software may be distributed under the terms of the
22 * GNU General Public License ("GPL") version 2 as published by the Free
23 * Software Foundation.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/kernel.h>
39#include <linux/types.h>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020040#include <linux/rhashtable.h>
41#include <linux/bitops.h>
42#include <linux/in6.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020043#include <linux/notifier.h>
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +010044#include <linux/inetdevice.h>
Ido Schimmel9db032b2017-03-16 09:08:17 +010045#include <linux/netdevice.h>
Ido Schimmel03ea01e2017-05-23 21:56:30 +020046#include <linux/if_bridge.h>
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +020047#include <linux/socket.h>
Ido Schimmel428b8512017-08-03 13:28:28 +020048#include <linux/route.h>
Yotam Gigic723c7352016-07-05 11:27:43 +020049#include <net/netevent.h>
Jiri Pirko6cf3c972016-07-05 11:27:39 +020050#include <net/neighbour.h>
51#include <net/arp.h>
Jiri Pirkob45f64d2016-09-26 12:52:31 +020052#include <net/ip_fib.h>
Ido Schimmel583419f2017-08-03 13:28:27 +020053#include <net/ip6_fib.h>
Ido Schimmel5d7bfd12017-03-16 09:08:14 +010054#include <net/fib_rules.h>
Petr Machata6ddb7422017-09-02 23:49:19 +020055#include <net/ip_tunnels.h>
Ido Schimmel57837882017-03-16 09:08:16 +010056#include <net/l3mdev.h>
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +020057#include <net/addrconf.h>
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +020058#include <net/ndisc.h>
59#include <net/ipv6.h>
Ido Schimmel04b1d4e2017-08-03 13:28:11 +020060#include <net/fib_notifier.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020061
62#include "spectrum.h"
63#include "core.h"
64#include "reg.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020065#include "spectrum_cnt.h"
66#include "spectrum_dpipe.h"
Petr Machata38ebc0f2017-09-02 23:49:17 +020067#include "spectrum_ipip.h"
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +020068#include "spectrum_router.h"
Ido Schimmel464dce12016-07-02 11:00:15 +020069
Ido Schimmel9011b672017-05-16 19:38:25 +020070struct mlxsw_sp_vr;
71struct mlxsw_sp_lpm_tree;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020072struct mlxsw_sp_rif_ops;
Ido Schimmel9011b672017-05-16 19:38:25 +020073
74struct mlxsw_sp_router {
75 struct mlxsw_sp *mlxsw_sp;
Ido Schimmel5f9efff2017-05-16 19:38:27 +020076 struct mlxsw_sp_rif **rifs;
Ido Schimmel9011b672017-05-16 19:38:25 +020077 struct mlxsw_sp_vr *vrs;
78 struct rhashtable neigh_ht;
79 struct rhashtable nexthop_group_ht;
80 struct rhashtable nexthop_ht;
81 struct {
82 struct mlxsw_sp_lpm_tree *trees;
83 unsigned int tree_count;
84 } lpm;
85 struct {
86 struct delayed_work dw;
87 unsigned long interval; /* ms */
88 } neighs_update;
89 struct delayed_work nexthop_probe_dw;
90#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
91 struct list_head nexthop_neighs_list;
Petr Machata1012b9a2017-09-02 23:49:23 +020092 struct list_head ipip_list;
Ido Schimmel9011b672017-05-16 19:38:25 +020093 bool aborted;
Ido Schimmel7e39d112017-05-16 19:38:28 +020094 struct notifier_block fib_nb;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +020095 const struct mlxsw_sp_rif_ops **rif_ops_arr;
Petr Machata38ebc0f2017-09-02 23:49:17 +020096 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
Ido Schimmel9011b672017-05-16 19:38:25 +020097};
98
Ido Schimmel4724ba562017-03-10 08:53:39 +010099struct mlxsw_sp_rif {
100 struct list_head nexthop_list;
101 struct list_head neigh_list;
102 struct net_device *dev;
Ido Schimmela1107482017-05-26 08:37:39 +0200103 struct mlxsw_sp_fid *fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100104 unsigned char addr[ETH_ALEN];
105 int mtu;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100106 u16 rif_index;
Ido Schimmel69132292017-03-10 08:53:42 +0100107 u16 vr_id;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200108 const struct mlxsw_sp_rif_ops *ops;
109 struct mlxsw_sp *mlxsw_sp;
110
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200111 unsigned int counter_ingress;
112 bool counter_ingress_valid;
113 unsigned int counter_egress;
114 bool counter_egress_valid;
Ido Schimmel4724ba562017-03-10 08:53:39 +0100115};
116
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200117struct mlxsw_sp_rif_params {
118 struct net_device *dev;
119 union {
120 u16 system_port;
121 u16 lag_id;
122 };
123 u16 vid;
124 bool lag;
125};
126
Ido Schimmel4d93cee2017-05-26 08:37:34 +0200127struct mlxsw_sp_rif_subport {
128 struct mlxsw_sp_rif common;
129 union {
130 u16 system_port;
131 u16 lag_id;
132 };
133 u16 vid;
134 bool lag;
135};
136
Petr Machata6ddb7422017-09-02 23:49:19 +0200137struct mlxsw_sp_rif_ipip_lb {
138 struct mlxsw_sp_rif common;
139 struct mlxsw_sp_rif_ipip_lb_config lb_config;
140 u16 ul_vr_id; /* Reserved for Spectrum-2. */
141};
142
143struct mlxsw_sp_rif_params_ipip_lb {
144 struct mlxsw_sp_rif_params common;
145 struct mlxsw_sp_rif_ipip_lb_config lb_config;
146};
147
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200148struct mlxsw_sp_rif_ops {
149 enum mlxsw_sp_rif_type type;
150 size_t rif_size;
151
152 void (*setup)(struct mlxsw_sp_rif *rif,
153 const struct mlxsw_sp_rif_params *params);
154 int (*configure)(struct mlxsw_sp_rif *rif);
155 void (*deconfigure)(struct mlxsw_sp_rif *rif);
156 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
157};
158
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200159static unsigned int *
160mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
161 enum mlxsw_sp_rif_counter_dir dir)
162{
163 switch (dir) {
164 case MLXSW_SP_RIF_COUNTER_EGRESS:
165 return &rif->counter_egress;
166 case MLXSW_SP_RIF_COUNTER_INGRESS:
167 return &rif->counter_ingress;
168 }
169 return NULL;
170}
171
172static bool
173mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
174 enum mlxsw_sp_rif_counter_dir dir)
175{
176 switch (dir) {
177 case MLXSW_SP_RIF_COUNTER_EGRESS:
178 return rif->counter_egress_valid;
179 case MLXSW_SP_RIF_COUNTER_INGRESS:
180 return rif->counter_ingress_valid;
181 }
182 return false;
183}
184
185static void
186mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
187 enum mlxsw_sp_rif_counter_dir dir,
188 bool valid)
189{
190 switch (dir) {
191 case MLXSW_SP_RIF_COUNTER_EGRESS:
192 rif->counter_egress_valid = valid;
193 break;
194 case MLXSW_SP_RIF_COUNTER_INGRESS:
195 rif->counter_ingress_valid = valid;
196 break;
197 }
198}
199
200static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
201 unsigned int counter_index, bool enable,
202 enum mlxsw_sp_rif_counter_dir dir)
203{
204 char ritr_pl[MLXSW_REG_RITR_LEN];
205 bool is_egress = false;
206 int err;
207
208 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
209 is_egress = true;
210 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
211 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
212 if (err)
213 return err;
214
215 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
216 is_egress);
217 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
218}
219
220int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
221 struct mlxsw_sp_rif *rif,
222 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
223{
224 char ricnt_pl[MLXSW_REG_RICNT_LEN];
225 unsigned int *p_counter_index;
226 bool valid;
227 int err;
228
229 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
230 if (!valid)
231 return -EINVAL;
232
233 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
234 if (!p_counter_index)
235 return -EINVAL;
236 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
237 MLXSW_REG_RICNT_OPCODE_NOP);
238 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
239 if (err)
240 return err;
241 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
242 return 0;
243}
244
245static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
246 unsigned int counter_index)
247{
248 char ricnt_pl[MLXSW_REG_RICNT_LEN];
249
250 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
251 MLXSW_REG_RICNT_OPCODE_CLEAR);
252 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
253}
254
255int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
256 struct mlxsw_sp_rif *rif,
257 enum mlxsw_sp_rif_counter_dir dir)
258{
259 unsigned int *p_counter_index;
260 int err;
261
262 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
263 if (!p_counter_index)
264 return -EINVAL;
265 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
266 p_counter_index);
267 if (err)
268 return err;
269
270 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
271 if (err)
272 goto err_counter_clear;
273
274 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
275 *p_counter_index, true, dir);
276 if (err)
277 goto err_counter_edit;
278 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
279 return 0;
280
281err_counter_edit:
282err_counter_clear:
283 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
284 *p_counter_index);
285 return err;
286}
287
288void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
289 struct mlxsw_sp_rif *rif,
290 enum mlxsw_sp_rif_counter_dir dir)
291{
292 unsigned int *p_counter_index;
293
Arkadi Sharshevsky6b1206b2017-05-18 09:18:53 +0200294 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
295 return;
296
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +0200297 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
298 if (WARN_ON(!p_counter_index))
299 return;
300 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
301 *p_counter_index, false, dir);
302 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
303 *p_counter_index);
304 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
305}
306
Ido Schimmele4f3c1c2017-05-26 08:37:40 +0200307static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
308{
309 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
310 struct devlink *devlink;
311
312 devlink = priv_to_devlink(mlxsw_sp->core);
313 if (!devlink_dpipe_table_counter_enabled(devlink,
314 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
315 return;
316 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
317}
318
319static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
320{
321 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
322
323 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
324}
325
Ido Schimmel4724ba562017-03-10 08:53:39 +0100326static struct mlxsw_sp_rif *
327mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
328 const struct net_device *dev);
329
Ido Schimmel7dcc18a2017-07-18 10:10:30 +0200330#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
Ido Schimmel9011b672017-05-16 19:38:25 +0200331
332struct mlxsw_sp_prefix_usage {
333 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
334};
335
Jiri Pirko53342022016-07-04 08:23:08 +0200336#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
337 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
338
339static bool
340mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
341 struct mlxsw_sp_prefix_usage *prefix_usage2)
342{
343 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
344}
345
Jiri Pirko6b75c482016-07-04 08:23:09 +0200346static bool
347mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
348{
349 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
350
351 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
352}
353
354static void
355mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
356 struct mlxsw_sp_prefix_usage *prefix_usage2)
357{
358 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
359}
360
361static void
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200362mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
363 unsigned char prefix_len)
364{
365 set_bit(prefix_len, prefix_usage->b);
366}
367
368static void
369mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
370 unsigned char prefix_len)
371{
372 clear_bit(prefix_len, prefix_usage->b);
373}
374
375struct mlxsw_sp_fib_key {
376 unsigned char addr[sizeof(struct in6_addr)];
377 unsigned char prefix_len;
378};
379
Jiri Pirko61c503f2016-07-04 08:23:11 +0200380enum mlxsw_sp_fib_entry_type {
381 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
382 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
383 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
Petr Machata4607f6d2017-09-02 23:49:25 +0200384
385 /* This is a special case of local delivery, where a packet should be
386 * decapsulated on reception. Note that there is no corresponding ENCAP,
387 * because that's a type of next hop, not of FIB entry. (There can be
388 * several next hops in a REMOTE entry, and some of them may be
389 * encapsulating entries.)
390 */
391 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
Jiri Pirko61c503f2016-07-04 08:23:11 +0200392};
393
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200394struct mlxsw_sp_nexthop_group;
Ido Schimmel9011b672017-05-16 19:38:25 +0200395struct mlxsw_sp_fib;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200396
Ido Schimmel9aecce12017-02-09 10:28:42 +0100397struct mlxsw_sp_fib_node {
398 struct list_head entry_list;
Jiri Pirkob45f64d2016-09-26 12:52:31 +0200399 struct list_head list;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100400 struct rhash_head ht_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100401 struct mlxsw_sp_fib *fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100402 struct mlxsw_sp_fib_key key;
403};
404
Petr Machata4607f6d2017-09-02 23:49:25 +0200405struct mlxsw_sp_fib_entry_decap {
406 struct mlxsw_sp_ipip_entry *ipip_entry;
407 u32 tunnel_index;
408};
409
Ido Schimmel9aecce12017-02-09 10:28:42 +0100410struct mlxsw_sp_fib_entry {
411 struct list_head list;
412 struct mlxsw_sp_fib_node *fib_node;
413 enum mlxsw_sp_fib_entry_type type;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +0200414 struct list_head nexthop_group_node;
415 struct mlxsw_sp_nexthop_group *nh_group;
Petr Machata4607f6d2017-09-02 23:49:25 +0200416 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200417};
418
Ido Schimmel4f1c7f12017-07-18 10:10:26 +0200419struct mlxsw_sp_fib4_entry {
420 struct mlxsw_sp_fib_entry common;
421 u32 tb_id;
422 u32 prio;
423 u8 tos;
424 u8 type;
425};
426
Ido Schimmel428b8512017-08-03 13:28:28 +0200427struct mlxsw_sp_fib6_entry {
428 struct mlxsw_sp_fib_entry common;
429 struct list_head rt6_list;
430 unsigned int nrt6;
431};
432
433struct mlxsw_sp_rt6 {
434 struct list_head list;
435 struct rt6_info *rt;
436};
437
Ido Schimmel9011b672017-05-16 19:38:25 +0200438struct mlxsw_sp_lpm_tree {
439 u8 id; /* tree ID */
440 unsigned int ref_count;
441 enum mlxsw_sp_l3proto proto;
442 struct mlxsw_sp_prefix_usage prefix_usage;
443};
444
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200445struct mlxsw_sp_fib {
446 struct rhashtable ht;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100447 struct list_head node_list;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100448 struct mlxsw_sp_vr *vr;
449 struct mlxsw_sp_lpm_tree *lpm_tree;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200450 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
451 struct mlxsw_sp_prefix_usage prefix_usage;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100452 enum mlxsw_sp_l3proto proto;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200453};
454
Ido Schimmel9011b672017-05-16 19:38:25 +0200455struct mlxsw_sp_vr {
456 u16 id; /* virtual router ID */
457 u32 tb_id; /* kernel fib table id */
458 unsigned int rif_count;
459 struct mlxsw_sp_fib *fib4;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200460 struct mlxsw_sp_fib *fib6;
Ido Schimmel9011b672017-05-16 19:38:25 +0200461};
462
Ido Schimmel9aecce12017-02-09 10:28:42 +0100463static const struct rhashtable_params mlxsw_sp_fib_ht_params;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200464
Ido Schimmel76610eb2017-03-10 08:53:41 +0100465static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
466 enum mlxsw_sp_l3proto proto)
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200467{
468 struct mlxsw_sp_fib *fib;
469 int err;
470
471 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
472 if (!fib)
473 return ERR_PTR(-ENOMEM);
474 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
475 if (err)
476 goto err_rhashtable_init;
Ido Schimmel9aecce12017-02-09 10:28:42 +0100477 INIT_LIST_HEAD(&fib->node_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100478 fib->proto = proto;
479 fib->vr = vr;
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200480 return fib;
481
482err_rhashtable_init:
483 kfree(fib);
484 return ERR_PTR(err);
485}
486
487static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
488{
Ido Schimmel9aecce12017-02-09 10:28:42 +0100489 WARN_ON(!list_empty(&fib->node_list));
Ido Schimmel76610eb2017-03-10 08:53:41 +0100490 WARN_ON(fib->lpm_tree);
Jiri Pirko5e9c16c2016-07-04 08:23:04 +0200491 rhashtable_destroy(&fib->ht);
492 kfree(fib);
493}
494
Jiri Pirko53342022016-07-04 08:23:08 +0200495static struct mlxsw_sp_lpm_tree *
Ido Schimmel382dbb42017-03-10 08:53:40 +0100496mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200497{
498 static struct mlxsw_sp_lpm_tree *lpm_tree;
499 int i;
500
Ido Schimmel9011b672017-05-16 19:38:25 +0200501 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
502 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Ido Schimmel382dbb42017-03-10 08:53:40 +0100503 if (lpm_tree->ref_count == 0)
504 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200505 }
506 return NULL;
507}
508
509static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
510 struct mlxsw_sp_lpm_tree *lpm_tree)
511{
512 char ralta_pl[MLXSW_REG_RALTA_LEN];
513
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200514 mlxsw_reg_ralta_pack(ralta_pl, true,
515 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
516 lpm_tree->id);
Jiri Pirko53342022016-07-04 08:23:08 +0200517 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
518}
519
Ido Schimmelcc702672017-08-14 10:54:03 +0200520static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
521 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200522{
523 char ralta_pl[MLXSW_REG_RALTA_LEN];
524
Ido Schimmel1a9234e662016-09-19 08:29:26 +0200525 mlxsw_reg_ralta_pack(ralta_pl, false,
526 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
527 lpm_tree->id);
Ido Schimmelcc702672017-08-14 10:54:03 +0200528 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
Jiri Pirko53342022016-07-04 08:23:08 +0200529}
530
531static int
532mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
533 struct mlxsw_sp_prefix_usage *prefix_usage,
534 struct mlxsw_sp_lpm_tree *lpm_tree)
535{
536 char ralst_pl[MLXSW_REG_RALST_LEN];
537 u8 root_bin = 0;
538 u8 prefix;
539 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
540
541 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
542 root_bin = prefix;
543
544 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
545 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
546 if (prefix == 0)
547 continue;
548 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
549 MLXSW_REG_RALST_BIN_NO_CHILD);
550 last_prefix = prefix;
551 }
552 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
553}
554
555static struct mlxsw_sp_lpm_tree *
556mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
557 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100558 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200559{
560 struct mlxsw_sp_lpm_tree *lpm_tree;
561 int err;
562
Ido Schimmel382dbb42017-03-10 08:53:40 +0100563 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
Jiri Pirko53342022016-07-04 08:23:08 +0200564 if (!lpm_tree)
565 return ERR_PTR(-EBUSY);
566 lpm_tree->proto = proto;
567 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
568 if (err)
569 return ERR_PTR(err);
570
571 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
572 lpm_tree);
573 if (err)
574 goto err_left_struct_set;
Jiri Pirko2083d362016-10-25 11:25:56 +0200575 memcpy(&lpm_tree->prefix_usage, prefix_usage,
576 sizeof(lpm_tree->prefix_usage));
Jiri Pirko53342022016-07-04 08:23:08 +0200577 return lpm_tree;
578
579err_left_struct_set:
580 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
581 return ERR_PTR(err);
582}
583
Ido Schimmelcc702672017-08-14 10:54:03 +0200584static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
585 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200586{
Ido Schimmelcc702672017-08-14 10:54:03 +0200587 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200588}
589
590static struct mlxsw_sp_lpm_tree *
591mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
592 struct mlxsw_sp_prefix_usage *prefix_usage,
Ido Schimmel382dbb42017-03-10 08:53:40 +0100593 enum mlxsw_sp_l3proto proto)
Jiri Pirko53342022016-07-04 08:23:08 +0200594{
595 struct mlxsw_sp_lpm_tree *lpm_tree;
596 int i;
597
Ido Schimmel9011b672017-05-16 19:38:25 +0200598 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
599 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko8b99bec2016-10-25 11:25:57 +0200600 if (lpm_tree->ref_count != 0 &&
601 lpm_tree->proto == proto &&
Jiri Pirko53342022016-07-04 08:23:08 +0200602 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
603 prefix_usage))
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200604 return lpm_tree;
Jiri Pirko53342022016-07-04 08:23:08 +0200605 }
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200606 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
607}
Jiri Pirko53342022016-07-04 08:23:08 +0200608
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200609static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
610{
Jiri Pirko53342022016-07-04 08:23:08 +0200611 lpm_tree->ref_count++;
Jiri Pirko53342022016-07-04 08:23:08 +0200612}
613
Ido Schimmelcc702672017-08-14 10:54:03 +0200614static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
615 struct mlxsw_sp_lpm_tree *lpm_tree)
Jiri Pirko53342022016-07-04 08:23:08 +0200616{
617 if (--lpm_tree->ref_count == 0)
Ido Schimmelcc702672017-08-14 10:54:03 +0200618 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
Jiri Pirko53342022016-07-04 08:23:08 +0200619}
620
Ido Schimmeld7a60302017-06-08 08:47:43 +0200621#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
Ido Schimmel8494ab02017-03-24 08:02:47 +0100622
623static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko53342022016-07-04 08:23:08 +0200624{
625 struct mlxsw_sp_lpm_tree *lpm_tree;
Ido Schimmel8494ab02017-03-24 08:02:47 +0100626 u64 max_trees;
Jiri Pirko53342022016-07-04 08:23:08 +0200627 int i;
628
Ido Schimmel8494ab02017-03-24 08:02:47 +0100629 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
630 return -EIO;
631
632 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
Ido Schimmel9011b672017-05-16 19:38:25 +0200633 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
634 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
Ido Schimmel8494ab02017-03-24 08:02:47 +0100635 sizeof(struct mlxsw_sp_lpm_tree),
636 GFP_KERNEL);
Ido Schimmel9011b672017-05-16 19:38:25 +0200637 if (!mlxsw_sp->router->lpm.trees)
Ido Schimmel8494ab02017-03-24 08:02:47 +0100638 return -ENOMEM;
639
Ido Schimmel9011b672017-05-16 19:38:25 +0200640 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
641 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
Jiri Pirko53342022016-07-04 08:23:08 +0200642 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
643 }
Ido Schimmel8494ab02017-03-24 08:02:47 +0100644
645 return 0;
646}
647
648static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
649{
Ido Schimmel9011b672017-05-16 19:38:25 +0200650 kfree(mlxsw_sp->router->lpm.trees);
Jiri Pirko53342022016-07-04 08:23:08 +0200651}
652
Ido Schimmel76610eb2017-03-10 08:53:41 +0100653static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
654{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200655 return !!vr->fib4 || !!vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100656}
657
Jiri Pirko6b75c482016-07-04 08:23:09 +0200658static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
659{
660 struct mlxsw_sp_vr *vr;
661 int i;
662
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200663 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200664 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100665 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirko6b75c482016-07-04 08:23:09 +0200666 return vr;
667 }
668 return NULL;
669}
670
671static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200672 const struct mlxsw_sp_fib *fib, u8 tree_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200673{
674 char raltb_pl[MLXSW_REG_RALTB_LEN];
675
Ido Schimmel76610eb2017-03-10 08:53:41 +0100676 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
677 (enum mlxsw_reg_ralxx_protocol) fib->proto,
Ido Schimmel0adb2142017-08-14 10:54:04 +0200678 tree_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200679 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
680}
681
682static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100683 const struct mlxsw_sp_fib *fib)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200684{
685 char raltb_pl[MLXSW_REG_RALTB_LEN];
686
687 /* Bind to tree 0 which is default */
Ido Schimmel76610eb2017-03-10 08:53:41 +0100688 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
689 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200690 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
691}
692
693static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
694{
695 /* For our purpose, squash main and local table into one */
696 if (tb_id == RT_TABLE_LOCAL)
697 tb_id = RT_TABLE_MAIN;
698 return tb_id;
699}
700
701static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +0100702 u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200703{
704 struct mlxsw_sp_vr *vr;
705 int i;
706
707 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Nogah Frankel9497c042016-09-20 11:16:54 +0200708
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200709 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200710 vr = &mlxsw_sp->router->vrs[i];
Ido Schimmel76610eb2017-03-10 08:53:41 +0100711 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200712 return vr;
713 }
714 return NULL;
715}
716
Ido Schimmel76610eb2017-03-10 08:53:41 +0100717static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
718 enum mlxsw_sp_l3proto proto)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200719{
Ido Schimmel76610eb2017-03-10 08:53:41 +0100720 switch (proto) {
721 case MLXSW_SP_L3_PROTO_IPV4:
722 return vr->fib4;
723 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200724 return vr->fib6;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100725 }
726 return NULL;
727}
728
729static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
730 u32 tb_id)
731{
Jiri Pirko6b75c482016-07-04 08:23:09 +0200732 struct mlxsw_sp_vr *vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200733 int err;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200734
735 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
736 if (!vr)
737 return ERR_PTR(-EBUSY);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100738 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
739 if (IS_ERR(vr->fib4))
740 return ERR_CAST(vr->fib4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200741 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
742 if (IS_ERR(vr->fib6)) {
743 err = PTR_ERR(vr->fib6);
744 goto err_fib6_create;
745 }
Jiri Pirko6b75c482016-07-04 08:23:09 +0200746 vr->tb_id = tb_id;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200747 return vr;
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200748
749err_fib6_create:
750 mlxsw_sp_fib_destroy(vr->fib4);
751 vr->fib4 = NULL;
752 return ERR_PTR(err);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200753}
754
Ido Schimmel76610eb2017-03-10 08:53:41 +0100755static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200756{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200757 mlxsw_sp_fib_destroy(vr->fib6);
758 vr->fib6 = NULL;
Ido Schimmel76610eb2017-03-10 08:53:41 +0100759 mlxsw_sp_fib_destroy(vr->fib4);
760 vr->fib4 = NULL;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200761}
762
Ido Schimmel76610eb2017-03-10 08:53:41 +0100763static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200764{
765 struct mlxsw_sp_vr *vr;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200766
767 tb_id = mlxsw_sp_fix_tb_id(tb_id);
Ido Schimmel76610eb2017-03-10 08:53:41 +0100768 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
769 if (!vr)
770 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200771 return vr;
772}
773
Ido Schimmel76610eb2017-03-10 08:53:41 +0100774static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200775{
Ido Schimmela3d9bc52017-07-18 10:10:22 +0200776 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
777 list_empty(&vr->fib6->node_list))
Ido Schimmel76610eb2017-03-10 08:53:41 +0100778 mlxsw_sp_vr_destroy(vr);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200779}
780
Ido Schimmelfc922bb2017-08-14 10:54:05 +0200781static bool
782mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
783 enum mlxsw_sp_l3proto proto, u8 tree_id)
784{
785 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
786
787 if (!mlxsw_sp_vr_is_used(vr))
788 return false;
789 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
790 return true;
791 return false;
792}
793
794static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
795 struct mlxsw_sp_fib *fib,
796 struct mlxsw_sp_lpm_tree *new_tree)
797{
798 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
799 int err;
800
801 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
802 if (err)
803 return err;
804 fib->lpm_tree = new_tree;
805 mlxsw_sp_lpm_tree_hold(new_tree);
806 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
807 return 0;
808}
809
810static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
811 struct mlxsw_sp_fib *fib,
812 struct mlxsw_sp_lpm_tree *new_tree)
813{
814 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
815 enum mlxsw_sp_l3proto proto = fib->proto;
816 u8 old_id, new_id = new_tree->id;
817 struct mlxsw_sp_vr *vr;
818 int i, err;
819
820 if (!old_tree)
821 goto no_replace;
822 old_id = old_tree->id;
823
824 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
825 vr = &mlxsw_sp->router->vrs[i];
826 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
827 continue;
828 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
829 mlxsw_sp_vr_fib(vr, proto),
830 new_tree);
831 if (err)
832 goto err_tree_replace;
833 }
834
835 return 0;
836
837err_tree_replace:
838 for (i--; i >= 0; i--) {
839 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
840 continue;
841 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
842 mlxsw_sp_vr_fib(vr, proto),
843 old_tree);
844 }
845 return err;
846
847no_replace:
848 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
849 if (err)
850 return err;
851 fib->lpm_tree = new_tree;
852 mlxsw_sp_lpm_tree_hold(new_tree);
853 return 0;
854}
855
856static void
857mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
858 enum mlxsw_sp_l3proto proto,
859 struct mlxsw_sp_prefix_usage *req_prefix_usage)
860{
861 int i;
862
863 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
864 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
865 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
866 unsigned char prefix;
867
868 if (!mlxsw_sp_vr_is_used(vr))
869 continue;
870 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
871 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
872 }
873}
874
Nogah Frankel9497c042016-09-20 11:16:54 +0200875static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
Jiri Pirko6b75c482016-07-04 08:23:09 +0200876{
877 struct mlxsw_sp_vr *vr;
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200878 u64 max_vrs;
Jiri Pirko6b75c482016-07-04 08:23:09 +0200879 int i;
880
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200881 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
Nogah Frankel9497c042016-09-20 11:16:54 +0200882 return -EIO;
883
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200884 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
Ido Schimmel9011b672017-05-16 19:38:25 +0200885 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
886 GFP_KERNEL);
887 if (!mlxsw_sp->router->vrs)
Nogah Frankel9497c042016-09-20 11:16:54 +0200888 return -ENOMEM;
889
Jiri Pirkoc1a38312016-10-21 16:07:23 +0200890 for (i = 0; i < max_vrs; i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +0200891 vr = &mlxsw_sp->router->vrs[i];
Jiri Pirko6b75c482016-07-04 08:23:09 +0200892 vr->id = i;
893 }
Nogah Frankel9497c042016-09-20 11:16:54 +0200894
895 return 0;
896}
897
Ido Schimmelac571de2016-11-14 11:26:32 +0100898static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
899
Nogah Frankel9497c042016-09-20 11:16:54 +0200900static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
901{
Ido Schimmel30572242016-12-03 16:45:01 +0100902 /* At this stage we're guaranteed not to have new incoming
903 * FIB notifications and the work queue is free from FIBs
904 * sitting on top of mlxsw netdevs. However, we can still
905 * have other FIBs queued. Flush the queue before flushing
906 * the device's tables. No need for locks, as we're the only
907 * writer.
908 */
909 mlxsw_core_flush_owq();
Ido Schimmelac571de2016-11-14 11:26:32 +0100910 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +0200911 kfree(mlxsw_sp->router->vrs);
Jiri Pirko6b75c482016-07-04 08:23:09 +0200912}
913
Petr Machata6ddb7422017-09-02 23:49:19 +0200914static struct net_device *
915__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
916{
917 struct ip_tunnel *tun = netdev_priv(ol_dev);
918 struct net *net = dev_net(ol_dev);
919
920 return __dev_get_by_index(net, tun->parms.link);
921}
922
923static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
924{
925 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
926
927 if (d)
928 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
929 else
930 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
931}
932
Petr Machata1012b9a2017-09-02 23:49:23 +0200933static struct mlxsw_sp_rif *
934mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
935 const struct mlxsw_sp_rif_params *params);
936
937static struct mlxsw_sp_rif_ipip_lb *
938mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
939 enum mlxsw_sp_ipip_type ipipt,
940 struct net_device *ol_dev)
941{
942 struct mlxsw_sp_rif_params_ipip_lb lb_params;
943 const struct mlxsw_sp_ipip_ops *ipip_ops;
944 struct mlxsw_sp_rif *rif;
945
946 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
947 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
948 .common.dev = ol_dev,
949 .common.lag = false,
950 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
951 };
952
953 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common);
954 if (IS_ERR(rif))
955 return ERR_CAST(rif);
956 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
957}
958
959static struct mlxsw_sp_ipip_entry *
960mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
961 enum mlxsw_sp_ipip_type ipipt,
962 struct net_device *ol_dev)
963{
964 struct mlxsw_sp_ipip_entry *ipip_entry;
965 struct mlxsw_sp_ipip_entry *ret = NULL;
966
967 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
968 if (!ipip_entry)
969 return ERR_PTR(-ENOMEM);
970
971 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
972 ol_dev);
973 if (IS_ERR(ipip_entry->ol_lb)) {
974 ret = ERR_CAST(ipip_entry->ol_lb);
975 goto err_ol_ipip_lb_create;
976 }
977
978 ipip_entry->ipipt = ipipt;
979 ipip_entry->ol_dev = ol_dev;
980
981 return ipip_entry;
982
983err_ol_ipip_lb_create:
984 kfree(ipip_entry);
985 return ret;
986}
987
988static void
989mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry)
990{
991 WARN_ON(ipip_entry->ref_count > 0);
992 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
993 kfree(ipip_entry);
994}
995
996static __be32
997mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev)
998{
999 struct ip_tunnel *tun = netdev_priv(ol_dev);
1000
1001 return tun->parms.iph.saddr;
1002}
1003
1004union mlxsw_sp_l3addr
1005mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
1006 const struct net_device *ol_dev)
1007{
1008 switch (proto) {
1009 case MLXSW_SP_L3_PROTO_IPV4:
1010 return (union mlxsw_sp_l3addr) {
1011 .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev),
1012 };
1013 case MLXSW_SP_L3_PROTO_IPV6:
1014 break;
1015 };
1016
1017 WARN_ON(1);
1018 return (union mlxsw_sp_l3addr) {
1019 .addr4 = 0,
1020 };
1021}
1022
Petr Machataee954d1a2017-09-02 23:49:29 +02001023__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
1024{
1025 struct ip_tunnel *tun = netdev_priv(ol_dev);
1026
1027 return tun->parms.iph.daddr;
1028}
1029
1030union mlxsw_sp_l3addr
1031mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1032 const struct net_device *ol_dev)
1033{
1034 switch (proto) {
1035 case MLXSW_SP_L3_PROTO_IPV4:
1036 return (union mlxsw_sp_l3addr) {
1037 .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev),
1038 };
1039 case MLXSW_SP_L3_PROTO_IPV6:
1040 break;
1041 };
1042
1043 WARN_ON(1);
1044 return (union mlxsw_sp_l3addr) {
1045 .addr4 = 0,
1046 };
1047}
1048
Petr Machata1012b9a2017-09-02 23:49:23 +02001049static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1050 const union mlxsw_sp_l3addr *addr2)
1051{
1052 return !memcmp(addr1, addr2, sizeof(*addr1));
1053}
1054
1055static bool
1056mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
1057 const enum mlxsw_sp_l3proto ul_proto,
1058 union mlxsw_sp_l3addr saddr,
1059 u32 ul_tb_id,
1060 struct mlxsw_sp_ipip_entry *ipip_entry)
1061{
1062 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1063 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1064 union mlxsw_sp_l3addr tun_saddr;
1065
1066 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1067 return false;
1068
1069 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
1070 return tun_ul_tb_id == ul_tb_id &&
1071 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
1072}
1073
Petr Machata4607f6d2017-09-02 23:49:25 +02001074static int
1075mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
1076 struct mlxsw_sp_fib_entry *fib_entry,
1077 struct mlxsw_sp_ipip_entry *ipip_entry)
1078{
1079 u32 tunnel_index;
1080 int err;
1081
1082 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1083 if (err)
1084 return err;
1085
1086 ipip_entry->decap_fib_entry = fib_entry;
1087 fib_entry->decap.ipip_entry = ipip_entry;
1088 fib_entry->decap.tunnel_index = tunnel_index;
1089 return 0;
1090}
1091
1092static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1093 struct mlxsw_sp_fib_entry *fib_entry)
1094{
1095 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1096 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1097 fib_entry->decap.ipip_entry = NULL;
1098 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1099}
1100
Petr Machata1cc38fb2017-09-02 23:49:26 +02001101static struct mlxsw_sp_fib_node *
1102mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1103 size_t addr_len, unsigned char prefix_len);
Petr Machata4607f6d2017-09-02 23:49:25 +02001104static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1105 struct mlxsw_sp_fib_entry *fib_entry);
1106
1107static void
1108mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1109 struct mlxsw_sp_ipip_entry *ipip_entry)
1110{
1111 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1112
1113 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1114 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1115
1116 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1117}
1118
Petr Machata1cc38fb2017-09-02 23:49:26 +02001119static void
1120mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1121 struct mlxsw_sp_ipip_entry *ipip_entry,
1122 struct mlxsw_sp_fib_entry *decap_fib_entry)
1123{
1124 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1125 ipip_entry))
1126 return;
1127 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1128
1129 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1130 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1131}
1132
1133/* Given an IPIP entry, find the corresponding decap route. */
1134static struct mlxsw_sp_fib_entry *
1135mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1136 struct mlxsw_sp_ipip_entry *ipip_entry)
1137{
1138 static struct mlxsw_sp_fib_node *fib_node;
1139 const struct mlxsw_sp_ipip_ops *ipip_ops;
1140 struct mlxsw_sp_fib_entry *fib_entry;
1141 unsigned char saddr_prefix_len;
1142 union mlxsw_sp_l3addr saddr;
1143 struct mlxsw_sp_fib *ul_fib;
1144 struct mlxsw_sp_vr *ul_vr;
1145 const void *saddrp;
1146 size_t saddr_len;
1147 u32 ul_tb_id;
1148 u32 saddr4;
1149
1150 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1151
1152 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1153 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1154 if (!ul_vr)
1155 return NULL;
1156
1157 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1158 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1159 ipip_entry->ol_dev);
1160
1161 switch (ipip_ops->ul_proto) {
1162 case MLXSW_SP_L3_PROTO_IPV4:
1163 saddr4 = be32_to_cpu(saddr.addr4);
1164 saddrp = &saddr4;
1165 saddr_len = 4;
1166 saddr_prefix_len = 32;
1167 break;
1168 case MLXSW_SP_L3_PROTO_IPV6:
1169 WARN_ON(1);
1170 return NULL;
1171 }
1172
1173 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1174 saddr_prefix_len);
1175 if (!fib_node || list_empty(&fib_node->entry_list))
1176 return NULL;
1177
1178 fib_entry = list_first_entry(&fib_node->entry_list,
1179 struct mlxsw_sp_fib_entry, list);
1180 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1181 return NULL;
1182
1183 return fib_entry;
1184}
1185
Petr Machata1012b9a2017-09-02 23:49:23 +02001186static struct mlxsw_sp_ipip_entry *
1187mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1188 enum mlxsw_sp_ipip_type ipipt,
1189 struct net_device *ol_dev)
1190{
1191 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1192 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata1cc38fb2017-09-02 23:49:26 +02001193 struct mlxsw_sp_fib_entry *decap_fib_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02001194 struct mlxsw_sp_ipip_entry *ipip_entry;
1195 enum mlxsw_sp_l3proto ul_proto;
1196 union mlxsw_sp_l3addr saddr;
1197
1198 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1199 ipip_list_node) {
1200 if (ipip_entry->ol_dev == ol_dev)
1201 goto inc_ref_count;
1202
1203 /* The configuration where several tunnels have the same local
1204 * address in the same underlay table needs special treatment in
1205 * the HW. That is currently not implemented in the driver.
1206 */
1207 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1208 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1209 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1210 ul_tb_id, ipip_entry))
1211 return ERR_PTR(-EEXIST);
1212 }
1213
1214 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1215 if (IS_ERR(ipip_entry))
1216 return ipip_entry;
1217
Petr Machata1cc38fb2017-09-02 23:49:26 +02001218 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
1219 if (decap_fib_entry)
1220 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1221 decap_fib_entry);
1222
Petr Machata1012b9a2017-09-02 23:49:23 +02001223 list_add_tail(&ipip_entry->ipip_list_node,
1224 &mlxsw_sp->router->ipip_list);
1225
1226inc_ref_count:
1227 ++ipip_entry->ref_count;
1228 return ipip_entry;
1229}
1230
1231static void
1232mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1233 struct mlxsw_sp_ipip_entry *ipip_entry)
1234{
1235 if (--ipip_entry->ref_count == 0) {
1236 list_del(&ipip_entry->ipip_list_node);
Petr Machata4607f6d2017-09-02 23:49:25 +02001237 if (ipip_entry->decap_fib_entry)
1238 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
Petr Machata1012b9a2017-09-02 23:49:23 +02001239 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1240 }
1241}
1242
Petr Machata4607f6d2017-09-02 23:49:25 +02001243static bool
1244mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1245 const struct net_device *ul_dev,
1246 enum mlxsw_sp_l3proto ul_proto,
1247 union mlxsw_sp_l3addr ul_dip,
1248 struct mlxsw_sp_ipip_entry *ipip_entry)
1249{
1250 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1251 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1252 struct net_device *ipip_ul_dev;
1253
1254 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1255 return false;
1256
1257 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1258 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1259 ul_tb_id, ipip_entry) &&
1260 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1261}
1262
1263/* Given decap parameters, find the corresponding IPIP entry. */
1264static struct mlxsw_sp_ipip_entry *
1265mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1266 const struct net_device *ul_dev,
1267 enum mlxsw_sp_l3proto ul_proto,
1268 union mlxsw_sp_l3addr ul_dip)
1269{
1270 struct mlxsw_sp_ipip_entry *ipip_entry;
1271
1272 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1273 ipip_list_node)
1274 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1275 ul_proto, ul_dip,
1276 ipip_entry))
1277 return ipip_entry;
1278
1279 return NULL;
1280}
1281
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001282struct mlxsw_sp_neigh_key {
Jiri Pirko33b13412016-11-10 12:31:04 +01001283 struct neighbour *n;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001284};
1285
1286struct mlxsw_sp_neigh_entry {
Ido Schimmel9665b742017-02-08 11:16:42 +01001287 struct list_head rif_list_node;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001288 struct rhash_head ht_node;
1289 struct mlxsw_sp_neigh_key key;
1290 u16 rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001291 bool connected;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001292 unsigned char ha[ETH_ALEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001293 struct list_head nexthop_list; /* list of nexthops using
1294 * this neigh entry
1295 */
Yotam Gigib2157142016-07-05 11:27:51 +02001296 struct list_head nexthop_neighs_list_node;
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001297 unsigned int counter_index;
1298 bool counter_valid;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001299};
1300
1301static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1302 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1303 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1304 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1305};
1306
Arkadi Sharshevskyf17cc842017-08-24 08:40:04 +02001307struct mlxsw_sp_neigh_entry *
1308mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1309 struct mlxsw_sp_neigh_entry *neigh_entry)
1310{
1311 if (!neigh_entry) {
1312 if (list_empty(&rif->neigh_list))
1313 return NULL;
1314 else
1315 return list_first_entry(&rif->neigh_list,
1316 typeof(*neigh_entry),
1317 rif_list_node);
1318 }
1319 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
1320 return NULL;
1321 return list_next_entry(neigh_entry, rif_list_node);
1322}
1323
1324int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1325{
1326 return neigh_entry->key.n->tbl->family;
1327}
1328
1329unsigned char *
1330mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1331{
1332 return neigh_entry->ha;
1333}
1334
1335u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1336{
1337 struct neighbour *n;
1338
1339 n = neigh_entry->key.n;
1340 return ntohl(*((__be32 *) n->primary_key));
1341}
1342
Arkadi Sharshevsky02507682017-08-31 17:59:15 +02001343struct in6_addr *
1344mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1345{
1346 struct neighbour *n;
1347
1348 n = neigh_entry->key.n;
1349 return (struct in6_addr *) &n->primary_key;
1350}
1351
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001352int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1353 struct mlxsw_sp_neigh_entry *neigh_entry,
1354 u64 *p_counter)
1355{
1356 if (!neigh_entry->counter_valid)
1357 return -EINVAL;
1358
1359 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1360 p_counter, NULL);
1361}
1362
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001363static struct mlxsw_sp_neigh_entry *
1364mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1365 u16 rif)
1366{
1367 struct mlxsw_sp_neigh_entry *neigh_entry;
1368
1369 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
1370 if (!neigh_entry)
1371 return NULL;
1372
1373 neigh_entry->key.n = n;
1374 neigh_entry->rif = rif;
1375 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
1376
1377 return neigh_entry;
1378}
1379
1380static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
1381{
1382 kfree(neigh_entry);
1383}
1384
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001385static int
1386mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1387 struct mlxsw_sp_neigh_entry *neigh_entry)
1388{
Ido Schimmel9011b672017-05-16 19:38:25 +02001389 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001390 &neigh_entry->ht_node,
1391 mlxsw_sp_neigh_ht_params);
1392}
1393
1394static void
1395mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1396 struct mlxsw_sp_neigh_entry *neigh_entry)
1397{
Ido Schimmel9011b672017-05-16 19:38:25 +02001398 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001399 &neigh_entry->ht_node,
1400 mlxsw_sp_neigh_ht_params);
1401}
1402
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001403static bool
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001404mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1405 struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001406{
1407 struct devlink *devlink;
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001408 const char *table_name;
1409
1410 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1411 case AF_INET:
1412 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1413 break;
1414 case AF_INET6:
1415 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1416 break;
1417 default:
1418 WARN_ON(1);
1419 return false;
1420 }
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001421
1422 devlink = priv_to_devlink(mlxsw_sp->core);
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001423 return devlink_dpipe_table_counter_enabled(devlink, table_name);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001424}
1425
1426static void
1427mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1428 struct mlxsw_sp_neigh_entry *neigh_entry)
1429{
Arkadi Sharshevsky1ed55742017-08-31 17:59:18 +02001430 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001431 return;
1432
1433 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1434 return;
1435
1436 neigh_entry->counter_valid = true;
1437}
1438
1439static void
1440mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1441 struct mlxsw_sp_neigh_entry *neigh_entry)
1442{
1443 if (!neigh_entry->counter_valid)
1444 return;
1445 mlxsw_sp_flow_counter_free(mlxsw_sp,
1446 neigh_entry->counter_index);
1447 neigh_entry->counter_valid = false;
1448}
1449
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001450static struct mlxsw_sp_neigh_entry *
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001451mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001452{
1453 struct mlxsw_sp_neigh_entry *neigh_entry;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001454 struct mlxsw_sp_rif *rif;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001455 int err;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001456
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001457 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1458 if (!rif)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001459 return ERR_PTR(-EINVAL);
1460
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001461 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001462 if (!neigh_entry)
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001463 return ERR_PTR(-ENOMEM);
1464
1465 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1466 if (err)
1467 goto err_neigh_entry_insert;
1468
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001469 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01001470 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01001471
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001472 return neigh_entry;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001473
1474err_neigh_entry_insert:
1475 mlxsw_sp_neigh_entry_free(neigh_entry);
1476 return ERR_PTR(err);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001477}
1478
1479static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001480mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1481 struct mlxsw_sp_neigh_entry *neigh_entry)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001482{
Ido Schimmel9665b742017-02-08 11:16:42 +01001483 list_del(&neigh_entry->rif_list_node);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001484 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001485 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1486 mlxsw_sp_neigh_entry_free(neigh_entry);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001487}
1488
1489static struct mlxsw_sp_neigh_entry *
Jiri Pirko33b13412016-11-10 12:31:04 +01001490mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001491{
Jiri Pirko33b13412016-11-10 12:31:04 +01001492 struct mlxsw_sp_neigh_key key;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001493
Jiri Pirko33b13412016-11-10 12:31:04 +01001494 key.n = n;
Ido Schimmel9011b672017-05-16 19:38:25 +02001495 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001496 &key, mlxsw_sp_neigh_ht_params);
1497}
1498
Yotam Gigic723c7352016-07-05 11:27:43 +02001499static void
1500mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1501{
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001502 unsigned long interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001503
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001504#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevskya6c9b5d2017-07-18 10:10:18 +02001505 interval = min_t(unsigned long,
1506 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1507 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001508#else
1509 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1510#endif
Ido Schimmel9011b672017-05-16 19:38:25 +02001511 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
Yotam Gigic723c7352016-07-05 11:27:43 +02001512}
1513
1514static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1515 char *rauhtd_pl,
1516 int ent_index)
1517{
1518 struct net_device *dev;
1519 struct neighbour *n;
1520 __be32 dipn;
1521 u32 dip;
1522 u16 rif;
1523
1524 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1525
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001526 if (!mlxsw_sp->router->rifs[rif]) {
Yotam Gigic723c7352016-07-05 11:27:43 +02001527 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1528 return;
1529 }
1530
1531 dipn = htonl(dip);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02001532 dev = mlxsw_sp->router->rifs[rif]->dev;
Yotam Gigic723c7352016-07-05 11:27:43 +02001533 n = neigh_lookup(&arp_tbl, &dipn, dev);
1534 if (!n) {
1535 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1536 &dip);
1537 return;
1538 }
1539
1540 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1541 neigh_event_send(n, NULL);
1542 neigh_release(n);
1543}
1544
Ido Schimmeldf9a21f2017-08-15 09:10:33 +02001545#if IS_ENABLED(CONFIG_IPV6)
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001546static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1547 char *rauhtd_pl,
1548 int rec_index)
1549{
1550 struct net_device *dev;
1551 struct neighbour *n;
1552 struct in6_addr dip;
1553 u16 rif;
1554
1555 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1556 (char *) &dip);
1557
1558 if (!mlxsw_sp->router->rifs[rif]) {
1559 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1560 return;
1561 }
1562
1563 dev = mlxsw_sp->router->rifs[rif]->dev;
1564 n = neigh_lookup(&nd_tbl, &dip, dev);
1565 if (!n) {
1566 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1567 &dip);
1568 return;
1569 }
1570
1571 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1572 neigh_event_send(n, NULL);
1573 neigh_release(n);
1574}
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001575#else
1576static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1577 char *rauhtd_pl,
1578 int rec_index)
1579{
1580}
1581#endif
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001582
Yotam Gigic723c7352016-07-05 11:27:43 +02001583static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1584 char *rauhtd_pl,
1585 int rec_index)
1586{
1587 u8 num_entries;
1588 int i;
1589
1590 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1591 rec_index);
1592 /* Hardware starts counting at 0, so add 1. */
1593 num_entries++;
1594
1595 /* Each record consists of several neighbour entries. */
1596 for (i = 0; i < num_entries; i++) {
1597 int ent_index;
1598
1599 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1600 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1601 ent_index);
1602 }
1603
1604}
1605
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001606static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1607 char *rauhtd_pl,
1608 int rec_index)
1609{
1610 /* One record contains one entry. */
1611 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1612 rec_index);
1613}
1614
Yotam Gigic723c7352016-07-05 11:27:43 +02001615static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1616 char *rauhtd_pl, int rec_index)
1617{
1618 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1619 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1620 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1621 rec_index);
1622 break;
1623 case MLXSW_REG_RAUHTD_TYPE_IPV6:
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001624 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1625 rec_index);
Yotam Gigic723c7352016-07-05 11:27:43 +02001626 break;
1627 }
1628}
1629
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001630static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1631{
1632 u8 num_rec, last_rec_index, num_entries;
1633
1634 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1635 last_rec_index = num_rec - 1;
1636
1637 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1638 return false;
1639 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1640 MLXSW_REG_RAUHTD_TYPE_IPV6)
1641 return true;
1642
1643 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1644 last_rec_index);
1645 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1646 return true;
1647 return false;
1648}
1649
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001650static int
1651__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1652 char *rauhtd_pl,
1653 enum mlxsw_reg_rauhtd_type type)
Yotam Gigic723c7352016-07-05 11:27:43 +02001654{
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001655 int i, num_rec;
1656 int err;
Yotam Gigic723c7352016-07-05 11:27:43 +02001657
1658 /* Make sure the neighbour's netdev isn't removed in the
1659 * process.
1660 */
1661 rtnl_lock();
1662 do {
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001663 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
Yotam Gigic723c7352016-07-05 11:27:43 +02001664 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1665 rauhtd_pl);
1666 if (err) {
1667 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1668 break;
1669 }
1670 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1671 for (i = 0; i < num_rec; i++)
1672 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1673 i);
Arkadi Sharshevsky42cdb332016-11-11 16:34:26 +01001674 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
Yotam Gigic723c7352016-07-05 11:27:43 +02001675 rtnl_unlock();
1676
Arkadi Sharshevsky60f040c2017-07-18 10:10:17 +02001677 return err;
1678}
1679
1680static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1681{
1682 enum mlxsw_reg_rauhtd_type type;
1683 char *rauhtd_pl;
1684 int err;
1685
1686 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1687 if (!rauhtd_pl)
1688 return -ENOMEM;
1689
1690 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1691 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1692 if (err)
1693 goto out;
1694
1695 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1696 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1697out:
Yotam Gigic723c7352016-07-05 11:27:43 +02001698 kfree(rauhtd_pl);
Yotam Gigib2157142016-07-05 11:27:51 +02001699 return err;
1700}
1701
1702static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1703{
1704 struct mlxsw_sp_neigh_entry *neigh_entry;
1705
1706 /* Take RTNL mutex here to prevent lists from changes */
1707 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001708 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001709 nexthop_neighs_list_node)
Yotam Gigib2157142016-07-05 11:27:51 +02001710 /* If this neigh have nexthops, make the kernel think this neigh
1711 * is active regardless of the traffic.
1712 */
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001713 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigib2157142016-07-05 11:27:51 +02001714 rtnl_unlock();
1715}
1716
1717static void
1718mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1719{
Ido Schimmel9011b672017-05-16 19:38:25 +02001720 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
Yotam Gigib2157142016-07-05 11:27:51 +02001721
Ido Schimmel9011b672017-05-16 19:38:25 +02001722 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigib2157142016-07-05 11:27:51 +02001723 msecs_to_jiffies(interval));
1724}
1725
1726static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1727{
Ido Schimmel9011b672017-05-16 19:38:25 +02001728 struct mlxsw_sp_router *router;
Yotam Gigib2157142016-07-05 11:27:51 +02001729 int err;
1730
Ido Schimmel9011b672017-05-16 19:38:25 +02001731 router = container_of(work, struct mlxsw_sp_router,
1732 neighs_update.dw.work);
1733 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001734 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02001735 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
Yotam Gigib2157142016-07-05 11:27:51 +02001736
Ido Schimmel9011b672017-05-16 19:38:25 +02001737 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
Yotam Gigib2157142016-07-05 11:27:51 +02001738
Ido Schimmel9011b672017-05-16 19:38:25 +02001739 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
Yotam Gigic723c7352016-07-05 11:27:43 +02001740}
1741
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001742static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1743{
1744 struct mlxsw_sp_neigh_entry *neigh_entry;
Ido Schimmel9011b672017-05-16 19:38:25 +02001745 struct mlxsw_sp_router *router;
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001746
Ido Schimmel9011b672017-05-16 19:38:25 +02001747 router = container_of(work, struct mlxsw_sp_router,
1748 nexthop_probe_dw.work);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001749 /* Iterate over nexthop neighbours, find those who are unresolved and
1750 * send arp on them. This solves the chicken-egg problem when
1751 * the nexthop wouldn't get offloaded until the neighbor is resolved
1752 * but it wouldn't get resolved ever in case traffic is flowing in HW
1753 * using different nexthop.
1754 *
1755 * Take RTNL mutex here to prevent lists from changes.
1756 */
1757 rtnl_lock();
Ido Schimmel9011b672017-05-16 19:38:25 +02001758 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
Ido Schimmel8a0b7272017-02-06 16:20:15 +01001759 nexthop_neighs_list_node)
Ido Schimmel01b1aa32017-02-06 16:20:16 +01001760 if (!neigh_entry->connected)
Jiri Pirko33b13412016-11-10 12:31:04 +01001761 neigh_event_send(neigh_entry->key.n, NULL);
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001762 rtnl_unlock();
1763
Ido Schimmel9011b672017-05-16 19:38:25 +02001764 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001765 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1766}
1767
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02001768static void
1769mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1770 struct mlxsw_sp_neigh_entry *neigh_entry,
1771 bool removing);
1772
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001773static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001774{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001775 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1776 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1777}
1778
1779static void
1780mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1781 struct mlxsw_sp_neigh_entry *neigh_entry,
1782 enum mlxsw_reg_rauht_op op)
1783{
Jiri Pirko33b13412016-11-10 12:31:04 +01001784 struct neighbour *n = neigh_entry->key.n;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001785 u32 dip = ntohl(*((__be32 *) n->primary_key));
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001786 char rauht_pl[MLXSW_REG_RAUHT_LEN];
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001787
1788 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1789 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001790 if (neigh_entry->counter_valid)
1791 mlxsw_reg_rauht_pack_counter(rauht_pl,
1792 neigh_entry->counter_index);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001793 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1794}
1795
1796static void
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001797mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1798 struct mlxsw_sp_neigh_entry *neigh_entry,
1799 enum mlxsw_reg_rauht_op op)
1800{
1801 struct neighbour *n = neigh_entry->key.n;
1802 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1803 const char *dip = n->primary_key;
1804
1805 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1806 dip);
Arkadi Sharshevsky7cfcbc72017-08-24 08:40:08 +02001807 if (neigh_entry->counter_valid)
1808 mlxsw_reg_rauht_pack_counter(rauht_pl,
1809 neigh_entry->counter_index);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001810 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1811}
1812
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001813bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001814{
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001815 struct neighbour *n = neigh_entry->key.n;
1816
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001817 /* Packets with a link-local destination address are trapped
1818 * after LPM lookup and never reach the neighbour table, so
1819 * there is no need to program such neighbours to the device.
1820 */
1821 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1822 IPV6_ADDR_LINKLOCAL)
1823 return true;
1824 return false;
1825}
1826
1827static void
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001828mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1829 struct mlxsw_sp_neigh_entry *neigh_entry,
1830 bool adding)
1831{
1832 if (!adding && !neigh_entry->connected)
1833 return;
1834 neigh_entry->connected = adding;
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001835 if (neigh_entry->key.n->tbl->family == AF_INET) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001836 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1837 mlxsw_sp_rauht_op(adding));
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001838 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
Arkadi Sharshevsky1d1056d82017-08-31 17:59:13 +02001839 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001840 return;
1841 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1842 mlxsw_sp_rauht_op(adding));
1843 } else {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001844 WARN_ON_ONCE(1);
Arkadi Sharshevskyd5eb89c2017-07-18 10:10:15 +02001845 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001846}
1847
Arkadi Sharshevskya481d712017-08-24 08:40:10 +02001848void
1849mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1850 struct mlxsw_sp_neigh_entry *neigh_entry,
1851 bool adding)
1852{
1853 if (adding)
1854 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1855 else
1856 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1857 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1858}
1859
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001860struct mlxsw_sp_neigh_event_work {
1861 struct work_struct work;
1862 struct mlxsw_sp *mlxsw_sp;
1863 struct neighbour *n;
1864};
1865
1866static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1867{
1868 struct mlxsw_sp_neigh_event_work *neigh_work =
1869 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1870 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1871 struct mlxsw_sp_neigh_entry *neigh_entry;
1872 struct neighbour *n = neigh_work->n;
1873 unsigned char ha[ETH_ALEN];
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001874 bool entry_connected;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001875 u8 nud_state, dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001876
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001877 /* If these parameters are changed after we release the lock,
1878 * then we are guaranteed to receive another event letting us
1879 * know about it.
1880 */
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001881 read_lock_bh(&n->lock);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001882 memcpy(ha, n->ha, ETH_ALEN);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001883 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01001884 dead = n->dead;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001885 read_unlock_bh(&n->lock);
1886
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001887 rtnl_lock();
Ido Schimmel93a87e52016-12-23 09:32:49 +01001888 entry_connected = nud_state & NUD_VALID && !dead;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001889 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1890 if (!entry_connected && !neigh_entry)
1891 goto out;
1892 if (!neigh_entry) {
1893 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1894 if (IS_ERR(neigh_entry))
1895 goto out;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001896 }
1897
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001898 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1899 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1900 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1901
1902 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1903 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1904
1905out:
1906 rtnl_unlock();
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001907 neigh_release(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001908 kfree(neigh_work);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001909}
1910
Jiri Pirkoe7322632016-09-01 10:37:43 +02001911int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1912 unsigned long event, void *ptr)
Yotam Gigic723c7352016-07-05 11:27:43 +02001913{
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001914 struct mlxsw_sp_neigh_event_work *neigh_work;
Yotam Gigic723c7352016-07-05 11:27:43 +02001915 struct mlxsw_sp_port *mlxsw_sp_port;
1916 struct mlxsw_sp *mlxsw_sp;
1917 unsigned long interval;
1918 struct neigh_parms *p;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001919 struct neighbour *n;
Yotam Gigic723c7352016-07-05 11:27:43 +02001920
1921 switch (event) {
1922 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1923 p = ptr;
1924
1925 /* We don't care about changes in the default table. */
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001926 if (!p->dev || (p->tbl->family != AF_INET &&
1927 p->tbl->family != AF_INET6))
Yotam Gigic723c7352016-07-05 11:27:43 +02001928 return NOTIFY_DONE;
1929
1930 /* We are in atomic context and can't take RTNL mutex,
1931 * so use RCU variant to walk the device chain.
1932 */
1933 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1934 if (!mlxsw_sp_port)
1935 return NOTIFY_DONE;
1936
1937 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1938 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
Ido Schimmel9011b672017-05-16 19:38:25 +02001939 mlxsw_sp->router->neighs_update.interval = interval;
Yotam Gigic723c7352016-07-05 11:27:43 +02001940
1941 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1942 break;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001943 case NETEVENT_NEIGH_UPDATE:
1944 n = ptr;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001945
Ido Schimmelb5f3e0d2017-07-24 09:56:00 +02001946 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001947 return NOTIFY_DONE;
1948
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001949 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001950 if (!mlxsw_sp_port)
1951 return NOTIFY_DONE;
1952
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001953 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1954 if (!neigh_work) {
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001955 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001956 return NOTIFY_BAD;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001957 }
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001958
1959 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1960 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1961 neigh_work->n = n;
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001962
1963 /* Take a reference to ensure the neighbour won't be
1964 * destructed until we drop the reference in delayed
1965 * work.
1966 */
1967 neigh_clone(n);
Ido Schimmel5c8802f2017-02-06 16:20:13 +01001968 mlxsw_core_schedule_work(&neigh_work->work);
1969 mlxsw_sp_port_dev_put(mlxsw_sp_port);
Yotam Gigia6bf9e92016-07-05 11:27:44 +02001970 break;
Yotam Gigic723c7352016-07-05 11:27:43 +02001971 }
1972
1973 return NOTIFY_DONE;
1974}
1975
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001976static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1977{
Yotam Gigic723c7352016-07-05 11:27:43 +02001978 int err;
1979
Ido Schimmel9011b672017-05-16 19:38:25 +02001980 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
Yotam Gigic723c7352016-07-05 11:27:43 +02001981 &mlxsw_sp_neigh_ht_params);
1982 if (err)
1983 return err;
1984
1985 /* Initialize the polling interval according to the default
1986 * table.
1987 */
1988 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1989
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001990 /* Create the delayed works for the activity_update */
Ido Schimmel9011b672017-05-16 19:38:25 +02001991 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
Yotam Gigic723c7352016-07-05 11:27:43 +02001992 mlxsw_sp_router_neighs_update_work);
Ido Schimmel9011b672017-05-16 19:38:25 +02001993 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
Yotam Gigi0b2361d2016-07-05 11:27:52 +02001994 mlxsw_sp_router_probe_unresolved_nexthops);
Ido Schimmel9011b672017-05-16 19:38:25 +02001995 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1996 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
Yotam Gigic723c7352016-07-05 11:27:43 +02001997 return 0;
Jiri Pirko6cf3c972016-07-05 11:27:39 +02001998}
1999
2000static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
2001{
Ido Schimmel9011b672017-05-16 19:38:25 +02002002 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
2003 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
2004 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
Jiri Pirko6cf3c972016-07-05 11:27:39 +02002005}
2006
Ido Schimmel9665b742017-02-08 11:16:42 +01002007static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002008 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002009{
2010 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
2011
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002012 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002013 rif_list_node) {
2014 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
Ido Schimmel9665b742017-02-08 11:16:42 +01002015 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
Ido Schimmel4a3c67a2017-07-21 20:31:38 +02002016 }
Ido Schimmel9665b742017-02-08 11:16:42 +01002017}
2018
Petr Machata35225e42017-09-02 23:49:22 +02002019enum mlxsw_sp_nexthop_type {
2020 MLXSW_SP_NEXTHOP_TYPE_ETH,
Petr Machata1012b9a2017-09-02 23:49:23 +02002021 MLXSW_SP_NEXTHOP_TYPE_IPIP,
Petr Machata35225e42017-09-02 23:49:22 +02002022};
2023
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002024struct mlxsw_sp_nexthop_key {
2025 struct fib_nh *fib_nh;
2026};
2027
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002028struct mlxsw_sp_nexthop {
2029 struct list_head neigh_list_node; /* member of neigh entry list */
Ido Schimmel9665b742017-02-08 11:16:42 +01002030 struct list_head rif_list_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002031 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2032 * this belongs to
2033 */
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002034 struct rhash_head ht_node;
2035 struct mlxsw_sp_nexthop_key key;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002036 unsigned char gw_addr[sizeof(struct in6_addr)];
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002037 int ifindex;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002038 struct mlxsw_sp_rif *rif;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002039 u8 should_offload:1, /* set indicates this neigh is connected and
2040 * should be put to KVD linear area of this group.
2041 */
2042 offloaded:1, /* set in case the neigh is actually put into
2043 * KVD linear area of this group.
2044 */
2045 update:1; /* set indicates that MAC of this neigh should be
2046 * updated in HW
2047 */
Petr Machata35225e42017-09-02 23:49:22 +02002048 enum mlxsw_sp_nexthop_type type;
2049 union {
2050 struct mlxsw_sp_neigh_entry *neigh_entry;
Petr Machata1012b9a2017-09-02 23:49:23 +02002051 struct mlxsw_sp_ipip_entry *ipip_entry;
Petr Machata35225e42017-09-02 23:49:22 +02002052 };
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002053};
2054
2055struct mlxsw_sp_nexthop_group {
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002056 void *priv;
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002057 struct rhash_head ht_node;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002058 struct list_head fib_list; /* list of fib entries that use this group */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002059 struct neigh_table *neigh_tbl;
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002060 u8 adj_index_valid:1,
2061 gateway:1; /* routes using the group use a gateway */
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002062 u32 adj_index;
2063 u16 ecmp_size;
2064 u16 count;
2065 struct mlxsw_sp_nexthop nexthops[0];
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002066#define nh_rif nexthops[0].rif
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002067};
2068
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002069static struct fib_info *
2070mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2071{
2072 return nh_grp->priv;
2073}
2074
2075struct mlxsw_sp_nexthop_group_cmp_arg {
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002076 enum mlxsw_sp_l3proto proto;
2077 union {
2078 struct fib_info *fi;
2079 struct mlxsw_sp_fib6_entry *fib6_entry;
2080 };
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002081};
2082
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002083static bool
2084mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2085 const struct in6_addr *gw, int ifindex)
2086{
2087 int i;
2088
2089 for (i = 0; i < nh_grp->count; i++) {
2090 const struct mlxsw_sp_nexthop *nh;
2091
2092 nh = &nh_grp->nexthops[i];
2093 if (nh->ifindex == ifindex &&
2094 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2095 return true;
2096 }
2097
2098 return false;
2099}
2100
2101static bool
2102mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2103 const struct mlxsw_sp_fib6_entry *fib6_entry)
2104{
2105 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2106
2107 if (nh_grp->count != fib6_entry->nrt6)
2108 return false;
2109
2110 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2111 struct in6_addr *gw;
2112 int ifindex;
2113
2114 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2115 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2116 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2117 return false;
2118 }
2119
2120 return true;
2121}
2122
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002123static int
2124mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2125{
2126 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2127 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2128
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002129 switch (cmp_arg->proto) {
2130 case MLXSW_SP_L3_PROTO_IPV4:
2131 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2132 case MLXSW_SP_L3_PROTO_IPV6:
2133 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2134 cmp_arg->fib6_entry);
2135 default:
2136 WARN_ON(1);
2137 return 1;
2138 }
2139}
2140
2141static int
2142mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2143{
2144 return nh_grp->neigh_tbl->family;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002145}
2146
2147static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2148{
2149 const struct mlxsw_sp_nexthop_group *nh_grp = data;
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002150 const struct mlxsw_sp_nexthop *nh;
2151 struct fib_info *fi;
2152 unsigned int val;
2153 int i;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002154
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002155 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2156 case AF_INET:
2157 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2158 return jhash(&fi, sizeof(fi), seed);
2159 case AF_INET6:
2160 val = nh_grp->count;
2161 for (i = 0; i < nh_grp->count; i++) {
2162 nh = &nh_grp->nexthops[i];
2163 val ^= nh->ifindex;
2164 }
2165 return jhash(&val, sizeof(val), seed);
2166 default:
2167 WARN_ON(1);
2168 return 0;
2169 }
2170}
2171
2172static u32
2173mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2174{
2175 unsigned int val = fib6_entry->nrt6;
2176 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2177 struct net_device *dev;
2178
2179 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2180 dev = mlxsw_sp_rt6->rt->dst.dev;
2181 val ^= dev->ifindex;
2182 }
2183
2184 return jhash(&val, sizeof(val), seed);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002185}
2186
2187static u32
2188mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2189{
2190 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2191
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002192 switch (cmp_arg->proto) {
2193 case MLXSW_SP_L3_PROTO_IPV4:
2194 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2195 case MLXSW_SP_L3_PROTO_IPV6:
2196 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2197 default:
2198 WARN_ON(1);
2199 return 0;
2200 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002201}
2202
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002203static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002204 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002205 .hashfn = mlxsw_sp_nexthop_group_hash,
2206 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2207 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002208};
2209
2210static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2211 struct mlxsw_sp_nexthop_group *nh_grp)
2212{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002213 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2214 !nh_grp->gateway)
2215 return 0;
2216
Ido Schimmel9011b672017-05-16 19:38:25 +02002217 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002218 &nh_grp->ht_node,
2219 mlxsw_sp_nexthop_group_ht_params);
2220}
2221
2222static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2223 struct mlxsw_sp_nexthop_group *nh_grp)
2224{
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002225 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2226 !nh_grp->gateway)
2227 return;
2228
Ido Schimmel9011b672017-05-16 19:38:25 +02002229 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002230 &nh_grp->ht_node,
2231 mlxsw_sp_nexthop_group_ht_params);
2232}
2233
2234static struct mlxsw_sp_nexthop_group *
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002235mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2236 struct fib_info *fi)
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002237{
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002238 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2239
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002240 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002241 cmp_arg.fi = fi;
2242 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2243 &cmp_arg,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002244 mlxsw_sp_nexthop_group_ht_params);
2245}
2246
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02002247static struct mlxsw_sp_nexthop_group *
2248mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2249 struct mlxsw_sp_fib6_entry *fib6_entry)
2250{
2251 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2252
2253 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2254 cmp_arg.fib6_entry = fib6_entry;
2255 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2256 &cmp_arg,
2257 mlxsw_sp_nexthop_group_ht_params);
2258}
2259
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002260static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2261 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2262 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2263 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2264};
2265
2266static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2267 struct mlxsw_sp_nexthop *nh)
2268{
Ido Schimmel9011b672017-05-16 19:38:25 +02002269 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002270 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2271}
2272
2273static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2274 struct mlxsw_sp_nexthop *nh)
2275{
Ido Schimmel9011b672017-05-16 19:38:25 +02002276 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002277 mlxsw_sp_nexthop_ht_params);
2278}
2279
Ido Schimmelad178c82017-02-08 11:16:40 +01002280static struct mlxsw_sp_nexthop *
2281mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2282 struct mlxsw_sp_nexthop_key key)
2283{
Ido Schimmel9011b672017-05-16 19:38:25 +02002284 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
Ido Schimmelad178c82017-02-08 11:16:40 +01002285 mlxsw_sp_nexthop_ht_params);
2286}
2287
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002288static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002289 const struct mlxsw_sp_fib *fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002290 u32 adj_index, u16 ecmp_size,
2291 u32 new_adj_index,
2292 u16 new_ecmp_size)
2293{
2294 char raleu_pl[MLXSW_REG_RALEU_LEN];
2295
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002296 mlxsw_reg_raleu_pack(raleu_pl,
Ido Schimmel76610eb2017-03-10 08:53:41 +01002297 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2298 fib->vr->id, adj_index, ecmp_size, new_adj_index,
Ido Schimmel1a9234e662016-09-19 08:29:26 +02002299 new_ecmp_size);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002300 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2301}
2302
2303static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2304 struct mlxsw_sp_nexthop_group *nh_grp,
2305 u32 old_adj_index, u16 old_ecmp_size)
2306{
2307 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002308 struct mlxsw_sp_fib *fib = NULL;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002309 int err;
2310
2311 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel76610eb2017-03-10 08:53:41 +01002312 if (fib == fib_entry->fib_node->fib)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002313 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01002314 fib = fib_entry->fib_node->fib;
2315 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002316 old_adj_index,
2317 old_ecmp_size,
2318 nh_grp->adj_index,
2319 nh_grp->ecmp_size);
2320 if (err)
2321 return err;
2322 }
2323 return 0;
2324}
2325
2326static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2327 struct mlxsw_sp_nexthop *nh)
2328{
2329 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2330 char ratr_pl[MLXSW_REG_RATR_LEN];
2331
2332 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
Petr Machata89e41982017-09-02 23:49:15 +02002333 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2334 adj_index, neigh_entry->rif);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002335 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2336 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2337}
2338
Petr Machata1012b9a2017-09-02 23:49:23 +02002339static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2340 u32 adj_index,
2341 struct mlxsw_sp_nexthop *nh)
2342{
2343 const struct mlxsw_sp_ipip_ops *ipip_ops;
2344
2345 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2346 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2347}
2348
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002349static int
Petr Machata35225e42017-09-02 23:49:22 +02002350mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2351 struct mlxsw_sp_nexthop_group *nh_grp,
2352 bool reallocate)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002353{
2354 u32 adj_index = nh_grp->adj_index; /* base */
2355 struct mlxsw_sp_nexthop *nh;
2356 int i;
2357 int err;
2358
2359 for (i = 0; i < nh_grp->count; i++) {
2360 nh = &nh_grp->nexthops[i];
2361
2362 if (!nh->should_offload) {
2363 nh->offloaded = 0;
2364 continue;
2365 }
2366
Ido Schimmela59b7e02017-01-23 11:11:42 +01002367 if (nh->update || reallocate) {
Petr Machata35225e42017-09-02 23:49:22 +02002368 switch (nh->type) {
2369 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2370 err = mlxsw_sp_nexthop_mac_update
2371 (mlxsw_sp, adj_index, nh);
2372 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002373 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2374 err = mlxsw_sp_nexthop_ipip_update
2375 (mlxsw_sp, adj_index, nh);
2376 break;
Petr Machata35225e42017-09-02 23:49:22 +02002377 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002378 if (err)
2379 return err;
2380 nh->update = 0;
2381 nh->offloaded = 1;
2382 }
2383 adj_index++;
2384 }
2385 return 0;
2386}
2387
Ido Schimmel1819ae32017-07-21 18:04:28 +02002388static bool
2389mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2390 const struct mlxsw_sp_fib_entry *fib_entry);
2391
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002392static int
2393mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2394 struct mlxsw_sp_nexthop_group *nh_grp)
2395{
2396 struct mlxsw_sp_fib_entry *fib_entry;
2397 int err;
2398
2399 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
Ido Schimmel1819ae32017-07-21 18:04:28 +02002400 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2401 fib_entry))
2402 continue;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002403 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2404 if (err)
2405 return err;
2406 }
2407 return 0;
2408}
2409
2410static void
Ido Schimmel77d964e2017-08-02 09:56:05 +02002411mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2412 enum mlxsw_reg_ralue_op op, int err);
2413
2414static void
2415mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2416{
2417 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2418 struct mlxsw_sp_fib_entry *fib_entry;
2419
2420 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2421 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2422 fib_entry))
2423 continue;
2424 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2425 }
2426}
2427
2428static void
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002429mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2430 struct mlxsw_sp_nexthop_group *nh_grp)
2431{
2432 struct mlxsw_sp_nexthop *nh;
2433 bool offload_change = false;
2434 u32 adj_index;
2435 u16 ecmp_size = 0;
2436 bool old_adj_index_valid;
2437 u32 old_adj_index;
2438 u16 old_ecmp_size;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002439 int i;
2440 int err;
2441
Ido Schimmelb3e8d1e2017-02-08 11:16:32 +01002442 if (!nh_grp->gateway) {
2443 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2444 return;
2445 }
2446
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002447 for (i = 0; i < nh_grp->count; i++) {
2448 nh = &nh_grp->nexthops[i];
2449
Petr Machata56b8a9e2017-07-31 09:27:29 +02002450 if (nh->should_offload != nh->offloaded) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002451 offload_change = true;
2452 if (nh->should_offload)
2453 nh->update = 1;
2454 }
2455 if (nh->should_offload)
2456 ecmp_size++;
2457 }
2458 if (!offload_change) {
2459 /* Nothing was added or removed, so no need to reallocate. Just
2460 * update MAC on existing adjacency indexes.
2461 */
Petr Machata35225e42017-09-02 23:49:22 +02002462 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002463 if (err) {
2464 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2465 goto set_trap;
2466 }
2467 return;
2468 }
2469 if (!ecmp_size)
2470 /* No neigh of this group is connected so we just set
2471 * the trap and let everthing flow through kernel.
2472 */
2473 goto set_trap;
2474
Arkadi Sharshevsky13124442017-03-25 08:28:22 +01002475 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2476 if (err) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002477 /* We ran out of KVD linear space, just set the
2478 * trap and let everything flow through kernel.
2479 */
2480 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2481 goto set_trap;
2482 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002483 old_adj_index_valid = nh_grp->adj_index_valid;
2484 old_adj_index = nh_grp->adj_index;
2485 old_ecmp_size = nh_grp->ecmp_size;
2486 nh_grp->adj_index_valid = 1;
2487 nh_grp->adj_index = adj_index;
2488 nh_grp->ecmp_size = ecmp_size;
Petr Machata35225e42017-09-02 23:49:22 +02002489 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002490 if (err) {
2491 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2492 goto set_trap;
2493 }
2494
2495 if (!old_adj_index_valid) {
2496 /* The trap was set for fib entries, so we have to call
2497 * fib entry update to unset it and use adjacency index.
2498 */
2499 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2500 if (err) {
2501 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2502 goto set_trap;
2503 }
2504 return;
2505 }
2506
2507 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2508 old_adj_index, old_ecmp_size);
2509 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2510 if (err) {
2511 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2512 goto set_trap;
2513 }
Ido Schimmel77d964e2017-08-02 09:56:05 +02002514
2515 /* Offload state within the group changed, so update the flags. */
2516 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2517
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002518 return;
2519
2520set_trap:
2521 old_adj_index_valid = nh_grp->adj_index_valid;
2522 nh_grp->adj_index_valid = 0;
2523 for (i = 0; i < nh_grp->count; i++) {
2524 nh = &nh_grp->nexthops[i];
2525 nh->offloaded = 0;
2526 }
2527 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2528 if (err)
2529 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2530 if (old_adj_index_valid)
2531 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2532}
2533
2534static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2535 bool removing)
2536{
Petr Machata213666a2017-07-31 09:27:30 +02002537 if (!removing)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002538 nh->should_offload = 1;
Petr Machata213666a2017-07-31 09:27:30 +02002539 else if (nh->offloaded)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002540 nh->should_offload = 0;
2541 nh->update = 1;
2542}
2543
2544static void
2545mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2546 struct mlxsw_sp_neigh_entry *neigh_entry,
2547 bool removing)
2548{
2549 struct mlxsw_sp_nexthop *nh;
2550
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002551 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2552 neigh_list_node) {
2553 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2554 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2555 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002556}
2557
Ido Schimmel9665b742017-02-08 11:16:42 +01002558static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002559 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002560{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002561 if (nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002562 return;
2563
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002564 nh->rif = rif;
2565 list_add(&nh->rif_list_node, &rif->nexthop_list);
Ido Schimmel9665b742017-02-08 11:16:42 +01002566}
2567
2568static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2569{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002570 if (!nh->rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002571 return;
2572
2573 list_del(&nh->rif_list_node);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002574 nh->rif = NULL;
Ido Schimmel9665b742017-02-08 11:16:42 +01002575}
2576
Ido Schimmela8c97012017-02-08 11:16:35 +01002577static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2578 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002579{
2580 struct mlxsw_sp_neigh_entry *neigh_entry;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002581 struct neighbour *n;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002582 u8 nud_state, dead;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002583 int err;
2584
Ido Schimmelad178c82017-02-08 11:16:40 +01002585 if (!nh->nh_grp->gateway || nh->neigh_entry)
Ido Schimmelb8399a12017-02-08 11:16:33 +01002586 return 0;
2587
Jiri Pirko33b13412016-11-10 12:31:04 +01002588 /* Take a reference of neigh here ensuring that neigh would
Petr Machata8de3c172017-07-31 09:27:25 +02002589 * not be destructed before the nexthop entry is finished.
Jiri Pirko33b13412016-11-10 12:31:04 +01002590 * The reference is taken either in neigh_lookup() or
Ido Schimmelfd76d912017-02-06 16:20:17 +01002591 * in neigh_create() in case n is not found.
Jiri Pirko33b13412016-11-10 12:31:04 +01002592 */
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002593 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
Jiri Pirko33b13412016-11-10 12:31:04 +01002594 if (!n) {
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002595 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2596 nh->rif->dev);
Ido Schimmela8c97012017-02-08 11:16:35 +01002597 if (IS_ERR(n))
2598 return PTR_ERR(n);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002599 neigh_event_send(n, NULL);
Jiri Pirko33b13412016-11-10 12:31:04 +01002600 }
2601 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2602 if (!neigh_entry) {
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002603 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2604 if (IS_ERR(neigh_entry)) {
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002605 err = -EINVAL;
2606 goto err_neigh_entry_create;
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002607 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002608 }
Yotam Gigib2157142016-07-05 11:27:51 +02002609
2610 /* If that is the first nexthop connected to that neigh, add to
2611 * nexthop_neighs_list
2612 */
2613 if (list_empty(&neigh_entry->nexthop_list))
2614 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
Ido Schimmel9011b672017-05-16 19:38:25 +02002615 &mlxsw_sp->router->nexthop_neighs_list);
Yotam Gigib2157142016-07-05 11:27:51 +02002616
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002617 nh->neigh_entry = neigh_entry;
2618 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2619 read_lock_bh(&n->lock);
2620 nud_state = n->nud_state;
Ido Schimmel93a87e52016-12-23 09:32:49 +01002621 dead = n->dead;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002622 read_unlock_bh(&n->lock);
Ido Schimmel93a87e52016-12-23 09:32:49 +01002623 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002624
2625 return 0;
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002626
2627err_neigh_entry_create:
2628 neigh_release(n);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002629 return err;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002630}
2631
Ido Schimmela8c97012017-02-08 11:16:35 +01002632static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2633 struct mlxsw_sp_nexthop *nh)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002634{
2635 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
Ido Schimmela8c97012017-02-08 11:16:35 +01002636 struct neighbour *n;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002637
Ido Schimmelb8399a12017-02-08 11:16:33 +01002638 if (!neigh_entry)
Ido Schimmela8c97012017-02-08 11:16:35 +01002639 return;
2640 n = neigh_entry->key.n;
Ido Schimmelb8399a12017-02-08 11:16:33 +01002641
Ido Schimmel58312122016-12-23 09:32:50 +01002642 __mlxsw_sp_nexthop_neigh_update(nh, true);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002643 list_del(&nh->neigh_list_node);
Ido Schimmele58be792017-02-08 11:16:28 +01002644 nh->neigh_entry = NULL;
Yotam Gigib2157142016-07-05 11:27:51 +02002645
2646 /* If that is the last nexthop connected to that neigh, remove from
2647 * nexthop_neighs_list
2648 */
Ido Schimmele58be792017-02-08 11:16:28 +01002649 if (list_empty(&neigh_entry->nexthop_list))
2650 list_del(&neigh_entry->nexthop_neighs_list_node);
Yotam Gigib2157142016-07-05 11:27:51 +02002651
Ido Schimmel5c8802f2017-02-06 16:20:13 +01002652 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2653 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2654
2655 neigh_release(n);
Ido Schimmela8c97012017-02-08 11:16:35 +01002656}
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002657
Petr Machata6ddb7422017-09-02 23:49:19 +02002658static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2659 const struct net_device *dev,
2660 enum mlxsw_sp_ipip_type *p_type)
2661{
2662 struct mlxsw_sp_router *router = mlxsw_sp->router;
2663 const struct mlxsw_sp_ipip_ops *ipip_ops;
2664 enum mlxsw_sp_ipip_type ipipt;
2665
2666 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2667 ipip_ops = router->ipip_ops_arr[ipipt];
2668 if (dev->type == ipip_ops->dev_type) {
2669 if (p_type)
2670 *p_type = ipipt;
2671 return true;
2672 }
2673 }
2674 return false;
2675}
2676
Petr Machata1012b9a2017-09-02 23:49:23 +02002677static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2678 enum mlxsw_sp_ipip_type ipipt,
2679 struct mlxsw_sp_nexthop *nh,
2680 struct net_device *ol_dev)
2681{
2682 if (!nh->nh_grp->gateway || nh->ipip_entry)
2683 return 0;
2684
2685 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2686 if (IS_ERR(nh->ipip_entry))
2687 return PTR_ERR(nh->ipip_entry);
2688
2689 __mlxsw_sp_nexthop_neigh_update(nh, false);
2690 return 0;
2691}
2692
2693static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2694 struct mlxsw_sp_nexthop *nh)
2695{
2696 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2697
2698 if (!ipip_entry)
2699 return;
2700
2701 __mlxsw_sp_nexthop_neigh_update(nh, true);
2702 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2703 nh->ipip_entry = NULL;
2704}
2705
2706static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2707 const struct fib_nh *fib_nh,
2708 enum mlxsw_sp_ipip_type *p_ipipt)
2709{
2710 struct net_device *dev = fib_nh->nh_dev;
2711
2712 return dev &&
2713 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2714 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2715}
2716
Petr Machata35225e42017-09-02 23:49:22 +02002717static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2718 struct mlxsw_sp_nexthop *nh)
2719{
2720 switch (nh->type) {
2721 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2722 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2723 mlxsw_sp_nexthop_rif_fini(nh);
2724 break;
Petr Machata1012b9a2017-09-02 23:49:23 +02002725 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
Petr Machatade0f43c2017-10-02 12:14:57 +02002726 mlxsw_sp_nexthop_rif_fini(nh);
Petr Machata1012b9a2017-09-02 23:49:23 +02002727 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2728 break;
Petr Machata35225e42017-09-02 23:49:22 +02002729 }
2730}
2731
2732static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2733 struct mlxsw_sp_nexthop *nh,
2734 struct fib_nh *fib_nh)
2735{
Petr Machata1012b9a2017-09-02 23:49:23 +02002736 struct mlxsw_sp_router *router = mlxsw_sp->router;
Petr Machata35225e42017-09-02 23:49:22 +02002737 struct net_device *dev = fib_nh->nh_dev;
Petr Machata1012b9a2017-09-02 23:49:23 +02002738 enum mlxsw_sp_ipip_type ipipt;
Petr Machata35225e42017-09-02 23:49:22 +02002739 struct mlxsw_sp_rif *rif;
2740 int err;
2741
Petr Machata1012b9a2017-09-02 23:49:23 +02002742 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2743 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2744 MLXSW_SP_L3_PROTO_IPV4)) {
2745 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
Petr Machatade0f43c2017-10-02 12:14:57 +02002746 err = mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2747 if (err)
2748 return err;
2749 mlxsw_sp_nexthop_rif_init(nh, &nh->ipip_entry->ol_lb->common);
2750 return 0;
Petr Machata1012b9a2017-09-02 23:49:23 +02002751 }
2752
Petr Machata35225e42017-09-02 23:49:22 +02002753 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2754 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2755 if (!rif)
2756 return 0;
2757
2758 mlxsw_sp_nexthop_rif_init(nh, rif);
2759 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2760 if (err)
2761 goto err_neigh_init;
2762
2763 return 0;
2764
2765err_neigh_init:
2766 mlxsw_sp_nexthop_rif_fini(nh);
2767 return err;
2768}
2769
2770static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2771 struct mlxsw_sp_nexthop *nh)
2772{
2773 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2774}
2775
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002776static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2777 struct mlxsw_sp_nexthop_group *nh_grp,
2778 struct mlxsw_sp_nexthop *nh,
2779 struct fib_nh *fib_nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002780{
2781 struct net_device *dev = fib_nh->nh_dev;
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002782 struct in_device *in_dev;
Ido Schimmela8c97012017-02-08 11:16:35 +01002783 int err;
2784
2785 nh->nh_grp = nh_grp;
2786 nh->key.fib_nh = fib_nh;
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002787 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
Ido Schimmela8c97012017-02-08 11:16:35 +01002788 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2789 if (err)
2790 return err;
2791
Ido Schimmel97989ee2017-03-10 08:53:38 +01002792 if (!dev)
2793 return 0;
2794
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002795 in_dev = __in_dev_get_rtnl(dev);
2796 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2797 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2798 return 0;
2799
Petr Machata35225e42017-09-02 23:49:22 +02002800 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmela8c97012017-02-08 11:16:35 +01002801 if (err)
2802 goto err_nexthop_neigh_init;
2803
2804 return 0;
2805
2806err_nexthop_neigh_init:
2807 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2808 return err;
2809}
2810
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002811static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2812 struct mlxsw_sp_nexthop *nh)
Ido Schimmela8c97012017-02-08 11:16:35 +01002813{
Petr Machata35225e42017-09-02 23:49:22 +02002814 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01002815 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002816}
2817
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002818static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2819 unsigned long event, struct fib_nh *fib_nh)
Ido Schimmelad178c82017-02-08 11:16:40 +01002820{
2821 struct mlxsw_sp_nexthop_key key;
2822 struct mlxsw_sp_nexthop *nh;
Ido Schimmelad178c82017-02-08 11:16:40 +01002823
Ido Schimmel9011b672017-05-16 19:38:25 +02002824 if (mlxsw_sp->router->aborted)
Ido Schimmelad178c82017-02-08 11:16:40 +01002825 return;
2826
2827 key.fib_nh = fib_nh;
2828 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2829 if (WARN_ON_ONCE(!nh))
2830 return;
2831
Ido Schimmelad178c82017-02-08 11:16:40 +01002832 switch (event) {
2833 case FIB_EVENT_NH_ADD:
Petr Machata35225e42017-09-02 23:49:22 +02002834 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002835 break;
2836 case FIB_EVENT_NH_DEL:
Petr Machata35225e42017-09-02 23:49:22 +02002837 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01002838 break;
2839 }
2840
2841 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2842}
2843
Ido Schimmel9665b742017-02-08 11:16:42 +01002844static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002845 struct mlxsw_sp_rif *rif)
Ido Schimmel9665b742017-02-08 11:16:42 +01002846{
2847 struct mlxsw_sp_nexthop *nh, *tmp;
2848
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01002849 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
Petr Machata35225e42017-09-02 23:49:22 +02002850 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
Ido Schimmel9665b742017-02-08 11:16:42 +01002851 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2852 }
2853}
2854
Petr Machata9b014512017-09-02 23:49:20 +02002855static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2856 const struct fib_info *fi)
2857{
Petr Machata1012b9a2017-09-02 23:49:23 +02002858 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2859 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
Petr Machata9b014512017-09-02 23:49:20 +02002860}
2861
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002862static struct mlxsw_sp_nexthop_group *
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002863mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002864{
2865 struct mlxsw_sp_nexthop_group *nh_grp;
2866 struct mlxsw_sp_nexthop *nh;
2867 struct fib_nh *fib_nh;
2868 size_t alloc_size;
2869 int i;
2870 int err;
2871
2872 alloc_size = sizeof(*nh_grp) +
2873 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2874 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2875 if (!nh_grp)
2876 return ERR_PTR(-ENOMEM);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002877 nh_grp->priv = fi;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002878 INIT_LIST_HEAD(&nh_grp->fib_list);
Ido Schimmel58adf2c2017-07-18 10:10:19 +02002879 nh_grp->neigh_tbl = &arp_tbl;
2880
Petr Machata9b014512017-09-02 23:49:20 +02002881 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002882 nh_grp->count = fi->fib_nhs;
Ido Schimmel7387dbb2017-07-12 09:12:53 +02002883 fib_info_hold(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002884 for (i = 0; i < nh_grp->count; i++) {
2885 nh = &nh_grp->nexthops[i];
2886 fib_nh = &fi->fib_nh[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002887 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002888 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002889 goto err_nexthop4_init;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002890 }
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002891 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2892 if (err)
2893 goto err_nexthop_group_insert;
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002894 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2895 return nh_grp;
2896
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002897err_nexthop_group_insert:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002898err_nexthop4_init:
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002899 for (i--; i >= 0; i--) {
2900 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002901 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Ido Schimmeldf6dd79b2017-02-08 14:36:49 +01002902 }
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002903 fib_info_put(fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002904 kfree(nh_grp);
2905 return ERR_PTR(err);
2906}
2907
2908static void
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002909mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2910 struct mlxsw_sp_nexthop_group *nh_grp)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002911{
2912 struct mlxsw_sp_nexthop *nh;
2913 int i;
2914
Ido Schimmele9ad5e72017-02-08 11:16:29 +01002915 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002916 for (i = 0; i < nh_grp->count; i++) {
2917 nh = &nh_grp->nexthops[i];
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002918 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002919 }
Ido Schimmel58312122016-12-23 09:32:50 +01002920 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2921 WARN_ON_ONCE(nh_grp->adj_index_valid);
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002922 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002923 kfree(nh_grp);
2924}
2925
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002926static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2927 struct mlxsw_sp_fib_entry *fib_entry,
2928 struct fib_info *fi)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002929{
2930 struct mlxsw_sp_nexthop_group *nh_grp;
2931
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02002932 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002933 if (!nh_grp) {
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002934 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002935 if (IS_ERR(nh_grp))
2936 return PTR_ERR(nh_grp);
2937 }
2938 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2939 fib_entry->nh_group = nh_grp;
2940 return 0;
2941}
2942
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002943static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2944 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002945{
2946 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2947
2948 list_del(&fib_entry->nexthop_group_node);
2949 if (!list_empty(&nh_grp->fib_list))
2950 return;
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02002951 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02002952}
2953
Ido Schimmel013b20f2017-02-08 11:16:36 +01002954static bool
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002955mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2956{
2957 struct mlxsw_sp_fib4_entry *fib4_entry;
2958
2959 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2960 common);
2961 return !fib4_entry->tos;
2962}
2963
2964static bool
Ido Schimmel013b20f2017-02-08 11:16:36 +01002965mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2966{
2967 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2968
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02002969 switch (fib_entry->fib_node->fib->proto) {
2970 case MLXSW_SP_L3_PROTO_IPV4:
2971 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2972 return false;
2973 break;
2974 case MLXSW_SP_L3_PROTO_IPV6:
2975 break;
2976 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01002977
Ido Schimmel013b20f2017-02-08 11:16:36 +01002978 switch (fib_entry->type) {
2979 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2980 return !!nh_group->adj_index_valid;
2981 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel70ad3502017-02-08 11:16:38 +01002982 return !!nh_group->nh_rif;
Petr Machata4607f6d2017-09-02 23:49:25 +02002983 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
2984 return true;
Ido Schimmel013b20f2017-02-08 11:16:36 +01002985 default:
2986 return false;
2987 }
2988}
2989
Ido Schimmel428b8512017-08-03 13:28:28 +02002990static struct mlxsw_sp_nexthop *
2991mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2992 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2993{
2994 int i;
2995
2996 for (i = 0; i < nh_grp->count; i++) {
2997 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2998 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2999
3000 if (nh->rif && nh->rif->dev == rt->dst.dev &&
3001 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
3002 &rt->rt6i_gateway))
3003 return nh;
3004 continue;
3005 }
3006
3007 return NULL;
3008}
3009
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003010static void
3011mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3012{
3013 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3014 int i;
3015
Petr Machata4607f6d2017-09-02 23:49:25 +02003016 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3017 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003018 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3019 return;
3020 }
3021
3022 for (i = 0; i < nh_grp->count; i++) {
3023 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3024
3025 if (nh->offloaded)
3026 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3027 else
3028 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3029 }
3030}
3031
3032static void
3033mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3034{
3035 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3036 int i;
3037
3038 for (i = 0; i < nh_grp->count; i++) {
3039 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3040
3041 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3042 }
3043}
3044
Ido Schimmel428b8512017-08-03 13:28:28 +02003045static void
3046mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3047{
3048 struct mlxsw_sp_fib6_entry *fib6_entry;
3049 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3050
3051 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3052 common);
3053
3054 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3055 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
Ido Schimmelfe400792017-08-15 09:09:49 +02003056 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003057 return;
3058 }
3059
3060 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3061 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3062 struct mlxsw_sp_nexthop *nh;
3063
3064 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3065 if (nh && nh->offloaded)
Ido Schimmelfe400792017-08-15 09:09:49 +02003066 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003067 else
Ido Schimmelfe400792017-08-15 09:09:49 +02003068 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003069 }
3070}
3071
3072static void
3073mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3074{
3075 struct mlxsw_sp_fib6_entry *fib6_entry;
3076 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3077
3078 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3079 common);
3080 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3081 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3082
Ido Schimmelfe400792017-08-15 09:09:49 +02003083 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
Ido Schimmel428b8512017-08-03 13:28:28 +02003084 }
3085}
3086
Ido Schimmel013b20f2017-02-08 11:16:36 +01003087static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3088{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003089 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003090 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003091 mlxsw_sp_fib4_entry_offload_set(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003092 break;
3093 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003094 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3095 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003096 }
3097}
3098
3099static void
3100mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3101{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003102 switch (fib_entry->fib_node->fib->proto) {
Ido Schimmel013b20f2017-02-08 11:16:36 +01003103 case MLXSW_SP_L3_PROTO_IPV4:
Ido Schimmel3984d1a2017-08-02 09:56:03 +02003104 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003105 break;
3106 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02003107 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3108 break;
Ido Schimmel013b20f2017-02-08 11:16:36 +01003109 }
Ido Schimmel013b20f2017-02-08 11:16:36 +01003110}
3111
3112static void
3113mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3114 enum mlxsw_reg_ralue_op op, int err)
3115{
3116 switch (op) {
3117 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
Ido Schimmel013b20f2017-02-08 11:16:36 +01003118 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3119 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3120 if (err)
3121 return;
Ido Schimmel1353ee72017-08-02 09:56:04 +02003122 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003123 mlxsw_sp_fib_entry_offload_set(fib_entry);
Ido Schimmel1353ee72017-08-02 09:56:04 +02003124 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
Ido Schimmel013b20f2017-02-08 11:16:36 +01003125 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3126 return;
3127 default:
3128 return;
3129 }
3130}
3131
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003132static void
3133mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3134 const struct mlxsw_sp_fib_entry *fib_entry,
3135 enum mlxsw_reg_ralue_op op)
3136{
3137 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
3138 enum mlxsw_reg_ralxx_protocol proto;
3139 u32 *p_dip;
3140
3141 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3142
3143 switch (fib->proto) {
3144 case MLXSW_SP_L3_PROTO_IPV4:
3145 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3146 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3147 fib_entry->fib_node->key.prefix_len,
3148 *p_dip);
3149 break;
3150 case MLXSW_SP_L3_PROTO_IPV6:
3151 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3152 fib_entry->fib_node->key.prefix_len,
3153 fib_entry->fib_node->key.addr);
3154 break;
3155 }
3156}
3157
3158static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3159 struct mlxsw_sp_fib_entry *fib_entry,
3160 enum mlxsw_reg_ralue_op op)
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003161{
3162 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003163 enum mlxsw_reg_ralue_trap_action trap_action;
3164 u16 trap_id = 0;
3165 u32 adjacency_index = 0;
3166 u16 ecmp_size = 0;
3167
3168 /* In case the nexthop group adjacency index is valid, use it
3169 * with provided ECMP size. Otherwise, setup trap and pass
3170 * traffic to kernel.
3171 */
Ido Schimmel4b411472017-02-08 11:16:37 +01003172 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003173 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3174 adjacency_index = fib_entry->nh_group->adj_index;
3175 ecmp_size = fib_entry->nh_group->ecmp_size;
3176 } else {
3177 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3178 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3179 }
3180
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003181 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003182 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3183 adjacency_index, ecmp_size);
3184 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3185}
3186
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003187static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3188 struct mlxsw_sp_fib_entry *fib_entry,
3189 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003190{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003191 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003192 enum mlxsw_reg_ralue_trap_action trap_action;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003193 char ralue_pl[MLXSW_REG_RALUE_LEN];
Ido Schimmel70ad3502017-02-08 11:16:38 +01003194 u16 trap_id = 0;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003195 u16 rif_index = 0;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003196
3197 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3198 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003199 rif_index = rif->rif_index;
Ido Schimmel70ad3502017-02-08 11:16:38 +01003200 } else {
3201 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3202 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3203 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003204
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003205 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01003206 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3207 rif_index);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003208 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3209}
3210
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003211static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3212 struct mlxsw_sp_fib_entry *fib_entry,
3213 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003214{
3215 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirko61c503f2016-07-04 08:23:11 +02003216
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003217 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003218 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3219 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3220}
3221
Petr Machata4607f6d2017-09-02 23:49:25 +02003222static int
3223mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3224 struct mlxsw_sp_fib_entry *fib_entry,
3225 enum mlxsw_reg_ralue_op op)
3226{
3227 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3228 const struct mlxsw_sp_ipip_ops *ipip_ops;
3229
3230 if (WARN_ON(!ipip_entry))
3231 return -EINVAL;
3232
3233 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3234 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3235 fib_entry->decap.tunnel_index);
3236}
3237
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003238static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3239 struct mlxsw_sp_fib_entry *fib_entry,
3240 enum mlxsw_reg_ralue_op op)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003241{
3242 switch (fib_entry->type) {
3243 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003244 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003245 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003246 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003247 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003248 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
Petr Machata4607f6d2017-09-02 23:49:25 +02003249 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3250 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3251 fib_entry, op);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003252 }
3253 return -EINVAL;
3254}
3255
3256static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3257 struct mlxsw_sp_fib_entry *fib_entry,
3258 enum mlxsw_reg_ralue_op op)
3259{
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003260 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
Ido Schimmel013b20f2017-02-08 11:16:36 +01003261
Ido Schimmel013b20f2017-02-08 11:16:36 +01003262 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
Ido Schimmel9dbf4d72017-07-18 10:10:24 +02003263
Ido Schimmel013b20f2017-02-08 11:16:36 +01003264 return err;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003265}
3266
3267static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3268 struct mlxsw_sp_fib_entry *fib_entry)
3269{
Jiri Pirko7146da32016-09-01 10:37:41 +02003270 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3271 MLXSW_REG_RALUE_OP_WRITE_WRITE);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003272}
3273
3274static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3275 struct mlxsw_sp_fib_entry *fib_entry)
3276{
3277 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3278 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3279}
3280
Jiri Pirko61c503f2016-07-04 08:23:11 +02003281static int
Ido Schimmel013b20f2017-02-08 11:16:36 +01003282mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3283 const struct fib_entry_notifier_info *fen_info,
3284 struct mlxsw_sp_fib_entry *fib_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003285{
Petr Machata4607f6d2017-09-02 23:49:25 +02003286 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3287 struct net_device *dev = fen_info->fi->fib_dev;
3288 struct mlxsw_sp_ipip_entry *ipip_entry;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003289 struct fib_info *fi = fen_info->fi;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003290
Ido Schimmel97989ee2017-03-10 08:53:38 +01003291 switch (fen_info->type) {
Ido Schimmel97989ee2017-03-10 08:53:38 +01003292 case RTN_LOCAL:
Petr Machata4607f6d2017-09-02 23:49:25 +02003293 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3294 MLXSW_SP_L3_PROTO_IPV4, dip);
3295 if (ipip_entry) {
3296 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3297 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3298 fib_entry,
3299 ipip_entry);
3300 }
3301 /* fall through */
3302 case RTN_BROADCAST:
Jiri Pirko61c503f2016-07-04 08:23:11 +02003303 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3304 return 0;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003305 case RTN_UNREACHABLE: /* fall through */
3306 case RTN_BLACKHOLE: /* fall through */
3307 case RTN_PROHIBIT:
3308 /* Packets hitting these routes need to be trapped, but
3309 * can do so with a lower priority than packets directed
3310 * at the host, so use action type local instead of trap.
3311 */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003312 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003313 return 0;
3314 case RTN_UNICAST:
Petr Machata9b014512017-09-02 23:49:20 +02003315 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
Ido Schimmel97989ee2017-03-10 08:53:38 +01003316 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
Petr Machata9b014512017-09-02 23:49:20 +02003317 else
3318 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Ido Schimmel97989ee2017-03-10 08:53:38 +01003319 return 0;
3320 default:
3321 return -EINVAL;
3322 }
Jiri Pirkoa7ff87a2016-07-05 11:27:50 +02003323}
3324
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003325static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003326mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3327 struct mlxsw_sp_fib_node *fib_node,
3328 const struct fib_entry_notifier_info *fen_info)
Jiri Pirko5b004412016-09-01 10:37:40 +02003329{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003330 struct mlxsw_sp_fib4_entry *fib4_entry;
Jiri Pirko5b004412016-09-01 10:37:40 +02003331 struct mlxsw_sp_fib_entry *fib_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003332 int err;
3333
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003334 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3335 if (!fib4_entry)
3336 return ERR_PTR(-ENOMEM);
3337 fib_entry = &fib4_entry->common;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003338
3339 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
3340 if (err)
3341 goto err_fib4_entry_type_set;
3342
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003343 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003344 if (err)
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003345 goto err_nexthop4_group_get;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003346
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003347 fib4_entry->prio = fen_info->fi->fib_priority;
3348 fib4_entry->tb_id = fen_info->tb_id;
3349 fib4_entry->type = fen_info->type;
3350 fib4_entry->tos = fen_info->tos;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003351
3352 fib_entry->fib_node = fib_node;
3353
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003354 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003355
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003356err_nexthop4_group_get:
Ido Schimmel9aecce12017-02-09 10:28:42 +01003357err_fib4_entry_type_set:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003358 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003359 return ERR_PTR(err);
3360}
3361
3362static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003363 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003364{
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02003365 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003366 kfree(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003367}
3368
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003369static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003370mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3371 const struct fib_entry_notifier_info *fen_info)
3372{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003373 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003374 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel160e22a2017-07-18 10:10:20 +02003375 struct mlxsw_sp_fib *fib;
3376 struct mlxsw_sp_vr *vr;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003377
Ido Schimmel160e22a2017-07-18 10:10:20 +02003378 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3379 if (!vr)
3380 return NULL;
3381 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
3382
3383 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3384 sizeof(fen_info->dst),
3385 fen_info->dst_len);
3386 if (!fib_node)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003387 return NULL;
3388
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003389 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3390 if (fib4_entry->tb_id == fen_info->tb_id &&
3391 fib4_entry->tos == fen_info->tos &&
3392 fib4_entry->type == fen_info->type &&
Arkadi Sharshevskyba31d362017-08-14 21:09:19 +02003393 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3394 fen_info->fi) {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003395 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003396 }
3397 }
3398
3399 return NULL;
3400}
3401
3402static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3403 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3404 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3405 .key_len = sizeof(struct mlxsw_sp_fib_key),
3406 .automatic_shrinking = true,
3407};
3408
3409static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3410 struct mlxsw_sp_fib_node *fib_node)
3411{
3412 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3413 mlxsw_sp_fib_ht_params);
3414}
3415
3416static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3417 struct mlxsw_sp_fib_node *fib_node)
3418{
3419 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3420 mlxsw_sp_fib_ht_params);
3421}
3422
3423static struct mlxsw_sp_fib_node *
3424mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3425 size_t addr_len, unsigned char prefix_len)
3426{
3427 struct mlxsw_sp_fib_key key;
3428
3429 memset(&key, 0, sizeof(key));
3430 memcpy(key.addr, addr, addr_len);
3431 key.prefix_len = prefix_len;
3432 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3433}
3434
3435static struct mlxsw_sp_fib_node *
Ido Schimmel76610eb2017-03-10 08:53:41 +01003436mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
Ido Schimmel9aecce12017-02-09 10:28:42 +01003437 size_t addr_len, unsigned char prefix_len)
3438{
3439 struct mlxsw_sp_fib_node *fib_node;
3440
3441 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3442 if (!fib_node)
3443 return NULL;
3444
3445 INIT_LIST_HEAD(&fib_node->entry_list);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003446 list_add(&fib_node->list, &fib->node_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003447 memcpy(fib_node->key.addr, addr, addr_len);
3448 fib_node->key.prefix_len = prefix_len;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003449
3450 return fib_node;
3451}
3452
3453static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3454{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003455 list_del(&fib_node->list);
3456 WARN_ON(!list_empty(&fib_node->entry_list));
3457 kfree(fib_node);
3458}
3459
3460static bool
3461mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3462 const struct mlxsw_sp_fib_entry *fib_entry)
3463{
3464 return list_first_entry(&fib_node->entry_list,
3465 struct mlxsw_sp_fib_entry, list) == fib_entry;
3466}
3467
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003468static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3469 struct mlxsw_sp_fib *fib,
3470 struct mlxsw_sp_fib_node *fib_node)
3471{
3472 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3473 struct mlxsw_sp_lpm_tree *lpm_tree;
3474 int err;
3475
3476 /* Since the tree is shared between all virtual routers we must
3477 * make sure it contains all the required prefix lengths. This
3478 * can be computed by either adding the new prefix length to the
3479 * existing prefix usage of a bound tree, or by aggregating the
3480 * prefix lengths across all virtual routers and adding the new
3481 * one as well.
3482 */
3483 if (fib->lpm_tree)
3484 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3485 &fib->lpm_tree->prefix_usage);
3486 else
3487 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3488 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3489
3490 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3491 fib->proto);
3492 if (IS_ERR(lpm_tree))
3493 return PTR_ERR(lpm_tree);
3494
3495 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3496 return 0;
3497
3498 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3499 if (err)
3500 return err;
3501
3502 return 0;
3503}
3504
3505static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3506 struct mlxsw_sp_fib *fib)
3507{
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003508 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3509 return;
3510 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3511 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3512 fib->lpm_tree = NULL;
3513}
3514
Ido Schimmel9aecce12017-02-09 10:28:42 +01003515static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3516{
3517 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003518 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003519
3520 if (fib->prefix_ref_count[prefix_len]++ == 0)
3521 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3522}
3523
3524static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3525{
3526 unsigned char prefix_len = fib_node->key.prefix_len;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003527 struct mlxsw_sp_fib *fib = fib_node->fib;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003528
3529 if (--fib->prefix_ref_count[prefix_len] == 0)
3530 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
3531}
3532
Ido Schimmel76610eb2017-03-10 08:53:41 +01003533static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3534 struct mlxsw_sp_fib_node *fib_node,
3535 struct mlxsw_sp_fib *fib)
3536{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003537 int err;
3538
3539 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3540 if (err)
3541 return err;
3542 fib_node->fib = fib;
3543
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003544 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3545 if (err)
3546 goto err_fib_lpm_tree_link;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003547
3548 mlxsw_sp_fib_node_prefix_inc(fib_node);
3549
3550 return 0;
3551
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003552err_fib_lpm_tree_link:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003553 fib_node->fib = NULL;
3554 mlxsw_sp_fib_node_remove(fib, fib_node);
3555 return err;
3556}
3557
3558static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3559 struct mlxsw_sp_fib_node *fib_node)
3560{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003561 struct mlxsw_sp_fib *fib = fib_node->fib;
3562
3563 mlxsw_sp_fib_node_prefix_dec(fib_node);
Ido Schimmelfc922bb2017-08-14 10:54:05 +02003564 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003565 fib_node->fib = NULL;
3566 mlxsw_sp_fib_node_remove(fib, fib_node);
3567}
3568
Ido Schimmel9aecce12017-02-09 10:28:42 +01003569static struct mlxsw_sp_fib_node *
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003570mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3571 size_t addr_len, unsigned char prefix_len,
3572 enum mlxsw_sp_l3proto proto)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003573{
3574 struct mlxsw_sp_fib_node *fib_node;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003575 struct mlxsw_sp_fib *fib;
Jiri Pirko5b004412016-09-01 10:37:40 +02003576 struct mlxsw_sp_vr *vr;
3577 int err;
3578
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003579 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
Jiri Pirko5b004412016-09-01 10:37:40 +02003580 if (IS_ERR(vr))
3581 return ERR_CAST(vr);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003582 fib = mlxsw_sp_vr_fib(vr, proto);
Jiri Pirko5b004412016-09-01 10:37:40 +02003583
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003584 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003585 if (fib_node)
3586 return fib_node;
3587
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003588 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003589 if (!fib_node) {
Jiri Pirko5b004412016-09-01 10:37:40 +02003590 err = -ENOMEM;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003591 goto err_fib_node_create;
Jiri Pirko5b004412016-09-01 10:37:40 +02003592 }
Jiri Pirko5b004412016-09-01 10:37:40 +02003593
Ido Schimmel76610eb2017-03-10 08:53:41 +01003594 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3595 if (err)
3596 goto err_fib_node_init;
3597
Ido Schimmel9aecce12017-02-09 10:28:42 +01003598 return fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003599
Ido Schimmel76610eb2017-03-10 08:53:41 +01003600err_fib_node_init:
3601 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003602err_fib_node_create:
Ido Schimmel76610eb2017-03-10 08:53:41 +01003603 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003604 return ERR_PTR(err);
3605}
3606
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003607static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3608 struct mlxsw_sp_fib_node *fib_node)
Jiri Pirko5b004412016-09-01 10:37:40 +02003609{
Ido Schimmel76610eb2017-03-10 08:53:41 +01003610 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
Jiri Pirko5b004412016-09-01 10:37:40 +02003611
Ido Schimmel9aecce12017-02-09 10:28:42 +01003612 if (!list_empty(&fib_node->entry_list))
3613 return;
Ido Schimmel76610eb2017-03-10 08:53:41 +01003614 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003615 mlxsw_sp_fib_node_destroy(fib_node);
Ido Schimmel76610eb2017-03-10 08:53:41 +01003616 mlxsw_sp_vr_put(vr);
Jiri Pirko5b004412016-09-01 10:37:40 +02003617}
3618
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003619static struct mlxsw_sp_fib4_entry *
Ido Schimmel9aecce12017-02-09 10:28:42 +01003620mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003621 const struct mlxsw_sp_fib4_entry *new4_entry)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003622{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003623 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003624
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003625 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3626 if (fib4_entry->tb_id > new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003627 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003628 if (fib4_entry->tb_id != new4_entry->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003629 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003630 if (fib4_entry->tos > new4_entry->tos)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003631 continue;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003632 if (fib4_entry->prio >= new4_entry->prio ||
3633 fib4_entry->tos < new4_entry->tos)
3634 return fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003635 }
3636
3637 return NULL;
3638}
3639
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003640static int
3641mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3642 struct mlxsw_sp_fib4_entry *new4_entry)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003643{
3644 struct mlxsw_sp_fib_node *fib_node;
3645
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003646 if (WARN_ON(!fib4_entry))
Ido Schimmel4283bce2017-02-09 10:28:43 +01003647 return -EINVAL;
3648
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003649 fib_node = fib4_entry->common.fib_node;
3650 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3651 common.list) {
3652 if (fib4_entry->tb_id != new4_entry->tb_id ||
3653 fib4_entry->tos != new4_entry->tos ||
3654 fib4_entry->prio != new4_entry->prio)
Ido Schimmel4283bce2017-02-09 10:28:43 +01003655 break;
3656 }
3657
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003658 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
Ido Schimmel4283bce2017-02-09 10:28:43 +01003659 return 0;
3660}
3661
Ido Schimmel9aecce12017-02-09 10:28:42 +01003662static int
Ido Schimmel9efbee62017-07-18 10:10:28 +02003663mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003664 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003665{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003666 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003667 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003668
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003669 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003670
Ido Schimmel4283bce2017-02-09 10:28:43 +01003671 if (append)
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003672 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3673 if (replace && WARN_ON(!fib4_entry))
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003674 return -EINVAL;
Ido Schimmel4283bce2017-02-09 10:28:43 +01003675
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003676 /* Insert new entry before replaced one, so that we can later
3677 * remove the second.
3678 */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003679 if (fib4_entry) {
3680 list_add_tail(&new4_entry->common.list,
3681 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003682 } else {
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003683 struct mlxsw_sp_fib4_entry *last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003684
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003685 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3686 if (new4_entry->tb_id > last->tb_id)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003687 break;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003688 fib4_entry = last;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003689 }
3690
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003691 if (fib4_entry)
3692 list_add(&new4_entry->common.list,
3693 &fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003694 else
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003695 list_add(&new4_entry->common.list,
3696 &fib_node->entry_list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003697 }
3698
3699 return 0;
3700}
3701
3702static void
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003703mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003704{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003705 list_del(&fib4_entry->common.list);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003706}
3707
Ido Schimmel80c238f2017-07-18 10:10:29 +02003708static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3709 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003710{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003711 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3712
Ido Schimmel9aecce12017-02-09 10:28:42 +01003713 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3714 return 0;
3715
3716 /* To prevent packet loss, overwrite the previously offloaded
3717 * entry.
3718 */
3719 if (!list_is_singular(&fib_node->entry_list)) {
3720 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3721 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3722
3723 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3724 }
3725
3726 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3727}
3728
Ido Schimmel80c238f2017-07-18 10:10:29 +02003729static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3730 struct mlxsw_sp_fib_entry *fib_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003731{
Ido Schimmel9efbee62017-07-18 10:10:28 +02003732 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3733
Ido Schimmel9aecce12017-02-09 10:28:42 +01003734 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3735 return;
3736
3737 /* Promote the next entry by overwriting the deleted entry */
3738 if (!list_is_singular(&fib_node->entry_list)) {
3739 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3740 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3741
3742 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3743 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3744 return;
3745 }
3746
3747 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3748}
3749
3750static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003751 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003752 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003753{
Ido Schimmel9aecce12017-02-09 10:28:42 +01003754 int err;
3755
Ido Schimmel9efbee62017-07-18 10:10:28 +02003756 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003757 if (err)
3758 return err;
3759
Ido Schimmel80c238f2017-07-18 10:10:29 +02003760 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003761 if (err)
Ido Schimmel80c238f2017-07-18 10:10:29 +02003762 goto err_fib_node_entry_add;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003763
Ido Schimmel9aecce12017-02-09 10:28:42 +01003764 return 0;
3765
Ido Schimmel80c238f2017-07-18 10:10:29 +02003766err_fib_node_entry_add:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003767 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003768 return err;
3769}
3770
3771static void
3772mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003773 struct mlxsw_sp_fib4_entry *fib4_entry)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003774{
Ido Schimmel80c238f2017-07-18 10:10:29 +02003775 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003776 mlxsw_sp_fib4_node_list_remove(fib4_entry);
Petr Machata4607f6d2017-09-02 23:49:25 +02003777
3778 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3779 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003780}
3781
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003782static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003783 struct mlxsw_sp_fib4_entry *fib4_entry,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003784 bool replace)
3785{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003786 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3787 struct mlxsw_sp_fib4_entry *replaced;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003788
3789 if (!replace)
3790 return;
3791
3792 /* We inserted the new entry before replaced one */
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003793 replaced = list_next_entry(fib4_entry, common.list);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003794
3795 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3796 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003797 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003798}
3799
Ido Schimmel9aecce12017-02-09 10:28:42 +01003800static int
3801mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel4283bce2017-02-09 10:28:43 +01003802 const struct fib_entry_notifier_info *fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003803 bool replace, bool append)
Ido Schimmel9aecce12017-02-09 10:28:42 +01003804{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003805 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003806 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003807 int err;
3808
Ido Schimmel9011b672017-05-16 19:38:25 +02003809 if (mlxsw_sp->router->aborted)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003810 return 0;
3811
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003812 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3813 &fen_info->dst, sizeof(fen_info->dst),
3814 fen_info->dst_len,
3815 MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003816 if (IS_ERR(fib_node)) {
3817 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3818 return PTR_ERR(fib_node);
3819 }
3820
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003821 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3822 if (IS_ERR(fib4_entry)) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003823 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003824 err = PTR_ERR(fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003825 goto err_fib4_entry_create;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003826 }
Jiri Pirko61c503f2016-07-04 08:23:11 +02003827
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003828 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003829 append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003830 if (err) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01003831 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3832 goto err_fib4_node_entry_link;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003833 }
Ido Schimmel9aecce12017-02-09 10:28:42 +01003834
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003835 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
Ido Schimmel599cf8f2017-02-09 10:28:44 +01003836
Jiri Pirko61c503f2016-07-04 08:23:11 +02003837 return 0;
3838
Ido Schimmel9aecce12017-02-09 10:28:42 +01003839err_fib4_node_entry_link:
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003840 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel9aecce12017-02-09 10:28:42 +01003841err_fib4_entry_create:
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003842 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003843 return err;
3844}
3845
Jiri Pirko37956d72016-10-20 16:05:43 +02003846static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3847 struct fib_entry_notifier_info *fen_info)
Jiri Pirko61c503f2016-07-04 08:23:11 +02003848{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003849 struct mlxsw_sp_fib4_entry *fib4_entry;
Ido Schimmel9aecce12017-02-09 10:28:42 +01003850 struct mlxsw_sp_fib_node *fib_node;
Jiri Pirko61c503f2016-07-04 08:23:11 +02003851
Ido Schimmel9011b672017-05-16 19:38:25 +02003852 if (mlxsw_sp->router->aborted)
Jiri Pirko37956d72016-10-20 16:05:43 +02003853 return;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003854
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003855 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3856 if (WARN_ON(!fib4_entry))
Jiri Pirko37956d72016-10-20 16:05:43 +02003857 return;
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003858 fib_node = fib4_entry->common.fib_node;
Jiri Pirko5b004412016-09-01 10:37:40 +02003859
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02003860 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3861 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02003862 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Jiri Pirko61c503f2016-07-04 08:23:11 +02003863}
Jiri Pirkob45f64d2016-09-26 12:52:31 +02003864
Ido Schimmel428b8512017-08-03 13:28:28 +02003865static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3866{
3867 /* Packets with link-local destination IP arriving to the router
3868 * are trapped to the CPU, so no need to program specific routes
3869 * for them.
3870 */
3871 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3872 return true;
3873
3874 /* Multicast routes aren't supported, so ignore them. Neighbour
3875 * Discovery packets are specifically trapped.
3876 */
3877 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3878 return true;
3879
3880 /* Cloned routes are irrelevant in the forwarding path. */
3881 if (rt->rt6i_flags & RTF_CACHE)
3882 return true;
3883
3884 return false;
3885}
3886
3887static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3888{
3889 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3890
3891 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3892 if (!mlxsw_sp_rt6)
3893 return ERR_PTR(-ENOMEM);
3894
3895 /* In case of route replace, replaced route is deleted with
3896 * no notification. Take reference to prevent accessing freed
3897 * memory.
3898 */
3899 mlxsw_sp_rt6->rt = rt;
3900 rt6_hold(rt);
3901
3902 return mlxsw_sp_rt6;
3903}
3904
3905#if IS_ENABLED(CONFIG_IPV6)
3906static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3907{
3908 rt6_release(rt);
3909}
3910#else
3911static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3912{
3913}
3914#endif
3915
3916static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3917{
3918 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3919 kfree(mlxsw_sp_rt6);
3920}
3921
3922static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3923{
3924 /* RTF_CACHE routes are ignored */
3925 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3926}
3927
3928static struct rt6_info *
3929mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3930{
3931 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3932 list)->rt;
3933}
3934
3935static struct mlxsw_sp_fib6_entry *
3936mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003937 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003938{
3939 struct mlxsw_sp_fib6_entry *fib6_entry;
3940
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02003941 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02003942 return NULL;
3943
3944 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3945 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3946
3947 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3948 * virtual router.
3949 */
3950 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3951 continue;
3952 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3953 break;
3954 if (rt->rt6i_metric < nrt->rt6i_metric)
3955 continue;
3956 if (rt->rt6i_metric == nrt->rt6i_metric &&
3957 mlxsw_sp_fib6_rt_can_mp(rt))
3958 return fib6_entry;
3959 if (rt->rt6i_metric > nrt->rt6i_metric)
3960 break;
3961 }
3962
3963 return NULL;
3964}
3965
3966static struct mlxsw_sp_rt6 *
3967mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3968 const struct rt6_info *rt)
3969{
3970 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3971
3972 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3973 if (mlxsw_sp_rt6->rt == rt)
3974 return mlxsw_sp_rt6;
3975 }
3976
3977 return NULL;
3978}
3979
Petr Machata8f28a302017-09-02 23:49:24 +02003980static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
3981 const struct rt6_info *rt,
3982 enum mlxsw_sp_ipip_type *ret)
3983{
3984 return rt->dst.dev &&
3985 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
3986}
3987
Petr Machata35225e42017-09-02 23:49:22 +02003988static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
3989 struct mlxsw_sp_nexthop_group *nh_grp,
3990 struct mlxsw_sp_nexthop *nh,
3991 const struct rt6_info *rt)
Ido Schimmel428b8512017-08-03 13:28:28 +02003992{
Petr Machata8f28a302017-09-02 23:49:24 +02003993 struct mlxsw_sp_router *router = mlxsw_sp->router;
Ido Schimmel428b8512017-08-03 13:28:28 +02003994 struct net_device *dev = rt->dst.dev;
Petr Machata8f28a302017-09-02 23:49:24 +02003995 enum mlxsw_sp_ipip_type ipipt;
Ido Schimmel428b8512017-08-03 13:28:28 +02003996 struct mlxsw_sp_rif *rif;
3997 int err;
3998
Petr Machata8f28a302017-09-02 23:49:24 +02003999 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4000 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4001 MLXSW_SP_L3_PROTO_IPV6)) {
4002 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
Petr Machatade0f43c2017-10-02 12:14:57 +02004003 err = mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4004 if (err)
4005 return err;
4006 mlxsw_sp_nexthop_rif_init(nh, &nh->ipip_entry->ol_lb->common);
4007 return 0;
Petr Machata8f28a302017-09-02 23:49:24 +02004008 }
4009
Petr Machata35225e42017-09-02 23:49:22 +02004010 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
Ido Schimmel428b8512017-08-03 13:28:28 +02004011 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4012 if (!rif)
4013 return 0;
4014 mlxsw_sp_nexthop_rif_init(nh, rif);
4015
4016 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4017 if (err)
4018 goto err_nexthop_neigh_init;
4019
4020 return 0;
4021
4022err_nexthop_neigh_init:
4023 mlxsw_sp_nexthop_rif_fini(nh);
4024 return err;
4025}
4026
Petr Machata35225e42017-09-02 23:49:22 +02004027static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4028 struct mlxsw_sp_nexthop *nh)
4029{
4030 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4031}
4032
4033static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4034 struct mlxsw_sp_nexthop_group *nh_grp,
4035 struct mlxsw_sp_nexthop *nh,
4036 const struct rt6_info *rt)
4037{
4038 struct net_device *dev = rt->dst.dev;
4039
4040 nh->nh_grp = nh_grp;
4041 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4042
4043 if (!dev)
4044 return 0;
4045 nh->ifindex = dev->ifindex;
4046
4047 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4048}
4049
Ido Schimmel428b8512017-08-03 13:28:28 +02004050static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4051 struct mlxsw_sp_nexthop *nh)
4052{
Petr Machata35225e42017-09-02 23:49:22 +02004053 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
Ido Schimmel428b8512017-08-03 13:28:28 +02004054}
4055
Petr Machataf6050ee2017-09-02 23:49:21 +02004056static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4057 const struct rt6_info *rt)
4058{
Petr Machata8f28a302017-09-02 23:49:24 +02004059 return rt->rt6i_flags & RTF_GATEWAY ||
4060 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
Petr Machataf6050ee2017-09-02 23:49:21 +02004061}
4062
Ido Schimmel428b8512017-08-03 13:28:28 +02004063static struct mlxsw_sp_nexthop_group *
4064mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4065 struct mlxsw_sp_fib6_entry *fib6_entry)
4066{
4067 struct mlxsw_sp_nexthop_group *nh_grp;
4068 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4069 struct mlxsw_sp_nexthop *nh;
4070 size_t alloc_size;
4071 int i = 0;
4072 int err;
4073
4074 alloc_size = sizeof(*nh_grp) +
4075 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4076 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4077 if (!nh_grp)
4078 return ERR_PTR(-ENOMEM);
4079 INIT_LIST_HEAD(&nh_grp->fib_list);
4080#if IS_ENABLED(CONFIG_IPV6)
4081 nh_grp->neigh_tbl = &nd_tbl;
4082#endif
4083 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4084 struct mlxsw_sp_rt6, list);
Petr Machataf6050ee2017-09-02 23:49:21 +02004085 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004086 nh_grp->count = fib6_entry->nrt6;
4087 for (i = 0; i < nh_grp->count; i++) {
4088 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4089
4090 nh = &nh_grp->nexthops[i];
4091 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4092 if (err)
4093 goto err_nexthop6_init;
4094 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4095 }
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004096
4097 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4098 if (err)
4099 goto err_nexthop_group_insert;
4100
Ido Schimmel428b8512017-08-03 13:28:28 +02004101 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4102 return nh_grp;
4103
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004104err_nexthop_group_insert:
Ido Schimmel428b8512017-08-03 13:28:28 +02004105err_nexthop6_init:
4106 for (i--; i >= 0; i--) {
4107 nh = &nh_grp->nexthops[i];
4108 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4109 }
4110 kfree(nh_grp);
4111 return ERR_PTR(err);
4112}
4113
4114static void
4115mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4116 struct mlxsw_sp_nexthop_group *nh_grp)
4117{
4118 struct mlxsw_sp_nexthop *nh;
4119 int i = nh_grp->count;
4120
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004121 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
Ido Schimmel428b8512017-08-03 13:28:28 +02004122 for (i--; i >= 0; i--) {
4123 nh = &nh_grp->nexthops[i];
4124 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4125 }
4126 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4127 WARN_ON(nh_grp->adj_index_valid);
4128 kfree(nh_grp);
4129}
4130
4131static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4132 struct mlxsw_sp_fib6_entry *fib6_entry)
4133{
4134 struct mlxsw_sp_nexthop_group *nh_grp;
4135
Arkadi Sharshevskye6f3b372017-08-14 21:09:20 +02004136 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4137 if (!nh_grp) {
4138 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4139 if (IS_ERR(nh_grp))
4140 return PTR_ERR(nh_grp);
4141 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004142
4143 list_add_tail(&fib6_entry->common.nexthop_group_node,
4144 &nh_grp->fib_list);
4145 fib6_entry->common.nh_group = nh_grp;
4146
4147 return 0;
4148}
4149
4150static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4151 struct mlxsw_sp_fib_entry *fib_entry)
4152{
4153 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4154
4155 list_del(&fib_entry->nexthop_group_node);
4156 if (!list_empty(&nh_grp->fib_list))
4157 return;
4158 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4159}
4160
4161static int
4162mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4163 struct mlxsw_sp_fib6_entry *fib6_entry)
4164{
4165 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4166 int err;
4167
4168 fib6_entry->common.nh_group = NULL;
4169 list_del(&fib6_entry->common.nexthop_group_node);
4170
4171 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4172 if (err)
4173 goto err_nexthop6_group_get;
4174
4175 /* In case this entry is offloaded, then the adjacency index
4176 * currently associated with it in the device's table is that
4177 * of the old group. Start using the new one instead.
4178 */
4179 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4180 if (err)
4181 goto err_fib_node_entry_add;
4182
4183 if (list_empty(&old_nh_grp->fib_list))
4184 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4185
4186 return 0;
4187
4188err_fib_node_entry_add:
4189 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4190err_nexthop6_group_get:
4191 list_add_tail(&fib6_entry->common.nexthop_group_node,
4192 &old_nh_grp->fib_list);
4193 fib6_entry->common.nh_group = old_nh_grp;
4194 return err;
4195}
4196
4197static int
4198mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4199 struct mlxsw_sp_fib6_entry *fib6_entry,
4200 struct rt6_info *rt)
4201{
4202 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4203 int err;
4204
4205 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4206 if (IS_ERR(mlxsw_sp_rt6))
4207 return PTR_ERR(mlxsw_sp_rt6);
4208
4209 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4210 fib6_entry->nrt6++;
4211
4212 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4213 if (err)
4214 goto err_nexthop6_group_update;
4215
4216 return 0;
4217
4218err_nexthop6_group_update:
4219 fib6_entry->nrt6--;
4220 list_del(&mlxsw_sp_rt6->list);
4221 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4222 return err;
4223}
4224
4225static void
4226mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4227 struct mlxsw_sp_fib6_entry *fib6_entry,
4228 struct rt6_info *rt)
4229{
4230 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4231
4232 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4233 if (WARN_ON(!mlxsw_sp_rt6))
4234 return;
4235
4236 fib6_entry->nrt6--;
4237 list_del(&mlxsw_sp_rt6->list);
4238 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4239 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4240}
4241
Petr Machataf6050ee2017-09-02 23:49:21 +02004242static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4243 struct mlxsw_sp_fib_entry *fib_entry,
Ido Schimmel428b8512017-08-03 13:28:28 +02004244 const struct rt6_info *rt)
4245{
4246 /* Packets hitting RTF_REJECT routes need to be discarded by the
4247 * stack. We can rely on their destination device not having a
4248 * RIF (it's the loopback device) and can thus use action type
4249 * local, which will cause them to be trapped with a lower
4250 * priority than packets that need to be locally received.
4251 */
Ido Schimmeld3b6d372017-09-01 10:58:55 +02004252 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel428b8512017-08-03 13:28:28 +02004253 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4254 else if (rt->rt6i_flags & RTF_REJECT)
4255 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
Petr Machataf6050ee2017-09-02 23:49:21 +02004256 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
Ido Schimmel428b8512017-08-03 13:28:28 +02004257 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4258 else
4259 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4260}
4261
4262static void
4263mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4264{
4265 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4266
4267 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4268 list) {
4269 fib6_entry->nrt6--;
4270 list_del(&mlxsw_sp_rt6->list);
4271 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4272 }
4273}
4274
4275static struct mlxsw_sp_fib6_entry *
4276mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4277 struct mlxsw_sp_fib_node *fib_node,
4278 struct rt6_info *rt)
4279{
4280 struct mlxsw_sp_fib6_entry *fib6_entry;
4281 struct mlxsw_sp_fib_entry *fib_entry;
4282 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4283 int err;
4284
4285 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4286 if (!fib6_entry)
4287 return ERR_PTR(-ENOMEM);
4288 fib_entry = &fib6_entry->common;
4289
4290 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4291 if (IS_ERR(mlxsw_sp_rt6)) {
4292 err = PTR_ERR(mlxsw_sp_rt6);
4293 goto err_rt6_create;
4294 }
4295
Petr Machataf6050ee2017-09-02 23:49:21 +02004296 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
Ido Schimmel428b8512017-08-03 13:28:28 +02004297
4298 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4299 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4300 fib6_entry->nrt6 = 1;
4301 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4302 if (err)
4303 goto err_nexthop6_group_get;
4304
4305 fib_entry->fib_node = fib_node;
4306
4307 return fib6_entry;
4308
4309err_nexthop6_group_get:
4310 list_del(&mlxsw_sp_rt6->list);
4311 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4312err_rt6_create:
4313 kfree(fib6_entry);
4314 return ERR_PTR(err);
4315}
4316
4317static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4318 struct mlxsw_sp_fib6_entry *fib6_entry)
4319{
4320 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4321 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4322 WARN_ON(fib6_entry->nrt6);
4323 kfree(fib6_entry);
4324}
4325
4326static struct mlxsw_sp_fib6_entry *
4327mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004328 const struct rt6_info *nrt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004329{
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004330 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004331
4332 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4333 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4334
4335 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4336 continue;
4337 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4338 break;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004339 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4340 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4341 mlxsw_sp_fib6_rt_can_mp(nrt))
4342 return fib6_entry;
4343 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4344 fallback = fallback ?: fib6_entry;
4345 }
Ido Schimmel428b8512017-08-03 13:28:28 +02004346 if (rt->rt6i_metric > nrt->rt6i_metric)
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004347 return fallback ?: fib6_entry;
Ido Schimmel428b8512017-08-03 13:28:28 +02004348 }
4349
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004350 return fallback;
Ido Schimmel428b8512017-08-03 13:28:28 +02004351}
4352
4353static int
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004354mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4355 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004356{
4357 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4358 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4359 struct mlxsw_sp_fib6_entry *fib6_entry;
4360
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004361 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4362
4363 if (replace && WARN_ON(!fib6_entry))
4364 return -EINVAL;
Ido Schimmel428b8512017-08-03 13:28:28 +02004365
4366 if (fib6_entry) {
4367 list_add_tail(&new6_entry->common.list,
4368 &fib6_entry->common.list);
4369 } else {
4370 struct mlxsw_sp_fib6_entry *last;
4371
4372 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4373 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4374
4375 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4376 break;
4377 fib6_entry = last;
4378 }
4379
4380 if (fib6_entry)
4381 list_add(&new6_entry->common.list,
4382 &fib6_entry->common.list);
4383 else
4384 list_add(&new6_entry->common.list,
4385 &fib_node->entry_list);
4386 }
4387
4388 return 0;
4389}
4390
4391static void
4392mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4393{
4394 list_del(&fib6_entry->common.list);
4395}
4396
4397static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004398 struct mlxsw_sp_fib6_entry *fib6_entry,
4399 bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004400{
4401 int err;
4402
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004403 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004404 if (err)
4405 return err;
4406
4407 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4408 if (err)
4409 goto err_fib_node_entry_add;
4410
4411 return 0;
4412
4413err_fib_node_entry_add:
4414 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4415 return err;
4416}
4417
4418static void
4419mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4420 struct mlxsw_sp_fib6_entry *fib6_entry)
4421{
4422 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4423 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4424}
4425
4426static struct mlxsw_sp_fib6_entry *
4427mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4428 const struct rt6_info *rt)
4429{
4430 struct mlxsw_sp_fib6_entry *fib6_entry;
4431 struct mlxsw_sp_fib_node *fib_node;
4432 struct mlxsw_sp_fib *fib;
4433 struct mlxsw_sp_vr *vr;
4434
4435 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4436 if (!vr)
4437 return NULL;
4438 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4439
4440 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4441 sizeof(rt->rt6i_dst.addr),
4442 rt->rt6i_dst.plen);
4443 if (!fib_node)
4444 return NULL;
4445
4446 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4447 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4448
4449 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4450 rt->rt6i_metric == iter_rt->rt6i_metric &&
4451 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4452 return fib6_entry;
4453 }
4454
4455 return NULL;
4456}
4457
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004458static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4459 struct mlxsw_sp_fib6_entry *fib6_entry,
4460 bool replace)
4461{
4462 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4463 struct mlxsw_sp_fib6_entry *replaced;
4464
4465 if (!replace)
4466 return;
4467
4468 replaced = list_next_entry(fib6_entry, common.list);
4469
4470 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4471 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4472 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4473}
4474
Ido Schimmel428b8512017-08-03 13:28:28 +02004475static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004476 struct rt6_info *rt, bool replace)
Ido Schimmel428b8512017-08-03 13:28:28 +02004477{
4478 struct mlxsw_sp_fib6_entry *fib6_entry;
4479 struct mlxsw_sp_fib_node *fib_node;
4480 int err;
4481
4482 if (mlxsw_sp->router->aborted)
4483 return 0;
4484
Ido Schimmelf36f5ac2017-08-03 13:28:30 +02004485 if (rt->rt6i_src.plen)
4486 return -EINVAL;
4487
Ido Schimmel428b8512017-08-03 13:28:28 +02004488 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4489 return 0;
4490
4491 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4492 &rt->rt6i_dst.addr,
4493 sizeof(rt->rt6i_dst.addr),
4494 rt->rt6i_dst.plen,
4495 MLXSW_SP_L3_PROTO_IPV6);
4496 if (IS_ERR(fib_node))
4497 return PTR_ERR(fib_node);
4498
4499 /* Before creating a new entry, try to append route to an existing
4500 * multipath entry.
4501 */
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004502 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004503 if (fib6_entry) {
4504 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4505 if (err)
4506 goto err_fib6_entry_nexthop_add;
4507 return 0;
4508 }
4509
4510 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4511 if (IS_ERR(fib6_entry)) {
4512 err = PTR_ERR(fib6_entry);
4513 goto err_fib6_entry_create;
4514 }
4515
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004516 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004517 if (err)
4518 goto err_fib6_node_entry_link;
4519
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004520 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4521
Ido Schimmel428b8512017-08-03 13:28:28 +02004522 return 0;
4523
4524err_fib6_node_entry_link:
4525 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4526err_fib6_entry_create:
4527err_fib6_entry_nexthop_add:
4528 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4529 return err;
4530}
4531
4532static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4533 struct rt6_info *rt)
4534{
4535 struct mlxsw_sp_fib6_entry *fib6_entry;
4536 struct mlxsw_sp_fib_node *fib_node;
4537
4538 if (mlxsw_sp->router->aborted)
4539 return;
4540
4541 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4542 return;
4543
4544 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4545 if (WARN_ON(!fib6_entry))
4546 return;
4547
4548 /* If route is part of a multipath entry, but not the last one
4549 * removed, then only reduce its nexthop group.
4550 */
4551 if (!list_is_singular(&fib6_entry->rt6_list)) {
4552 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4553 return;
4554 }
4555
4556 fib_node = fib6_entry->common.fib_node;
4557
4558 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4559 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4560 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4561}
4562
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004563static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4564 enum mlxsw_reg_ralxx_protocol proto,
4565 u8 tree_id)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004566{
4567 char ralta_pl[MLXSW_REG_RALTA_LEN];
4568 char ralst_pl[MLXSW_REG_RALST_LEN];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004569 int i, err;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004570
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004571 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004572 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4573 if (err)
4574 return err;
4575
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004576 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004577 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4578 if (err)
4579 return err;
4580
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004581 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004582 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004583 char raltb_pl[MLXSW_REG_RALTB_LEN];
4584 char ralue_pl[MLXSW_REG_RALUE_LEN];
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004585
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004586 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004587 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4588 raltb_pl);
4589 if (err)
4590 return err;
4591
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004592 mlxsw_reg_ralue_pack(ralue_pl, proto,
4593 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
Ido Schimmelb5d90e62017-03-10 08:53:43 +01004594 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4595 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4596 ralue_pl);
4597 if (err)
4598 return err;
4599 }
4600
4601 return 0;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004602}
4603
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004604static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4605{
4606 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4607 int err;
4608
4609 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4610 MLXSW_SP_LPM_TREE_MIN);
4611 if (err)
4612 return err;
4613
4614 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4615 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4616 MLXSW_SP_LPM_TREE_MIN + 1);
4617}
4618
Ido Schimmel9aecce12017-02-09 10:28:42 +01004619static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4620 struct mlxsw_sp_fib_node *fib_node)
4621{
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004622 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004623
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004624 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4625 common.list) {
4626 bool do_break = &tmp->common.list == &fib_node->entry_list;
Ido Schimmel9aecce12017-02-09 10:28:42 +01004627
Ido Schimmel4f1c7f12017-07-18 10:10:26 +02004628 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4629 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
Ido Schimmel731ea1c2017-07-18 10:10:21 +02004630 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004631 /* Break when entry list is empty and node was freed.
4632 * Otherwise, we'll access freed memory in the next
4633 * iteration.
4634 */
4635 if (do_break)
4636 break;
4637 }
4638}
4639
Ido Schimmel428b8512017-08-03 13:28:28 +02004640static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4641 struct mlxsw_sp_fib_node *fib_node)
4642{
4643 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4644
4645 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4646 common.list) {
4647 bool do_break = &tmp->common.list == &fib_node->entry_list;
4648
4649 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4650 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4651 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4652 if (do_break)
4653 break;
4654 }
4655}
4656
Ido Schimmel9aecce12017-02-09 10:28:42 +01004657static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4658 struct mlxsw_sp_fib_node *fib_node)
4659{
Ido Schimmel76610eb2017-03-10 08:53:41 +01004660 switch (fib_node->fib->proto) {
Ido Schimmel9aecce12017-02-09 10:28:42 +01004661 case MLXSW_SP_L3_PROTO_IPV4:
4662 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4663 break;
4664 case MLXSW_SP_L3_PROTO_IPV6:
Ido Schimmel428b8512017-08-03 13:28:28 +02004665 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
Ido Schimmel9aecce12017-02-09 10:28:42 +01004666 break;
4667 }
4668}
4669
Ido Schimmel76610eb2017-03-10 08:53:41 +01004670static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4671 struct mlxsw_sp_vr *vr,
4672 enum mlxsw_sp_l3proto proto)
4673{
4674 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
4675 struct mlxsw_sp_fib_node *fib_node, *tmp;
4676
4677 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4678 bool do_break = &tmp->list == &fib->node_list;
4679
4680 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4681 if (do_break)
4682 break;
4683 }
4684}
4685
Ido Schimmelac571de2016-11-14 11:26:32 +01004686static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004687{
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004688 int i;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004689
Jiri Pirkoc1a38312016-10-21 16:07:23 +02004690 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
Ido Schimmel9011b672017-05-16 19:38:25 +02004691 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
Ido Schimmelac571de2016-11-14 11:26:32 +01004692
Ido Schimmel76610eb2017-03-10 08:53:41 +01004693 if (!mlxsw_sp_vr_is_used(vr))
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004694 continue;
Ido Schimmel76610eb2017-03-10 08:53:41 +01004695 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
Ido Schimmela3d9bc52017-07-18 10:10:22 +02004696
4697 /* If virtual router was only used for IPv4, then it's no
4698 * longer used.
4699 */
4700 if (!mlxsw_sp_vr_is_used(vr))
4701 continue;
4702 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004703 }
Ido Schimmelac571de2016-11-14 11:26:32 +01004704}
4705
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004706static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
Ido Schimmelac571de2016-11-14 11:26:32 +01004707{
4708 int err;
4709
Ido Schimmel9011b672017-05-16 19:38:25 +02004710 if (mlxsw_sp->router->aborted)
Ido Schimmeld331d302016-11-16 09:51:58 +01004711 return;
4712 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 +01004713 mlxsw_sp_router_fib_flush(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02004714 mlxsw_sp->router->aborted = true;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004715 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4716 if (err)
4717 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4718}
4719
Ido Schimmel30572242016-12-03 16:45:01 +01004720struct mlxsw_sp_fib_event_work {
Ido Schimmela0e47612017-02-06 16:20:10 +01004721 struct work_struct work;
Ido Schimmelad178c82017-02-08 11:16:40 +01004722 union {
Ido Schimmel428b8512017-08-03 13:28:28 +02004723 struct fib6_entry_notifier_info fen6_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004724 struct fib_entry_notifier_info fen_info;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004725 struct fib_rule_notifier_info fr_info;
Ido Schimmelad178c82017-02-08 11:16:40 +01004726 struct fib_nh_notifier_info fnh_info;
4727 };
Ido Schimmel30572242016-12-03 16:45:01 +01004728 struct mlxsw_sp *mlxsw_sp;
4729 unsigned long event;
4730};
4731
Ido Schimmel66a57632017-08-03 13:28:26 +02004732static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004733{
Ido Schimmel30572242016-12-03 16:45:01 +01004734 struct mlxsw_sp_fib_event_work *fib_work =
Ido Schimmela0e47612017-02-06 16:20:10 +01004735 container_of(work, struct mlxsw_sp_fib_event_work, work);
Ido Schimmel30572242016-12-03 16:45:01 +01004736 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004737 struct fib_rule *rule;
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004738 bool replace, append;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004739 int err;
4740
Ido Schimmel30572242016-12-03 16:45:01 +01004741 /* Protect internal structures from changes */
4742 rtnl_lock();
4743 switch (fib_work->event) {
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004744 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel4283bce2017-02-09 10:28:43 +01004745 case FIB_EVENT_ENTRY_APPEND: /* fall through */
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004746 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004747 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel4283bce2017-02-09 10:28:43 +01004748 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4749 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
Ido Schimmel599cf8f2017-02-09 10:28:44 +01004750 replace, append);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004751 if (err)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004752 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel30572242016-12-03 16:45:01 +01004753 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004754 break;
4755 case FIB_EVENT_ENTRY_DEL:
Ido Schimmel30572242016-12-03 16:45:01 +01004756 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4757 fib_info_put(fib_work->fen_info.fi);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004758 break;
4759 case FIB_EVENT_RULE_ADD: /* fall through */
4760 case FIB_EVENT_RULE_DEL:
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004761 rule = fib_work->fr_info.rule;
Ido Schimmelc7f6e662017-03-16 09:08:20 +01004762 if (!fib4_rule_default(rule) && !rule->l3mdev)
Ido Schimmelbc65a8a2017-07-18 10:10:25 +02004763 mlxsw_sp_router_fib_abort(mlxsw_sp);
Ido Schimmel5d7bfd12017-03-16 09:08:14 +01004764 fib_rule_put(rule);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004765 break;
Ido Schimmelad178c82017-02-08 11:16:40 +01004766 case FIB_EVENT_NH_ADD: /* fall through */
4767 case FIB_EVENT_NH_DEL:
Ido Schimmel0e6ea2a2017-07-18 10:10:27 +02004768 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4769 fib_work->fnh_info.fib_nh);
Ido Schimmelad178c82017-02-08 11:16:40 +01004770 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4771 break;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004772 }
Ido Schimmel30572242016-12-03 16:45:01 +01004773 rtnl_unlock();
4774 kfree(fib_work);
4775}
4776
Ido Schimmel66a57632017-08-03 13:28:26 +02004777static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4778{
Ido Schimmel583419f2017-08-03 13:28:27 +02004779 struct mlxsw_sp_fib_event_work *fib_work =
4780 container_of(work, struct mlxsw_sp_fib_event_work, work);
4781 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4782 struct fib_rule *rule;
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004783 bool replace;
Ido Schimmel428b8512017-08-03 13:28:28 +02004784 int err;
Ido Schimmel583419f2017-08-03 13:28:27 +02004785
4786 rtnl_lock();
4787 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004788 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004789 case FIB_EVENT_ENTRY_ADD:
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004790 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel428b8512017-08-03 13:28:28 +02004791 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004792 fib_work->fen6_info.rt, replace);
Ido Schimmel428b8512017-08-03 13:28:28 +02004793 if (err)
4794 mlxsw_sp_router_fib_abort(mlxsw_sp);
4795 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4796 break;
4797 case FIB_EVENT_ENTRY_DEL:
4798 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4799 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4800 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004801 case FIB_EVENT_RULE_ADD: /* fall through */
4802 case FIB_EVENT_RULE_DEL:
4803 rule = fib_work->fr_info.rule;
4804 if (!fib6_rule_default(rule) && !rule->l3mdev)
4805 mlxsw_sp_router_fib_abort(mlxsw_sp);
4806 fib_rule_put(rule);
4807 break;
4808 }
4809 rtnl_unlock();
4810 kfree(fib_work);
Ido Schimmel66a57632017-08-03 13:28:26 +02004811}
4812
4813static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4814 struct fib_notifier_info *info)
4815{
4816 switch (fib_work->event) {
4817 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4818 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4819 case FIB_EVENT_ENTRY_ADD: /* fall through */
4820 case FIB_EVENT_ENTRY_DEL:
4821 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4822 /* Take referece on fib_info to prevent it from being
4823 * freed while work is queued. Release it afterwards.
4824 */
4825 fib_info_hold(fib_work->fen_info.fi);
4826 break;
4827 case FIB_EVENT_RULE_ADD: /* fall through */
4828 case FIB_EVENT_RULE_DEL:
4829 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4830 fib_rule_get(fib_work->fr_info.rule);
4831 break;
4832 case FIB_EVENT_NH_ADD: /* fall through */
4833 case FIB_EVENT_NH_DEL:
4834 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4835 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4836 break;
4837 }
4838}
4839
4840static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4841 struct fib_notifier_info *info)
4842{
Ido Schimmel583419f2017-08-03 13:28:27 +02004843 switch (fib_work->event) {
Ido Schimmel0a7fd1a2017-08-03 13:28:29 +02004844 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
Ido Schimmel428b8512017-08-03 13:28:28 +02004845 case FIB_EVENT_ENTRY_ADD: /* fall through */
4846 case FIB_EVENT_ENTRY_DEL:
4847 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4848 rt6_hold(fib_work->fen6_info.rt);
4849 break;
Ido Schimmel583419f2017-08-03 13:28:27 +02004850 case FIB_EVENT_RULE_ADD: /* fall through */
4851 case FIB_EVENT_RULE_DEL:
4852 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4853 fib_rule_get(fib_work->fr_info.rule);
4854 break;
4855 }
Ido Schimmel66a57632017-08-03 13:28:26 +02004856}
4857
Ido Schimmel30572242016-12-03 16:45:01 +01004858/* Called with rcu_read_lock() */
4859static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4860 unsigned long event, void *ptr)
4861{
Ido Schimmel30572242016-12-03 16:45:01 +01004862 struct mlxsw_sp_fib_event_work *fib_work;
4863 struct fib_notifier_info *info = ptr;
Ido Schimmel7e39d112017-05-16 19:38:28 +02004864 struct mlxsw_sp_router *router;
Ido Schimmel30572242016-12-03 16:45:01 +01004865
Ido Schimmel8e29f972017-09-15 15:31:07 +02004866 if (!net_eq(info->net, &init_net) ||
4867 (info->family != AF_INET && info->family != AF_INET6))
Ido Schimmel30572242016-12-03 16:45:01 +01004868 return NOTIFY_DONE;
4869
4870 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4871 if (WARN_ON(!fib_work))
4872 return NOTIFY_BAD;
4873
Ido Schimmel7e39d112017-05-16 19:38:28 +02004874 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4875 fib_work->mlxsw_sp = router->mlxsw_sp;
Ido Schimmel30572242016-12-03 16:45:01 +01004876 fib_work->event = event;
4877
Ido Schimmel66a57632017-08-03 13:28:26 +02004878 switch (info->family) {
4879 case AF_INET:
4880 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4881 mlxsw_sp_router_fib4_event(fib_work, info);
Ido Schimmel30572242016-12-03 16:45:01 +01004882 break;
Ido Schimmel66a57632017-08-03 13:28:26 +02004883 case AF_INET6:
4884 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4885 mlxsw_sp_router_fib6_event(fib_work, info);
Ido Schimmelad178c82017-02-08 11:16:40 +01004886 break;
Ido Schimmel30572242016-12-03 16:45:01 +01004887 }
4888
Ido Schimmela0e47612017-02-06 16:20:10 +01004889 mlxsw_core_schedule_work(&fib_work->work);
Ido Schimmel30572242016-12-03 16:45:01 +01004890
Jiri Pirkob45f64d2016-09-26 12:52:31 +02004891 return NOTIFY_DONE;
4892}
4893
Ido Schimmel4724ba562017-03-10 08:53:39 +01004894static struct mlxsw_sp_rif *
4895mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4896 const struct net_device *dev)
4897{
4898 int i;
4899
4900 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
Ido Schimmel5f9efff2017-05-16 19:38:27 +02004901 if (mlxsw_sp->router->rifs[i] &&
4902 mlxsw_sp->router->rifs[i]->dev == dev)
4903 return mlxsw_sp->router->rifs[i];
Ido Schimmel4724ba562017-03-10 08:53:39 +01004904
4905 return NULL;
4906}
4907
4908static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4909{
4910 char ritr_pl[MLXSW_REG_RITR_LEN];
4911 int err;
4912
4913 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4914 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4915 if (WARN_ON_ONCE(err))
4916 return err;
4917
4918 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4919 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4920}
4921
4922static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004923 struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004924{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004925 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4926 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4927 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01004928}
4929
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004930static bool
4931mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4932 unsigned long event)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004933{
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004934 struct inet6_dev *inet6_dev;
4935 bool addr_list_empty = true;
4936 struct in_device *idev;
4937
Ido Schimmel4724ba562017-03-10 08:53:39 +01004938 switch (event) {
4939 case NETDEV_UP:
Petr Machataf1b1f272017-07-31 09:27:28 +02004940 return rif == NULL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004941 case NETDEV_DOWN:
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02004942 idev = __in_dev_get_rtnl(dev);
4943 if (idev && idev->ifa_list)
4944 addr_list_empty = false;
4945
4946 inet6_dev = __in6_dev_get(dev);
4947 if (addr_list_empty && inet6_dev &&
4948 !list_empty(&inet6_dev->addr_list))
4949 addr_list_empty = false;
4950
4951 if (rif && addr_list_empty &&
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01004952 !netif_is_l3_slave(rif->dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01004953 return true;
4954 /* It is possible we already removed the RIF ourselves
4955 * if it was assigned to a netdev that is now a bridge
4956 * or LAG slave.
4957 */
4958 return false;
4959 }
4960
4961 return false;
4962}
4963
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004964static enum mlxsw_sp_rif_type
4965mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4966 const struct net_device *dev)
4967{
4968 enum mlxsw_sp_fid_type type;
4969
Petr Machata6ddb7422017-09-02 23:49:19 +02004970 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4971 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4972
4973 /* Otherwise RIF type is derived from the type of the underlying FID. */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02004974 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4975 type = MLXSW_SP_FID_TYPE_8021Q;
4976 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4977 type = MLXSW_SP_FID_TYPE_8021Q;
4978 else if (netif_is_bridge_master(dev))
4979 type = MLXSW_SP_FID_TYPE_8021D;
4980 else
4981 type = MLXSW_SP_FID_TYPE_RFID;
4982
4983 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4984}
4985
Ido Schimmelde5ed992017-06-04 16:53:40 +02004986static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
Ido Schimmel4724ba562017-03-10 08:53:39 +01004987{
4988 int i;
4989
Ido Schimmelde5ed992017-06-04 16:53:40 +02004990 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4991 if (!mlxsw_sp->router->rifs[i]) {
4992 *p_rif_index = i;
4993 return 0;
4994 }
4995 }
Ido Schimmel4724ba562017-03-10 08:53:39 +01004996
Ido Schimmelde5ed992017-06-04 16:53:40 +02004997 return -ENOBUFS;
Ido Schimmel4724ba562017-03-10 08:53:39 +01004998}
4999
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005000static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5001 u16 vr_id,
5002 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005003{
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005004 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005005
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005006 rif = kzalloc(rif_size, GFP_KERNEL);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005007 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005008 return NULL;
5009
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005010 INIT_LIST_HEAD(&rif->nexthop_list);
5011 INIT_LIST_HEAD(&rif->neigh_list);
5012 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5013 rif->mtu = l3_dev->mtu;
5014 rif->vr_id = vr_id;
5015 rif->dev = l3_dev;
5016 rif->rif_index = rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005017
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005018 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005019}
5020
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005021struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5022 u16 rif_index)
5023{
5024 return mlxsw_sp->router->rifs[rif_index];
5025}
5026
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005027u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5028{
5029 return rif->rif_index;
5030}
5031
Petr Machata92107cf2017-09-02 23:49:28 +02005032u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5033{
5034 return lb_rif->common.rif_index;
5035}
5036
5037u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5038{
5039 return lb_rif->ul_vr_id;
5040}
5041
Arkadi Sharshevskyfd1b9d42017-03-28 17:24:16 +02005042int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5043{
5044 return rif->dev->ifindex;
5045}
5046
Ido Schimmel4724ba562017-03-10 08:53:39 +01005047static struct mlxsw_sp_rif *
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005048mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5049 const struct mlxsw_sp_rif_params *params)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005050{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005051 u32 tb_id = l3mdev_fib_table(params->dev);
5052 const struct mlxsw_sp_rif_ops *ops;
Petr Machata010cadf2017-09-02 23:49:18 +02005053 struct mlxsw_sp_fid *fid = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005054 enum mlxsw_sp_rif_type type;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005055 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005056 struct mlxsw_sp_vr *vr;
5057 u16 rif_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005058 int err;
5059
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005060 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5061 ops = mlxsw_sp->router->rif_ops_arr[type];
5062
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005063 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5064 if (IS_ERR(vr))
5065 return ERR_CAST(vr);
Petr Machata28a04c72017-10-02 12:14:56 +02005066 vr->rif_count++;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005067
Ido Schimmelde5ed992017-06-04 16:53:40 +02005068 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5069 if (err)
5070 goto err_rif_index_alloc;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005071
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005072 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
Ido Schimmela13a5942017-05-26 08:37:33 +02005073 if (!rif) {
5074 err = -ENOMEM;
5075 goto err_rif_alloc;
5076 }
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005077 rif->mlxsw_sp = mlxsw_sp;
5078 rif->ops = ops;
Ido Schimmela13a5942017-05-26 08:37:33 +02005079
Petr Machata010cadf2017-09-02 23:49:18 +02005080 if (ops->fid_get) {
5081 fid = ops->fid_get(rif);
5082 if (IS_ERR(fid)) {
5083 err = PTR_ERR(fid);
5084 goto err_fid_get;
5085 }
5086 rif->fid = fid;
Ido Schimmel4d93cee2017-05-26 08:37:34 +02005087 }
5088
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005089 if (ops->setup)
5090 ops->setup(rif, params);
5091
5092 err = ops->configure(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005093 if (err)
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005094 goto err_configure;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005095
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005096 mlxsw_sp_rif_counters_alloc(rif);
Ido Schimmel5f9efff2017-05-16 19:38:27 +02005097 mlxsw_sp->router->rifs[rif_index] = rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005098
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005099 return rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005100
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005101err_configure:
Petr Machata010cadf2017-09-02 23:49:18 +02005102 if (fid)
5103 mlxsw_sp_fid_put(fid);
Ido Schimmela1107482017-05-26 08:37:39 +02005104err_fid_get:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005105 kfree(rif);
5106err_rif_alloc:
Ido Schimmelde5ed992017-06-04 16:53:40 +02005107err_rif_index_alloc:
Petr Machata28a04c72017-10-02 12:14:56 +02005108 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005109 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005110 return ERR_PTR(err);
5111}
5112
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005113void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005114{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005115 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5116 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
Ido Schimmela1107482017-05-26 08:37:39 +02005117 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005118 struct mlxsw_sp_vr *vr;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005119
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005120 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005121 vr = &mlxsw_sp->router->vrs[rif->vr_id];
Arkadi Sharshevskye0c0afd2017-03-28 17:24:15 +02005122
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005123 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005124 mlxsw_sp_rif_counters_free(rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005125 ops->deconfigure(rif);
Petr Machata010cadf2017-09-02 23:49:18 +02005126 if (fid)
5127 /* Loopback RIFs are not associated with a FID. */
5128 mlxsw_sp_fid_put(fid);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005129 kfree(rif);
Petr Machata28a04c72017-10-02 12:14:56 +02005130 vr->rif_count--;
Ido Schimmelc9ec53f2017-05-26 08:37:38 +02005131 mlxsw_sp_vr_put(vr);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005132}
5133
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005134static void
5135mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5136 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5137{
5138 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5139
5140 params->vid = mlxsw_sp_port_vlan->vid;
5141 params->lag = mlxsw_sp_port->lagged;
5142 if (params->lag)
5143 params->lag_id = mlxsw_sp_port->lag_id;
5144 else
5145 params->system_port = mlxsw_sp_port->local_port;
5146}
5147
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005148static int
Ido Schimmela1107482017-05-26 08:37:39 +02005149mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005150 struct net_device *l3_dev)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005151{
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005152 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005153 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005154 u16 vid = mlxsw_sp_port_vlan->vid;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005155 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005156 struct mlxsw_sp_fid *fid;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005157 int err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005158
Ido Schimmel1b8f09a2017-05-26 08:37:36 +02005159 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005160 if (!rif) {
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005161 struct mlxsw_sp_rif_params params = {
5162 .dev = l3_dev,
5163 };
5164
5165 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5166 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005167 if (IS_ERR(rif))
5168 return PTR_ERR(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005169 }
5170
Ido Schimmela1107482017-05-26 08:37:39 +02005171 /* FID was already created, just take a reference */
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005172 fid = rif->ops->fid_get(rif);
Ido Schimmela1107482017-05-26 08:37:39 +02005173 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5174 if (err)
5175 goto err_fid_port_vid_map;
5176
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005177 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005178 if (err)
5179 goto err_port_vid_learning_set;
5180
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005181 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005182 BR_STATE_FORWARDING);
5183 if (err)
5184 goto err_port_vid_stp_set;
5185
Ido Schimmela1107482017-05-26 08:37:39 +02005186 mlxsw_sp_port_vlan->fid = fid;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005187
Ido Schimmel4724ba562017-03-10 08:53:39 +01005188 return 0;
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005189
5190err_port_vid_stp_set:
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005191 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005192err_port_vid_learning_set:
Ido Schimmela1107482017-05-26 08:37:39 +02005193 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5194err_fid_port_vid_map:
5195 mlxsw_sp_fid_put(fid);
Ido Schimmel03ea01e2017-05-23 21:56:30 +02005196 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005197}
5198
Ido Schimmela1107482017-05-26 08:37:39 +02005199void
5200mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005201{
Ido Schimmelce95e152017-05-26 08:37:27 +02005202 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005203 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005204 u16 vid = mlxsw_sp_port_vlan->vid;
Ido Schimmelce95e152017-05-26 08:37:27 +02005205
Ido Schimmela1107482017-05-26 08:37:39 +02005206 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5207 return;
Ido Schimmel4aafc362017-05-26 08:37:25 +02005208
Ido Schimmela1107482017-05-26 08:37:39 +02005209 mlxsw_sp_port_vlan->fid = NULL;
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005210 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5211 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
Ido Schimmela1107482017-05-26 08:37:39 +02005212 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5213 /* If router port holds the last reference on the rFID, then the
5214 * associated Sub-port RIF will be destroyed.
5215 */
5216 mlxsw_sp_fid_put(fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005217}
5218
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005219static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5220 struct net_device *port_dev,
5221 unsigned long event, u16 vid)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005222{
5223 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
Ido Schimmelce95e152017-05-26 08:37:27 +02005224 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005225
Ido Schimmelce95e152017-05-26 08:37:27 +02005226 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005227 if (WARN_ON(!mlxsw_sp_port_vlan))
5228 return -EINVAL;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005229
5230 switch (event) {
5231 case NETDEV_UP:
Ido Schimmela1107482017-05-26 08:37:39 +02005232 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005233 l3_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005234 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005235 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005236 break;
5237 }
5238
5239 return 0;
5240}
5241
5242static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5243 unsigned long event)
5244{
Jiri Pirko2b94e582017-04-18 16:55:37 +02005245 if (netif_is_bridge_port(port_dev) ||
5246 netif_is_lag_port(port_dev) ||
5247 netif_is_ovs_port(port_dev))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005248 return 0;
5249
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005250 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005251}
5252
5253static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5254 struct net_device *lag_dev,
5255 unsigned long event, u16 vid)
5256{
5257 struct net_device *port_dev;
5258 struct list_head *iter;
5259 int err;
5260
5261 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5262 if (mlxsw_sp_port_dev_check(port_dev)) {
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005263 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5264 port_dev,
5265 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005266 if (err)
5267 return err;
5268 }
5269 }
5270
5271 return 0;
5272}
5273
5274static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5275 unsigned long event)
5276{
5277 if (netif_is_bridge_port(lag_dev))
5278 return 0;
5279
5280 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5281}
5282
Ido Schimmel4724ba562017-03-10 08:53:39 +01005283static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005284 unsigned long event)
5285{
5286 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005287 struct mlxsw_sp_rif_params params = {
5288 .dev = l3_dev,
5289 };
Ido Schimmela1107482017-05-26 08:37:39 +02005290 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005291
5292 switch (event) {
5293 case NETDEV_UP:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005294 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5295 if (IS_ERR(rif))
5296 return PTR_ERR(rif);
5297 break;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005298 case NETDEV_DOWN:
Ido Schimmela1107482017-05-26 08:37:39 +02005299 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005300 mlxsw_sp_rif_destroy(rif);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005301 break;
5302 }
5303
5304 return 0;
5305}
5306
5307static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5308 unsigned long event)
5309{
5310 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005311 u16 vid = vlan_dev_vlan_id(vlan_dev);
5312
Ido Schimmel6b27c8a2017-06-28 09:03:12 +03005313 if (netif_is_bridge_port(vlan_dev))
5314 return 0;
5315
Ido Schimmel4724ba562017-03-10 08:53:39 +01005316 if (mlxsw_sp_port_dev_check(real_dev))
Ido Schimmel7cbecf22017-05-26 08:37:28 +02005317 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5318 event, vid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005319 else if (netif_is_lag_master(real_dev))
5320 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5321 vid);
Ido Schimmelc57529e2017-05-26 08:37:31 +02005322 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005323 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005324
5325 return 0;
5326}
5327
Ido Schimmelb1e45522017-04-30 19:47:14 +03005328static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5329 unsigned long event)
5330{
5331 if (mlxsw_sp_port_dev_check(dev))
5332 return mlxsw_sp_inetaddr_port_event(dev, event);
5333 else if (netif_is_lag_master(dev))
5334 return mlxsw_sp_inetaddr_lag_event(dev, event);
5335 else if (netif_is_bridge_master(dev))
Ido Schimmela1107482017-05-26 08:37:39 +02005336 return mlxsw_sp_inetaddr_bridge_event(dev, event);
Ido Schimmelb1e45522017-04-30 19:47:14 +03005337 else if (is_vlan_dev(dev))
5338 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5339 else
5340 return 0;
5341}
5342
Ido Schimmel4724ba562017-03-10 08:53:39 +01005343int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5344 unsigned long event, void *ptr)
5345{
5346 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5347 struct net_device *dev = ifa->ifa_dev->dev;
5348 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005349 struct mlxsw_sp_rif *rif;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005350 int err = 0;
5351
5352 mlxsw_sp = mlxsw_sp_lower_get(dev);
5353 if (!mlxsw_sp)
5354 goto out;
5355
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005356 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005357 if (!mlxsw_sp_rif_should_config(rif, dev, event))
Ido Schimmel4724ba562017-03-10 08:53:39 +01005358 goto out;
5359
Ido Schimmelb1e45522017-04-30 19:47:14 +03005360 err = __mlxsw_sp_inetaddr_event(dev, event);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005361out:
5362 return notifier_from_errno(err);
5363}
5364
Arkadi Sharshevsky5ea12372017-07-18 10:10:13 +02005365struct mlxsw_sp_inet6addr_event_work {
5366 struct work_struct work;
5367 struct net_device *dev;
5368 unsigned long event;
5369};
5370
5371static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5372{
5373 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5374 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5375 struct net_device *dev = inet6addr_work->dev;
5376 unsigned long event = inet6addr_work->event;
5377 struct mlxsw_sp *mlxsw_sp;
5378 struct mlxsw_sp_rif *rif;
5379
5380 rtnl_lock();
5381 mlxsw_sp = mlxsw_sp_lower_get(dev);
5382 if (!mlxsw_sp)
5383 goto out;
5384
5385 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5386 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5387 goto out;
5388
5389 __mlxsw_sp_inetaddr_event(dev, event);
5390out:
5391 rtnl_unlock();
5392 dev_put(dev);
5393 kfree(inet6addr_work);
5394}
5395
5396/* Called with rcu_read_lock() */
5397int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5398 unsigned long event, void *ptr)
5399{
5400 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5401 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5402 struct net_device *dev = if6->idev->dev;
5403
5404 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5405 return NOTIFY_DONE;
5406
5407 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5408 if (!inet6addr_work)
5409 return NOTIFY_BAD;
5410
5411 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5412 inet6addr_work->dev = dev;
5413 inet6addr_work->event = event;
5414 dev_hold(dev);
5415 mlxsw_core_schedule_work(&inet6addr_work->work);
5416
5417 return NOTIFY_DONE;
5418}
5419
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005420static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
Ido Schimmel4724ba562017-03-10 08:53:39 +01005421 const char *mac, int mtu)
5422{
5423 char ritr_pl[MLXSW_REG_RITR_LEN];
5424 int err;
5425
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005426 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005427 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5428 if (err)
5429 return err;
5430
5431 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5432 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5433 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5434 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5435}
5436
5437int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5438{
5439 struct mlxsw_sp *mlxsw_sp;
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005440 struct mlxsw_sp_rif *rif;
Ido Schimmela1107482017-05-26 08:37:39 +02005441 u16 fid_index;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005442 int err;
5443
5444 mlxsw_sp = mlxsw_sp_lower_get(dev);
5445 if (!mlxsw_sp)
5446 return 0;
5447
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005448 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5449 if (!rif)
Ido Schimmel4724ba562017-03-10 08:53:39 +01005450 return 0;
Ido Schimmela1107482017-05-26 08:37:39 +02005451 fid_index = mlxsw_sp_fid_index(rif->fid);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005452
Ido Schimmela1107482017-05-26 08:37:39 +02005453 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005454 if (err)
5455 return err;
5456
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005457 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5458 dev->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005459 if (err)
5460 goto err_rif_edit;
5461
Ido Schimmela1107482017-05-26 08:37:39 +02005462 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005463 if (err)
5464 goto err_rif_fdb_op;
5465
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005466 ether_addr_copy(rif->addr, dev->dev_addr);
5467 rif->mtu = dev->mtu;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005468
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005469 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005470
5471 return 0;
5472
5473err_rif_fdb_op:
Arkadi Sharshevskybf952332017-03-17 09:38:00 +01005474 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005475err_rif_edit:
Ido Schimmela1107482017-05-26 08:37:39 +02005476 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005477 return err;
5478}
5479
Ido Schimmelb1e45522017-04-30 19:47:14 +03005480static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5481 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005482{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005483 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005484
Ido Schimmelb1e45522017-04-30 19:47:14 +03005485 /* If netdev is already associated with a RIF, then we need to
5486 * destroy it and create a new one with the new virtual router ID.
Ido Schimmel7179eb52017-03-16 09:08:18 +01005487 */
Ido Schimmelb1e45522017-04-30 19:47:14 +03005488 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5489 if (rif)
5490 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005491
Ido Schimmelb1e45522017-04-30 19:47:14 +03005492 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005493}
5494
Ido Schimmelb1e45522017-04-30 19:47:14 +03005495static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5496 struct net_device *l3_dev)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005497{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005498 struct mlxsw_sp_rif *rif;
Ido Schimmel7179eb52017-03-16 09:08:18 +01005499
Ido Schimmelb1e45522017-04-30 19:47:14 +03005500 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5501 if (!rif)
Ido Schimmel7179eb52017-03-16 09:08:18 +01005502 return;
Ido Schimmelb1e45522017-04-30 19:47:14 +03005503 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
Ido Schimmel7179eb52017-03-16 09:08:18 +01005504}
5505
Ido Schimmelb1e45522017-04-30 19:47:14 +03005506int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5507 struct netdev_notifier_changeupper_info *info)
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005508{
Ido Schimmelb1e45522017-04-30 19:47:14 +03005509 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5510 int err = 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005511
Ido Schimmelb1e45522017-04-30 19:47:14 +03005512 if (!mlxsw_sp)
5513 return 0;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005514
Ido Schimmelb1e45522017-04-30 19:47:14 +03005515 switch (event) {
5516 case NETDEV_PRECHANGEUPPER:
5517 return 0;
5518 case NETDEV_CHANGEUPPER:
5519 if (info->linking)
5520 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5521 else
5522 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5523 break;
5524 }
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005525
Ido Schimmelb1e45522017-04-30 19:47:14 +03005526 return err;
Ido Schimmel3d70e4582017-03-16 09:08:19 +01005527}
5528
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005529static struct mlxsw_sp_rif_subport *
5530mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
Ido Schimmela1107482017-05-26 08:37:39 +02005531{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005532 return container_of(rif, struct mlxsw_sp_rif_subport, common);
Ido Schimmela1107482017-05-26 08:37:39 +02005533}
5534
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005535static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5536 const struct mlxsw_sp_rif_params *params)
5537{
5538 struct mlxsw_sp_rif_subport *rif_subport;
5539
5540 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5541 rif_subport->vid = params->vid;
5542 rif_subport->lag = params->lag;
5543 if (params->lag)
5544 rif_subport->lag_id = params->lag_id;
5545 else
5546 rif_subport->system_port = params->system_port;
5547}
5548
5549static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5550{
5551 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5552 struct mlxsw_sp_rif_subport *rif_subport;
5553 char ritr_pl[MLXSW_REG_RITR_LEN];
5554
5555 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5556 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
Petr Machata9571e822017-09-02 23:49:14 +02005557 rif->rif_index, rif->vr_id, rif->dev->mtu);
5558 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005559 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5560 rif_subport->lag ? rif_subport->lag_id :
5561 rif_subport->system_port,
5562 rif_subport->vid);
5563
5564 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5565}
5566
5567static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5568{
Petr Machata010cadf2017-09-02 23:49:18 +02005569 int err;
5570
5571 err = mlxsw_sp_rif_subport_op(rif, true);
5572 if (err)
5573 return err;
5574
5575 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5576 mlxsw_sp_fid_index(rif->fid), true);
5577 if (err)
5578 goto err_rif_fdb_op;
5579
5580 mlxsw_sp_fid_rif_set(rif->fid, rif);
5581 return 0;
5582
5583err_rif_fdb_op:
5584 mlxsw_sp_rif_subport_op(rif, false);
5585 return err;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005586}
5587
5588static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5589{
Petr Machata010cadf2017-09-02 23:49:18 +02005590 struct mlxsw_sp_fid *fid = rif->fid;
5591
5592 mlxsw_sp_fid_rif_set(fid, NULL);
5593 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5594 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005595 mlxsw_sp_rif_subport_op(rif, false);
5596}
5597
5598static struct mlxsw_sp_fid *
5599mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5600{
5601 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5602}
5603
5604static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5605 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5606 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5607 .setup = mlxsw_sp_rif_subport_setup,
5608 .configure = mlxsw_sp_rif_subport_configure,
5609 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5610 .fid_get = mlxsw_sp_rif_subport_fid_get,
5611};
5612
5613static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5614 enum mlxsw_reg_ritr_if_type type,
5615 u16 vid_fid, bool enable)
5616{
5617 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5618 char ritr_pl[MLXSW_REG_RITR_LEN];
5619
5620 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
Petr Machata9571e822017-09-02 23:49:14 +02005621 rif->dev->mtu);
5622 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005623 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5624
5625 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5626}
5627
5628static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5629{
5630 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5631}
5632
5633static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5634{
5635 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5636 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5637 int err;
5638
5639 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5640 if (err)
5641 return err;
5642
Ido Schimmel0d284812017-07-18 10:10:12 +02005643 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5644 mlxsw_sp_router_port(mlxsw_sp), true);
5645 if (err)
5646 goto err_fid_mc_flood_set;
5647
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005648 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5649 mlxsw_sp_router_port(mlxsw_sp), true);
5650 if (err)
5651 goto err_fid_bc_flood_set;
5652
Petr Machata010cadf2017-09-02 23:49:18 +02005653 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5654 mlxsw_sp_fid_index(rif->fid), true);
5655 if (err)
5656 goto err_rif_fdb_op;
5657
5658 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005659 return 0;
5660
Petr Machata010cadf2017-09-02 23:49:18 +02005661err_rif_fdb_op:
5662 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5663 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005664err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005665 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5666 mlxsw_sp_router_port(mlxsw_sp), false);
5667err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005668 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5669 return err;
5670}
5671
5672static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5673{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005674 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005675 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5676 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005677
Petr Machata010cadf2017-09-02 23:49:18 +02005678 mlxsw_sp_fid_rif_set(fid, NULL);
5679 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5680 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005681 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5682 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005683 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5684 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005685 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5686}
5687
5688static struct mlxsw_sp_fid *
5689mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5690{
5691 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5692
5693 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5694}
5695
5696static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5697 .type = MLXSW_SP_RIF_TYPE_VLAN,
5698 .rif_size = sizeof(struct mlxsw_sp_rif),
5699 .configure = mlxsw_sp_rif_vlan_configure,
5700 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5701 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5702};
5703
5704static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5705{
5706 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5707 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5708 int err;
5709
5710 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5711 true);
5712 if (err)
5713 return err;
5714
Ido Schimmel0d284812017-07-18 10:10:12 +02005715 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5716 mlxsw_sp_router_port(mlxsw_sp), true);
5717 if (err)
5718 goto err_fid_mc_flood_set;
5719
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005720 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5721 mlxsw_sp_router_port(mlxsw_sp), true);
5722 if (err)
5723 goto err_fid_bc_flood_set;
5724
Petr Machata010cadf2017-09-02 23:49:18 +02005725 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5726 mlxsw_sp_fid_index(rif->fid), true);
5727 if (err)
5728 goto err_rif_fdb_op;
5729
5730 mlxsw_sp_fid_rif_set(rif->fid, rif);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005731 return 0;
5732
Petr Machata010cadf2017-09-02 23:49:18 +02005733err_rif_fdb_op:
5734 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5735 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005736err_fid_bc_flood_set:
Ido Schimmel0d284812017-07-18 10:10:12 +02005737 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5738 mlxsw_sp_router_port(mlxsw_sp), false);
5739err_fid_mc_flood_set:
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005740 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5741 return err;
5742}
5743
5744static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5745{
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005746 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
Petr Machata010cadf2017-09-02 23:49:18 +02005747 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5748 struct mlxsw_sp_fid *fid = rif->fid;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005749
Petr Machata010cadf2017-09-02 23:49:18 +02005750 mlxsw_sp_fid_rif_set(fid, NULL);
5751 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5752 mlxsw_sp_fid_index(fid), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005753 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5754 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmel0d284812017-07-18 10:10:12 +02005755 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5756 mlxsw_sp_router_port(mlxsw_sp), false);
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005757 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5758}
5759
5760static struct mlxsw_sp_fid *
5761mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5762{
5763 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5764}
5765
5766static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5767 .type = MLXSW_SP_RIF_TYPE_FID,
5768 .rif_size = sizeof(struct mlxsw_sp_rif),
5769 .configure = mlxsw_sp_rif_fid_configure,
5770 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5771 .fid_get = mlxsw_sp_rif_fid_fid_get,
5772};
5773
Petr Machata6ddb7422017-09-02 23:49:19 +02005774static struct mlxsw_sp_rif_ipip_lb *
5775mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5776{
5777 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5778}
5779
5780static void
5781mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5782 const struct mlxsw_sp_rif_params *params)
5783{
5784 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5785 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5786
5787 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5788 common);
5789 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5790 rif_lb->lb_config = params_lb->lb_config;
5791}
5792
5793static int
5794mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5795 struct mlxsw_sp_vr *ul_vr, bool enable)
5796{
5797 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5798 struct mlxsw_sp_rif *rif = &lb_rif->common;
5799 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5800 char ritr_pl[MLXSW_REG_RITR_LEN];
5801 u32 saddr4;
5802
5803 switch (lb_cf.ul_protocol) {
5804 case MLXSW_SP_L3_PROTO_IPV4:
5805 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5806 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5807 rif->rif_index, rif->vr_id, rif->dev->mtu);
5808 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5809 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5810 ul_vr->id, saddr4, lb_cf.okey);
5811 break;
5812
5813 case MLXSW_SP_L3_PROTO_IPV6:
5814 return -EAFNOSUPPORT;
5815 }
5816
5817 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5818}
5819
5820static int
5821mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5822{
5823 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5824 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5825 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5826 struct mlxsw_sp_vr *ul_vr;
5827 int err;
5828
5829 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5830 if (IS_ERR(ul_vr))
5831 return PTR_ERR(ul_vr);
5832
5833 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5834 if (err)
5835 goto err_loopback_op;
5836
5837 lb_rif->ul_vr_id = ul_vr->id;
5838 ++ul_vr->rif_count;
5839 return 0;
5840
5841err_loopback_op:
5842 mlxsw_sp_vr_put(ul_vr);
5843 return err;
5844}
5845
5846static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5847{
5848 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5849 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5850 struct mlxsw_sp_vr *ul_vr;
5851
5852 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5853 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5854
5855 --ul_vr->rif_count;
5856 mlxsw_sp_vr_put(ul_vr);
5857}
5858
5859static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5860 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5861 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5862 .setup = mlxsw_sp_rif_ipip_lb_setup,
5863 .configure = mlxsw_sp_rif_ipip_lb_configure,
5864 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5865};
5866
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005867static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5868 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5869 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5870 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
Petr Machata6ddb7422017-09-02 23:49:19 +02005871 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005872};
5873
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005874static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5875{
5876 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5877
5878 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5879 sizeof(struct mlxsw_sp_rif *),
5880 GFP_KERNEL);
5881 if (!mlxsw_sp->router->rifs)
5882 return -ENOMEM;
Ido Schimmele4f3c1c2017-05-26 08:37:40 +02005883
5884 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5885
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005886 return 0;
5887}
5888
5889static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5890{
5891 int i;
5892
5893 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5894 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5895
5896 kfree(mlxsw_sp->router->rifs);
5897}
5898
Petr Machatadcbda282017-10-20 09:16:16 +02005899static int
5900mlxsw_sp_ipip_config_tigcr(struct mlxsw_sp *mlxsw_sp)
5901{
5902 char tigcr_pl[MLXSW_REG_TIGCR_LEN];
5903
5904 mlxsw_reg_tigcr_pack(tigcr_pl, true, 0);
5905 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tigcr), tigcr_pl);
5906}
5907
Petr Machata38ebc0f2017-09-02 23:49:17 +02005908static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5909{
5910 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
Petr Machata1012b9a2017-09-02 23:49:23 +02005911 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
Petr Machatadcbda282017-10-20 09:16:16 +02005912 return mlxsw_sp_ipip_config_tigcr(mlxsw_sp);
Petr Machata38ebc0f2017-09-02 23:49:17 +02005913}
5914
5915static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5916{
Petr Machata1012b9a2017-09-02 23:49:23 +02005917 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
Petr Machata38ebc0f2017-09-02 23:49:17 +02005918}
5919
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005920static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5921{
Ido Schimmel7e39d112017-05-16 19:38:28 +02005922 struct mlxsw_sp_router *router;
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005923
5924 /* Flush pending FIB notifications and then flush the device's
5925 * table before requesting another dump. The FIB notification
5926 * block is unregistered, so no need to take RTNL.
5927 */
5928 mlxsw_core_flush_owq();
Ido Schimmel7e39d112017-05-16 19:38:28 +02005929 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5930 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
Ido Schimmelc3852ef2016-12-03 16:45:07 +01005931}
5932
Ido Schimmel4724ba562017-03-10 08:53:39 +01005933static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5934{
5935 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5936 u64 max_rifs;
5937 int err;
5938
5939 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5940 return -EIO;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005941 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005942
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005943 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005944 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5945 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5946 if (err)
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005947 return err;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005948 return 0;
Ido Schimmel4724ba562017-03-10 08:53:39 +01005949}
5950
5951static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5952{
5953 char rgcr_pl[MLXSW_REG_RGCR_LEN];
Ido Schimmel4724ba562017-03-10 08:53:39 +01005954
Arkadi Sharshevskye29237e2017-07-18 10:10:09 +02005955 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005956 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
Ido Schimmel4724ba562017-03-10 08:53:39 +01005957}
5958
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005959int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5960{
Ido Schimmel9011b672017-05-16 19:38:25 +02005961 struct mlxsw_sp_router *router;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005962 int err;
5963
Ido Schimmel9011b672017-05-16 19:38:25 +02005964 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5965 if (!router)
5966 return -ENOMEM;
5967 mlxsw_sp->router = router;
5968 router->mlxsw_sp = mlxsw_sp;
5969
5970 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005971 err = __mlxsw_sp_router_init(mlxsw_sp);
5972 if (err)
Ido Schimmel9011b672017-05-16 19:38:25 +02005973 goto err_router_init;
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005974
Ido Schimmel348b8fc2017-05-16 19:38:29 +02005975 err = mlxsw_sp_rifs_init(mlxsw_sp);
5976 if (err)
5977 goto err_rifs_init;
5978
Petr Machata38ebc0f2017-09-02 23:49:17 +02005979 err = mlxsw_sp_ipips_init(mlxsw_sp);
5980 if (err)
5981 goto err_ipips_init;
5982
Ido Schimmel9011b672017-05-16 19:38:25 +02005983 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
Ido Schimmelc53b8e12017-02-08 11:16:30 +01005984 &mlxsw_sp_nexthop_ht_params);
5985 if (err)
5986 goto err_nexthop_ht_init;
5987
Ido Schimmel9011b672017-05-16 19:38:25 +02005988 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
Ido Schimmele9ad5e72017-02-08 11:16:29 +01005989 &mlxsw_sp_nexthop_group_ht_params);
5990 if (err)
5991 goto err_nexthop_group_ht_init;
5992
Ido Schimmel8494ab02017-03-24 08:02:47 +01005993 err = mlxsw_sp_lpm_init(mlxsw_sp);
5994 if (err)
5995 goto err_lpm_init;
5996
Jiri Pirkob45f64d2016-09-26 12:52:31 +02005997 err = mlxsw_sp_vrs_init(mlxsw_sp);
5998 if (err)
5999 goto err_vrs_init;
6000
Ido Schimmel8c9583a2016-10-27 15:12:57 +02006001 err = mlxsw_sp_neigh_init(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006002 if (err)
6003 goto err_neigh_init;
6004
Ido Schimmel7e39d112017-05-16 19:38:28 +02006005 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6006 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006007 mlxsw_sp_router_fib_dump_flush);
6008 if (err)
6009 goto err_register_fib_notifier;
6010
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006011 return 0;
6012
Ido Schimmelc3852ef2016-12-03 16:45:07 +01006013err_register_fib_notifier:
6014 mlxsw_sp_neigh_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006015err_neigh_init:
6016 mlxsw_sp_vrs_fini(mlxsw_sp);
6017err_vrs_init:
Ido Schimmel8494ab02017-03-24 08:02:47 +01006018 mlxsw_sp_lpm_fini(mlxsw_sp);
6019err_lpm_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006020 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
Ido Schimmele9ad5e72017-02-08 11:16:29 +01006021err_nexthop_group_ht_init:
Ido Schimmel9011b672017-05-16 19:38:25 +02006022 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Ido Schimmelc53b8e12017-02-08 11:16:30 +01006023err_nexthop_ht_init:
Petr Machata38ebc0f2017-09-02 23:49:17 +02006024 mlxsw_sp_ipips_fini(mlxsw_sp);
6025err_ipips_init:
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006026 mlxsw_sp_rifs_fini(mlxsw_sp);
6027err_rifs_init:
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006028 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006029err_router_init:
6030 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006031 return err;
6032}
6033
6034void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6035{
Ido Schimmel7e39d112017-05-16 19:38:28 +02006036 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006037 mlxsw_sp_neigh_fini(mlxsw_sp);
6038 mlxsw_sp_vrs_fini(mlxsw_sp);
Ido Schimmel8494ab02017-03-24 08:02:47 +01006039 mlxsw_sp_lpm_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006040 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6041 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
Petr Machata38ebc0f2017-09-02 23:49:17 +02006042 mlxsw_sp_ipips_fini(mlxsw_sp);
Ido Schimmel348b8fc2017-05-16 19:38:29 +02006043 mlxsw_sp_rifs_fini(mlxsw_sp);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006044 __mlxsw_sp_router_fini(mlxsw_sp);
Ido Schimmel9011b672017-05-16 19:38:25 +02006045 kfree(mlxsw_sp->router);
Jiri Pirkob45f64d2016-09-26 12:52:31 +02006046}